Commit c00d584d authored by Ian McLean's avatar Ian McLean
Browse files

Issue #3169054: Use PHPMailer library to parse email addresses

parent c387fa61
Loading
Loading
Loading
Loading
+0 −64
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@
 */

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Routing\RouteMatchInterface;

/**
@@ -33,66 +32,3 @@ function phpmailer_smtp_mail($key, &$message, $params) {
      break;
  }
}

/**
 * Extract address and optional display name of an e-mail address.
 *
 * @param string $string
 *   A string containing one or more valid e-mail address(es) separated with
 *   commas.
 *
 * @return array
 *   An array containing all found e-mail addresses split into mail and name.
 *
 * @see http://tools.ietf.org/html/rfc5322#section-3.4
 */
function phpmailer_smtp_parse_address($string) {
  $parsed = [];

  // The display name may contain commas (3.4). Extract all quoted strings
  // (3.2.4) to a stack and replace them with a placeholder to prevent
  // splitting at wrong places.
  $string = preg_replace_callback('(".*?(?<!\\\\)")', '_phpmailer_smtp_stack', $string);

  // Build a regex that matches a name-addr (3.4).
  // @see valid_email_address()
  $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
  $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
  $ipv4 = '[0-9]{1,3}(?:\.[0-9]{1,3}){3}';
  $ipv6 = '[0-9a-fA-F]{1,4}(?:\:[0-9a-fA-F]{1,4}){7}';
  $address = "$user@(?:$domain|(?:\[(?:$ipv4|$ipv6)\]))";
  $adr_rx = "/^(?P<name>.*)\s<(?P<address>$address)>$/";

  // Split string into multiple parts and process each.
  foreach (explode(',', $string) as $email) {
    // Re-inject stripped placeholders.
    $email = preg_replace_callback('(\x01)', '_phpmailer_smtp_stack', trim($email));
    // Check if it's a name-addr or a plain address (3.4).
    if (preg_match($adr_rx, $email, $matches)) {
      // PHPMailer expects an unencoded display name.
      $parsed[] = ['mail' => $matches['address'], 'name' => Unicode::mimeHeaderDecode(stripslashes($matches['name']))];
    }
    else {
      $parsed[] = ['mail' => trim($email, '<>'), 'name' => ''];
    }
  }
  return $parsed;
}

/**
 * Implements a FIFO stack to store extracted quoted strings.
 */
function _phpmailer_smtp_stack($matches = NULL) {
  $string = $matches[0];
  static $stack = [];

  if ($string == "\x01") {
    // Unescape quoted characters (3.2.4) to prevent double escaping.
    return str_replace(['\"', '\\\\'], ['"', '\\'], array_shift($stack));
  }
  // Remove surrounding quotes and push on stack.
  array_push($stack, substr($string, 1, -1));
  // Return placeholder substitution. 0x01 may never appear outside a quoted
  // string (3.2.3).
  return "\x01";
}
+14 −14
Original line number Diff line number Diff line
@@ -340,9 +340,9 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory
  public function mail(array $message) {
    try {
      // Parse 'From' address.
      $from = phpmailer_smtp_parse_address($message['headers']['From']);
      $from = $this->parseAddresses($message['headers']['From']);
      $from = reset($from);
      $this->From = $from['mail'];
      $this->From = $from['address'];
      if ($from['name'] != '') {
        $this->FromName = $from['name'];
      }
@@ -352,18 +352,18 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory
      $phpmailer_smtp_debug_email = $this->configFactory->get('system.maintenance')->get('phpmailer_smtp_debug_email');
      if (empty($phpmailer_smtp_debug_email)) {
        // Set recipients.
        foreach (phpmailer_smtp_parse_address($message['to']) as $address) {
          $this->AddAddress($address['mail'], $address['name']);
        foreach ($this->parseAddresses($message['to']) as $address) {
          $this->AddAddress($address['address'], $address['name']);
        }
        // Extract CCs and BCCs from headers.
        if (!empty($message['headers']['Cc'])) {
          foreach (phpmailer_smtp_parse_address($message['headers']['Cc']) as $address) {
            $this->AddCC($address['mail'], $address['name']);
          foreach ($this->parseAddresses($message['headers']['Cc']) as $address) {
            $this->AddCC($address['address'], $address['name']);
          }
        }
        if (!empty($message['headers']['Bcc'])) {
          foreach (phpmailer_smtp_parse_address($message['headers']['Bcc']) as $address) {
            $this->AddBCC($address['mail'], $address['name']);
          foreach ($this->parseAddresses($message['headers']['Bcc']) as $address) {
            $this->AddBCC($address['address'], $address['name']);
          }
        }
      }
@@ -376,8 +376,8 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory

      // Extract Reply-To from headers.
      if (isset($message['headers']['Reply-To'])) {
        foreach (phpmailer_smtp_parse_address($message['headers']['Reply-To']) as $address) {
          $this->AddReplyTo($address['mail'], $address['name']);
        foreach ($this->parseAddresses($message['headers']['Reply-To']) as $address) {
          $this->AddReplyTo($address['address'], $address['name']);
        }
        unset($message['headers']['Reply-To']);
      }
@@ -385,7 +385,7 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory
      elseif ($this->config->get('smtp_always_replyto')) {
        // If no Reply-To header has been explicitly set, use the From address
        // to be able to respond to e-mails sent via Google Mail.
        $this->AddReplyTo($from['mail'], $from['name']);
        $this->AddReplyTo($from['address'], $from['name']);
      }

      // Extract Content-Type and charset.
@@ -429,9 +429,9 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory
      }

      // Set default sender address.
      $envelopeSender = phpmailer_smtp_parse_address($message['from']);
      $envelopeSender = $this->parseAddresses($message['from']);
      $envelopeSender = reset($envelopeSender);
      $this->Sender = $envelopeSender['mail'];
      $this->Sender = $envelopeSender['address'];

      // Check envelope sender option.
      $senderOption = $this->config->get('smtp_envelope_sender_option');
@@ -441,7 +441,7 @@ class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactory
      }

      if ($senderOption === 'from_address') {
        $this->Sender = $from['mail'];
        $this->Sender = $from['address'];
      }

      if ($senderOption === 'other') {