Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.775
diff -u -r1.775 user.module
--- modules/user/user.module	24 Apr 2007 13:53:15 -0000	1.775
+++ modules/user/user.module	30 Apr 2007 06:15:59 -0000
@@ -801,13 +801,19 @@
     'access callback' => 'user_is_anonymous',
     'type' => MENU_LOCAL_TASK,
   );
-  $items['user/reset/%/%/%'] = array(
+  $items['user/reset-password/%/%/%'] = array(
     'title' => t('Reset password'),
     'page callback' => 'drupal_get_form',
     'page arguments' => array('user_pass_reset', 2, 3, 4),
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  $items['user/change-mail'] = array(
+    'title' => t('Change e-mail'),
+    'callback' => 'user_change_mail',
+    'access' => TRUE,
+    'type' => MENU_CALLBACK
+  );
   $items['user/help'] = array(
     'title' => t('Help'),
     'page callback' => 'user_help_page',
@@ -1250,7 +1256,7 @@
           $form['message'] = array('#value' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date</p><p>Click on this button to login to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
           $form['help'] = array('#value' => '<p>'. t('This login can be used only once.') .'</p>');
           $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
-          $form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
+          $form['#action'] = url("user/reset-password/$uid/$timestamp/$hashed_pass/login");
           return $form;
         }
       }
@@ -1269,7 +1275,7 @@
 
 function user_pass_reset_url($account) {
   $timestamp = time();
-  return url("user/reset/$account->uid/$timestamp/". user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
+  return url("user/reset-password/$account->uid/$timestamp/". user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE);
 }
 
 function user_pass_rehash($password, $timestamp, $login) {
@@ -1544,6 +1550,89 @@
   if (isset($edit['roles'])) {
     $edit['roles'] = array_filter($edit['roles']);
   }
+  if ($user->mail != $edit['mail'] && !user_access('administer users')) {
+    $timestamp = time();
+    $pass = $edit['pass'] ? md5($edit['pass']) : $user->pass;
+    $hash = user_email_rehash($pass, $edit['mail']);
+    $url = url("user/change-mail/$user->uid/$timestamp/". $edit['mail'] ."/$hash", NULL, NULL, TRUE);
+    $variables = array('!username' => $user->name, '!site' => variable_get('site_name', 'drupal'), '!email_url' => $url);
+    $subject = _user_mail_text('mail_change_subject', $variables);
+    $body = _user_mail_text('mail_change_body', $variables);
+    $from = variable_get('site_mail', ini_get('sendmail_from'));
+    if ($mail_success = drupal_mail('user_change_mail', $edit['mail'], $subject, $body, $from)) {
+      $body = _user_mail_text('mail_change_original_body', $variables);
+      drupal_mail('user_change_mail_original', $user->mail, $subject, $body, $from);
+      drupal_set_message(t('You will receive a confirmation e-mail at your new e-mail address shortly. You must follow the link provided in that e-mail within one day in order to complete your change.'));
+    }
+    unset($edit['mail']);
+  }
+}
+
+function user_email_rehash($pass, $mail) {
+  return md5($pass . $mail);
+}
+
+/**
+ * Menu callback; process one time email change confirm and redirects to the user page on success.
+ */
+function user_change_mail($uid, $timestamp, $new_mail, $hash, $action = '') {
+  global $user;
+
+  $account = user_load(array('uid' => $uid));
+
+  // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds.
+  $timeout = 86400;
+  $current = time();
+  // Some redundant checks for extra security ?
+  if ($timestamp < $current && $account = user_load(array('uid' => $uid, 'status' => 1)) ) {
+    if ($current - $timestamp > $timeout) {
+      drupal_set_message(t('You have tried to use a one-time e-mail change link for %account that has expired--your change of e-mail request was not completed. Please visit your account edit page if you wish to attempt the change again.', array('%account' => $account->name)), 'error');
+      if ($account->uid == $user->uid) {
+        drupal_goto("user/$account->uid/edit");
+      }
+      else {
+        drupal_goto('');
+      }
+    }
+    else if ($user->uid && $user->uid != $account->uid) {
+      drupal_set_message(t('You are currently logged in as %user, and are attempting to confirm an e-mail change for %account, which is not allowed. Please log in as %account and initiate a new change of e-mail request.', array('%user' => $user->name, '%account' => $account->name)), 'error');
+      drupal_goto('');
+    }
+    else if ($hash != user_email_rehash($account->pass, $new_mail)) {
+      drupal_set_message(t('There was a problem verifying your change of e-mail request--please visit your account edit page and attempt the change again'), 'error');
+      if ($user->uid) {
+        drupal_goto("user/$user->uid/edit");
+      }
+      else {
+        drupal_goto('user/login', "destination=user/$user->uid/edit");
+      }
+    }
+    else if ($timestamp > $account->login && $timestamp < $current) {
+      watchdog('user', t('User %name used one-time e-mail change link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp)));
+      db_query("UPDATE {users} SET mail = '%s' WHERE uid = %d", $new_mail, $account->uid);
+      drupal_set_message(t('Your e-mail address is now %mail.', array('%mail' => $new_mail)));
+      if ($user->uid) {
+        drupal_goto('user/' . $user->uid);
+      }
+      else {
+        drupal_goto('user');
+      }
+    }
+    else {
+      drupal_set_message(t('You have tried to use a one-time e-mail change link which has either been used or has expired. Please request a new one.'), 'error');
+      if ($user->uid) {
+        drupal_goto('user/'. $user->uid .'/edit');
+      }
+      else {
+        drupal_goto('user/login', 'destination=user/'. $user->uid .'/edit');
+      }
+    }
+  }
+  else {
+    // Deny access, no more clues.
+    // Everything will be in the watchdog's URL for the administrator to check.
+    drupal_access_denied();
+  }
 }
 
 function user_edit($category = 'account') {
@@ -1688,6 +1777,12 @@
         return t('Replacement login information for !username at !site', $variables);
       case 'pass_body':
         return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables);
+      case 'mail_change_subject':
+        return t('E-mail change information for !username at !site', $variables);
+      case 'mail_change_body':
+        return t("!username,\n\nA request to change your e-mail address has been made at !site. You need to verify the change by clicking on the link below or copying and pasting it in your browser:\n\n!email_url\n\nThis is a one-time URL, so it can be used only once. It expires after one day. If not used, your e-mail address at !site will not change.", $variables);
+      case 'mail_change_original_body':
+        return t("!username,\n\nA request to change your e-mail address has been made at !site. In order to complete the change you will need to follow the instructions sent to your new e-mail address within one day.", $variables);
     }
   }
 }
