Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.685
diff -u -F^f -r1.685 user.module
--- modules/user/user.module	4 Oct 2006 06:20:38 -0000	1.685
+++ modules/user/user.module	8 Oct 2006 06:11:03 -0000
@@ -704,6 +704,8 @@ function user_menu($may_cache) {
       'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass'), 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK);
     $items[] = array('path' => 'user/reset', 'title' => t('reset password'),
       'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass_reset'), 'access' => TRUE, 'type' => MENU_CALLBACK);
+    $items[] = array('path' => 'user/change/mail', 'title' => t('change email'),
+      'callback' => 'user_change_mail', 'access' => TRUE, 'type' => MENU_CALLBACK);
     $items[] = array('path' => 'user/help', 'title' => t('help'),
       'callback' => 'user_help_page', 'type' => MENU_CALLBACK);
 
@@ -1123,9 +1125,10 @@ function user_pass_reset($uid, $timestam
   }
 }
 
+
 function user_pass_reset_url($account) {
   $timestamp = time();
-  return url("user/reset/$account->uid/$timestamp/".user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE);
+  return url("user/reset/$account->uid/$timestamp/". user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE);
 }
 
 function user_pass_rehash($password, $timestamp, $login) {
@@ -1372,6 +1375,78 @@ function _user_edit_submit($uid, &$edit)
   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 = $user->name ."<$user->mail>";
+    $headers = array('X-Mailer' => 'Drupal', 'Return-path' => $from);
+    if ($mail_success = drupal_mail('mail_change_mail_send', $edit['mail'], $subject, $body, $from, $headers)) {
+      drupal_set_message(t('Please check your new email address for confirmation and follow the link provided to complete the 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 edit link that has expired. Please request a new one using the form below.'), 'error');
+      drupal_goto('user/password');
+    }
+    else if ($user->uid && $user->uid != $account->uid) {
+      drupal_set_message(t('The account identification numbers did not match'), 'error');
+      drupal_goto('user/'. $user->uid .'/edit');
+    }
+    else if ($hash != user_email_rehash($account->pass, $new_mail)) {
+      drupal_set_message(t('There was a problem with this one time link. Please try again'), 'error');
+      drupal_goto('user/'. $user->uid .'/edit');
+    }
+    else if ($timestamp > $account->login && $timestamp < $current) {
+      watchdog('user', t('User %name used one-time email 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 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 edit link which has either been used or is no longer valid. 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') {
@@ -1523,6 +1598,10 @@ function _user_mail_text($messageid, $va
         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('Email 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 this link 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 it is not used, your e-mail address change will not be processed.\n\n", $variables);
     }
   }
 }
