Index: includes/token.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/token.inc,v
retrieving revision 1.10
diff -u -9 -p -r1.10 token.inc
--- includes/token.inc	18 Oct 2010 01:13:07 -0000	1.10
+++ includes/token.inc	17 Nov 2010 20:13:33 -0000
@@ -140,24 +140,27 @@ function token_scan($text) {
  *   replacement process. Supported options are:
  *   - 'language' A language object to be used when generating locale-sensitive
  *     tokens.
  *   - 'callback' A callback function that will be used to post-process the array
  *     of token replacements after they are generated. Can be used when modules
  *     require special formatting of token text, for example URL encoding or
  *     truncation to a specific length.
  *   - 'sanitize' A boolean flag indicating that tokens should be sanitized for
  *     display to a web browser. Developers who set this option to FALSE assume
- *     responsibility for running filter_xss(), check_plain() or other
- *     appropriate scrubbing functions before displaying data to users.
+ *     responsibility for running filter_xss() or other appropriate scrubbing
+ *     functions before displaying data to users.
  * @return
  *   An associative array of replacement values, keyed by the original 'raw'
  *   tokens that were found in the source text. For example:
  *   $results['[node:title]'] = 'My new node';
+ *   Values are always considered HTML, so all plain-text values have already
+ *   been passed through check_plain(). Depending on $options['sanitize],
+ *   callers may need to sanitize the HTML using filter_xss() etc.
  *
  * @see hook_tokens()
  * @see hook_tokens_alter()
  */
 function token_generate($type, array $tokens, array $data = array(), array $options = array()) {
   $options += array('sanitize' => TRUE);
   $replacements = module_invoke_all('tokens', $type, $tokens, $data, $options);
 
   // Allow other modules to alter the replacements.
Index: modules/comment/comment.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.test,v
retrieving revision 1.92
diff -u -9 -p -r1.92 comment.test
--- modules/comment/comment.test	8 Nov 2010 21:36:45 -0000	1.92
+++ modules/comment/comment.test	17 Nov 2010 20:13:33 -0000
@@ -1280,52 +1280,45 @@ class CommentTokenReplaceTestCase extend
 
     // Add HTML to ensure that sanitation of some fields tested directly.
     $comment->subject = '<blink>Blinking Comment</blink>';
     $instance = field_info_instance('comment', 'body', 'comment_body');
 
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[comment:cid]'] = $comment->cid;
     $tests['[comment:hostname]'] = check_plain($comment->hostname);
-    $tests['[comment:name]'] = filter_xss($comment->name);
+    $tests['[comment:name]'] = check_plain($comment->name);
     $tests['[comment:mail]'] = check_plain($this->admin_user->mail);
     $tests['[comment:homepage]'] = check_url($comment->homepage);
-    $tests['[comment:title]'] = filter_xss($comment->subject);
+    $tests['[comment:title]'] = check_plain($comment->subject);
     $tests['[comment:body]'] = _text_sanitize($instance, LANGUAGE_NONE, $comment->comment_body[LANGUAGE_NONE][0], 'value');
-    $tests['[comment:url]'] = url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid));
-    $tests['[comment:edit-url]'] = url('comment/' . $comment->cid . '/edit', $url_options);
-    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $language->language);
-    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed, 2, $language->language);
+    $tests['[comment:url]'] = check_plain(url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid)));
+    $tests['[comment:edit-url]'] = check_plain(url('comment/' . $comment->cid . '/edit', $url_options));
+    $tests['[comment:created:since]'] = check_plain(format_interval(REQUEST_TIME - $comment->created, 2, $language->language));
+    $tests['[comment:changed:since]'] = check_plain(format_interval(REQUEST_TIME - $comment->changed, 2, $language->language));
     $tests['[comment:parent:cid]'] = $comment->pid;
     $tests['[comment:parent:title]'] = check_plain($parent_comment->subject);
     $tests['[comment:node:nid]'] = $comment->nid;
     $tests['[comment:node:title]'] = check_plain($node->title);
     $tests['[comment:author:uid]'] = $comment->uid;
     $tests['[comment:author:name]'] = check_plain($this->admin_user->name);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('comment' => $comment), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized comment token %token replaced.', array('%token' => $input)));
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[comment:hostname]'] = $comment->hostname;
-    $tests['[comment:name]'] = $comment->name;
-    $tests['[comment:mail]'] = $this->admin_user->mail;
-    $tests['[comment:homepage]'] = $comment->homepage;
-    $tests['[comment:title]'] = $comment->subject;
     $tests['[comment:body]'] = $comment->comment_body[LANGUAGE_NONE][0]['value'];
     $tests['[comment:parent:title]'] = $parent_comment->subject;
-    $tests['[comment:node:title]'] = $node->title;
-    $tests['[comment:author:name]'] = $this->admin_user->name;
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('comment' => $comment), array('language' => $language, 'sanitize' => FALSE));
       $this->assertFalse(strcmp($output, $expected), t('Unsanitized comment token %token replaced.', array('%token' => $input)));
     }
 
     // Load node so comment_count gets computed.
     $node = node_load($node->nid);
 
