Final Act including iconv_mime_decode

This commit is contained in:
SirLouen 2025-08-17 00:01:46 +02:00
parent d2fc22a4f1
commit 99b482752e
2 changed files with 29 additions and 73 deletions

View File

@ -1238,15 +1238,14 @@ class PHPMailer
* @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
*
* @param string $addrstr The address list string
* @param bool $useimap Whether to use the IMAP extension to parse the list
* @param string $charset The charset to use when decoding the address list string.
*
* @return array
*/
public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591)
public static function parseAddresses($addrstr, $charset = self::CHARSET_ISO88591)
{
$addresses = [];
if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
if (function_exists('imap_rfc822_parse_adrlist')) {
//Use this built-in parser if it's available
$list = imap_rfc822_parse_adrlist($addrstr, '');
// Clear any potential IMAP errors to get rid of notices being thrown at end of script.
@ -3671,17 +3670,25 @@ class PHPMailer
if (!is_string($value) || $value === '') {
return '';
}
if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $value)) {
// Detect the presence of any RFC2047 encoded-words
$hasEncodedWord = (bool) preg_match('/=\?.*\?=/s', $value);
if ($hasEncodedWord && defined('MB_CASE_UPPER')) {
$origCharset = mb_internal_encoding();
// Always decode to UTF-8 to provide a consistent, modern output encoding
mb_internal_encoding(self::CHARSET_UTF8);
mb_internal_encoding($charset);
//Undo any RFC2047-encoded spaces-as-underscores
$value = str_replace('_', '=20', $value);
//Decode the name
// Decode the header value
$value = mb_decode_mimeheader($value);
mb_internal_encoding($origCharset);
} elseif ($hasEncodedWord && function_exists('iconv_mime_decode')) {
// Use iconv as a fallback when mbstring is not available
$mode = defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR') ? ICONV_MIME_DECODE_CONTINUE_ON_ERROR : 0;
$decoded = @iconv_mime_decode($value, $mode, $charset);
if ($decoded !== false) {
$value = $decoded;
}
}
return $value;
}

View File

