diff --git a/includes/token.inc b/includes/token.inc
index 7a5fea1..16c056e 100644
--- a/includes/token.inc
+++ b/includes/token.inc
@@ -157,13 +157,18 @@ function token_scan($text) {
  *     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. If the values
+ *   are to be used in a non-HTML context, e.g. in the subject of an email, they
+ *   should be converted to plain text using decode_entities(strip_tags(...)).
  *
  * @see hook_tokens()
  * @see hook_tokens_alter()
diff --git a/modules/comment/comment.test b/modules/comment/comment.test
index c9478f4..dc13833 100644
--- a/modules/comment/comment.test
+++ b/modules/comment/comment.test
@@ -1747,15 +1747,15 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     $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;
@@ -1772,15 +1772,8 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     }
 
     // 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));
diff --git a/modules/comment/comment.tokens.inc b/modules/comment/comment.tokens.inc
index d62b7e2..82a3681 100644
--- a/modules/comment/comment.tokens.inc
+++ b/modules/comment/comment.tokens.inc
@@ -127,12 +127,12 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
 
         // 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':
@@ -143,15 +143,15 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
           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':
@@ -163,38 +163,37 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
         // 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;
       }
     }
diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc
index 2af3cb6..fbeaeed 100644
--- a/modules/file/file.field.inc
+++ b/modules/file/file.field.inc
@@ -578,7 +578,7 @@ function file_field_widget_uri($field, $instance, $data = array()) {
   $destination = trim($instance['settings']['file_directory'], '/');
 
   // Replace tokens.
-  $destination = token_replace($destination, $data);
+  $destination = decode_entities(strip_tags(token_replace($destination, $data, array('sanitize' => FALSE))));
 
   return $field['settings']['uri_scheme'] . '://' . $destination;
 }
diff --git a/modules/node/node.test b/modules/node/node.test
index 56a2d34..f503ccf 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -2268,12 +2268,12 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
     $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(format_username($account));
-    $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.'));
@@ -2284,7 +2284,6 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
     }
 
     // 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;
diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc
index 80dbda5..c3cb718 100644
--- a/modules/node/node.tokens.inc
+++ b/modules/node/node.tokens.inc
@@ -121,16 +121,16 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr
           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':
@@ -144,29 +144,29 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr
           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;
       }
     }
diff --git a/modules/poll/poll.test b/modules/poll/poll.test
index 6fabf95..554a51f 100644
--- a/modules/poll/poll.test
+++ b/modules/poll/poll.test
@@ -642,7 +642,7 @@ class PollTokenReplaceTestCase extends PollTestCase {
     // 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);
@@ -654,14 +654,6 @@ class PollTokenReplaceTestCase extends PollTestCase {
       $output = token_replace($input, array('node' => $poll), array('language' => $language));
       $this->assertEqual($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->assertEqual($output, $expected, t('Unsanitized poll token %token replaced.', array('%token' => $input)));
-    }
   }
 }
 
diff --git a/modules/poll/poll.tokens.inc b/modules/poll/poll.tokens.inc
index eda628b..90069d6 100644
--- a/modules/poll/poll.tokens.inc
+++ b/modules/poll/poll.tokens.inc
@@ -70,7 +70,7 @@ function poll_tokens($type, $tokens, array $data = array(), array $options = arr
 
         case 'poll-winner':
           if (isset($winner)) {
-            $replacements[$original] = $sanitize ? filter_xss($winner['chtext']) : $winner['chtext'];
+            $replacements[$original] = check_plain($winner['chtext']);
           }
           else {
             $replacements[$original] = '';
@@ -97,7 +97,7 @@ function poll_tokens($type, $tokens, array $data = array(), array $options = arr
           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;
       }
     }
diff --git a/modules/statistics/statistics.tokens.inc b/modules/statistics/statistics.tokens.inc
index c2c8fc3..42bc6d8 100644
--- a/modules/statistics/statistics.tokens.inc
+++ b/modules/statistics/statistics.tokens.inc
@@ -49,7 +49,7 @@ function statistics_tokens($type, $tokens, array $data = array(), array $options
       }
       elseif ($name == 'last-view') {
         $statistics = statistics_get($node->nid);
-        $replacements[$original] = format_date($statistics['timestamp']);
+        $replacements[$original] = check_plain(format_date($statistics['timestamp']));
       }
     }
 
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index acbc843..356187e 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -4082,21 +4082,31 @@ function hook_tokens($type, $tokens, array $data = array(), array $options = arr
           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;
       }
     }