Index: modules/comment/comment.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.tokens.inc,v
retrieving revision 1.13
diff -u -9 -p -r1.13 comment.tokens.inc
--- modules/comment/comment.tokens.inc	7 Jul 2010 17:00:42 -0000	1.13
+++ modules/comment/comment.tokens.inc	17 Nov 2010 20:13:33 -0000
@@ -122,86 +122,85 @@ function comment_tokens($type, $tokens, 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         // Simple key values on the comment.
         case 'cid':
           $replacements[$original] = $comment->cid;
           break;
 
         // Poster identity information for comments
         case 'hostname':
-          $replacements[$original] = $sanitize ? check_plain($comment->hostname) : $comment->hostname;
+          $replacements[$original] = check_plain($comment->hostname);
           break;
 
         case 'name':
           $name = ($comment->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $comment->name;
-          $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+          $replacements[$original] = check_plain($name);
           break;
 
         case 'mail':
           if ($comment->uid != 0) {
             $account = user_load($comment->uid);
             $mail = $account->mail;
           }
           else {
             $mail = $comment->mail;
           }
-          $replacements[$original] = $sanitize ? check_plain($mail) : $mail;
+          $replacements[$original] = check_plain($mail);
           break;
 
         case 'homepage':
-          $replacements[$original] = $sanitize ? check_url($comment->homepage) : $comment->homepage;
+          $replacements[$original] = $sanitize ? check_url($comment->homepage) : check_plain($comment->homepage);
           break;
 
         case 'title':
-          $replacements[$original] = $sanitize ? filter_xss($comment->subject) : $comment->subject;
+          $replacements[$original] = check_plain($comment->subject);
           break;
 
         case 'body':
           $item = $comment->comment_body[LANGUAGE_NONE][0];
           $instance = field_info_instance('comment', 'body', 'comment_body');
           $replacements[$original] = $sanitize ? _text_sanitize($instance, LANGUAGE_NONE, $item, 'value') : $item['value'];
           break;
 
         // Comment related URLs.
         case 'url':
           $url_options['fragment']  = 'comment-' . $comment->cid;
-          $replacements[$original] = url('comment/' . $comment->cid, $url_options);
+          $replacements[$original] = check_plain(url('comment/' . $comment->cid, $url_options));
           break;
 
         case 'edit-url':
           $url_options['fragment'] = NULL;
-          $replacements[$original] = url('comment/' . $comment->cid . '/edit', $url_options);
+          $replacements[$original] = check_plain(url('comment/' . $comment->cid . '/edit', $url_options));
           break;
 
         // Default values for the chained tokens handled below.
         case 'author':
-          $replacements[$original] = $sanitize ? filter_xss($comment->name) : $comment->name;
+          $replacements[$original] = check_plain($comment->name);
           break;
 
         case 'parent':
           if (!empty($comment->pid)) {
             $parent = comment_load($comment->pid);
-            $replacements[$original] = $sanitize ? filter_xss($parent->subject) : $parent->subject;
+            $replacements[$original] = check_plain($parent->subject);
           }
           break;
 
         case 'created':
-          $replacements[$original] = format_date($comment->created, 'medium', '', NULL, $language_code);
+          $replacements[$original] = check_plain(format_date($comment->created, 'medium', '', NULL, $language_code));
           break;
 
         case 'changed':
-          $replacements[$original] = format_date($comment->changed, 'medium', '', NULL, $language_code);
+          $replacements[$original] = check_plain(format_date($comment->changed, 'medium', '', NULL, $language_code));
           break;
 
         case 'node':
           $node = node_load($comment->nid);
-          $title = $node->title;
-          $replacements[$original] = $sanitize ? filter_xss($title) : $title;
+          $replacements[$original] = check_plain($node->title);
           break;
       }
     }
 
     // Chained token relationships.
     if ($node_tokens = token_find_with_prefix($tokens, 'node')) {
       $node = node_load($comment->nid);
       $replacements += token_generate('node', $node_tokens, array('node' => $node), $options);
     }
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.100
diff -u -9 -p -r1.100 node.test
--- modules/node/node.test	12 Nov 2010 03:02:02 -0000	1.100
+++ modules/node/node.test	17 Nov 2010 20:13:35 -0000
@@ -1966,35 +1966,34 @@ class NodeTokenReplaceTestCase extends D
     $tests['[node:nid]'] = $node->nid;
     $tests['[node:vid]'] = $node->vid;
     $tests['[node:tnid]'] = $node->tnid;
     $tests['[node:type]'] = 'article';
     $tests['[node:type-name]'] = 'Article';
     $tests['[node:title]'] = check_plain($node->title);
     $tests['[node:body]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'value');
     $tests['[node:summary]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'summary');
     $tests['[node:language]'] = check_plain($node->language);
-    $tests['[node:url]'] = url('node/' . $node->nid, $url_options);
-    $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options);
+    $tests['[node:url]'] = check_plain(url('node/' . $node->nid, $url_options));
+    $tests['[node:edit-url]'] = check_plain(url('node/' . $node->nid . '/edit', $url_options));
     $tests['[node:author:uid]'] = $node->uid;
     $tests['[node:author:name]'] = check_plain($account->name);
-    $tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $language->language);
-    $tests['[node:changed:since]'] = format_interval(REQUEST_TIME - $node->changed, 2, $language->language);
+    $tests['[node:created:since]'] = check_plain(format_interval(REQUEST_TIME - $node->created, 2, $language->language));
+    $tests['[node:changed:since]'] = check_plain(format_interval(REQUEST_TIME - $node->changed, 2, $language->language));
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('node' => $node), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized node token %token replaced.', array('%token' => $input)));
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[node:title]'] = $node->title;
     $tests['[node:body]'] = $node->body[$node->language][0]['value'];
     $tests['[node:summary]'] = $node->body[$node->language][0]['summary'];
     $tests['[node:language]'] = $node->language;
     $tests['[node:author:name]'] = $account->name;
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
       $this->assertFalse(strcmp($output, $expected), t('Unsanitized node token %token replaced.', array('%token' => $input)));
     }
