<?php
// $Id: multiple_email.module,v 1.1.2.3 2008/01/07 18:14:57 joshbenner Exp $

/**
 * @file
 * multiple_email module file
 */

/**
 * Implementation of hook_help().u
 */
function multiple_email_help($section, $arg) {
  switch ($section) {
    case 'admin/help#multiple_email':
    case 'admin/help/multiple_email':
      $path = drupal_get_path('module', 'multiple_email') ."/README.txt";
      return filter_filter('process', 2, NULL, file_get_contents($path) );
      break;
  }
}

/**
 * Implementation of hook_init()
 */
function multiple_email_enable() {
  // Add each variable and its default value to this array
  $vars = array(
    'confirm_deadline' => 5,
    'confirm_attempts' => 3,
    'hide_field' => 1,
  );

  // Specify every message type to have its defaults initialized
  $messages = array(
    'confirmation',
    'expire'
   );
   $parts = array(
    'subject',
    'body',
   );
  foreach ($messages as $type) {
    foreach ($parts as $part) {
      $func = "multiple_email_default_{$part}";
      $vars["{$type}_{$part}"] = $func($type);
    }
  }

  foreach ($vars as $var => $default) {
    $current = variable_get("multiple_email_$var", $default);
    variable_set("multiple_email_$var", $current);
  }

  drupal_set_message(t("Multiple Email settings are available under !link",
    array( '!link' => l('Administer > Site configuration > Multiple Email Settings ',  'admin/settings/multiple-email/settings' ) )
  ));
}

/**
 * Implementation of hook_perm()
 *
 */
function multiple_email_perm() {
  return array('multiple emails', 'administer multiple emails');
}

/**
 * Implementation of hook_menu()
 *
 * @param boolean $may_cache
 * @return array
 */
function multiple_email_menu() {
  $items = array();

 $items['admin/settings/multiple-email'] = array(
    'title' => 'Multiple Email Settings',
    'description' => 'Control behavior of the Multiple Email Addresses module',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('multiple_email_admin_settings'),
    'access callback' => user_access('administer site configuration'),
  );
  
  $items['user/%user/edit/email-addresses/view'] = array(
    'title' => 'View Email Address',
    'page callback' => 'multiple_email_addresses_page',
    'page arguments' => array(1, 'email-addresses'),
    'access callback' => 'multiple_email_access_pages',
    'access arguments' => array(1),
    'file' => 'multiple_email_addresses_page.inc',
    'type' => MENU_CALLBACK,
  );  
    
  $items['user/%user/edit/email-addresses/add'] = array(
    'title' => 'Add Email Address',
    'page callback' => 'multiple_email_add_page',
    'page arguments' => array(1, 'email-addresses'),
    'access callback' => 'multiple_email_access_pages',
    'access arguments' => array(1),
    'file' => 'multiple_email_add_page.inc',
    'type' => MENU_CALLBACK,
  );

  $items['user/%user/edit/email-addresses/confirm'] = array(
    'title' => 'Confirm Email Address',
    'page callback' => 'multiple_email_confirm_page',
    'page arguments' => array(1),
    'access callback' => 'multiple_email_confirm_access',
    'access arguments' => array(1, 5),
    'file' => 'multiple_email_confirm_page.inc',
    'type' => MENU_CALLBACK,
  );

  $items['user/%user/edit/email-addresses/primary'] = array(
    'title' => 'Make Email Primary Address',
    'page callback' => 'multiple_email_primary_page',
    'page arguments' => array(1),
    'access callback' => 'multiple_email_primary_access',
    'access arguments' => array(1, 5),
    'file' => 'multiple_email_primary_page.inc',
    'type' => MENU_CALLBACK,
  );

  $items['user/%user/edit/email-addresses/delete'] = array(
    'title' => 'Delete Email Address',
    'page callback' => 'multiple_email_delete_page',
    'page arguments' => array(1),
    'access callback' => 'multiple_email_delete_access',
    'access arguments' => array(1, 5),
    'file' => 'multiple_email_delete_page.inc',
    'type' => MENU_CALLBACK,
  );
   
  return $items;
}

function multiple_email_confirm_access($account, $eid) {
  global $user;

  $email = multiple_email_get_address($eid);
  if($email->uid == $account->uid && ((user_access('multiple emails') == TRUE && $account->uid == $user->uid) || (user_access('administer multiple emails') == TRUE))) {
    return TRUE;
  }

  return FALSE;
}

function multiple_email_primary_access($account, $eid) {
  global $user;

  $email = multiple_email_get_address($eid);
  if($email->primary_address == 0 && $email->uid == $account->uid && ((user_access('multiple emails') == TRUE && $account->uid == $user->uid && $email->confirmed == 1) || (user_access('administer multiple emails') == TRUE))) {
    return TRUE;
  }

  return FALSE;
}

function multiple_email_delete_access($account, $eid) {
  global $user;

  $email = multiple_email_get_address($eid);
  if($email->primary_address == 0 && $email->uid == $account->uid && ((user_access('multiple emails') == TRUE && $account->uid == $user->uid) || (user_access('administer multiple emails') == TRUE))) {
    return TRUE;
  }

  return FALSE;
}

function multiple_email_access_pages($account) {
  global $user;
  
  if((user_access('multiple emails') == TRUE && $account->uid == $user->uid) || user_access('administer multiple emails') == TRUE) {
    return TRUE;
  } else {
    return FALSE;
  }
}

function multiple_email_menu_alter(&$items) {
    $item = &$items['user/%user_category/edit/email-addresses'];
    $item = array(
      'page callback' => 'multiple_email_addresses_page',
      'page arguments' => array(1, 'email-addresses'),
      'access callback' => 'multiple_email_access_pages',
      'file' => 'multiple_email_addresses_page.inc',
      'file path' => drupal_get_path('module', 'multiple_email'),
    ) + $item;
}

/**
 * Implementation of hook_user()
 *
 * @param string $op
 * @param array $edit
 * @param object $account
 * @param string $category
 * @return mixed
 */
function multiple_email_user($op, &$edit, &$account, $category = NULL) {
  if($op == 'categories') {
    return array(array('name' => 'email-addresses', 'title' => 'E-Mail addresses', 'weight' => 100));
  }

  if($category == 'email-addresses') {
    switch ($op) {
      case 'form':
        $form = array();
        return $form;     
        break;
    }
  }
}

/**
 * Implementation of hook_form_alter()
 *
 * Remove email field from profile edit -- this will be done in address
 * management screen, now.
 *
 * @param string $form_id
 * @param array $form
 * @return array
 */
function multiple_email_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'user_edit' && variable_get('multiple_email_hide_field', TRUE)) {
    // We can't remove the email field, but bcb_user_user()'s submit op
    //   hook will catch the mail field and remove it before it's sent
    //   to the database. So instead, we hide it.
    $form['account']['mail']['#prefix'] = '<div style="display:none;">';
    $form['account']['mail']['#suffix'] = '</div>';
    // TODO: Put admin-only fields here for changing primary email
    // ... or maybe a separate admin email management interface
  }
  
  if ($form_id == 'multiple_email_primary_form' || $form_id == 'multiple_email_delete_form') {
    // The "Cancel" button action.
    if ($form_state['post']['cancel'] || $form_state['clicked_button']['#name'] == 'cancel') {
      // Define an path for redirect    
      drupal_goto('user/'. $form['account']['#value']->uid. '/edit/email-addresses');
      return;
    }
 
    // "Submit" button weight
    $form['submit']['#weight'] = 50;
 
    // "Preview" button weight
    $form['delete']['#weight'] = 50;
 
    // Add the "Cancel" button
    $form['cancel'] = array(
      '#type' => 'submit',
      '#name' => 'cancel',
      '#value' => t('Cancel'),
      '#weight' => $form['submit']['#weight'] + 1,
    );
  }
}

/**
 * Implementation of hook_cron()
 *
 */
