Index: includes/mail.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/mail.inc,v
retrieving revision 1.25
diff -u -p -r1.25 mail.inc
--- includes/mail.inc	1 Sep 2009 17:40:27 -0000	1.25
+++ includes/mail.inc	3 Oct 2009 04:15:36 -0000
@@ -14,7 +14,7 @@
  * appropriate places in the template. Processed e-mail templates are
  * requested from hook_mail() from the module sending the e-mail. Any module
  * can modify the composed e-mail message array using hook_mail_alter().
- * Finally drupal_mail_sending_system()->mail() sends the e-mail, which can
+ * Finally drupal_mail_class('system')->mail() sends the e-mail, which can
  * be reused if the exact same composed e-mail is to be sent to multiple
  * recipients.
  *
@@ -79,7 +79,7 @@
  *   Sets From to this value, if given.
  * @param $send
  *   Send the message directly, without calling
- *   drupal_mail_sending_system()->mail() manually.
+ *   drupal_mail_class('system')->mail() manually.
  * @return
  *   The $message array structure containing all details of the
  *   message. If already sent ($send = TRUE), then the 'result' element
@@ -129,12 +129,15 @@ function drupal_mail($module, $key, $to,
   // Invoke hook_mail_alter() to allow all modules to alter the resulting e-mail.
   drupal_alter('mail', $message);
 
-  // Concatenate and wrap the e-mail body.
-  $message['body'] = is_array($message['body']) ? drupal_wrap_mail(implode("\n\n", $message['body'])) : drupal_wrap_mail($message['body']);
+  // Format the message body.
+  drupal_mail_class('format', $module, $key)
+    ->formatPreprocess($message)
+    ->format($message)
+    ->formatPostprocess($message);
 
   // Optionally send e-mail.
   if ($send) {
-    $message['result'] = drupal_mail_sending_system($module, $key)->mail($message);
+    $message['result'] = drupal_mail_class('system', $module, $key)->mail($message);
 
     // Log errors
     if (!$message['result']) {
@@ -154,7 +157,7 @@ function drupal_mail($module, $key, $to,
  *
  * The selection of a particular implementation is controlled via the variable
  * 'mail_sending_system', which is a keyed array.  The default implementation
- * is the class whose name is the value of 'default-system' key. A more specific
+ * is the class whose name is the value of 'default-class' key. A more specific
  * match first to key and then to module will be used in preference to the
  * default. To specificy a different class for all mail sent by one module, set
  * the class name as the value for the key corresponding to the module name. To
@@ -167,7 +170,7 @@ function drupal_mail($module, $key, $to,
  *
  * @code
  * array(
- *   'default-system' => 'DefaultMailSystem',
+ *   'default-class' => 'DefaultMailSystem',
  *   'user' => 'DevelMailLog',
  * );
  * @endcode
@@ -177,7 +180,7 @@ function drupal_mail($module, $key, $to,
  *
  * @code
  * array(
- *   'default-system' => 'DefaultMailSystem',
+ *   'default-class' => 'DefaultMailSystem',
  *   'user' => 'DevelMailLog',
  *   'contact_page_autoreply' => 'DrupalDevNullMailSend',
  * );
@@ -189,17 +192,28 @@ function drupal_mail($module, $key, $to,
  * sending or for sending via a remote gateway so as to reduce the load
  * on the local server.
  *
+ * @param $type
+ *   The mail interface to retrieve, e.g. 'format' or 'system'.
  * @param $module
  *   The module name which was used by drupal_mail() to invoke hook_mail().
  * @param $key
  *   A key to identify the e-mail sent. The final e-mail ID for the e-mail
  *   alter hook in drupal_mail() would have been {$module}_{$key}.
  */
-function drupal_mail_sending_system($module, $key) {
+function drupal_mail_class($type, $module, $key) {
   $instances = &drupal_static(__FUNCTION__, array());
 
   $id = $module . '_' . $key;
-  $configuration = variable_get('mail_sending_system', array('default-system' => 'DefaultMailSystem'));
+
+  if ($type == 'format') {
+    $configuration = variable_get('mail_formatter', array('default-class' => 'DefaultTextMailFormat'));
+  }
+  elseif ($type == 'system') {
+    $configuration = variable_get('mail_sending_system', array('default-class' => 'DefaultMailSystem'));
+  }
+  else {
+    throw new Exception(t('Mail interface %interface does not exist.', array('%interface' => 'Mail' . ucfirst($type) . 'Interface')));
+  }
 
   // Look for overrides for the default class, starting from the most specific
   // id, and falling back to the module name.
@@ -210,25 +224,69 @@ function drupal_mail_sending_system($mod
     $class = $configuration[$module];
   }
   else {
-    $class = $configuration['default-system'];
+    $class = $configuration['default-class'];
   }
 
   if (empty($instances[$class])) {
     $interfaces = class_implements($class);
-    if (isset($interfaces['MailSystemInterface'])) {
+    if (isset($interfaces['MailInterface'])) {
       $instances[$class] = new $class;
     }
     else {
-      throw new Exception(t('Class %class does not implement interface %interface', array('%class' => $class, '%interface' => 'MailSystemInterface')));
+      throw new Exception(t('Class %class does not implement interface %interface', array('%class' => $class, '%interface' => 'MailInterface')));
     }
   }
   return $instances[$class];
 }
 
 /**
+ * The base interface for all mail interfaces.
+ */
+interface MailInterface {
+}
+
+/**
+ * An interface for pluggable mail formatters.
+ */
+interface MailFormatInterface extends MailInterface {
+  /**
+   * Preprocess a message composed by drupal_mail() prior formatting.
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   */
+   public function formatPreprocess(array &$message);
+
+  /**
+   * Format a message composed by drupal_mail().
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   */
+   public function format(array &$message);
+
+  /**
+   * Postprocess a message composed by drupal_mail() after formatting.
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   */
+   public function formatPostprocess(array &$message);
+}
+
+/**
  * An interface for pluggable mail back-ends.
  */
-interface MailSystemInterface {
+interface MailSystemInterface extends MailInterface {
   /**
    * Send an e-mail message composed by drupal_mail().
    *
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.155
diff -u -p -r1.155 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	29 Sep 2009 15:31:15 -0000	1.155
+++ modules/simpletest/drupal_web_test_case.php	3 Oct 2009 04:17:21 -0000
@@ -1106,7 +1106,7 @@ class DrupalWebTestCase extends DrupalTe
     $language = language_default();
 
     // Use the test mail class instead of the default mail handler class.
-    variable_set('mail_sending_system', array('default-system' => 'TestingMailSystem'));
+    variable_set('mail_sending_system', array('default-class' => 'TestingMailSystem'));
 
     // Use temporary files directory with the same prefix as the database.
     $public_files_directory  = $this->originalFileDirectory . '/' . $db_prefix;
@@ -2564,3 +2564,4 @@ function simpletest_verbose($message, $o
   }
   return FALSE;
 }
+
Index: modules/simpletest/simpletest.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v
retrieving revision 1.32
diff -u -p -r1.32 simpletest.test
--- modules/simpletest/simpletest.test	29 Sep 2009 15:31:15 -0000	1.32
+++ modules/simpletest/simpletest.test	3 Oct 2009 02:26:15 -0000
@@ -339,7 +339,7 @@ class SimpleTestMailCaptureTestCase exte
     $this->assertEqual(count($captured_emails), 0, t('The captured e-mails queue is empty.'), t('E-mail'));
 
     // Send the e-mail.
-    $response = drupal_mail_sending_system('simpletest', 'drupal_mail_test')->mail($message);
+    $response = drupal_mail_class('system', 'simpletest', 'drupal_mail_test')->mail($message);
 
     // Ensure that there is one e-mail in the captured e-mails array.
     $captured_emails = $this->drupalGetMails();
@@ -360,7 +360,7 @@ class SimpleTestMailCaptureTestCase exte
         'to' => $this->randomName(32) . '@example.com',
         'body' => $this->randomString(512),
       );
-      drupal_mail_sending_system('drupal_mail_test', $index)->mail($message);
+      drupal_mail_class('system', 'drupal_mail_test', $index)->mail($message);
     }
 
     // There should now be 6 e-mails captured.
@@ -377,7 +377,7 @@ class SimpleTestMailCaptureTestCase exte
 
     // Send the last e-mail again, so we can confirm that the drupalGetMails-filter
     // correctly returns all e-mails with a given property/value.
-    drupal_mail_sending_system('drupal_mail_test', $index)->mail($message);
+    drupal_mail_class('system', 'drupal_mail_test', $index)->mail($message);
     $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test_4'));
     $this->assertEqual(count($captured_emails), 2, t('All e-mails with the same id are returned when filtering by id.'), t('E-mail'));
   }
Index: modules/simpletest/tests/mail.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/mail.test,v
retrieving revision 1.1
diff -u -p -r1.1 mail.test
--- modules/simpletest/tests/mail.test	31 Aug 2009 18:30:26 -0000	1.1
+++ modules/simpletest/tests/mail.test	3 Oct 2009 01:41:34 -0000
@@ -25,7 +25,7 @@ class MailTestCase extends DrupalWebTest
     parent::setUp();
 
     // Set MailTestCase (i.e. this class) as the SMTP library
-    variable_set('mail_sending_system', array('default-system' => 'MailTestCase'));
+    variable_set('mail_sending_system', array('default-class' => 'MailTestCase'));
   }
 
   /**
Index: modules/system/mail.sending.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/mail.sending.inc,v
retrieving revision 1.1
diff -u -p -r1.1 mail.sending.inc
--- modules/system/mail.sending.inc	31 Aug 2009 18:30:27 -0000	1.1
+++ modules/system/mail.sending.inc	3 Oct 2009 04:17:49 -0000
@@ -3,10 +3,61 @@
 
 /**
  * @file
- * Drupal core implementations of the DrupalMailSendingInterface.
+ * Drupal core implementations of MailInterface.
  */
 
 /**
+ * The default Drupal mail formatting class for plain-text e-mails.
+ */
+class DefaultTextMailFormat implements MailFormatInterface {
+  /**
+   * Concatenate the e-mail body.
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   */
+   public function formatPreprocess(array &$message) {
+     // Join the body array into one string.
+     $message['body'] = implode("\n\n", $message['body']);
+     // Remove any HTML from the message.
+     $message['body'] = drupal_html_to_text($message['body']);
+     return $this;
+   }
+
+  /**
+   * Wrap the e-mail body for plain-text mails.
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   *
+   * @see drupal_wrap_mail()
+   */
+   public function format(array &$message) {
+     $message['body'] = drupal_wrap_mail($message['body']);
+     return $this;
+   }
+
+  /**
+   * Postprocess a message composed by drupal_mail() after formatting.
+   *
+   * @param &$message
+   *   A message array, as described in hook_mail_alter().
+   *
+   * @return
+   *   $this. $message is passed by reference.
+   */
+   public function formatPostprocess(array &$message) {
+     return $this;
+   }
+}
+
+/**
  * The default Drupal mail sending library using PHP's mail function.
  */
 class DefaultMailSystem implements MailSystemInterface {
@@ -43,9 +94,10 @@ class DefaultMailSystem implements MailS
  * A mail sending implementation that captures sent messages to a variable.
  *
  * This class is for running tests or for development.
+ *
+ * @todo This needs to live in this file for unknown reason.
  */
 class TestingMailSystem implements MailSystemInterface {
-
   /**
    * Accept an e-mail message and store it in a variable.
    *
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.80
diff -u -p -r1.80 system.api.php
--- modules/system/system.api.php	1 Oct 2009 13:16:17 -0000	1.80
+++ modules/system/system.api.php	3 Oct 2009 02:26:36 -0000
@@ -677,7 +677,7 @@ function hook_image_toolkits() {
  *
  * Email messages sent using functions other than drupal_mail() will not
  * invoke hook_mail_alter(). For example, a contributed module directly
- * calling the drupal_mail_sending_system()->mail() or PHP mail() function
+ * calling the drupal_mail_class('system')->mail() or PHP mail() function
  * will not invoke this hook. All core modules use drupal_mail() for
  * messaging, it is best practice but not manditory in contributed modules.
  *
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.800
diff -u -p -r1.800 system.module
--- modules/system/system.module	30 Sep 2009 18:36:02 -0000	1.800
+++ modules/system/system.module	3 Oct 2009 02:35:01 -0000
@@ -2566,7 +2566,7 @@ function system_mail($key, &$message, $p
   $body = token_replace($context['message'], $context);
 
   $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
-  $message['body'][] = drupal_html_to_text($body);
+  $message['body'][] = $body;
 }
 
 function system_message_action_form($context) {
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1055
diff -u -p -r1.1055 user.module
--- modules/user/user.module	2 Oct 2009 14:49:10 -0000	1.1055
+++ modules/user/user.module	3 Oct 2009 02:26:44 -0000
@@ -2897,7 +2897,7 @@ function user_preferred_language($accoun
  * @param $language
  *  Optional language to use for the notification, overriding account language.
  * @return
- *  The return value from drupal_mail_sending_system()->mail(), if ends up
+ *  The return value from drupal_mail_class('system')->mail(), if ends up
  *  being called.
  */
 function _user_mail_notify($op, $account, $language = NULL) {