Index: modules/node/node.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tokens.inc,v
retrieving revision 1.15
diff -u -9 -p -r1.15 node.tokens.inc
--- modules/node/node.tokens.inc	29 Jun 2010 00:57:19 -0000	1.15
+++ modules/node/node.tokens.inc	17 Nov 2010 20:13:35 -0000
@@ -116,64 +116,64 @@ function node_tokens($type, $tokens, arr
         case 'vid':
           $replacements[$original] = $node->vid;
           break;
 
         case 'tnid':
           $replacements[$original] = $node->tnid;
           break;
 
         case 'type':
-          $replacements[$original] = $sanitize ? check_plain($node->type) : $node->type;
+          $replacements[$original] = check_plain($node->type);
           break;
 
         case 'type-name':
           $type_name = node_type_get_name($node);
-          $replacements[$original] = $sanitize ? check_plain($type_name) : $type_name;
+          $replacements[$original] = check_plain($type_name);
           break;
 
         case 'title':
-          $replacements[$original] = $sanitize ? check_plain($node->title) : $node->title;
+          $replacements[$original] = check_plain($node->title);
           break;
 
         case 'body':
         case 'summary':
           if (!empty($node->body)) {
             $item = $node->body[$node->language][0];
             $column = ($name == 'body') ? 'value' : 'summary';
             $instance = field_info_instance('node', 'body', $node->type);
             $replacements[$original] = $sanitize ? _text_sanitize($instance, $node->language, $item, $column) : $item[$column];
           }
           break;
 
         case 'language':
-          $replacements[$original] = $sanitize ? check_plain($node->language) : $node->language;
+          $replacements[$original] = check_plain($node->language);
           break;
 
         case 'url':
-          $replacements[$original] = url('node/' . $node->nid, $url_options);
+          $replacements[$original] = check_plain(url('node/' . $node->nid, $url_options));
           break;
 
         case 'edit-url':
-          $replacements[$original] = url('node/' . $node->nid . '/edit', $url_options);
+          $replacements[$original] = check_plain(url('node/' . $node->nid . '/edit', $url_options));
           break;
 
         // Default values for the chained tokens handled below.
         case 'author':
           $name = ($node->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $node->name;
-          $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+          $replacements[$original] = check_plain($name);
           break;
 
         case 'created':
-          $replacements[$original] = format_date($node->created, 'medium', '', NULL, $language_code);
+          $replacements[$original] = check_plain(format_date($node->created, 'medium', '', NULL, $language_code));
           break;
 
         case 'changed':
-          $replacements[$original] = format_date($node->changed, 'medium', '', NULL, $language_code);
+          $replacements[$original] = check_plain(format_date($node->changed, 'medium', '', NULL, $language_code));
           break;
       }
     }
 
     if ($author_tokens = token_find_with_prefix($tokens, 'author')) {
       $author = user_load($node->uid);
       $replacements += token_generate('user', $author_tokens, array('user' => $author), $options);
     }
 
Index: modules/poll/poll.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.test,v
retrieving revision 1.39
diff -u -9 -p -r1.39 poll.test
--- modules/poll/poll.test	25 Oct 2010 15:51:21 -0000	1.39
+++ modules/poll/poll.test	17 Nov 2010 20:13:37 -0000
@@ -637,38 +637,30 @@ class PollTokenReplaceTestCase extends P
     );
     $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
     $this->drupalLogout();
 
     $poll = node_load($poll_nid, NULL, TRUE);
 
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[node:poll-votes]'] = 4;
-    $tests['[node:poll-winner]'] = filter_xss($poll->choice[1]['chtext']);
+    $tests['[node:poll-winner]'] = check_plain($poll->choice[1]['chtext']);
     $tests['[node:poll-winner-votes]'] = 2;
     $tests['[node:poll-winner-percent]'] = 50;
     $tests['[node:poll-duration]'] = format_interval($poll->runtime, 1, $language->language);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('node' => $poll), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized poll token %token replaced.', array('%token' => $input)));
     }