function multiple_email_cron() {
  $deadline = (int)variable_get('multiple_email_confirm_deadline', 5);
  if ($deadline) {
    $result = db_query("
      SELECT 
        e.eid,
        e.time_code_generated,
        IF(u.mail = e.email, 1, 0) as primary_address 
      FROM 
        {multiple_email} e
        INNER JOIN {users} u ON (u.uid = e.uid)
      WHERE confirmed=0");
    $now = time();
    while ($row = db_fetch_object($result)) {
      if (strtotime("+$deadline days", $row->time_code_generated) <= $now && !$row->primary_address) {
        multiple_email_expire_address($row->eid);
      }
    }
  }
}

/**
 * Settings form for site configuration section
 *
 * @ingroup forms
 */
function multiple_email_admin_settings() {
  $form['multiple_email_hide_field'] = array(
    '#type' => 'select',
    '#title' => t('Hide Email Field'),
    '#description' => t('Hides the email field when editing a user'),
    '#options' => array('No', 'Yes'),
    '#default_value' => variable_get('multiple_email_hide_field', 1),
  );
  $form['multiple_email_confirm_attempts'] = array(
    '#type' => 'textfield',
    '#size' => 4,
    '#title' => t('Confirm Attempts'),
    '#description' => t('How many times a user enters a confirmation code before a new one is generated. If set to 0, no new codes are sent after the first one.'),
    '#default_value' => variable_get('multiple_email_confirm_attempts', 3),
  );
  $form['multiple_email_confirm_deadline'] = array(
    '#type' => 'textfield',
    '#size' => 4,
    '#title' => t('Confirm Days'),
    '#description' => t('How many days a user has to enter a confirmation code. If 0, emails pending confirmation do not expire.'),
    '#default_value' => variable_get('multiple_email_confirm_deadline', 5),
  );
  $vars = '!username, !site, !email, !confirm_code, !confirm_url, !confirm_deadline';
  $form['multiple_email_confirmation_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Confirmation Email Subject'),
    '#description' => t('Customize the subject of the message to be sent when a user adds a new email to their account.')
      . '<br/>'
      . t('Available variables are:')
      . $vars,
    '#default_value' => variable_get('multiple_email_confirmation_subject', multiple_email_default_subject('confirmation')),
  );
  $form['multiple_email_confirmation_body'] = array(
    '#type' => 'textarea',
    '#title' => t('Confirmation Email Body'),
    '#description' => t('Customize the body of the message to be sent when a user adds a new email to their account.')
      . '<br/>'
      . t('Available variables are:')
      . $vars,
    '#default_value' => variable_get('multiple_email_confirmation_body', multiple_email_default_body('confirmation')),
  );
  $form['multiple_email_expire_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Expire Email Subject'),
    '#description' => t('Customize the subject of the message to be sent when an unconfirmed email address expires.')
      . '<br/>'
      . t('Available variables are:')
      . $vars,
    '#default_value' => variable_get('multiple_email_expire_subject', multiple_email_default_subject('expire')),
  );
  $form['multiple_email_expire_body'] = array(
    '#type' => 'textarea',
    '#title' => t('Expire Email Body'),
    '#description' => t('Customize the body of the message to be sent when an unconfirmed email address expires.')
      . '<br/>'
      . t('Available variables are:')
      . $vars,
    '#default_value' => variable_get('multiple_email_expire_body', multiple_email_default_body('expire')),
  );

  return system_settings_form($form);
}

/**
 * Validation for multiple_email_admin_settings
 *
 * @param string $form_id
 * @param array $form_values
 */
function multiple_email_admin_settings_validate($form, &$form_state) {
  if (!is_numeric($form_state['values']['multiple_email_confirm_attempts'])) {
    form_set_error('multiple_email_confirm_attempts', 'Confirm attempts must be an number!');
  }
  if (!is_numeric($form_state['values']['multiple_email_confirm_deadline'])) {
    form_set_error('multiple_email_confirm_deadline', 'Confirm Days must be an number!');
  }
}

/**
 * Returns an array of information about the specified user's associated email
 * addresses.
 *
 * Index 0 contains an associative array of all the addresses in
 * eid=>addresses format. Subsequent indexes are the eid of the address,
 * then an object of properties corresponding to columns in the table.
 *
 * @param integer $uid
 * @param array $tablesortHeaders
 * @return array
 */
function multiple_email_load_addresses($uid, $tablesortHeaders = null) {
  $results = db_query("
    SELECT
      a.eid,
      a.uid,
      a.email,
      a.time_registered,
      a.confirmed,
      a.confirm_code,
      a.time_code_generated,
      a.attempts,
      IF(a.email = u.mail, 1, 0) AS primary_address
    FROM
      {multiple_email} a
    INNER JOIN {users} u ON (u.uid = a.uid)
    WHERE
      a.uid = %d"
      . ($tablesortHeaders ? ' ' . tablesort_sql($tablesortHeaders) : ''),
    $uid
  );

  while ($row = db_fetch_object($results)) {
    $addresses[0][] = $row->email;
    $addresses[$row->eid] = $row;
  }
  
  return $addresses;
}

/**
 * Loads a single address from the email registry and returns it as an object.
 *
 * @param integer $eid
 * @return object
 */
