Commit 6cc95966 authored by Drew Webber's avatar Drew Webber
Browse files

Issue #3098058 by mcdruid, SAVEL, alexpott, alexandra.vecher,...

Issue #3098058 by mcdruid, SAVEL, alexpott, alexandra.vecher, nikolas.tatianenko, kiamlaluno, sjerdo, RobLoach, catch, cburschka, carlos8f, penyaskito, gdud, theborg, pillarsdotnet, olamaekle, naxoc: [D7] Use site name in From: header for system e-mails
parent 62a95e34
Loading
Loading
Loading
Loading
+62 −2
Original line number Diff line number Diff line
@@ -12,6 +12,12 @@
 */
define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') !== FALSE) ? "\r\n" : "\n");


/**
 * Special characters, defined in RFC_2822.
 */
define('MAIL_RFC_2822_SPECIALS', '()<>[]:;@\,."');

/**
 * Composes and optionally sends an e-mail message.
 *
@@ -148,8 +154,13 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
    // Return-Path headers should have a domain authorized to use the originating
    // SMTP server.
    $headers['From'] = $headers['Sender'] = $headers['Return-Path'] = $default_from;

    if (variable_get('mail_display_name_site_name', FALSE)) {
      $display_name = variable_get('site_name', 'Drupal');
      $headers['From'] = drupal_mail_format_display_name($display_name) . ' <' . $default_from . '>';
    }
  }
  if ($from) {
  if ($from && $from != $default_from) {
    $headers['From'] = $from;
  }
  $message['headers'] = $headers;
@@ -557,10 +568,59 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
  return $output . $footnotes;
}

/**
 * Return a RFC-2822 compliant "display-name" component.
 *
 * The "display-name" component is used in mail header "Originator" fields
 * (From, Sender, Reply-to) to give a human-friendly description of the
 * address, i.e. From: My Display Name <xyz@example.org>. RFC-822 and
 * RFC-2822 define its syntax and rules. This method gets as input a string
 * to be used as "display-name" and formats it to be RFC compliant.
 *
 * @param string $string
 *   A string to be used as "display-name".
 *
 * @return string
 *   A RFC compliant version of the string, ready to be used as
 *   "display-name" in mail originator header fields.
 */
function drupal_mail_format_display_name($string) {
  // Make sure we don't process html-encoded characters. They may create
  // unneeded trouble if left encoded, besides they will be correctly
  // processed if decoded.
  $string = decode_entities($string);

  // If string contains non-ASCII characters it must be (short) encoded
  // according to RFC-2047. The output of a "B" (Base64) encoded-word is
  // always safe to be used as display-name.
  $safe_display_name = mime_header_encode($string, TRUE);

  // Encoded-words are always safe to be used as display-name because don't
  // contain any RFC 2822 "specials" characters. However
  // mimeHeaderEncode() encodes a string only if it contains any
  // non-ASCII characters, and leaves its value untouched (un-encoded) if
  // ASCII only. For this reason in order to produce a valid display-name we
  // still need to make sure there are no "specials" characters left.
  if (preg_match('/[' . preg_quote(MAIL_RFC_2822_SPECIALS) . ']/', $safe_display_name)) {

    // If string is already quoted, it may or may not be escaped properly, so
    // don't trust it and reset.
    if (preg_match('/^"(.+)"$/', $safe_display_name, $matches)) {
      $safe_display_name = str_replace(array('\\\\', '\\"'), array('\\', '"'), $matches[1]);
    }

    // Transform the string in a RFC-2822 "quoted-string" by wrapping it in
    // double-quotes. Also make sure '"' and '\' occurrences are escaped.
    $safe_display_name = '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $safe_display_name) . '"';
  }

  return $safe_display_name;
}

/**
 * Wraps words on a single line.
 *
 * Callback for array_walk() winthin drupal_wrap_mail().
 * Callback for array_walk() within drupal_wrap_mail().
 */