-
-    // Generate and test unsanitized tokens.
-    $tests['[node:poll-winner]'] = $poll->choice[1]['chtext'];
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $poll), array('language' => $language, 'sanitize' => FALSE));
-      $this->assertFalse(strcmp($output, $expected), t('Unsanitized poll token %token replaced.', array('%token' => $input)));
-    }
   }
 }
 
 class PollExpirationTestCase extends PollTestCase {
   public static function getInfo() {
     return array(
       'name' => 'Poll expiration',
       'description' => 'Test the poll auto-expiration logic.',
       'group' => 'Poll',
Index: modules/poll/poll.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.tokens.inc,v
retrieving revision 1.5
diff -u -9 -p -r1.5 poll.tokens.inc
--- modules/poll/poll.tokens.inc	20 Apr 2010 09:48:06 -0000	1.5
+++ modules/poll/poll.tokens.inc	17 Nov 2010 20:13:37 -0000
@@ -65,19 +65,19 @@ function poll_tokens($type, $tokens, arr
     }
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'poll-votes':
           $replacements[$original] = $total_votes;
           break;
 
         case 'poll-winner':
           if (isset($winner)) {
-            $replacements[$original] = $sanitize ? filter_xss($winner['chtext']) : $winner['chtext'];
+            $replacements[$original] = check_plain($winner['chtext']);
           }
           else {
             $replacements[$original] = '';
           }
           break;
 
         case 'poll-winner-votes':
           if (isset($winner)) {
             $replacements[$original] = $winner['chvotes'];
@@ -92,17 +92,17 @@ function poll_tokens($type, $tokens, arr
             $percent = ($winner['chvotes'] / $total_votes) * 100;
             $replacements[$original] = number_format($percent, 0);
           }
           else {
             $replacements[$original] = '';
           }
           break;
 
         case 'poll-duration':
-          $replacements[$original] = format_interval($node->runtime, 1, $language_code);
+          $replacements[$original] = check_plain(format_interval($node->runtime, 1, $language_code));
           break;
       }
     }
   }
 
   return $replacements;
 }
Index: modules/statistics/statistics.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.tokens.inc,v
retrieving revision 1.4
diff -u -9 -p -r1.4 statistics.tokens.inc
--- modules/statistics/statistics.tokens.inc	20 Apr 2010 09:48:06 -0000	1.4
+++ modules/statistics/statistics.tokens.inc	17 Nov 2010 20:13:37 -0000
@@ -44,19 +44,19 @@ function statistics_tokens($type, $token
         $statistics = statistics_get($node->nid);
         $replacements[$original] = $statistics['totalcount'];
       }
       elseif ($name == 'day-count') {
         $statistics = statistics_get($node->nid);
         $replacements[$original] = $statistics['daycount'];
       }
       elseif ($name == 'last-view') {
         $statistics = statistics_get($node->nid);
-        $replacements[$original] = format_date($statistics['timestamp']);
+        $replacements[$original] = check_plain(format_date($statistics['timestamp']));
       }
     }
 
     if ($created_tokens = token_find_with_prefix($tokens, 'last-view')) {
       $statistics = statistics_get($node->nid);
       $replacements += token_generate('date', $created_tokens, array('date' => $statistics['timestamp']), $options);
     }
   }
 
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.214
diff -u -9 -p -r1.214 system.api.php
--- modules/system/system.api.php	15 Nov 2010 08:29:03 -0000	1.214
+++ modules/system/system.api.php	17 Nov 2010 20:13:43 -0000
@@ -3980,33 +3980,43 @@ function hook_tokens($type, $tokens, arr
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         // Simple key values on the node.
         case 'nid':
           $replacements[$original] = $node->nid;
           break;
 
         case 'title':
-          $replacements[$original] = $sanitize ? check_plain($node->title) : $node->title;
+          $replacements[$original] = check_plain($node->title);
+          break;
+
+        case 'body':
+        case 'summary':
+          if (!empty($node->body)) {
+            $item = $node->body[$node->language][0];
+            $column = ($name == 'body') ? 'value' : 'summary';
+            $instance = field_info_instance('node', 'body', $node->type);
+            $replacements[$original] = $sanitize ? _text_sanitize($instance, $node->language, $item, $column) : $item[$column];
+          }
           break;
 
         case 'edit-url':
-          $replacements[$original] = url('node/' . $node->nid . '/edit', $url_options);
+          $replacements[$original] = check_plain(url('node/' . $node->nid . '/edit', $url_options));
           break;
 
         // Default values for the chained tokens handled below.
         case 'author':
           $name = ($node->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $node->name;
-          $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+          $replacements[$original] = check_plain($name);
           break;
 
         case 'created':
-          $replacements[$original] = format_date($node->created, 'medium', '', NULL, $language_code);
+          $replacements[$original] = check_plain(format_date($node->created, 'medium', '', NULL, $language_code));
           break;
       }
     }
 
     if ($author_tokens = token_find_with_prefix($tokens, 'author')) {
       $author = user_load($node->uid);
       $replacements += token_generate('user', $author_tokens, array('user' => $author), $options);
     }
 