function multiple_email_get_address($eid) {
  $result = db_query("
    SELECT
      a.eid,
      a.uid,
      a.email,
      a.time_registered,
      a.time_registered,
      a.confirmed,
      a.confirm_code,
      a.time_code_generated,
      a.attempts,
      IF(a.email = u.mail, 1, 0) AS primary_address
    FROM
      {multiple_email} a
    INNER JOIN {users} u ON (u.uid = a.uid)
    WHERE
      a.eid = %d",
    $eid
  );

  return db_fetch_object($result);
}

/**
 * Finds the specified email and returns an object containing its data.
 * Returns false if email cannot be found.
 *
 * @param string $email
 * @return mixed
 */
function multiple_email_find_address($email) {
  $result = db_query("SELECT eid FROM {multiple_email} WHERE email = '%s'", $email);
  while($row = db_fetch_array($result)) {
    return multiple_email_get_address($row['eid']);
  }

  return FALSE;
}

/**
 * Adds an unconfirmed email address to the email registry.
 *
 * If you specify the 3rd parameter to be true, the email address will be
 * recorded as having been confirmed.
 *
 * Returns the email's eid on success, false on failure.
 *
 * @param integer $uid
 * @param string $email
 * @param boolean $confirmed
 * @return mixed
 */
function multiple_email_register_email($uid, $email, $confirmed = false) {
  $success = db_query("
    INSERT INTO {multiple_email}
      (`uid`,`email`,`time_registered`,`confirmed`,`confirm_code`,`time_code_generated`)
    VALUES
      (%d, '%s', %d, %d, '%s', %d)",
    $uid,
    $email,
    time(),
    (int)$confirmed,
    $code = multiple_email_code(10),
    time()
  );

  if ($success !== false) {
    $result = db_query("SELECT LAST_INSERT_ID() AS eid");
    $r = db_fetch_array($result);
    $email = multiple_email_get_address($r['eid']);
    module_invoke_all('multiple_email_register', $email);
    return $r['eid'];
  } else {
    return false;
  }
}

/**
 * Marks an email address as confirmed in the email registry
 *
 * @param object $email
 *  Email row object to confirm
 * @return void
 */
function multiple_email_confirm_email($email) {
  watchdog('Multiple Email', t("Marking address '!email' confirmed for user !uid", array('!email'=>$email->email,'!uid'=>$email->uid)), l(t('edit'), "user/$uid"));
  db_query("
    UPDATE {multiple_email} SET
      confirmed = 1
    WHERE
      eid = %d",
    $email->eid
  );
  $email->confirmed = 1;
  module_invoke_all('multiple_email_confirm', $email);
}

/**
 * Generates a random string of given length from given characters.
 *
 * If no characters are specified, then it uses a-zA-Z0-9. Characters are
 * specified as a string containing every valid character. Duplicates will
 * (in theory) increase that character's chances of occurring in the random
 * string.
 *
 * @param integer $length
 * @param string $chars
 * @return string
 */
function multiple_email_code($length, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUBWXYZ0123456789') {
  $cl = strlen($chars) - 1;
  $out = '';
  for ($i=0; $i < $length; $i++) {
    $out .= $chars[rand(0, $cl)];
  }
  return $out;
}

/**
 * Deletes specified email from registry - no error checking!
 *
 * @param integer $eid
 * @return mixed
 */
function multiple_email_delete_email($eid) {
  return db_query("DELETE FROM {multiple_email} WHERE eid=%d", $eid);
}

/**
 * Changes specified user's primary email - no error checking!
 *
 * @param object $account
 * @param object $email
 */
function multiple_email_make_primary($email) {
  $account = new stdClass();
  $account->uid = $email->uid;
  user_save($account, array('mail' => $email->email));
}

/**
 * Sends the confirmation email for the specified address to the specified
 * user account.
 *
 * @param object $account
 * @param object $email
 */
function multiple_email_send_confirmation($account, $email) {
  global $language;

  $params = array(
    'headers' => array(),
    'subject' => multiple_email_message_part('subject', 'confirmation', $account, $email),
    'body' => multiple_email_message_part('body', 'confirmation', $account, $email)
  );
  
  $from = variable_get('site_mail', ini_get('sendmail_from'));
  drupal_mail('multiple_email', 'confirmation', $email->email, $language, $params, $from);
}

