Merge branch 'PHP-8.0'

* PHP-8.0:
  Fix #80751: Comma in recipient name breaks email delivery
This commit is contained in:
Christoph M. Becker 2021-03-01 18:50:58 +01:00
commit 87a5a0e2cb
2 changed files with 179 additions and 16 deletions

View File

@ -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

View File

@ -333,6 +333,37 @@ PHPAPI char *GetSMErrorText(int index)
}
}
/* strtok_r like, but recognizes quoted-strings */
static char *find_address(char *list, char **state)
{
zend_bool in_quotes = 0;
char *p = list;
if (list == NULL) {
if (*state == NULL) {
return NULL;
}
p = list = *state;
}
*state = NULL;
while ((p = strpbrk(p, ",\"\\")) != NULL) {
if (*p == '\\' && in_quotes) {
if (p[1] == '\0') {
/* invalid address; let SMTP server deal with it */
break;
}
p++;
} else if (*p == '"') {
in_quotes = !in_quotes;
} else if (*p == ',' && !in_quotes) {
*p = '\0';
*state = p + 1;
break;
}
p++;
}
return list;
}
/*********************************************************************
// Name: SendText
@ -357,7 +388,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
{
int res;
char *p;
char *tempMailTo, *token;
char *tempMailTo, *token, *token_state;
const char *pos1, *pos2;
char *server_response = NULL;
char *stripped_header = NULL;
@ -411,7 +442,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
tempMailTo = estrdup(mailTo);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -425,14 +456,14 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
if (mailCc && *mailCc) {
tempMailTo = estrdup(mailCc);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -446,7 +477,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
}
@ -472,7 +503,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
tempMailTo = estrndup(pos1, pos2 - pos1);
}
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -486,7 +517,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL,&token_state);
}
efree(tempMailTo);
}
@ -497,7 +528,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
if (mailBcc && *mailBcc) {
tempMailTo = estrdup(mailBcc);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -511,7 +542,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
}
@ -545,7 +576,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
}
}
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -559,7 +590,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char *
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
@ -979,6 +1010,46 @@ static unsigned long GetAddr(LPSTR szHost)
return (lAddr);
} /* end GetAddr() */
/* returns the contents of an angle-addr (caller needs to efree) or NULL */
static char *get_angle_addr(char *address)
{
zend_bool in_quotes = 0;
char *p1 = address, *p2;
while ((p1 = strpbrk(p1, "<\"\\")) != NULL) {
if (*p1 == '\\' && in_quotes) {
if (p1[1] == '\0') {
/* invalid address; let SMTP server deal with it */
return NULL;
}
p1++;
} else if (*p1 == '"') {
in_quotes = !in_quotes;
} else if (*p1 == '<' && !in_quotes) {
break;
}
p1++;
}
if (p1 == NULL) return NULL;
p2 = ++p1;
while ((p2 = strpbrk(p2, ">\"\\")) != NULL) {
if (*p2 == '\\' && in_quotes) {
if (p2[1] == '\0') {
/* invalid address; let SMTP server deal with it */
return NULL;
}
p2++;
} else if (*p2 == '"') {
in_quotes = !in_quotes;
} else if (*p2 == '>' && !in_quotes) {
break;
}
p2++;
}
if (p2 == NULL) return NULL;
return estrndup(p1, p2 - p1);
}
/*********************************************************************
// Name: int FormatEmailAddress
@ -994,13 +1065,12 @@ static unsigned long GetAddr(LPSTR szHost)
// History:
//********************************************************************/
static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString) {
char *tmpAddress1, *tmpAddress2;
char *tmpAddress;
int result;
if( (tmpAddress1 = strchr(EmailAddress, '<')) && (tmpAddress2 = strchr(tmpAddress1, '>')) ) {
*tmpAddress2 = 0; // terminate the string temporarily.
result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString , tmpAddress1+1);
*tmpAddress2 = '>'; // put it back the way it was.
if ((tmpAddress = get_angle_addr(EmailAddress)) != NULL) {
result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString, tmpAddress);
efree(tmpAddress);
return result;
}
return snprintf(Buf, MAIL_BUFFER_SIZE , FormatString , EmailAddress );