@@ -4038,19 +4048,18 @@ function hook_tokens_alter(array &$repla
   $options = $context['options'];
 
   if (isset($options['language'])) {
     $url_options['language'] = $options['language'];
     $language_code = $options['language']->language;
   }
   else {
     $language_code = NULL;
   }
-  $sanitize = !empty($options['sanitize']);
 
   if ($context['type'] == 'node' && !empty($context['data']['node'])) {
     $node = $context['data']['node'];
 
     // Alter the [node:title] token, and replace it with the rendered content
     // of a field (field_title).
     if (isset($context['tokens']['title'])) {
       $title = field_view_field('node', $node, 'field_title', 'default', $language_code);
       $replacements[$context['tokens']['title']] = drupal_render($title);
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.150
diff -u -9 -p -r1.150 system.test
--- modules/system/system.test	28 Oct 2010 01:33:41 -0000	1.150
+++ modules/system/system.test	17 Nov 2010 20:13:44 -0000
@@ -1511,92 +1511,94 @@ class TokenReplaceTestCase extends Drupa
     );
   }
 
   /**
    * Creates a user and a node, then tests the tokens generated from them.
    */
   function testTokenReplacement() {
     // Create the initial objects.
     $account = $this->drupalCreateUser();
-    $node = $this->drupalCreateNode(array('uid' => $account->uid));
+    $node = $this->drupalCreateNode(array('uid' => $account->uid, 'type' => 'page'));
     $node->title = '<blink>Blinking Text</blink>';
+    $node->body[LANGUAGE_NONE][0]['value'] = 'Invalid HTML: & <>';
     global $user, $language;
 
     $source  = '[node:title]';         // Title of the node we passed in
     $source .= '[node:author:name]';   // Node author's name
     $source .= '[node:created:since]'; // Time since the node was created
     $source .= '[current-user:name]';  // Current user's name
     $source .= '[date:short]';         // Short date format of REQUEST_TIME
     $source .= '[user:name]';          // No user passed in, should be untouched
     $source .= '[bogus:token]';        // Non-existent token
 
     $target  = check_plain($node->title);
     $target .= check_plain($account->name);
-    $target .= format_interval(REQUEST_TIME - $node->created, 2, $language->language);
+    $target .= check_plain(format_interval(REQUEST_TIME - $node->created, 2, $language->language));
     $target .= check_plain($user->name);
-    $target .= format_date(REQUEST_TIME, 'short', '', NULL, $language->language);
+    $target .= check_plain(format_date(REQUEST_TIME, 'short', '', NULL, $language->language));
 
     // Test that the clear parameter cleans out non-existent tokens.
     $result = token_replace($source, array('node' => $node), array('language' => $language, 'clear' => TRUE));
-    $result = $this->assertFalse(strcmp($target, $result), 'Valid tokens replaced while invalid tokens cleared out.');
+    $result = $this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens cleared out.');
 
     // Test without using the clear parameter (non-existant token untouched).
     $target .= '[user:name]';
     $target .= '[bogus:token]';
     $result = token_replace($source, array('node' => $node), array('language' => $language));
     $this->assertFalse(strcmp($target, $result), 'Valid tokens replaced while invalid tokens ignored.');
 
     // Check that the results of token_generate are sanitized properly. This does NOT
     // test the cleanliness of every token -- just that the $sanitize flag is being
     // passed properly through the call stack and being handled correctly by a 'known'
-    // token, [node:title].
-    $raw_tokens = array('title' => '[node:title]');
+    // token, [node:body].
+    $raw_tokens = array('body' => '[node:body]');
     $generated = token_generate('node', $raw_tokens, array('node' => $node));
-    $this->assertFalse(strcmp($generated['[node:title]'], check_plain($node->title)), t('Token sanitized.'));
+    $instance = field_info_instance('node', 'body', 'page');
+    $this->assertEqual($generated['[node:body]'], _text_sanitize($instance, $node->language, $node->body[LANGUAGE_NONE][0], 'value'), t('Token sanitized.'));
+    $this->assertNotEqual($generated['[node:body]'], $node->body[LANGUAGE_NONE][0]['value'], t('Token sanitized.'));
 
     $generated = token_generate('node', $raw_tokens, array('node' => $node), array('sanitize' => FALSE));
-    $this->assertFalse(strcmp($generated['[node:title]'], $node->title), t('Unsanitized token generated properly.'));
+    $this->assertEqual($generated['[node:body]'], $node->body[LANGUAGE_NONE][0]['value'], t('Unsanitized token generated properly.'));
   }
 
   /**
    * Tests the generation of all system site information tokens.
    */
   function testSystemSiteTokenReplacement() {
     global $language;
     $url_options = array(
       'absolute' => TRUE,
       'language' => $language,
     );
 
     // Set a few site variables.
     variable_set('site_name', '<strong>Drupal<strong>');
     variable_set('site_slogan', '<blink>Slogan</blink>');
-    variable_set('site_mission', '<em>Mission</em>');
 
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[site:name]'] = check_plain(variable_get('site_name', 'Drupal'));
-    $tests['[site:slogan]'] = check_plain(variable_get('site_slogan', ''));
+    $tests['[site:slogan]'] = filter_xss_admin(variable_get('site_slogan', ''));
     $tests['[site:mail]'] = 'simpletest@example.com';
-    $tests['[site:url]'] = url('<front>', $url_options);
-    $tests['[site:url-brief]'] = preg_replace(array('!^https?://!', '!/$!'), '', url('<front>', $url_options));
-    $tests['[site:login-url]'] = url('user', $url_options);
+    $tests['[site:url]'] = check_plain(url('<front>', $url_options));
+    $tests['[site:url-brief]'] = check_plain(preg_replace(array('!^https?://!', '!/$!'), '', url('<front>', $url_options)));
+    $tests['[site:login-url]'] = check_plain(url('user', $url_options));
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array(), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized system site information token %token replaced.', array('%token' => $input)));
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[site:name]'] = variable_get('site_name', 'Drupal');
+    $tests['[site:name]'] = check_plain(variable_get('site_name', 'Drupal'));
     $tests['[site:slogan]'] = variable_get('site_slogan', '');
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array(), array('language' => $language, 'sanitize' => FALSE));
       $this->assertFalse(strcmp($output, $expected), t('Unsanitized system site information token %token replaced.', array('%token' => $input)));
     }
   }
 
   /**
@@ -1604,24 +1606,24 @@ class TokenReplaceTestCase extends Drupa
    */
   function testSystemDateTokenReplacement() {
     global $language;
 
     // Set time to one hour before request.
     $date = REQUEST_TIME - 3600;
 
     // Generate and test tokens.
     $tests = array();
-    $tests['[date:short]'] = format_date($date, 'short', '', NULL, $language->language);
-    $tests['[date:medium]'] = format_date($date, 'medium', '', NULL, $language->language);
-    $tests['[date:long]'] = format_date($date, 'long', '', NULL, $language->language);
-    $tests['[date:custom:m/j/Y]'] = format_date($date, 'custom', 'm/j/Y', NULL, $language->language);
-    $tests['[date:since]'] = format_interval((REQUEST_TIME - $date), 2, $language->language);
-    $tests['[date:raw]'] = filter_xss($date);
+    $tests['[date:short]'] = check_plain(format_date($date, 'short', '', NULL, $language->language));
+    $tests['[date:medium]'] = check_plain(format_date($date, 'medium', '', NULL, $language->language));
+    $tests['[date:long]'] = check_plain(format_date($date, 'long', '', NULL, $language->language));
+    $tests['[date:custom:m/j/Y]'] = check_plain(format_date($date, 'custom', 'm/j/Y', NULL, $language->language));
+    $tests['[date:since]'] = check_plain(format_interval((REQUEST_TIME - $date), 2, $language->language));
+    $tests['[date:raw]'] = check_plain($date);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('date' => $date), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Date token %token replaced.', array('%token' => $input)));
     }
   }