@@ -4140,7 +4150,6 @@ function hook_tokens_alter(array &$replacements, array $context) {
   else {
     $language_code = NULL;
   }
-  $sanitize = !empty($options['sanitize']);
 
   if ($context['type'] == 'node' && !empty($context['data']['node'])) {
     $node = $context['data']['node'];
diff --git a/modules/system/system.test b/modules/system/system.test
index 9944619..5b0cf9e 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -1810,8 +1810,9 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
   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
@@ -1824,9 +1825,9 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
 
     $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));
@@ -1841,8 +1842,8 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     // 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->assertEqual($generated['[node:title]'], check_plain($node->title), t('Token sanitized.'));
 
@@ -1896,11 +1897,11 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     // 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.'));
@@ -1911,7 +1912,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     }
 
     // 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) {
@@ -1931,12 +1932,12 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
 
     // 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.'));
diff --git a/modules/system/system.tokens.inc b/modules/system/system.tokens.inc
index 56ddf29..1c86cac 100644
--- a/modules/system/system.tokens.inc
+++ b/modules/system/system.tokens.inc
@@ -143,28 +143,28 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a
       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;
       }
     }
@@ -181,30 +181,30 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a
     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));
       }
     }
   }
@@ -221,15 +221,15 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a
 
         // Essential file data
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($file->filename) : $file->filename;
+          $replacements[$original] = check_plain($file->filename);
           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':
@@ -237,18 +237,18 @@ function system_tokens($type, $tokens, array $data = array(), array $options = a
           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);
           $name = format_username($account);
-          $replacements[$original] = $sanitize ? check_plain($name) : $name;
+          $replacements[$original] = check_plain($name);
           break;
       }
     }
diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test
index aa7cc2e..f995baa 100644
--- a/modules/taxonomy/taxonomy.test
+++ b/modules/taxonomy/taxonomy.test
@@ -1185,7 +1185,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $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));
@@ -1200,17 +1200,6 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
       $this->assertEqual($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->assertEqual($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;
@@ -1228,7 +1217,6 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[vocabulary:name]'] = $this->vocabulary->name;
     $tests['[vocabulary:description]'] = $this->vocabulary->description;
 
     foreach ($tests as $input => $expected) {
diff --git a/modules/taxonomy/taxonomy.tokens.inc b/modules/taxonomy/taxonomy.tokens.inc
index f8ae457..ab95a8c 100644
--- a/modules/taxonomy/taxonomy.tokens.inc
+++ b/modules/taxonomy/taxonomy.tokens.inc
@@ -102,16 +102,16 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options =
           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':
@@ -157,7 +157,7 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options =
           break;
 
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($vocabulary->name) : $vocabulary->name;
+          $replacements[$original] = check_plain($vocabulary->name);
           break;
 
         case 'description':
diff --git a/modules/user/user.module b/modules/user/user.module
index 044ad46..9cfce41 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2575,7 +2575,7 @@ function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
 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);
 }
 
diff --git a/modules/user/user.test b/modules/user/user.test
index 6ecbfac..2087957 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -1931,16 +1931,6 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
       $output = token_replace($input, array('user' => $account), array('language' => $language));
       $this->assertEqual($output, $expected, t('Sanitized user token %token replaced.', array('%token' => $input)));
     }
-
-    // Generate and test unsanitized tokens.
-    $tests['[user:name]'] = format_username($account);
-    $tests['[user:mail]'] = $account->mail;
-    $tests['[current-user:name]'] = format_username($global_account);
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
-      $this->assertEqual($output, $expected, t('Unsanitized user token %token replaced.', array('%token' => $input)));
-    }
   }
 }
 
diff --git a/modules/user/user.tokens.inc b/modules/user/user.tokens.inc
index 8dcea4b..343ecd3 100644
--- a/modules/user/user.tokens.inc
+++ b/modules/user/user.tokens.inc
@@ -70,7 +70,6 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
   else {
     $language_code = NULL;
   }
-  $sanitize = !empty($options['sanitize']);
 
   $replacements = array();
 
@@ -86,29 +85,29 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
 
         case 'name':
           $name = format_username($account);
-          $replacements[$original] = $sanitize ? check_plain($name) : $name;
+          $replacements[$original] = check_plain($name);
           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;
       }
     }
