diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ce7a7c88..18808995 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -116,7 +116,7 @@ jobs: with: php-version: ${{ matrix.php }} coverage: ${{ steps.set_cov.outputs.COV }} - ini-values: sendmail_path=/usr/sbin/sendmail -t -i, zend.multibyte=1, zend.script_encoding=UTF-8, default_charset=UTF-8, error_reporting=E_ALL, display_errors=On + ini-values: sendmail_path=/usr/sbin/sendmail -t -i, error_reporting=E_ALL, display_errors=On extensions: imap, mbstring, intl, ctype, filter, hash # Install dependencies and handle caching in one go. diff --git a/src/PHPMailer.php b/src/PHPMailer.php index d8d8bd45..3f11d93b 100644 --- a/src/PHPMailer.php +++ b/src/PHPMailer.php @@ -1188,7 +1188,7 @@ class PHPMailer * * @return array */ - public static function parseAddresses($addrstr, $useimap = true) + public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) { $addresses = []; if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { @@ -1209,7 +1209,10 @@ class PHPMailer defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/', $address->personal) ) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); $address->personal = mb_decode_mimeheader($address->personal); + mb_internal_encoding($origCharset); } $addresses[] = [ @@ -1240,7 +1243,10 @@ class PHPMailer //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled //If this name is encoded, decode it if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/', $name)) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); $name = mb_decode_mimeheader($name); + mb_internal_encoding($origCharset); } $addresses[] = [ //Remove any surrounding quotes and spaces from the name @@ -1723,7 +1729,7 @@ class PHPMailer fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); - $addrinfo = static::parseAddresses($toAddr); + $addrinfo = static::parseAddresses($toAddr, true, $this->charSet); $this->doCallback( ($result === 0), [[$addrinfo['address'], $addrinfo['name']]], @@ -1883,7 +1889,7 @@ class PHPMailer if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); - $addrinfo = static::parseAddresses($toAddr); + $addrinfo = static::parseAddresses($toAddr, true, $this->charSet); $this->doCallback( $result, [[$addrinfo['address'], $addrinfo['name']]], diff --git a/test/PHPMailer/ParseAddressesTest.php b/test/PHPMailer/ParseAddressesTest.php index 1bc6ec65..6f08a7f4 100644 --- a/test/PHPMailer/ParseAddressesTest.php +++ b/test/PHPMailer/ParseAddressesTest.php @@ -19,6 +19,11 @@ use Yoast\PHPUnitPolyfills\TestCases\TestCase; /** * Test RFC822 address splitting. * + * @todo Additional tests need to be added to verify the correct handling of inputs which + * include a different encoding than UTF8 or even mixed encoding. For more information + * on what these test cases should look like and should test, please see + * {@link https://github.com/PHPMailer/PHPMailer/pull/2449} for context. + * * @covers \PHPMailer\PHPMailer\PHPMailer::parseAddresses */ final class ParseAddressesTest extends TestCase @@ -34,10 +39,16 @@ final class ParseAddressesTest extends TestCase * * @param string $addrstr The address list string. * @param array $expected The expected function output. + * @param string $charset Optional. The charset to use. */ - public function testAddressSplittingNative($addrstr, $expected) + public function testAddressSplittingNative($addrstr, $expected, $charset = null) { - $parsed = PHPMailer::parseAddresses($addrstr, false); + if (isset($charset)) { + $parsed = PHPMailer::parseAddresses($addrstr, false, $charset); + } else { + $parsed = PHPMailer::parseAddresses($addrstr, false); + } + $expectedOutput = $expected['default']; if (empty($expected['native+mbstring']) === false) { $expectedOutput = $expected['native+mbstring']; @@ -59,10 +70,16 @@ final class ParseAddressesTest extends TestCase * * @param string $addrstr The address list string. * @param array $expected The expected function output. + * @param string $charset Optional. The charset to use. */ - public function testAddressSplittingImap($addrstr, $expected) + public function testAddressSplittingImap($addrstr, $expected, $charset = null) { - $parsed = PHPMailer::parseAddresses($addrstr, true); + if (isset($charset)) { + $parsed = PHPMailer::parseAddresses($addrstr, true, $charset); + } else { + $parsed = PHPMailer::parseAddresses($addrstr, true); + } + $expectedOutput = $expected['default']; if (empty($expected['imap+mbstring']) === false) { $expectedOutput = $expected['imap+mbstring']; @@ -81,14 +98,20 @@ final class ParseAddressesTest extends TestCase * * @param string $addrstr The address list string. * @param array $expected The expected function output. + * @param string $charset Optional. The charset to use. */ - public function testAddressSplittingNativeNoMbstring($addrstr, $expected) + public function testAddressSplittingNativeNoMbstring($addrstr, $expected, $charset = null) { if (extension_loaded('mbstring')) { $this->markTestSkipped('Test requires MbString *not* to be available'); } - $parsed = PHPMailer::parseAddresses($addrstr, false); + if (isset($charset)) { + $parsed = PHPMailer::parseAddresses($addrstr, false, $charset); + } else { + $parsed = PHPMailer::parseAddresses($addrstr, false); + } + $expectedOutput = $expected['default']; if (empty($expected['native--mbstring']) === false) { $expectedOutput = $expected['native--mbstring']; @@ -109,14 +132,20 @@ final class ParseAddressesTest extends TestCase * * @param string $addrstr The address list string. * @param array $expected The expected function output. + * @param string $charset Optional. The charset to use. */ - public function testAddressSplittingImapNoMbstring($addrstr, $expected) + public function testAddressSplittingImapNoMbstring($addrstr, $expected, $charset = null) { if (extension_loaded('mbstring')) { $this->markTestSkipped('Test requires MbString *not* to be available'); } - $parsed = PHPMailer::parseAddresses($addrstr, true); + if (isset($charset)) { + $parsed = PHPMailer::parseAddresses($addrstr, true, $charset); + } else { + $parsed = PHPMailer::parseAddresses($addrstr, true); + } + $expectedOutput = $expected['default']; if (empty($expected['imap--mbstring']) === false) { $expectedOutput = $expected['imap--mbstring']; @@ -157,6 +186,7 @@ final class ParseAddressesTest extends TestCase * - `imap` Expected output from the IMAP implementation with or without Mbstring. * - `imap+mbstring` Expected output from the IMAP implementation with Mbstring. * - `imap--mbstring` Expected output from the IMAP implementation without Mbstring. + * Also optionally, an additional `charset` key can be passed, */ public function dataAddressSplitting() { @@ -282,6 +312,7 @@ final class ParseAddressesTest extends TestCase ], ], ], + 'charset' => PHPMailer::CHARSET_UTF8, ], // Test cases with invalid addresses.