Index: modules/system/system.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.tokens.inc,v
retrieving revision 1.13
diff -u -9 -p -r1.13 system.tokens.inc
--- modules/system/system.tokens.inc	28 Oct 2010 01:33:41 -0000	1.13
+++ modules/system/system.tokens.inc	17 Nov 2010 20:13:44 -0000
@@ -142,127 +142,127 @@ function system_tokens($type, $tokens, a
   $sanitize = !empty($options['sanitize']);
 
   $replacements = array();
 
   if ($type == 'site') {
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'name':
           $site_name = variable_get('site_name', 'Drupal');
-          $replacements[$original] = $sanitize ? check_plain($site_name) : $site_name;
+          $replacements[$original] = check_plain($site_name);
           break;
 
         case 'slogan':
           $slogan = variable_get('site_slogan', '');
-          $replacements[$original] = $sanitize ? check_plain($slogan) : $slogan;
+          $replacements[$original] = $sanitize ? filter_xss_admin($slogan) : $slogan;
           break;
 
         case 'mail':
-          $replacements[$original] = variable_get('site_mail', '');
+          $replacements[$original] = check_plain(variable_get('site_mail', ''));
           break;
 
         case 'url':
-          $replacements[$original] = url('<front>', $url_options);
+          $replacements[$original] = check_plain(url('<front>', $url_options));
           break;
 
         case 'url-brief':
-          $replacements[$original] = preg_replace(array('!^https?://!', '!/$!'), '', url('<front>', $url_options));
+          $replacements[$original] = check_plain(preg_replace(array('!^https?://!', '!/$!'), '', url('<front>', $url_options)));
           break;
 
         case 'login-url':
-          $replacements[$original] = url('user', $url_options);
+          $replacements[$original] = check_plain(url('user', $url_options));
           break;
       }
     }
   }
 
   elseif ($type == 'date') {
     if (empty($data['date'])) {
       $date = REQUEST_TIME;
     }
     else {
       $date = $data['date'];
     }
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'short':
-          $replacements[$original] = format_date($date, 'short', '', NULL, $langcode);
+          $replacements[$original] = check_plain(format_date($date, 'short', '', NULL, $langcode));
           break;
 
         case 'medium':
-          $replacements[$original] = format_date($date, 'medium', '', NULL, $langcode);
+          $replacements[$original] = check_plain(format_date($date, 'medium', '', NULL, $langcode));
           break;
 
         case 'long':
-          $replacements[$original] = format_date($date, 'long', '', NULL, $langcode);
+          $replacements[$original] = check_plain(format_date($date, 'long', '', NULL, $langcode));
           break;
 
         case 'since':
-          $replacements[$original] = format_interval((REQUEST_TIME - $date), 2, $langcode);
+          $replacements[$original] = check_plain(format_interval((REQUEST_TIME - $date), 2, $langcode));
           break;
 
         case 'raw':
-          $replacements[$original] = $sanitize ? check_plain($date) : $date;
+          $replacements[$original] = check_plain($date);
           break;
       }
     }
 
     if ($created_tokens = token_find_with_prefix($tokens, 'custom')) {
       foreach ($created_tokens as $name => $original) {
-        $replacements[$original] = format_date($date, 'custom', $name, NULL, $langcode);
+        $replacements[$original] = check_plain(format_date($date, 'custom', $name, NULL, $langcode));
       }
     }
   }
 
   elseif ($type == 'file' && !empty($data['file'])) {
     $file = $data['file'];
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         // Basic keys and values.
         case 'fid':
           $replacements[$original] = $file->fid;
           break;
 
         // Essential file data
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($file->filename) : $file->filename;
+          $replacements[$original] = check_plain($file->filename);
           break;
 
         case 'description':
-          $replacements[$original] = $sanitize ? check_plain($file->description) : $file->description;
+          $replacements[$original] = check_plain($file->description);
           break;
 
         case 'path':
-          $replacements[$original] = $sanitize ? check_plain($file->uri) : $file->uri;
+          $replacements[$original] = check_plain($file->uri);
           break;
 
         case 'mime':
-          $replacements[$original] = $sanitize ? check_plain($file->filemime) : $file->filemime;
+          $replacements[$original] = check_plain($file->filemime);
           break;
 
         case 'size':
           $replacements[$original] = format_size($file->filesize);
           break;
 
         case 'url':
-          $replacements[$original] = $sanitize ? check_plain(file_create_url($file->uri)) : file_create_url($file->uri);
+          $replacements[$original] = check_plain(url(file_create_url($file->uri), $url_options));
           break;
 
         // These tokens are default variations on the chained tokens handled below.
         case 'timestamp':
-          $replacements[$original] = format_date($file->timestamp, 'medium', '', NULL, $langcode);
+          $replacements[$original] = check_plain(format_date($file->timestamp, 'medium', '', NULL, $langcode));
           break;
 
         case 'owner':
           $account = user_load($file->uid);
-          $replacements[$original] = $sanitize ? check_plain($account->name) : $account->name;
+          $replacements[$original] = check_plain($account->name);
           break;
       }
     }
 
     if ($date_tokens = token_find_with_prefix($tokens, 'timestamp')) {
       $replacements += token_generate('date', $date_tokens, array('date' => $file->timestamp), $options);
     }
 
     if (($owner_tokens = token_find_with_prefix($tokens, 'owner')) && $account = user_load($file->uid)) {
Index: modules/taxonomy/taxonomy.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v
retrieving revision 1.96
diff -u -9 -p -r1.96 taxonomy.test
--- modules/taxonomy/taxonomy.test	13 Oct 2010 13:43:21 -0000	1.96
+++ modules/taxonomy/taxonomy.test	17 Nov 2010 20:13:45 -0000
@@ -1073,61 +1073,49 @@ class TaxonomyTokenReplaceTestCase exten
       $output = token_replace($input, array('term' => $term1), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
 
     // Generate and test sanitized tokens for term2.
     $tests = array();
     $tests['[term:tid]'] = $term2->tid;
     $tests['[term:name]'] = check_plain($term2->name);
     $tests['[term:description]'] = check_markup($term2->description, $term2->format);
-    $tests['[term:url]'] = url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE));
+    $tests['[term:url]'] = check_plain(url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE)));
     $tests['[term:node-count]'] = 1;
     $tests['[term:parent:name]'] = check_plain($term1->name);
     $tests['[term:parent:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE));
     $tests['[term:parent:parent:name]'] = '[term:parent:parent:name]';
     $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('term' => $term2), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
 
-    // Generate and test unsanitized tokens.
-    $tests['[term:name]'] = $term2->name;
-    $tests['[term:description]'] = $term2->description;
-    $tests['[term:parent:name]'] = $term1->name;
-    $tests['[term:vocabulary:name]'] = $this->vocabulary->name;
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('term' => $term2), array('language' => $language, 'sanitize' => FALSE));
-      $this->assertFalse(strcmp($output, $expected), t('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
-    }
-
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[vocabulary:vid]'] = $this->vocabulary->vid;
     $tests['[vocabulary:name]'] = check_plain($this->vocabulary->name);
     $tests['[vocabulary:description]'] = filter_xss($this->vocabulary->description);
     $tests['[vocabulary:node-count]'] = 1;
     $tests['[vocabulary:term-count]'] = 2;
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[vocabulary:name]'] = $this->vocabulary->name;
     $tests['[vocabulary:description]'] = $this->vocabulary->description;
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language, 'sanitize' => FALSE));
       $this->assertFalse(strcmp($output, $expected), t('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
   }
 }
Index: modules/taxonomy/taxonomy.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.tokens.inc,v
retrieving revision 1.10
diff -u -9 -p -r1.10 taxonomy.tokens.inc
--- modules/taxonomy/taxonomy.tokens.inc	14 Nov 2010 00:25:44 -0000	1.10
+++ modules/taxonomy/taxonomy.tokens.inc	17 Nov 2010 20:13:45 -0000
@@ -97,28 +97,28 @@ function taxonomy_tokens($type, $tokens,
     $term = $data['term'];
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'tid':
           $replacements[$original] = $term->tid;
           break;
 
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($term->name) : $term->name;
+          $replacements[$original] = check_plain($term->name);
           break;
 
         case 'description':
-          $replacements[$original] = $sanitize ? check_markup($term->description, $term->format, '', TRUE) : $term->description;
+          $replacements[$original] = check_markup($term->description, $term->format, '', TRUE);
           break;
 
         case 'url':
           $uri = entity_uri('taxonomy_term', $term);
-          $replacements[$original] = url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE)));
+          $replacements[$original] = check_plain(url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))));
           break;
 
         case 'node-count':
           $query = db_select('taxonomy_index');
           $query->condition('tid', $term->tid);
           $query->addTag('term_node_count');
           $count = $query->countQuery()->execute()->fetchField();
           $replacements[$original] = $count;
           break;