function _drupal_wrap_mail_line(&$line, $key, $values) {
  // Use soft-breaks only for purely quoted or unindented text.
+75 −0
Original line number Diff line number Diff line
@@ -59,6 +59,81 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
    $this->assertNull(self::$sent_message, 'Message was canceled.');
  }

  /**
   * Checks for the site name in an auto-generated From: header.
   */
  function testFromHeader() {
    global $language;
    $default_from = variable_get('site_mail', ini_get('sendmail_from'));
    $site_name = variable_get('site_name', 'Drupal');

    // Reset the class variable holding a copy of the last sent message.
    self::$sent_message = NULL;
    // Send an e-mail with a sender address specified.
    $from_email = 'someone_else@example.com';
    $message = drupal_mail('simpletest', 'from_test', 'from_test@example.com', $language, array(), $from_email);
    // Test that the from e-mail is just the e-mail and not the site name and
    // default sender e-mail.
    $this->assertEqual($from_email, self::$sent_message['headers']['From']);

    // Check default behavior is only email in FROM header.
    self::$sent_message = NULL;
    // Send an e-mail and check that the From-header contains only default mail address.
    variable_del('mail_display_name_site_name');
    $message = drupal_mail('simpletest', 'from_test', 'from_test@example.com', $language);
    $this->assertEqual($default_from, self::$sent_message['headers']['From']);

    self::$sent_message = NULL;
    // Send an e-mail and check that the From-header contains the site name.
    variable_set('mail_display_name_site_name', TRUE);
    $message = drupal_mail('simpletest', 'from_test', 'from_test@example.com', $language);
    $this->assertEqual($site_name . ' <' . $default_from . '>', self::$sent_message['headers']['From']);
  }

  /**
   * Checks for the site name in an auto-generated From: header.
   */
  function testFromHeaderRfc2822Compliant() {
    global $language;
    $default_from = variable_get('site_mail', ini_get('sendmail_from'));

    // Enable adding a site name to From.
    variable_set('mail_display_name_site_name', TRUE);

    $site_names = array(
      // Simple ASCII characters.
      'Test site' => 'Test site',
      // ASCII with html entity.
      'Test &amp; site' => 'Test & site',
      // Non-ASCII characters.
      'Tést site' => '=?UTF-8?B?VMOpc3Qgc2l0ZQ==?=',
      // Non-ASCII with special characters.
      'Tést; site' => '=?UTF-8?B?VMOpc3Q7IHNpdGU=?=',
      // Non-ASCII with html entity.
      'T&eacute;st; site' => '=?UTF-8?B?VMOpc3Q7IHNpdGU=?=',
      // ASCII with special characters.
      'Test; site' => '"Test; site"',
      // ASCII with special characters as html entity.
      'Test &lt; site' => '"Test < site"',
      // ASCII with special characters and '\'.
      'Test; \ "site"' => '"Test; \\\\ \"site\""',
      // String already RFC-2822 compliant.
      '"Test; site"' => '"Test; site"',
      // String already RFC-2822 compliant.
      '"Test; \\\\ \"site\""' => '"Test; \\\\ \"site\""',
    );

    foreach ($site_names as $original_name => $safe_string) {
      variable_set('site_name', $original_name);

      // Reset the class variable holding a copy of the last sent message.
      self::$sent_message = NULL;
      // Send an e-mail and check that the From-header contains is RFC-2822 compliant.
      drupal_mail('simpletest', 'from_test', 'from_test@example.com', $language);
      $this->assertEqual($safe_string . ' <' . $default_from . '>', self::$sent_message['headers']['From']);
    }
  }

  /**
   * Concatenate and wrap the e-mail body for plain-text mails.
   *
+15 −0
Original line number Diff line number Diff line
@@ -701,3 +701,18 @@
 * optimization and revert to the original behaviour.
 */
# $conf['variable_initialize_wait_for_lock'] = FALSE;

/**
 * Use site name as display-name in outgoing mail.
 *
 * Drupal can use the site name (i.e. the value of the site_name variable) as
 * the display-name when sending e-mail. For example this would mean the sender
 * might be "Acme Website" <acme@example.com> as opposed to just the e-mail
 * address alone. In order to avoid disruption this is not enabled by default
 * for existing sites. The feature can be enabled by setting this variable to
 * TRUE.
 *
 * @see https://tools.ietf.org/html/rfc2822
 * @see drupal_mail()
 */
$conf['mail_display_name_site_name'] = TRUE;