diff --git a/src/Plugin/WebformHandler/EmailWebformHandler.php b/src/Plugin/WebformHandler/EmailWebformHandler.php index 74b52bcb..c5caa290 100644 --- a/src/Plugin/WebformHandler/EmailWebformHandler.php +++ b/src/Plugin/WebformHandler/EmailWebformHandler.php @@ -23,6 +23,7 @@ use Drupal\webform\Plugin\WebformElementManagerInterface; use Drupal\webform\Plugin\WebformHandlerBase; use Drupal\webform\Plugin\WebformHandlerMessageInterface; use Drupal\webform\Twig\TwigExtension; +use Drupal\webform\Utility\Mail; use Drupal\webform\Utility\WebformElementHelper; use Drupal\webform\Utility\WebformOptionsHelper; use Drupal\webform\WebformSubmissionConditionsValidatorInterface; @@ -1069,7 +1070,7 @@ class EmailWebformHandler extends WebformHandlerBase implements WebformHandlerMe $message['from_name'] = preg_replace('/[<>]/', '', $message['from_name']); if (!empty($message['from_name'])) { - $from = $message['from_name'] . ' <' . $from . '>'; + $from = Mail::formatDisplayName($message['from_name']) . ' <' . $from . '>'; } $current_langcode = $this->languageManager->getCurrentLanguage()->getId(); diff --git a/src/Tests/Handler/WebformHandlerEmailBasicTest.php b/src/Tests/Handler/WebformHandlerEmailBasicTest.php index 4d132eea..73ef8ba5 100644 --- a/src/Tests/Handler/WebformHandlerEmailBasicTest.php +++ b/src/Tests/Handler/WebformHandlerEmailBasicTest.php @@ -113,7 +113,7 @@ class WebformHandlerEmailBasicTest extends WebformTestBase { ]; $this->postSubmission($webform, $edit); $sent_email = $this->getLastEmail(); - $this->assertEqual($sent_email['reply-to'], '"first_name" "last_name" '); + $this->assertEqual($sent_email['reply-to'], '"first_name\\" \\"last_name" '); $this->assertEqual($sent_email['subject'], 'This has & "special" \'characters\''); // NOTE: // Drupal's PhpMail::format function calls diff --git a/src/Utility/Mail.php b/src/Utility/Mail.php new file mode 100644 index 00000000..3ed9a477 --- /dev/null +++ b/src/Utility/Mail.php @@ -0,0 +1,71 @@ +[]:;@\,."'; + + /** + * 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 . 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. + */ + public static function formatDisplayName($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 = Html::decodeEntities($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 = Unicode::mimeHeaderEncode($string, TRUE); + + // Encoded-words are always safe to be used as display-name because don't + // contain any RFC 2822 "specials" characters. However + // Unicode::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(['\\\\', '\\"'], ['\\', '"'], $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(['\\', '"'], ['\\\\', '\\"'], $safe_display_name) . '"'; + + } + + return $safe_display_name; + } + +} diff --git a/webform.module b/webform.module index 2199dec0..24f0a76f 100644 --- a/webform.module +++ b/webform.module @@ -23,6 +23,7 @@ use Drupal\webform\Entity\Webform; use Drupal\webform\Entity\WebformSubmission; use Drupal\webform\Element\WebformMessage; use Drupal\webform\Plugin\WebformElement\ManagedFile; +use Drupal\webform\Utility\Mail; use Drupal\webform\Utility\WebformArrayHelper; use Drupal\webform\Utility\WebformElementHelper; use Drupal\webform\Utility\WebformDialogHelper; @@ -426,11 +427,11 @@ function webform_mail($key, &$message, $params) { $message['body'][] = $params['body']; // Set the header 'From'. - // Usingthe 'from_mail' so that the webform's email from value is used + // Using the 'from_mail' so that the webform's email from value is used // instead of site's email address. // @see: \Drupal\Core\Mail\MailManager::mail. if (!empty($params['from_mail'])) { - $message['from'] = $message['headers']['From'] = (!empty($params['from_name'])) ? Unicode::mimeHeaderEncode($params['from_name'], TRUE) . ' <' . $params['from_mail'] . '>' : $params['from_mail']; + $message['from'] = $message['headers']['From'] = (!empty($params['from_name'])) ? Mail::formatDisplayName($params['from_name']) . ' <' . $params['from_mail'] . '>' : $params['from_mail']; } // Set header 'Cc'. @@ -463,7 +464,7 @@ function webform_mail($key, &$message, $params) { $sender_mail = $params['sender_mail'] ?: ''; $sender_name = $params['sender_name'] ?: $params['from_name'] ?: ''; if ($sender_mail) { - $message['headers']['Sender'] = ($sender_name) ? Unicode::mimeHeaderEncode($sender_name, TRUE) . ' <' . $sender_mail . '>' : $sender_mail; + $message['headers']['Sender'] = ($sender_name) ? Mail::formatDisplayName($sender_name) . ' <' . $sender_mail . '>' : $sender_mail; } }