@@ -152,19 +152,19 @@ function taxonomy_tokens($type, $tokens,
     $vocabulary = $data['vocabulary'];
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'vid':
           $replacements[$original] = $vocabulary->vid;
           break;
 
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($vocabulary->name) : $vocabulary->name;
+          $replacements[$original] = check_plain($vocabulary->name);
           break;
 
         case 'description':
           $replacements[$original] = $sanitize ? filter_xss($vocabulary->description) : $vocabulary->description;
           break;
 
         case 'term-count':
           $query = db_select('taxonomy_term_data');
           $query->condition('vid', $vocabulary->vid);
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1217
diff -u -9 -p -r1.1217 user.module
--- modules/user/user.module	14 Nov 2010 22:15:39 -0000	1.1217
+++ modules/user/user.module	17 Nov 2010 20:13:47 -0000
@@ -2483,19 +2483,19 @@ function user_build_content($account, $v
   module_invoke_all('entity_view', $account, 'user', $view_mode, $langcode);
 }
 
 /**
  * Implements hook_mail().
  */
 function user_mail($key, &$message, $params) {
   $language = $message['language'];
   $variables = array('user' => $params['account']);
-  $message['subject'] .= _user_mail_text($key . '_subject', $language, $variables);
+  $message['subject'] .= decode_entities(strip_tags(_user_mail_text($key . '_subject', $language, $variables)));
   $message['body'][] = _user_mail_text($key . '_body', $language, $variables);
 }
 
 /**
  * Returns a mail string for a variable name.
  *
  * Used by user_mail() and the settings forms to retrieve strings.
  */
 function _user_mail_text($key, $language = NULL, $variables = array(), $replace = TRUE) {
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.105
diff -u -9 -p -r1.105 user.test
--- modules/user/user.test	15 Nov 2010 09:04:47 -0000	1.105
+++ modules/user/user.test	17 Nov 2010 20:13:49 -0000
@@ -1781,28 +1781,18 @@ class UserTokenReplaceTestCase extends D
     $tests['[current-user:name]'] = check_plain($user->name);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
       $output = token_replace($input, array('user' => $account), array('language' => $language));
       $this->assertFalse(strcmp($output, $expected), t('Sanitized user token %token replaced.', array('%token' => $input)));
     }
-
-    // Generate and test unsanitized tokens.
-    $tests['[user:name]'] = $account->name;
-    $tests['[user:mail]'] = $account->mail;
-    $tests['[current-user:name]'] = $user->name;
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
-      $this->assertFalse(strcmp($output, $expected), t('Unsanitized user token %token replaced.', array('%token' => $input)));
-    }
   }
 }
 
 /**
  * Test user search.
  */
 class UserUserSearchTestCase extends DrupalWebTestCase {
   public static function getInfo() {
     return array(
Index: modules/user/user.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.tokens.inc,v
retrieving revision 1.8
diff -u -9 -p -r1.8 user.tokens.inc
--- modules/user/user.tokens.inc	16 Oct 2010 20:09:17 -0000	1.8
+++ modules/user/user.tokens.inc	17 Nov 2010 20:13:49 -0000
@@ -65,57 +65,55 @@ function user_token_info() {
 function user_tokens($type, $tokens, array $data = array(), array $options = array()) {
   $url_options = array('absolute' => TRUE);
   if (isset($options['language'])) {
     $url_options['language'] = $options['language'];
     $language_code = $options['language']->language;
   }
   else {
     $language_code = NULL;
   }
-  $sanitize = !empty($options['sanitize']);
 
   $replacements = array();
 
   if ($type == 'user' && !empty($data['user'])) {
     $account = $data['user'];
     foreach ($tokens as $name => $original) {
       switch ($name) {
         // Basic user account information.
         case 'uid':
           // In the case of hook user_presave uid is not set yet.
           $replacements[$original] = !empty($account->uid) ? $account->uid : t('not yet assigned');
           break;
 
         case 'name':
-          $name = format_username($account);
-          $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+          $replacements[$original] = check_plain(format_username($account));
           break;
 
         case 'mail':
-          $replacements[$original] = $sanitize ? check_plain($account->mail) : $account->mail;
+          $replacements[$original] = check_plain($account->mail);
           break;
 
         case 'url':
-          $replacements[$original] = !empty($account->uid) ? url("user/$account->uid", $url_options) : t('not yet assigned');
+          $replacements[$original] = !empty($account->uid) ? check_plain(url("user/$account->uid", $url_options)) : t('not yet assigned');
           break;
 
         case 'edit-url':
-          $replacements[$original] = !empty($account->uid) ? url("user/$account->uid/edit", $url_options) : t('not yet assigned');
+          $replacements[$original] = !empty($account->uid) ? check_plain(url("user/$account->uid/edit", $url_options)) : t('not yet assigned');
           break;
 
         // These tokens are default variations on the chained tokens handled below.
         case 'last-login':
-          $replacements[$original] = !empty($account->login) ? format_date($account->login, 'medium', '', NULL, $language_code) : t('never');
+          $replacements[$original] = !empty($account->login) ? check_plain(format_date($account->login, 'medium', '', NULL, $language_code)) : t('never');
           break;
 
         case 'created':
           // In the case of user_presave the created date may not yet be set.
-          $replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $language_code) : t('not yet created');
+          $replacements[$original] = !empty($account->created) ? check_plain(format_date($account->created, 'medium', '', NULL, $language_code)) : t('not yet created');
           break;
       }
     }
 
     if ($login_tokens = token_find_with_prefix($tokens, 'last-login')) {
       $replacements += token_generate('date', $login_tokens, array('date' => $account->login), $options);
     }
 
     if ($registered_tokens = token_find_with_prefix($tokens, 'created')) {
