diff --git a/includes/mail.inc b/includes/mail.inc
index 7272df972e2970f1faeb0aa050ae3758fff93777..13a6f4643907bd080566626041b66184b993caf2 100644
--- a/includes/mail.inc
+++ b/includes/mail.inc
@@ -57,6 +57,12 @@
  *     user_mail_tokens($variables, $data, $options);
  *     switch($key) {
  *       case 'notice':
+ *         // If the recipient can receive such notices by instant-message, do
+ *         // not send by email.
+ *         if (example_im_send($key, $message, $params)) {
+ *           $message['send'] = FALSE;
+ *           break;
+ *         }
  *         $langcode = $message['language']->language;
  *         $message['subject'] = t('Notification from !site', $variables, array('langcode' => $langcode));
  *         $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, array('langcode' => $langcode));
@@ -65,6 +71,19 @@
  *   }
  * @endcode
  *
+ * Another example, which uses drupal_mail() to format a message for sending
+ * later:
+ *
+ * @code
+ *   $params = array('current_conditions' => $data);
+ *   $to = 'user@example.com';
+ *   $message = drupal_mail('example', 'notice', $to, $language, $params, FALSE);
+ *   // Only add to the spool if sending was not canceled.
+ *   if ($message['send']) {
+ *     example_spool_message($message);
+ *   }
+ * @endcode
+ *
  * @param $module
  *   A module name to invoke hook_mail() on. The {$module}_mail() hook will be
  *   called to complete the $message structure which will already contain common
@@ -86,8 +105,10 @@
  * @param $from
  *   Sets From to this value, if given.
  * @param $send
- *   Send the message directly, without calling drupal_mail_system()->mail()
- *   manually.
+ *   If TRUE, drupal_mail() will call drupal_mail_system()->mail() to deliver
+ *   the message, and store the result in $message['result']. Modules
+ *   implementing hook_mail_alter() may cancel sending by setting
+ *   $message['send'] to FALSE.
  *
  * @return
  *   The $message array structure containing all details of the
@@ -108,6 +129,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
     'from'     => isset($from) ? $from : $default_from,
     'language' => $language,
     'params'   => $params,
+    'send'     => TRUE,
     'subject'  => '',
     'body'     => array()
   );
@@ -148,12 +170,20 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
 
   // Optionally send e-mail.
   if ($send) {
-    $message['result'] = $system->mail($message);
-
-    // Log errors
-    if (!$message['result']) {
-      watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
-      drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+    // The original caller requested sending. Sending was canceled by one or
+    // more hook_mail_alter() implementations. We set 'result' to NULL, because
+    // FALSE indicates an error in sending.
+    if (empty($message['send'])) {
+      $message['result'] = NULL;
+    }
+    // Sending was originally requested and was not canceled.
+    else {
+      $message['result'] = $system->mail($message);
+      // Log errors.
+      if (!$message['result']) {
+        watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
+        drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+      }
     }
   }
 
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index b820f530793073385e790c7d8bd782f2f72a892b..980bc146351bcd9cc818b88e73228771c4528e34 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -504,3 +504,16 @@ function simpletest_clean_results_table($test_id = NULL) {
   }
   return 0;
 }
+
+/**
+ * Implements hook_mail_alter().
+ *
+ * Aborts sending of messages with ID 'simpletest_cancel_test'.
+ *
+ * @see MailTestCase::testCancelMessage()
+ */
+function simpletest_mail_alter(&$message) {
+  if ($message['id'] == 'simpletest_cancel_test') {
+    $message['send'] = FALSE;
+  }
+}
diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test
index a6c7b40e5ef3b2eb46fb79be440ddf39ddb3787c..09dcde60c0db8b0ffa0ca512289678d27d11b1a0 100644
--- a/modules/simpletest/tests/mail.test
+++ b/modules/simpletest/tests/mail.test
@@ -22,7 +22,7 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
   }
 
   function setUp() {
-    parent::setUp();
+    parent::setUp(array('simpletest'));
 
     // Set MailTestCase (i.e. this class) as the SMTP library
     variable_set('mail_system', array('default-system' => 'MailTestCase'));
@@ -35,10 +35,28 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
     global $language;
 
     // Use MailTestCase for sending a message.
-    $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language);
+    $message = drupal_mail('simpletest', 'mail_test', 'testing@example.com', $language);
 
     // Assert whether the message was sent through the send function.
-    $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.'));
+    $this->assertEqual(self::$sent_message['to'], 'testing@example.com', t('Pluggable mail system is extendable.'));
+  }
+
+  /**
+   * Test that message sending may be canceled.
+   *
+   * @see simpletest_mail_alter()
+   */
+  function testCancelMessage() {
+    global $language;
+
+    // Reset the class variable holding a copy of the last sent message.
+    self::$sent_message = NULL;
+
+    // Send a test message that simpletest_mail_alter should cancel.
+    $message = drupal_mail('simpletest', 'cancel_test', 'cancel@example.com', $language);
+
+    // Assert that the message was not actually sent.
+    $this->assertNull(self::$sent_message, 'Message was canceled.');
   }
 
   /**
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index a2e71d2ccf0eb904f95af6fe3f8aff289951ce60..b2ae3192fe62e7fcce5c2e9fa5ca5045d32041d9 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -1903,11 +1903,19 @@ function hook_image_toolkits() {
  *  - 'language':
  *     The language object used to build the message before hook_mail_alter()
  *     is invoked.
+ *  - 'send':
+ *     Set to FALSE to abort sending this email message.
  *
  * @see drupal_mail()
  */
 function hook_mail_alter(&$message) {
   if ($message['id'] == 'modulename_messagekey') {
+    if (!example_notifications_optin($message['to'], $message['id'])) {
+      // If the recipient has opted to not receive such messages, cancel
+      // sending.
+      $message['send'] = FALSE;
+      return;
+    }
     $message['body'][] = "--\nMail sent out from " . variable_get('site_name', t('Drupal'));
   }
 }