From 3511823348e9d3f0db8c74dd54bd2cc302eb9d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sat, 20 May 2017 14:24:24 +0300 Subject: [PATCH 1/5] add testcase with To header semicolon separated --- test/AddressListTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/AddressListTest.php b/test/AddressListTest.php index 63877c67..06f99755 100644 --- a/test/AddressListTest.php +++ b/test/AddressListTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase; use Zend\Mail\Address; use Zend\Mail\AddressList; +use Zend\Mail\Header; /** * @group Zend_Mail @@ -105,4 +106,21 @@ public function testDoesNotStoreDuplicatesAndFirstWins() $address = $this->list->get('zf-devteam@zend.com'); $this->assertNull($address->getName()); } + + + /** + * Microsoft Outlook sent emails are semicolon separated + * + * @see https://blogs.msdn.microsoft.com/oldnewthing/20150119-00/?p=44883 + */ + public function testSemicolonSeparator() + { + $header = 'Some User ; uzer2.surname@example.org; asda.fasd@example.net, root@example.org'; + + // this throws: 'The input exceeds the allowed length' + $to = Header\To::fromString('To:' . $header); + $addressList = $to->getAddressList(); + + $this->assertEquals('Some User', $addressList->get('some.user@example.com')->getName()); + } } From 9b77807c459a6754715630200c19618e967ba020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Thu, 8 Jun 2017 23:06:47 +0300 Subject: [PATCH 2/5] convert exception to assertion --- src/Header/AbstractAddressList.php | 1 + test/AddressListTest.php | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php index a296c45d..0954b6e4 100644 --- a/src/Header/AbstractAddressList.php +++ b/src/Header/AbstractAddressList.php @@ -80,6 +80,7 @@ function (&$value) use (&$wasEncoded) { $values = array_filter($values); + /** @var AddressList $addressList */ $addressList = $header->getAddressList(); foreach ($values as $address) { $addressList->addFromString($address); diff --git a/test/AddressListTest.php b/test/AddressListTest.php index 06f99755..457675d4 100644 --- a/test/AddressListTest.php +++ b/test/AddressListTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase; use Zend\Mail\Address; use Zend\Mail\AddressList; +use Zend\Mail\Exception\InvalidArgumentException; use Zend\Mail\Header; /** @@ -107,18 +108,23 @@ public function testDoesNotStoreDuplicatesAndFirstWins() $this->assertNull($address->getName()); } - /** - * Microsoft Outlook sent emails are semicolon separated + * Microsoft Outlook sends emails with semicolon separated To addresses. * * @see https://blogs.msdn.microsoft.com/oldnewthing/20150119-00/?p=44883 */ public function testSemicolonSeparator() { - $header = 'Some User ; uzer2.surname@example.org; asda.fasd@example.net, root@example.org'; - - // this throws: 'The input exceeds the allowed length' - $to = Header\To::fromString('To:' . $header); + $header = 'Some User ; uzer2.surname@example.org;' + . ' asda.fasd@example.net, root@example.org'; + + // In previous versions, this throws: 'The input exceeds the allowed + // length'; hence the try/catch block, to allow finding the root cause. + try { + $to = Header\To::fromString('To:' . $header); + } catch (InvalidArgumentException $e) { + $this->fail('Header\To::fromString should not throw'); + } $addressList = $to->getAddressList(); $this->assertEquals('Some User', $addressList->get('some.user@example.com')->getName()); From 5c2a409207982650bd3916a318d886e60fdacd7f Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 6 Jun 2018 11:07:42 -0500 Subject: [PATCH 3/5] Refactor if/else into if + (return|continue) --- src/AddressList.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/AddressList.php b/src/AddressList.php index 36116c0f..2d6a13ad 100644 --- a/src/AddressList.php +++ b/src/AddressList.php @@ -31,7 +31,9 @@ public function add($emailOrAddress, $name = null) { if (is_string($emailOrAddress)) { $emailOrAddress = $this->createAddress($emailOrAddress, $name); - } elseif (! $emailOrAddress instanceof Address\AddressInterface) { + } + + if (! $emailOrAddress instanceof Address\AddressInterface) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an email address or %s\Address object as its first argument; received "%s"', __METHOD__, @@ -65,14 +67,17 @@ public function addMany(array $addresses) foreach ($addresses as $key => $value) { if (is_int($key) || is_numeric($key)) { $this->add($value); - } elseif (is_string($key)) { - $this->add($key, $value); - } else { + continue; + } + + if (! is_string($key)) { throw new Exception\RuntimeException(sprintf( 'Invalid key type in provided addresses array ("%s")', (is_object($key) ? get_class($key) : var_export($key, 1)) )); } + + $this->add($key, $value); } return $this; } From fc5d02376619d489b83cc56a3095023c23848e65 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 6 Jun 2018 11:43:03 -0500 Subject: [PATCH 4/5] Allow comma or semicolon separation in a list This patch adds a new class, `AddressListParser`, with a single static method `parse()`. It loops through each character of the value to identify non-escaped, non-quoted delimiters, allowing either `,` or `;` to be used. `AbstractAddressList::fromString()` now uses the above method instead of `str_getcsv()` to extract the list of addresses. The patch also updates the test provided in #147 to test for the existence of all addresses in the test string. --- src/Header/AbstractAddressList.php | 2 +- src/Header/AddressListParser.php | 86 ++++++++++++++++++++++++++++++ test/AddressListTest.php | 3 ++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/Header/AddressListParser.php diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php index 0954b6e4..1ac31595 100644 --- a/src/Header/AbstractAddressList.php +++ b/src/Header/AbstractAddressList.php @@ -50,7 +50,7 @@ public static function fromString($headerLine) // split value on "," $fieldValue = str_replace(Headers::FOLDING, ' ', $fieldValue); $fieldValue = preg_replace('/[^:]+:([^;]*);/', '$1,', $fieldValue); - $values = str_getcsv($fieldValue, ','); + $values = AddressListParser::parse($fieldValue); $wasEncoded = false; array_walk( diff --git a/src/Header/AddressListParser.php b/src/Header/AddressListParser.php new file mode 100644 index 00000000..cb6141b0 --- /dev/null +++ b/src/Header/AddressListParser.php @@ -0,0 +1,86 @@ +getAddressList(); $this->assertEquals('Some User', $addressList->get('some.user@example.com')->getName()); + $this->assertTrue($addressList->has('uzer2.surname@example.org')); + $this->assertTrue($addressList->has('asda.fasd@example.net')); + $this->assertTrue($addressList->has('root@example.org')); } } From 83f9ddecdd4eb655b67823acf7d0da09d23820fe Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 6 Jun 2018 11:48:28 -0500 Subject: [PATCH 5/5] Adds CHANGELOG entry for #147 Also removes stub for 3.0.0; master branch is targeted at 2.9.x-dev. --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4235e9..ac1bf0e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ All notable changes to this project will be documented in this file, in reverse - [#213](https://github.com/zendframework/zend-mail/pull/213) re-adds support for PHP 5.6 and 7.0; ZF policy is never to bump the major version of a PHP requirement unless the package is bumping major version. +### Changed + +- Nothing. + ### Deprecated - Nothing. @@ -19,7 +23,8 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed -- Nothing. +- [#147](https://github.com/zendframework/zend-mail/pull/147) fixes how address lists are parsed, expanding the functionality to allow either + `,` or `;` delimiters (or both in combination). ## 2.9.0 - 2017-03-01