Merge pull request #3217 from SirLouen/patch/3210
Modularizing and Simplifying the Address Parser
This commit is contained in:
commit
5ddea0610b
|
|
@ -56,7 +56,8 @@
|
||||||
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||||
"psr/log": "For optional PSR-3 debug logging",
|
"psr/log": "For optional PSR-3 debug logging",
|
||||||
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication",
|
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication",
|
||||||
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
|
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||||
|
"ext-imap": "Needed to support advanced email address parsing according to RFC822"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$PHPMAILER_LANG['authenticate'] = 'Error SMTP: Imposible autentificar.';
|
$PHPMAILER_LANG['authenticate'] = 'Error SMTP: Imposible autentificar.';
|
||||||
$PHPMAILER_LANG['buggy_php'] = 'Tu versión de PHP está afectada por un bug que puede resultar en mensajes corruptos. Para arreglarlo, cambia a enviar usando SMTP, deshabilita la opción mail.add_x_header en tu php.ini, cambia a MacOS o Linux, o actualiza tu PHP a la versión 7.0.17+ o 7.1.3+.';
|
$PHPMAILER_LANG['buggy_php'] = 'Tu versión de PHP ha sido afectada por un bug que puede resultar en mensajes corruptos. Para arreglarlo, cambia a enviar usando SMTP, deshabilita la opción mail.add_x_header en tu php.ini, cambia a MacOS o Linux, o actualiza tu PHP a la versión 7.0.17+ o 7.1.3+.';
|
||||||
$PHPMAILER_LANG['connect_host'] = 'Error SMTP: Imposible conectar al servidor SMTP.';
|
$PHPMAILER_LANG['connect_host'] = 'Error SMTP: Imposible conectar al servidor SMTP.';
|
||||||
$PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Datos no aceptados.';
|
$PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Datos no aceptados.';
|
||||||
$PHPMAILER_LANG['empty_message'] = 'El cuerpo del mensaje está vacío.';
|
$PHPMAILER_LANG['empty_message'] = 'El cuerpo del mensaje está vacío.';
|
||||||
|
|
@ -18,7 +18,7 @@ $PHPMAILER_LANG['execute'] = 'Imposible ejecutar: ';
|
||||||
$PHPMAILER_LANG['extension_missing'] = 'Extensión faltante: ';
|
$PHPMAILER_LANG['extension_missing'] = 'Extensión faltante: ';
|
||||||
$PHPMAILER_LANG['file_access'] = 'Imposible acceder al archivo: ';
|
$PHPMAILER_LANG['file_access'] = 'Imposible acceder al archivo: ';
|
||||||
$PHPMAILER_LANG['file_open'] = 'Error de Archivo: Imposible abrir el archivo: ';
|
$PHPMAILER_LANG['file_open'] = 'Error de Archivo: Imposible abrir el archivo: ';
|
||||||
$PHPMAILER_LANG['from_failed'] = 'La(s) siguiente(s) direcciones de remitente fallaron: ';
|
$PHPMAILER_LANG['from_failed'] = 'La siguiente dirección de remitente falló: ';
|
||||||
$PHPMAILER_LANG['instantiate'] = 'Imposible crear una instancia de la función Mail.';
|
$PHPMAILER_LANG['instantiate'] = 'Imposible crear una instancia de la función Mail.';
|
||||||
$PHPMAILER_LANG['invalid_address'] = 'Imposible enviar: dirección de email inválido: ';
|
$PHPMAILER_LANG['invalid_address'] = 'Imposible enviar: dirección de email inválido: ';
|
||||||
$PHPMAILER_LANG['invalid_header'] = 'Nombre o valor de encabezado no válido';
|
$PHPMAILER_LANG['invalid_header'] = 'Nombre o valor de encabezado no válido';
|
||||||
|
|
@ -34,3 +34,4 @@ $PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falló.';
|
||||||
$PHPMAILER_LANG['smtp_detail'] = 'Detalle: ';
|
$PHPMAILER_LANG['smtp_detail'] = 'Detalle: ';
|
||||||
$PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: ';
|
$PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: ';
|
||||||
$PHPMAILER_LANG['variable_set'] = 'No se pudo configurar la variable: ';
|
$PHPMAILER_LANG['variable_set'] = 'No se pudo configurar la variable: ';
|
||||||
|
$PHPMAILER_LANG['imap_recommended'] = 'No se recomienda usar el analizador de direcciones simplificado. Instala la extensión IMAP de PHP para un análisis RFC822 más completo.';
|
||||||
|
|
|
||||||
|
|
@ -1280,40 +1280,61 @@ class PHPMailer
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Use this simpler parser
|
//Use this simpler parser
|
||||||
$list = explode(',', $addrstr);
|
$addresses = self::parseSimplerAddresses($addrstr, $charset);
|
||||||
foreach ($list as $address) {
|
}
|
||||||
$address = trim($address);
|
|
||||||
//Is there a separate name part?
|
return $addresses;
|
||||||
if (strpos($address, '<') === false) {
|
}
|
||||||
//No separate name, just use the whole thing
|
|
||||||
if (static::validateAddress($address)) {
|
/**
|
||||||
$addresses[] = [
|
* Parse a string containing one or more RFC822-style comma-separated email addresses
|
||||||
'name' => '',
|
* with the form "display name <address>" into an array of name/address pairs.
|
||||||
'address' => $address,
|
* Uses a simpler parser that does not require the IMAP extension but doesnt support
|
||||||
];
|
* the full RFC822 spec. For full RFC822 support, use the PHP IMAP extension.
|
||||||
}
|
*
|
||||||
} else {
|
* @param string $addrstr The address list string
|
||||||
list($name, $email) = explode('<', $address);
|
* @param string $charset The charset to use when decoding the address list string.
|
||||||
$email = trim(str_replace('>', '', $email));
|
*
|
||||||
$name = trim($name);
|
* @return array
|
||||||
if (static::validateAddress($email)) {
|
*/
|
||||||
//Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
|
protected static function parseSimplerAddresses($addrstr, $charset)
|
||||||
//If this name is encoded, decode it
|
{
|
||||||
if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) {
|
// Emit a runtime notice to recommend using the IMAP extension for full RFC822 parsing
|
||||||
$origCharset = mb_internal_encoding();
|
trigger_error(self::lang('imap_recommended'), E_USER_NOTICE);
|
||||||
mb_internal_encoding($charset);
|
|
||||||
//Undo any RFC2047-encoded spaces-as-underscores
|
$list = explode(',', $addrstr);
|
||||||
$name = str_replace('_', '=20', $name);
|
foreach ($list as $address) {
|
||||||
//Decode the name
|
$address = trim($address);
|
||||||
$name = mb_decode_mimeheader($name);
|
//Is there a separate name part?
|
||||||
mb_internal_encoding($origCharset);
|
if (strpos($address, '<') === false) {
|
||||||
}
|
//No separate name, just use the whole thing
|
||||||
$addresses[] = [
|
if (static::validateAddress($address)) {
|
||||||
//Remove any surrounding quotes and spaces from the name
|
$addresses[] = [
|
||||||
'name' => trim($name, '\'" '),
|
'name' => '',
|
||||||
'address' => $email,
|
'address' => $address,
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list($name, $email) = explode('<', $address);
|
||||||
|
$email = trim(str_replace('>', '', $email));
|
||||||
|
$name = trim($name);
|
||||||
|
if (static::validateAddress($email)) {
|
||||||
|
//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('/^=\?.*\?=$/s', $name)) {
|
||||||
|
$origCharset = mb_internal_encoding();
|
||||||
|
mb_internal_encoding($charset);
|
||||||
|
//Undo any RFC2047-encoded spaces-as-underscores
|
||||||
|
$name = str_replace('_', '=20', $name);
|
||||||
|
//Decode the name
|
||||||
|
$name = mb_decode_mimeheader($name);
|
||||||
|
mb_internal_encoding($origCharset);
|
||||||
}
|
}
|
||||||
|
$addresses[] = [
|
||||||
|
//Remove any surrounding quotes and spaces from the name
|
||||||
|
'name' => trim($name, '\'" '),
|
||||||
|
'address' => $email,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2433,6 +2454,8 @@ class PHPMailer
|
||||||
'smtp_error' => 'SMTP server error: ',
|
'smtp_error' => 'SMTP server error: ',
|
||||||
'variable_set' => 'Cannot set or reset variable: ',
|
'variable_set' => 'Cannot set or reset variable: ',
|
||||||
'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
|
'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
|
||||||
|
'imap_recommended' => 'Using simplified address parser is not recommended. ' .
|
||||||
|
'Install the PHP IMAP extension for full RFC822 parsing.',
|
||||||
];
|
];
|
||||||
if (empty($lang_path)) {
|
if (empty($lang_path)) {
|
||||||
//Calculate an absolute path so it can work if CWD is not here
|
//Calculate an absolute path so it can work if CWD is not here
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
namespace PHPMailer\Test\PHPMailer;
|
namespace PHPMailer\Test\PHPMailer;
|
||||||
|
|
||||||
use PHPMailer\PHPMailer\PHPMailer;
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use ReflectionMethod;
|
||||||
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
|
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,36 +29,6 @@ use Yoast\PHPUnitPolyfills\TestCases\TestCase;
|
||||||
*/
|
*/
|
||||||
final class ParseAddressesTest extends TestCase
|
final class ParseAddressesTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Test RFC822 address splitting using the PHPMailer native implementation
|
|
||||||
* with the Mbstring extension available.
|
|
||||||
*
|
|
||||||
* @requires extension mbstring
|
|
||||||
*
|
|
||||||
* @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 testAddressSplittingNative($addrstr, $expected, $charset = null)
|
|
||||||
{
|
|
||||||
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'];
|
|
||||||
} elseif (empty($expected['native']) === false) {
|
|
||||||
$expectedOutput = $expected['native'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->verifyExpectations($parsed, $expectedOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test RFC822 address splitting using the IMAP implementation
|
* Test RFC822 address splitting using the IMAP implementation
|
||||||
* with the Mbstring extension available.
|
* with the Mbstring extension available.
|
||||||
|
|
@ -89,38 +60,6 @@ final class ParseAddressesTest extends TestCase
|
||||||
$this->verifyExpectations($parsed, $expectedOutput);
|
$this->verifyExpectations($parsed, $expectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test RFC822 address splitting using the PHPMailer native implementation
|
|
||||||
* without the Mbstring extension.
|
|
||||||
*
|
|
||||||
* @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 testAddressSplittingNativeNoMbstring($addrstr, $expected, $charset = null)
|
|
||||||
{
|
|
||||||
if (extension_loaded('mbstring')) {
|
|
||||||
self::markTestSkipped('Test requires MbString *not* to be available');
|
|
||||||
}
|
|
||||||
|
|
||||||
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'];
|
|
||||||
} elseif (empty($expected['native']) === false) {
|
|
||||||
$expectedOutput = $expected['native'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->verifyExpectations($parsed, $expectedOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test RFC822 address splitting using the IMAP implementation
|
* Test RFC822 address splitting using the IMAP implementation
|
||||||
* without the Mbstring extension.
|
* without the Mbstring extension.
|
||||||
|
|
@ -155,6 +94,52 @@ final class ParseAddressesTest extends TestCase
|
||||||
$this->verifyExpectations($parsed, $expectedOutput);
|
$this->verifyExpectations($parsed, $expectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test RFC822 address splitting using the native implementation
|
||||||
|
*
|
||||||
|
* @dataProvider dataAddressSplittingNative
|
||||||
|
*
|
||||||
|
* @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, $charset = PHPMailer::CHARSET_ISO88591)
|
||||||
|
{
|
||||||
|
error_reporting(E_ALL & ~E_USER_NOTICE);
|
||||||
|
$reflMethod = new ReflectionMethod(PHPMailer::class, 'parseSimplerAddresses');
|
||||||
|
(\PHP_VERSION_ID < 80100) && $reflMethod->setAccessible(true);
|
||||||
|
$parsed = $reflMethod->invoke(null, $addrstr, $charset);
|
||||||
|
(\PHP_VERSION_ID < 80100) && $reflMethod->setAccessible(false);
|
||||||
|
$this->verifyExpectations($parsed, $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for testAddressSplittingNative.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* addrstr: string,
|
||||||
|
* expected: array{name: string, address: string}[]
|
||||||
|
* charset: string
|
||||||
|
*/
|
||||||
|
public function dataAddressSplittingNative()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Valid address: single address without name' => [
|
||||||
|
'addrstr' => 'joe@example.com',
|
||||||
|
'expected' => [
|
||||||
|
['name' => '', 'address' => 'joe@example.com'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'Valid address: two addresses with names' => [
|
||||||
|
'addrstr' => 'Joe User <joe@example.com>, Jill User <jill@example.net>',
|
||||||
|
'expected' => [
|
||||||
|
['name' => 'Joe User', 'address' => 'joe@example.com'],
|
||||||
|
['name' => 'Jill User', 'address' => 'jill@example.net'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the expectations.
|
* Verify the expectations.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue