Index: modules/node/node.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tokens.inc,v
retrieving revision 1.2
diff -u -r1.2 node.tokens.inc
--- modules/node/node.tokens.inc	23 Aug 2009 13:02:38 -0000	1.2
+++ modules/node/node.tokens.inc	28 Sep 2009 14:10:20 -0000
@@ -97,8 +97,8 @@
 function node_tokens($type, $tokens, array $data = array(), array $options = array()) {
   $url_options = array('absolute' => TRUE);
   if (isset($options['language'])) {
-    $url_options['language'] = $language;
-    $language_code = $language->language;
+    $url_options['language'] = $options['language'];
+    $language_code = $options['language']->language;
   }
   else {
     $language_code = NULL;
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.80
diff -u -r1.80 user.admin.inc
--- modules/user/user.admin.inc	19 Sep 2009 11:18:32 -0000	1.80
+++ modules/user/user.admin.inc	28 Sep 2009 14:10:21 -0000
@@ -406,7 +406,7 @@
   );
   // These email tokens are shared for all settings, so just define
   // the list once to help ensure they stay in sync.
-  $email_token_help = t('Available variables are:') . ' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url, !cancel_url.';
+  $email_token_help = t('Available variables are:') . ' [user:name], [user:mail], [user:edit-url], [site:name], [site:url], [site:login-url], [user:password], [user:cancel-url], [user:one-time-login-url], [site:url], [site:uri-brief], [site:login-url].';
 
   $form['email_admin_created'] = array(
     '#type' => 'fieldset',
@@ -765,7 +765,7 @@
   if ($form_state['values']['name']) {
     if ($form_state['values']['op'] == t('Save role')) {
       $role = user_role_load($form_state['values']['name']);
-      if ($role && $role->rid != $form_state['values']['rid']) {    
+      if ($role && $role->rid != $form_state['values']['rid']) {
         form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_state['values']['name'])));
       }
     }
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1053
diff -u -r1.1053 user.module
--- modules/user/user.module	25 Sep 2009 15:14:18 -0000	1.1053
+++ modules/user/user.module	28 Sep 2009 14:10:23 -0000
@@ -2057,7 +2057,7 @@
  *   The user account of the profile being viewed.
  *
  * To theme user profiles, copy modules/user/user-profile.tpl.php
- * to your theme directory, and edit it as instructed in that file's comments. 
+ * to your theme directory, and edit it as instructed in that file's comments.
  *
  * @param $account
  *   A user object.
@@ -2072,7 +2072,7 @@
   $build = $account->content;
   // We don't need duplicate rendering info in account->content.
   unset($account->content);
-  
+
   $build += array(
     '#theme' => 'user_profile',
     '#account' => $account,
@@ -2103,7 +2103,7 @@
  */
 function user_mail($key, &$message, $params) {
   $language = $message['language'];
-  $variables = user_mail_tokens($params['account'], $language);
+  $variables = array('user' => $params['account']);
   $message['subject'] .= _user_mail_text($key . '_subject', $language, $variables);
   $message['body'][] = _user_mail_text($key . '_body', $language, $variables);
 }
@@ -2114,64 +2114,62 @@
  * Used by user_mail() and the settings forms to retrieve strings.
  */
 function _user_mail_text($key, $language = NULL, $variables = array()) {
-  $langcode = isset($language) ? $language->language : NULL;
-
   if ($admin_setting = variable_get('user_mail_' . $key, FALSE)) {
     // An admin setting overrides the default string.
-    return strtr($admin_setting, $variables);
+    return token_replace($admin_setting, $variables, array('language' => $language, 'callback' => 'user_mail_tokens'));
   }
   else {
     // No override, return default string.
     switch ($key) {
       case 'register_no_approval_required_subject':
-        return t('Account details for !username at !site', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account details for [user:name] at [site:name]'), $variables, array('language' => $language));
       case 'register_no_approval_required_body':
-        return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by 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.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nThank you for registering at [site:name]. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: [user:password]\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n--  [site:name] team"), $variables, array('language' => $language));
       case 'register_admin_created_subject':
-        return t('An administrator created an account for you at !site', $variables, array('langcode' => $langcode));
+        return token_replace(t('An administrator created an account for you at [site:name]'), $variables, array('language' => $language));
       case 'register_admin_created_body':
-        return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by 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.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: [user:password]\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n--  [site:name] team"), $variables, array('language' => $language, 'callback' => 'user_mail_tokens'));
       case 'register_pending_approval_subject':
       case 'register_pending_approval_admin_subject':
-        return t('Account details for !username at !site (pending admin approval)', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account details for [user:name] at [site:name] (pending admin approval)'), $variables, array('language' => $language));
       case 'register_pending_approval_body':
-        return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n--  !site team", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n--  [site:name] team"), $variables, array('language' => $language));
       case 'register_pending_approval_admin_body':
-        return t("!username has applied for an account.\n\n!edit_uri", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name] has applied for an account.\n\n[user:edit-url]"), $variables, array('language' => $language));
       case 'password_reset_subject':
-        return t('Replacement login information for !username at !site', $variables, array('langcode' => $langcode));
+        return token_replace(t('Replacement login information for [user:name] at [site:name]'), $variables, array('language' => $language));
       case 'password_reset_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 by 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, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in to [site:uri-brief] by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-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 [user:edit-url] so you can change your password."), $variables, array('language' => $language, 'callback' => 'user_mail_tokens'));
       case 'status_activated_subject':
-        return t('Account details for !username at !site (approved)', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account details for [user:name] at [site:name] (approved)'), $variables, array('language' => $language));
       case 'status_activated_body':
-        return t("!username,\n\nYour account at !site has been activated.\n\nYou may now log in by 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.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\nOnce you have set your own password, you will be able to log in to !login_uri in the future using:\n\nusername: !username\n", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking on this link or copying and pasting it in your browser:\n\n[site:login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\nOnce you have set your own password, you will be able to log in to [site:login-url] in the future using:\n\nusername: [user:name]\n"), $variables, array('language' => $language, 'callback' => 'user_mail_tokens'));
       case 'status_blocked_subject':
-        return t('Account details for !username at !site (blocked)', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account details for [user:name] at [site:name] (blocked)'), $variables, array('language' => $language));
       case 'status_blocked_body':
-        return t("!username,\n\nYour account on !site has been blocked.", $variables, array('langcode' => $langcode));
+        return token_replace(t("[user:name],\n\nYour account on [site:name] has been blocked."), $variables, array('language' => $language));
 
       case 'cancel_confirm_subject':
-        return t('Account cancellation request for !username at !site', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account cancellation request for [user:name] at [site:name]'), $variables, array('language' => $language));
       case 'cancel_confirm_body':
-        return t("!username,
+        return token_replace(t("[user:name],
 
-A request to cancel your account has been made at !site.
+A request to cancel your account has been made at [site:name].
 
-You may now cancel your account on !uri_brief by clicking this link or copying and pasting it into your browser:
+You may now cancel your account on [site:uri-brief] by clicking this link or copying and pasting it into your browser:
 
-!cancel_url
+[user:cancel-url]
 
 NOTE: The cancellation of your account is not reversible.
 
-This link expires in one day and nothing will happen if it is not used.", $variables, array('langcode' => $langcode));
+This link expires in one day and nothing will happen if it is not used."), $variables, array('language' => $language, 'callback' => 'user_mail_tokens'));
 
       case 'status_canceled_subject':
-        return t('Account details for !username at !site (canceled)', $variables, array('langcode' => $langcode));
+        return token_replace(t('Account details for [user:name] at [site:name] (canceled)'), $variables, array('language' => $language));
       case 'status_canceled_body':
-        return t("!username,
+        return token_replace(t("[user:name],
 
-Your account on !site has been canceled.", $variables, array('langcode' => $langcode));
+Your account on [site:name] has been canceled."), $variables, array('language' => $language));
     }
   }
 }
@@ -2748,34 +2746,19 @@
 }
 
 /**
- * Return an array of token to value mappings for user e-mail messages.
+ * Callback that adds unsafe tokens for use in user_mail().
  *
- * @param $account
- *  The user object of the account being notified. Must contain at
- *  least the fields 'uid', 'name', and 'mail'.
- * @param $language
- *  Language object to generate the tokens with.
- * @return
- *  Array of mappings from token names to values (for use with strtr()).
+ * @see user_mail()
  */
-function user_mail_tokens($account, $language) {
-  global $base_url;
-  $tokens = array(
-    '!username' => $account->name,
-    '!site' => variable_get('site_name', 'Drupal'),
-    '!login_url' => user_pass_reset_url($account),
-    '!cancel_url' => user_cancel_url($account),
-    '!uri' => $base_url,
-    '!uri_brief' => preg_replace('!^https?://!', '', $base_url),
-    '!mailto' => $account->mail,
-    '!date' => format_date(REQUEST_TIME, 'medium', '', NULL, $language->language),
-    '!login_uri' => url('user', array('absolute' => TRUE, 'language' => $language)),
-    '!edit_uri' => url('user/' . $account->uid . '/edit', array('absolute' => TRUE, 'language' => $language)),
-  );
-  if (!empty($account->password)) {
-    $tokens['!password'] = $account->password;
+function user_mail_tokens(&$replacements, $data, $options) {
+  if (isset($data['user'])) {
+    $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']);
+    $replacements['[user:cancel-url]'] = user_cancel_url($data['user']);
+
+    if (isset($data['user']->password) && !empty($data['user']->password)) {
+      $replacements['[user:password]'] = $data['user']->password;
+    }
   }
-  return $tokens;
 }
 
 /**
Index: modules/user/user.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.tokens.inc,v
retrieving revision 1.1
diff -u -r1.1 user.tokens.inc
--- modules/user/user.tokens.inc	19 Aug 2009 20:19:37 -0000	1.1
+++ modules/user/user.tokens.inc	28 Sep 2009 14:10:23 -0000
@@ -41,6 +41,7 @@
     'name' => t("Edit URL"),
     'description' => t("The url of the account edit page."),
   );
+
   $user['last-login'] = array(
     'name' => t("Last login"),
     'description' => t("The date the user last logged in to the site."),
@@ -65,8 +66,8 @@
   global $user;
   $url_options = array('absolute' => TRUE);
   if (isset($options['language'])) {
-    $url_options['language'] = $language;
-    $language_code = $language->language;
+    $url_options['language'] = $options['language'];
+    $language_code = $options['language']->language;
   }
   else {
     $language_code = NULL;
Index: modules/comment/comment.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.tokens.inc,v
retrieving revision 1.1
diff -u -r1.1 comment.tokens.inc
--- modules/comment/comment.tokens.inc	19 Aug 2009 20:19:36 -0000	1.1
+++ modules/comment/comment.tokens.inc	28 Sep 2009 14:10:20 -0000
@@ -113,8 +113,8 @@
 function comment_tokens($type, $tokens, array $data = array(), array $options = array()) {
   $url_options = array('absolute' => TRUE);
   if (isset($options['language'])) {
-    $url_options['language'] = $language;
-    $language_code = $language->language;
+    $url_options['language'] = $options['language'];
+    $language_code = $options['language']->language;
   }
   else {
     $language_code = NULL;
Index: modules/system/system.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.tokens.inc,v
retrieving revision 1.2
diff -u -r1.2 system.tokens.inc
--- modules/system/system.tokens.inc	25 Aug 2009 10:27:15 -0000	1.2
+++ modules/system/system.tokens.inc	28 Sep 2009 14:10:21 -0000
@@ -147,6 +147,8 @@
  * Implement hook_tokens().
  */
 function system_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  global $base_url;
+
   $url_options = array('absolute' => TRUE);
   if (isset($language)) {
     $url_options['language'] = $language;
@@ -181,6 +183,14 @@
           $replacements[$original] = url('<front>', $url_options);
           break;
 
+        case 'uri':
+          $replacements[$original] = $base_url;
+          break;
+
+        case 'uri-brief':
+          $replacements[$original] = preg_replace('!^https?://!', '', $base_url);
+          break;
+
         case 'login-url':
           $replacements[$original] = url('user', $url_options);
           break;
