Decode encoded names in the address parser, see #2266

This commit is contained in:
Marcus Bointon 2021-02-18 11:46:07 +01:00
parent c2c5a3b9af
commit e2eb2304fe
No known key found for this signature in database
GPG Key ID: DE31CD6EB646AA24
4 changed files with 77 additions and 43 deletions

View File

@ -5,6 +5,7 @@
* Switch to Github Actions for CI
* Generate debug output for `mail()` and `sendmail` transports enable using the same mechanism as for SMTP: set `SMTPDebug` > 0
* Make the `mail()` transport set the envelope sender the same way as SMTP does, i.e. use whatever `From` is set to, only falling back to the `sendmail_from` php.ini setting if `From` is unset. This avoids errors from the `mail()` function if `Sender` is not set explicitly and php.ini is not configured. This is a minor functionality change, so bumps the minor version number.
* Extend `parseAddresses` to decode encoded names, improve tests
## Version 6.2.0
* PHP 8.0 compatibility, many thanks to @jrf_nl!

View File

@ -1196,6 +1196,11 @@ class PHPMailer
$address->mailbox . '@' . $address->host
)
) {
//Decode the name part if it's present and encoded
if (property_exists($address, 'personal') && preg_match('/^=\?.*\?=$/', $address->personal)) {
$address->personal = mb_decode_mimeheader($address->personal);
}
$addresses[] = [
'name' => (property_exists($address, 'personal') ? $address->personal : ''),
'address' => $address->mailbox . '@' . $address->host,
@ -1219,9 +1224,15 @@ class PHPMailer
} else {
list($name, $email) = explode('<', $address);
$email = trim(str_replace('>', '', $email));
$name = trim($name);
if (static::validateAddress($email)) {
//If this name is encoded, decode it
if (preg_match('/^=\?.*\?=$/', $name)) {
$name = mb_decode_mimeheader($name);
}
$addresses[] = [
'name' => trim(str_replace(['"', "'"], '', $name)),
//Remove any surrounding quotes and spaces from the name
'name' => trim($name, '\'" '),
'address' => $email,
];
}

View File

@ -3029,24 +3029,81 @@ EOT;
}
/**
* Test RFC822 address list parsing using PHPMailer's parser.
* @test
*/
public function imapParsedAddressList_parseAddress_returnsAddressArray()
{
$addressLine = 'joe@example.com, <me@example.com>, Joe Doe <doe@example.com>, "John O\'Groats" <johnog@example.net>,' .
' =?utf-8?B?0J3QsNC30LLQsNC90LjQtSDRgtC10YHRgtCw?= <encoded@example.org>';
//Test using PHPMailer's own parser
$expected = [
[
'name' => 'joe',
'name' => '',
'address' => 'joe@example.com',
],
[
'name' => 'me',
'address' => 'me@home.com',
'name' => '',
'address' => 'me@example.com',
],
[
'name' => 'Joe Doe',
'address' => 'doe@example.com',
],
[
'name' => "John O'Groats",
'address' => 'johnog@example.net',
],
[
'name' => 'Название теста',
'address' => 'encoded@example.org',
],
];
if (file_exists($this->INCLUDE_DIR . '/test/fakefunctions.php')) {
include $this->INCLUDE_DIR . '/test/fakefunctions.php';
$addresses = PHPMailer::parseAddresses('joe@example.com, me@home.com');
$this->assertEquals(asort($expected), asort($addresses));
$parsed = PHPMailer::parseAddresses($addressLine, false);
$this->assertSameSize($expected, $parsed);
for ($i = 0; $i < count($expected); $i++) {
$this->assertSame($expected[$i], $parsed[$i]);
}
}
/**
* Test RFC822 address list parsing using the IMAP extension's parser.
* @test
*/
public function imapParsedAddressList_parseAddress_returnsAddressArray_usingImap()
{
if (!extension_loaded('imap')) {
$this->markTestSkipped("imap extension missing, can't run this test");
}
$addressLine = 'joe@example.com, <me@example.com>, Joe Doe <doe@example.com>, "John O\'Groats" <johnog@example.net>,' .
' =?utf-8?B?0J3QsNC30LLQsNC90LjQtSDRgtC10YHRgtCw?= <encoded@example.org>';
$expected = [
[
'name' => '',
'address' => 'joe@example.com',
],
[
'name' => '',
'address' => 'me@example.com',
],
[
'name' => 'Joe Doe',
'address' => 'doe@example.com',
],
[
'name' => "John O'Groats",
'address' => 'johnog@example.net',
],
[
'name' => 'Название теста',
'address' => 'encoded@example.org',
],
];
$parsed = PHPMailer::parseAddresses($addressLine, true);
$this->assertSameSize($expected, $parsed);
for ($i = 0; $i < count($expected); $i++) {
$this->assertSame($expected[$i], $parsed[$i]);
}
}

View File

@ -13,38 +13,3 @@ if (!function_exists('mb_convert_encoding')) {
return true;
}
}
if (!function_exists('imap_rfc822_parse_adrlist')) {
function imap_rfc822_parse_adrlist($addressList)
{
$addresses = explode(',', $addressList);
$fakedAddresses = [];
foreach ($addresses as $address) {
$fakedAddresses[] = new FakeAddress($address);
}
return $fakedAddresses;
}
if (!class_exists(FakeAddress::class)) {
class FakeAddress
{
public $host = 'example.com';
public $mailbox = 'joe';
public $personal = 'joe example';
/**
* FakeAddress constructor.
*
* @param string $addressString
*/
public function __construct($addressString)
{
$addressParts = explode('@', $addressString);
$this->mailbox = trim($addressParts[0]);
$this->host = trim($addressParts[1]);
$this->personal = explode('.', $addressParts[1])[0];
}
}
}
}