function multiple_email_mail($key, &$message, $params) {

  switch($key) {
  case 'confirmation':
    $message['subject'] = $params['subject'];
    $message['body'][] = $params['body'];
    $message['headers'] = array_merge($message['headers'], $params['headers']);
    break;
  }
  
}



//  drupal_mail($module, $key, $to, $language, $params = array(), $from = NULL, $send = TRUE)

/**
 * Removes the specified address from the user who added it and sends their
 * primary email account a message notifying them about the expiration.
 *
 * @param integer $eid
 */
function multiple_email_expire_address($eid) {
  global $lanugage;
  
  $email = multiple_email_get_address($eid);
  $account = user_load(array('uid'=>$email->uid));
  $params = array(
    'subject' => multiple_email_message_part('subject', 'expire', $account, $email),
    'body' => multiple_email_message_part('body', 'expire', $account, $email)
  );  
  
  multiple_email_delete_email($eid);
  $form = variable_get('site_mail', ini_get('sendmail_from'));
  
  drupal_mail('multiple_email_expire', 'expire_address_mail', $account->mail, $language, $params, $from);
  watchdog('Multiple Email', "{$email->email} ({$email->eid}) for {$account->name} ({$account->uid}) has expired and been removed");
}

function multiple_email_expire_address_mail($key, &$message, $params) {
  $message['subject'] = $params['subject'];
  $message['body'][] = $params['body'];
  $message['headers'] = array_merge($message['headers'], $params['headers']);
}

/**
 * Returns the part of the specified email message based on site settings.
 *
 * @param string $part (subject or body)
 * @param string $type
 * @param object $account
 * @param object $email
 * @return string
 */
function multiple_email_message_part($part, $type, $account, $email) {
  $func = "multiple_email_default_{$part}";
  $setting = variable_get("multiple_email_{$type}_{$part}", $func($type));
  return multiple_email_var_replace($setting, $account, $email);
}

/**
 * Returns the module's default subject for the specified message type
 *
 * @param string $type
 * @return string
 */
function multiple_email_default_subject($type) {
  $subjects = array(
    'confirmation' => 'Confirm your email address at !site',
    'expire' => 'Your email address at !site has expired',
  );
  return $subjects[$type];
}

/**
 * Returns the module's default message for the specified message type
 *
 * @param string $type
 * @return string
 */
function multiple_email_default_body($type) {
  $func = "multiple_email_default_{$type}_body";
  return $func();
}

/**
 * Replaces various tokens with their value
 *
 * Valid tokens:
 * !username (User's username)
 * !site (Site's name)
 * !email (Email address in question)
 * !confirm_code (Email's confirmation code)
 * !confirm_url (URL to confirmation page for address)
 * !confirm_deadline (Human-readable time within which to confirm)
 *
 * @param string $text
 * @param object $account
 * @param object $email
 * @return string
 */
function multiple_email_var_replace($text, $account, $email) {
  $vars = array(
    '!username' => $account->name,
    '!email' => $email->email,
    '!site' => variable_get('site_name', 'our web site'),
    '!confirm_code' => $email->confirm_code,
    '!confirm_url' => url('user/'. $email->uid . '/edit/email-addresses/confirm/'. $email->eid .'/'. $email->confirm_code, array('absolute' => TRUE)),
    '!confirm_deadline' => variable_get('multiple_email_confirm_deadline', 5) . ' days',
  );
  return str_ireplace(array_keys($vars), array_values($vars), $text);
}

/**
 * Returns the default confirmation message for use upon installation or if
 * for some reason the variable doesn't exist.
 *
 * @return string
 */
function multiple_email_default_confirmation_body() {
  $message = <<<END_MESSAGE
!username,

You have added the email address '!email' to your account at !site. In order
to complete the registration of this email, you must confirm it by clicking
the link below and entering this confirmation code: !confirm_code

!confirm_url

If the web address does not appear as a link, you must copy the address out of
this email, and paste it into the address bar of your web browser.

If you do not confirm this email in !confirm_deadline, it will be unregistered
from your account.
END_MESSAGE;

  return $message;
}

/**
 * Returns the default message sent to a user when they have failed to confirm
 * an email address within the deadline.
 *
 * @return string
 */
function multiple_email_default_expire_body() {
  $message = <<<END_MESSAGE
!username,

You have failed to confirm the the email address '!email' within the
confirmation period of !confirm_deadline. Therefore, the email address
has been removed from your account.

You may add this address again, but you must confirm the address within the
specified deadline!
END_MESSAGE;

  return $message;
}