@ -31,17 +31,15 @@ final class ParseAddressesTest extends TestCase
/**
* Test RFC822 address splitting using the PHPMailer native implementation
*
* @requires extension mbstring
* @group mbstringRequired
* @dataProvider dataAddressSplitting
*
* @param string $addrstr The address list string.
* @param array $expected The expected function output.
* @param string $charset Optional. The charset to use.
*/
public function testAddressSplitting($addrstr, $expected, $charset = PHPMailer::CHARSET_ISO88591)
public function testAddressSplitting($addrstr, $expected)
{
$parsed = PHPMailer::parseAddresses($addrstr, false, $charset);
$parsed = PHPMailer::parseAddresses($addrstr, PHPMailer::CHARSET_UTF8);
$this->verifyExpectations($parsed, $expected);
}
@ -50,40 +48,16 @@ final class ParseAddressesTest extends TestCase
* Test decodeHeader using the PHPMailer
* with the Mbstring extension available.
*
* @requires extension mbstring
* @group mbstringExtRequired
* @dataProvider dataDecodeHeader
*
* @param string $addrstr The header string.
* @param array $expected The expected function output.
* @param string $charset Optional. The charset to use.
*/
public function testDecodeHeaderMbstring($str, $expected, $charset = PHPMailer::CHARSET_ISO88591)
public function testDecodeHeader($str, $expected)
{
$parsed = PHPMailer::decodeHeader($str, $charset);
$parsed = PHPMailer::decodeHeader($str, PHPMailer::CHARSET_UTF8);
$this->assertEquals($parsed, $expected['mbstring']);
}
/**
* Test decodeHeader using the PHPMailer native implementation
* without the Mbstring extension.
*
* @group mbstringExtDisabled
* @dataProvider dataDecodeHeader
*
* @param string $addrstr The header string.
* @param array $expected The expected function output.
* @param string $charset Optional. The charset to use.
*/
public function testDecodeHeaderNative($str, $expected, $charset = PHPMailer::CHARSET_ISO88591)
{
if (extension_loaded('mbstring')) {
self::markTestSkipped('Test requires MbString *not* to be available');
}
$parsed = PHPMailer::decodeHeader($str, $charset);
$this->assertEquals($parsed, $expected['native']);
$this->assertEquals($parsed, $expected);
}
/**
@ -142,7 +116,7 @@ final class ParseAddressesTest extends TestCase
'Valid address: single address, quotes within name' => [
'addrstr' => 'Tim "The Book" O\'Reilly <foo@example.com>',
'expected' => [
['name' => 'Tim "The Book" O\'Reilly', 'address' => 'foo@example.com'],
['name' => 'Tim The Book O\'Reilly', 'address' => 'foo@example.com'],
],
],
'Valid address: two addresses with names' => [
@ -174,8 +148,7 @@ final class ParseAddressesTest extends TestCase
['name' => "John O'Groats", 'address' => 'johnog@example.net'],
['name' => 'Название теста', 'address' => 'encoded@example.org'],
['name' => 'Welcome to our café! Willkommen in unserem Café! Привет в наше кафе!', 'address' => 'encoded3@example.org'],
],
'charset' => PHPMailer::CHARSET_UTF8,
]
],
// Test cases with invalid addresses.
@ -198,58 +171,34 @@ final class ParseAddressesTest extends TestCase
* Data provider for decodeHeader.
*
* @return array The array is expected to have an `addrstr` and an `expected` key.
* The `expected` key should - as a minimum - have a `mbstring` and `native` key.
* - `mbstring` Expected output from the native implementation with Mbstring.
* - `native` Expected output from the native implementation without Mbstring.
* The `expected` key should - as a minimum - have a single value.
*/
public function dataDecodeHeader()
{
return [
'UTF-8 B-encoded' => [
'name' => '=?utf-8?B?0J3QsNC30LLQsNC90LjQtSDRgtC10YHRgtCw?=',
'expected' => [
'mbstring' => 'Название теста',
'native' => '=?utf-8?B?0J3QsNC30LLQsNC90LjQtSDRgtC10YHRgtCw?=',
],
'charset' => PHPMailer::CHARSET_UTF8,
'expected' => 'Название теста',
],
'UTF-8 Q-encoded' => [
'name' => '=?UTF-8?Q?=D0=9D=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?Q?=D0=B5_=D1=82=D0=B5=D1=81=D1=82=D0=B0?=',
'expected' => [
'mbstring' => 'Название теста',
'native' => '=?koi8-r?Q?=D0=9D=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8?= =?koi8-r?Q?=D0=B5_=D1=82=D0=B5=D1=81=D1=82=D0=B0?=',
],
'charset' => PHPMailer::CHARSET_UTF8,
'expected' => 'Название теста',
],
'UTF-8 Q-encoded with space encoded as _' => [
'UTF-8 Q-encoded with multiple wrong labels and space encoded as _' => [
'name' => '=?UTF-8?Q?Welcome_to_our_caf=C3=A9!?= =?ISO-8859-1?Q?_Willkommen_in_unserem_Caf=E9!?= =?KOI8-R?Q?_=F0=D2=C9=D7=C5=D4_=D7_=CE=C1=DB=C5_=CB=C1=C6=C5!?=',
'expected' => [
'mbstring' => 'Welcome to our café! Willkommen in unserem Café! Привет в наше кафе!',
'native' => '=?UTF-8?Q?Welcome_to_our_caf=C3=A9!?= =?ISO-8859-1?Q?_Willkommen_in_unserem_Caf=E9!?= =?KOI8-R?Q?_=F0=D2=C9=D7=C5=D4_=D7_=CE=C1=DB=C5_=CB=C1=C6=C5!?=',
],
'charset' => PHPMailer::CHARSET_UTF8,
'expected' => 'Welcome to our café! Willkommen in unserem Café! Привет в наше кафе!',
],
'ISO-8859-1 Q-encoded' => [
'name' => '=?ISO-8859-1?Q?Willkommen_in_unserem_Caf=E9!?=',
'expected' => [
'mbstring' => 'Willkommen in unserem Café!',
'native' => 'Willkommen in unserem Café!',
],
'expected' => 'Willkommen in unserem Café!',
],
'Valid but wrongly labeled UTF-8 as ISO-8859-1' => [
'name' => '=?iso-8859-1?B?5pyD6K2w5a6k?=',
'expected' => [
'mbstring' => "æ\xC2\x9C\xC2\x83議室",
'native' => '=?iso-8859-1?B?5pyD6K2w5a6k?=',
],
'expected' => "æ\xC2\x9C\xC2\x83議室",
],
'SMTPUTF8 encoded' => [
'name' => '=?UTF-8?B?SGVsbG8g8J+MjSDkuJbnlYwgY2Fmw6k=?=',
'expected' => [
'mbstring' => 'Hello 🌍 世界 café',
'native' => '=?UTF-8?B?SGVsbG8g8J+MjSDkuJbnlYwgY2Fmw6k=?=',
],
'charset' => PHPMailer::CHARSET_UTF8,
'expected' => 'Hello 🌍 世界 café',
],
];
}