summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorChristoph M. Becker <cmbecker69@gmx.de>2021-03-01 16:18:40 +0100
committerChristoph M. Becker <cmbecker69@gmx.de>2021-03-01 18:46:21 +0100
commit71297a254b8f0d97c028f3324cbaf95adf8de33c (patch)
treed79731f1d753a65ecafdf13cf29dbbd49d871706 /ext
parent2c508c4d407e98a27ed2631ae88e2e10ee430003 (diff)
downloadphp-git-71297a254b8f0d97c028f3324cbaf95adf8de33c.tar.gz
Fix #80751: Comma in recipient name breaks email delivery
So far, `SendText()` simply separates potential email address lists at any comma, disregarding that commas inside a quoted-string do not delimit addresses. We fix that by introducing an own variant of `strtok_r()` which caters to quoted-strings. We also make `FormatEmailAddress()` aware of quoted strings. We do not cater to email address comments, and potentially other quirks of RFC 5322 email addresses, but catering to quoted-strings is supposed to solve almost all practical use cases. Co-authored-by: Nikita Popov <nikita.ppv@gmail.com> Closes GH-6735.
Diffstat (limited to 'ext')
-rw-r--r--ext/standard/tests/mail/bug80751.phpt93
1 files changed, 93 insertions, 0 deletions
diff --git a/ext/standard/tests/mail/bug80751.phpt b/ext/standard/tests/mail/bug80751.phpt
new file mode 100644
index 0000000000..d90be83851
--- /dev/null
+++ b/ext/standard/tests/mail/bug80751.phpt
@@ -0,0 +1,93 @@
+--TEST--
+Bug #80751 (Comma in recipient name breaks email delivery)
+--SKIPIF--
+<?php
+if (PHP_OS_FAMILY !== 'Windows') die('skip Windows only test');
+if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
+require_once __DIR__ . '/mail_skipif.inc';
+?>
+--INI--
+SMTP=localhost
+smtp_port=25
+--FILE--
+<?php
+require_once __DIR__ . '/mail_include.inc';
+
+function find_and_delete_message($username, $subject) {
+ global $default_mailbox, $password;
+
+ $imap_stream = imap_open($default_mailbox, $username, $password);
+ if ($imap_stream === false) {
+ die("Cannot connect to IMAP server $server: " . imap_last_error() . "\n");
+ }
+
+ $found = false;
+ $repeat_count = 20; // we will repeat a max of 20 times
+ while (!$found && $repeat_count > 0) {
+ // sleep for a while to allow msg to be delivered
+ sleep(1);
+
+ $num_messages = imap_check($imap_stream)->Nmsgs;
+ for ($i = $num_messages; $i > 0; $i--) {
+ $info = imap_headerinfo($imap_stream, $i);
+ if ($info->subject === $subject) {
+ $header = imap_fetchheader($imap_stream, $i);
+ echo "Return-Path header found: ";
+ var_dump(strpos($header, 'Return-Path: joe@example.com') !== false);
+ echo "To header found: ";
+ var_dump(strpos($header, 'To: "<bob@example.com>" <info@mail.local>') !== false);
+ echo "From header found: ";
+ var_dump(strpos($header, 'From: "<bob@example.com>" <joe@example.com>') !== false);
+ echo "Cc header found: ";
+ var_dump(strpos($header, 'Cc: "Lastname, Firstname\\\\" <admin@mail.local>') !== false);
+ imap_delete($imap_stream, $i);
+ $found = true;
+ break;
+ }
+ }
+ $repeat_count--;
+ }
+
+ imap_close($imap_stream, CL_EXPUNGE);
+ return $found;
+}
+
+$to = "\"<bob@example.com>\" <{$users[1]}@$domain>";
+$subject = bin2hex(random_bytes(16));
+$message = 'hello';
+$headers = "From: \"<bob@example.com>\" <joe@example.com>\r\n"
+ . "Cc: \"Lastname, Firstname\\\\\" <{$users[2]}@$domain>\r\n"
+ . "Bcc: \"Firstname \\\"Ni,ck\\\" Lastname\" <{$users[3]}@$domain>\r\n";
+
+$res = mail($to, $subject, $message, $headers);
+if ($res !== true) {
+ die("TEST FAILED : Unable to send test email\n");
+} else {
+ echo "Message sent OK\n";
+}
+
+foreach ([$users[1], $users[2], $users[3]] as $user) {
+ if (!find_and_delete_message("$user@$domain", $subject)) {
+ echo "TEST FAILED: email not delivered\n";
+ } else {
+ echo "TEST PASSED: Message sent and deleted OK\n";
+ }
+}
+?>
+--EXPECT--
+Message sent OK
+Return-Path header found: bool(true)
+To header found: bool(true)
+From header found: bool(true)
+Cc header found: bool(true)
+TEST PASSED: Message sent and deleted OK
+Return-Path header found: bool(true)
+To header found: bool(true)
+From header found: bool(true)
+Cc header found: bool(true)
+TEST PASSED: Message sent and deleted OK
+Return-Path header found: bool(true)
+To header found: bool(true)
+From header found: bool(true)
+Cc header found: bool(true)
+TEST PASSED: Message sent and deleted OK