1 commit a29b8fb3146e318ba3fd9a084859f2e39553c084
2 Author: Elan Ruusamäe <glen@delfi.ee>
3 Date: Wed Feb 10 09:35:53 2016 +0200
10 src/Header/HeaderWrap.php
12 diff --git a/src/Header/HeaderWrap.php b/src/Header/HeaderWrap.php
13 index df532edc..e0be2f56 100644
14 --- a/src/Header/HeaderWrap.php
15 +++ b/src/Header/HeaderWrap.php
16 @@ -116,7 +116,21 @@ abstract class HeaderWrap
18 public static function canBeEncoded($value)
20 - $encoded = iconv_mime_encode('x-test', $value, array('scheme' => 'Q'));
21 + // avoid any wrapping by specifying line length long enough
23 + // "x-test: =?ISO-8859-1?B?dGVzdA==?=" -> 33
26 + $line_length = strlen($value) * 4 + strlen($charset) + 16;
28 + $preferences = array(
30 + 'input-charset' => $charset,
31 + 'output-charset' => $charset,
32 + 'line-length' => $line_length,
35 + $encoded = iconv_mime_encode('x-test', $value, $preferences);
37 return (false !== $encoded);
40 commit 755b22727a126608611b6a58c289ad6744db21a9
41 Merge: 6034313f 393b43c9
42 Author: Elan Ruusamäe <glen@delfi.ee>
43 Date: Mon Feb 15 11:51:30 2016 +0200
45 Merge tag 'release-2.4.9' into develop-2.4
52 commit 222ace2631198f7498ae7c3799caaf6fc3a7936a
53 Merge: 755b2272 c1c73d7f
54 Author: Elan Ruusamäe <glen@delfi.ee>
55 Date: Fri Jan 13 17:39:32 2017 +0200
57 Merge tag 'release-2.4.11' into develop-2.4
79 - Fixes the [ZF2016-04 advisory](https://framework.zend.com/security/advisory/ZF2016-04)
80 ("Potential remote code execution in zend-mail via Sendmail adapter").
82 commit 8493b2a0610c59fbeeaf0ffc44340810d4d84d93
83 Author: Etienne CHAMPETIER <etienne.champetier@fiducial.net>
84 Date: Fri May 29 14:32:09 2015 +0200
86 Headers: fix bad sprintf call
88 Signed-off-by: Etienne CHAMPETIER <etienne.champetier@fiducial.net>
90 diff --git a/src/Headers.php b/src/Headers.php
91 index 3ceb10be..b416f52a 100644
94 @@ -220,6 +220,7 @@ class Headers implements Countable, Iterator
95 if (!is_string($headerFieldNameOrLine)) {
96 throw new Exception\InvalidArgumentException(sprintf(
97 '%s expects its first argument to be a string; received "%s"',
99 (is_object($headerFieldNameOrLine)
100 ? get_class($headerFieldNameOrLine)
101 : gettype($headerFieldNameOrLine))
103 commit 66eeb12567335f3a23aea7538a82e9a144464427
104 Author: Denis Sokolov <denis@sokolov.cc>
105 Date: Wed Jun 10 16:44:27 2015 +0300
107 Handle simple comments in address lists
109 diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php
110 index e7db7240..b13f9ebd 100644
111 --- a/src/Header/AbstractAddressList.php
112 +++ b/src/Header/AbstractAddressList.php
113 @@ -63,6 +63,7 @@ abstract class AbstractAddressList implements HeaderInterface
116 $value = trim($value);
117 + $value = self::stripComments($value);
121 @@ -155,4 +156,19 @@ abstract class AbstractAddressList implements HeaderInterface
122 $value = $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
123 return (empty($value)) ? '' : sprintf('%s: %s', $name, $value);
126 + // Supposed to be private, protected as a workaround for PHP bug 68194
127 + protected static function stripComments($value)
129 + return preg_replace(
142 commit 5064f95148c1d996b5b25bc556830da9a8458e6a
143 Author: Denis Sokolov <denis@sokolov.cc>
144 Date: Thu Jun 11 13:27:04 2015 +0300
146 Handle groups in address lists
148 diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php
149 index b13f9ebd..e0b4e78c 100644
150 --- a/src/Header/AbstractAddressList.php
151 +++ b/src/Header/AbstractAddressList.php
152 @@ -58,6 +58,7 @@ abstract class AbstractAddressList implements HeaderInterface
154 // split value on ","
155 $fieldValue = str_replace(Headers::FOLDING, ' ', $fieldValue);
156 + $fieldValue = preg_replace('/[^:]+:([^;]*);/', '$1,', $fieldValue);
157 $values = str_getcsv($fieldValue, ',');
160 @@ -66,6 +67,7 @@ abstract class AbstractAddressList implements HeaderInterface
161 $value = self::stripComments($value);
164 + $values = array_filter($values);
166 $addressList = $header->getAddressList();
167 foreach ($values as $address) {
169 commit ebdc224fb6847c39e9691631eef23b3b1c3eb6a0
170 Author: Stefano Torresi <stefano@torresi.io>
171 Date: Wed Jun 3 17:33:40 2015 +0200
173 fixes zendframework/zf2#7555
175 diff --git a/src/Header/Sender.php b/src/Header/Sender.php
176 index 2efc23bf..e7bcac63 100644
177 --- a/src/Header/Sender.php
178 +++ b/src/Header/Sender.php
179 @@ -39,25 +39,23 @@ class Sender implements HeaderInterface
181 // check to ensure proper header type for this factory
182 if (strtolower($name) !== 'sender') {
183 - throw new Exception\InvalidArgumentException('Invalid header line for Sender string');
184 + throw new Exception\InvalidArgumentException('Invalid header name for Sender string');
187 - $header = new static();
190 + $header = new static();
191 + $hasMatches = preg_match('/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/', $value, $matches);
193 - // Check for address, and set if found
194 - if (preg_match('/^(?P<name>.*?)<(?P<email>[^>]+)>$/', $value, $matches)) {
195 - $senderName = trim($matches['name']);
196 - if (empty($senderName)) {
197 - $senderName = null;
199 - $senderEmail = $matches['email'];
201 - $senderEmail = $value;
202 + if ($hasMatches !== 1) {
203 + throw new Exception\InvalidArgumentException('Invalid header value for Sender string');
206 + $senderName = trim($matches['name']);
208 + if (empty($senderName)) {
209 + $senderName = null;
212 - $header->setAddress($senderEmail, $senderName);
213 + $header->setAddress($matches['email'], $senderName);
218 commit c012507f8ae08eb92edb022c290d84dd9966fb46
219 Author: Stefano Torresi <stefano@torresi.io>
220 Date: Mon Oct 26 20:26:11 2015 +0100
224 diff --git a/src/Header/Sender.php b/src/Header/Sender.php
225 index e7bcac63..9f532949 100644
226 --- a/src/Header/Sender.php
227 +++ b/src/Header/Sender.php
228 @@ -43,6 +43,12 @@ class Sender implements HeaderInterface
231 $header = new static();
234 + * matches the header value so that the email must be enclosed by < > when a name is present
235 + * 'name' and 'email' capture groups correspond respectively to 'display-name' and 'addr-spec' in the ABNF
236 + * @see https://tools.ietf.org/html/rfc5322#section-3.4
238 $hasMatches = preg_match('/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/', $value, $matches);
240 if ($hasMatches !== 1) {
242 commit bfe40077aeed5c2cf401d23d0508d63b5c44d8c6
243 Author: Elan Ruusamäe <glen@delfi.ee>
244 Date: Sun Jan 31 00:03:58 2016 +0200
246 add format param to toArray
248 this allows exporting headers in raw or encoded form
250 diff --git a/src/Headers.php b/src/Headers.php
251 index b416f52a..c05cde1b 100644
252 --- a/src/Headers.php
253 +++ b/src/Headers.php
254 @@ -430,10 +430,11 @@ class Headers implements Countable, Iterator
256 * Return the headers container as an array
258 - * @todo determine how to produce single line headers, if they are supported
259 + * @param bool $format Return the values in Mime::Encoded or in Raw format
261 + * @todo determine how to produce single line headers, if they are supported
263 - public function toArray()
264 + public function toArray($format = Header\HeaderInterface::FORMAT_RAW)
267 /* @var $header Header\HeaderInterface */
268 @@ -443,9 +444,9 @@ class Headers implements Countable, Iterator
269 if (!isset($headers[$name])) {
270 $headers[$name] = array();
272 - $headers[$name][] = $header->getFieldValue();
273 + $headers[$name][] = $header->getFieldValue($format);
275 - $headers[$header->getFieldName()] = $header->getFieldValue();
276 + $headers[$header->getFieldName()] = $header->getFieldValue($format);
281 commit 8892c77410b08d2f0df90d4813904c10d741bd20
282 Author: Daniel Król <daniel@krol.me>
283 Date: Wed Dec 2 17:24:47 2015 +0100
285 Inverse decode-split order, allow special char containing labels
287 diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php
288 index e0b4e78c..64e5f8f9 100644
289 --- a/src/Header/AbstractAddressList.php
290 +++ b/src/Header/AbstractAddressList.php
291 @@ -42,31 +42,44 @@ abstract class AbstractAddressList implements HeaderInterface
292 public static function fromString($headerLine)
294 list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
295 - $decodedValue = HeaderWrap::mimeDecodeValue($fieldValue);
296 - $wasEncoded = ($decodedValue !== $fieldValue);
297 - $fieldValue = $decodedValue;
299 if (strtolower($fieldName) !== static::$type) {
300 throw new Exception\InvalidArgumentException(sprintf(
301 - 'Invalid header line for "%s" string',
305 - $header = new static();
307 - $header->setEncoding('UTF-8');
308 + 'Invalid header line for "%s" string',
313 // split value on ","
314 $fieldValue = str_replace(Headers::FOLDING, ' ', $fieldValue);
315 $fieldValue = preg_replace('/[^:]+:([^;]*);/', '$1,', $fieldValue);
316 - $values = str_getcsv($fieldValue, ',');
317 + $values = str_getcsv($fieldValue, ',');
319 + $wasEncoded = false;
322 - function (&$value) {
323 - $value = trim($value);
324 + function (&$value) use (&$wasEncoded) {
325 + $decodedValue = HeaderWrap::mimeDecodeValue($value);
326 + $wasEncoded = $wasEncoded || ($decodedValue !== $value);
327 + $value = trim($decodedValue);
328 $value = self::stripComments($value);
329 + $value = preg_replace(
331 + '#(?<!\\\)"(.*)(?<!\\\)"#', //quoted-text
332 + '#\\\([\x01-\x09\x0b\x0c\x0e-\x7f])#' //quoted-pair
342 + $header = new static();
344 + $header->setEncoding('UTF-8');
347 $values = array_filter($values);
349 $addressList = $header->getAddressList();
351 commit 9b54b914885a3c07fa7522fe1bedf7fb4482392b
352 Author: Daniel Król <daniel@krol.me>
353 Date: Wed Dec 2 17:01:45 2015 +0100
355 Use RFC compliant EOL to allow new lines in message body
356 http://www.ietf.org/rfc/rfc2821.txt
358 diff --git a/src/Message.php b/src/Message.php
359 index e16c2438..f83c2961 100644
360 --- a/src/Message.php
361 +++ b/src/Message.php
362 @@ -551,7 +551,7 @@ class Message
363 $message = new static();
366 - Mime\Decode::splitMessage($rawMessage, $headers, $content);
367 + Mime\Decode::splitMessage($rawMessage, $headers, $content, Headers::EOL);
368 if ($headers->has('mime-version')) {
369 // todo - restore body to mime\message
372 commit 788375fa8b156424a6b9afe40dac23ba6f00b916
373 Author: Marvin Feldmann <BreyndotEchse@users.noreply.github.com>
374 Date: Fri Apr 15 16:53:17 2016 +0200
378 diff --git a/src/Address.php b/src/Address.php
379 index e97878d5..e4c89d20 100644
380 --- a/src/Address.php
381 +++ b/src/Address.php
382 @@ -27,7 +27,7 @@ class Address implements Address\AddressInterface
384 public function __construct($email, $name = null)
386 - $emailAddressValidator = new EmailAddressValidator(Hostname::ALLOW_LOCAL);
387 + $emailAddressValidator = new EmailAddressValidator(Hostname::ALLOW_DNS | Hostname::ALLOW_LOCAL);
388 if (! is_string($email) || empty($email)) {
389 throw new Exception\InvalidArgumentException('Email must be a valid email address');
392 commit e37c5e15185143eaaff7f2c4c2a64f605926c978
393 Author: Marvin Feldmann <BreyndotEchse@users.noreply.github.com>
394 Date: Tue Apr 19 16:24:38 2016 +0200
396 Return email address(es) with ACE
398 diff --git a/src/Header/AbstractAddressList.php b/src/Header/AbstractAddressList.php
399 index 64e5f8f9..db40486a 100644
400 --- a/src/Header/AbstractAddressList.php
401 +++ b/src/Header/AbstractAddressList.php
402 @@ -94,6 +94,19 @@ abstract class AbstractAddressList implements HeaderInterface
403 return $this->fieldName;
407 + * Safely convert UTF-8 encoded domain name to ASCII
408 + * @param string $domainName the UTF-8 encoded email
411 + protected function idnToAscii($domainName)
413 + if (extension_loaded('intl')) {
414 + return (idn_to_ascii($domainName) ?: $domainName);
416 + return $domainName;
419 public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
422 @@ -103,22 +116,29 @@ abstract class AbstractAddressList implements HeaderInterface
423 $email = $address->getEmail();
424 $name = $address->getName();
426 - if (empty($name)) {
427 - $emails[] = $email;
431 - if (false !== strstr($name, ',')) {
432 + if (!empty($name) && false !== strstr($name, ',')) {
433 $name = sprintf('"%s"', $name);
436 if ($format === HeaderInterface::FORMAT_ENCODED
437 && 'ASCII' !== $encoding
439 - $name = HeaderWrap::mimeEncodeValue($name, $encoding);
440 + if (!empty($name)) {
441 + $name = HeaderWrap::mimeEncodeValue($name, $encoding);
444 + if (preg_match('/^(.+)@([^@]+)$/', $email, $matches)) {
445 + $localPart = $matches[1];
446 + $hostname = $this->idnToAscii($matches[2]);
447 + $email = sprintf('%s@%s', $localPart, $hostname);
451 - $emails[] = sprintf('%s <%s>', $name, $email);
452 + if (empty($name)) {
453 + $emails[] = $email;
455 + $emails[] = sprintf('%s <%s>', $name, $email);
459 // Ensure the values are valid before sending them.
461 commit e00ac0101b964063c70e282575a5b1bc4022f227
462 Author: Elan Ruusamäe <glen@delfi.ee>
463 Date: Sun May 14 15:09:56 2017 +0300
465 restore php 5.3 compat in HeaderValue. #129
467 Error in HeaderValue (version 2.4.11 and 2.4.10) for PHP 5.3.26
469 diff --git a/src/Header/HeaderValue.php b/src/Header/HeaderValue.php
470 index 884cdcf7..5104a512 100644
471 --- a/src/Header/HeaderValue.php
472 +++ b/src/Header/HeaderValue.php
473 @@ -87,7 +87,7 @@ final class HeaderValue
474 $lf = ord($value[$i + 1]);
475 $sp = ord($value[$i + 2]);
477 - if ($lf !== 10 || ! in_array($sp, [9, 32], true)) {
478 + if ($lf !== 10 || ! in_array($sp, array(9, 32), true)) {
483 commit 6bbdb6b5b8f549fa9f87cc5a6adbb558dfefeb99
484 Merge: 786a1418 e00ac010
485 Author: Elan Ruusamäe <glen@delfi.ee>
486 Date: Sun May 14 15:16:24 2017 +0300
488 Merge branch 'fix-129' into develop-2.4
491 https://github.com/zendframework/zend-mail/issues/129
493 commit ddf2e8ec39394774e753b9a44793e845134a3524
494 Author: Matthew Weier O'Phinney <matthew@zend.com>
495 Date: Thu May 5 12:56:11 2016 -0500
497 Merge branch 'hotfix/86'
501 diff --git a/src/Headers.php b/src/Headers.php
502 index c05cde1b..eac2bf9f 100644
503 --- a/src/Headers.php
504 +++ b/src/Headers.php
505 @@ -228,7 +228,11 @@ class Headers implements Countable, Iterator
508 if ($fieldValue === null) {
509 - $this->addHeader(Header\GenericHeader::fromString($headerFieldNameOrLine));
510 + $headers = $this->loadHeader($headerFieldNameOrLine);
511 + $headers = is_array($headers) ? $headers : [$headers];
512 + foreach ($headers as $header) {
513 + $this->addHeader($header);
515 } elseif (is_array($fieldValue)) {
516 foreach ($fieldValue as $i) {
517 $this->addHeader(Header\GenericMultiHeader::fromString($headerFieldNameOrLine . ':' . $i));
518 @@ -466,6 +470,19 @@ class Headers implements Countable, Iterator
522 + * Create Header object from header line
524 + * @param string $headerLine
525 + * @return Header\HeaderInterface|Header\HeaderInterface[]
527 + public function loadHeader($headerLine)
529 + list($name, ) = Header\GenericHeader::splitHeaderLine($headerLine);
530 + $class = $this->getPluginClassLoader()->load($name) ?: Header\GenericHeader::class;
531 + return $class::fromString($headerLine);
539 commit c3ac503689ffd00ac51e43c97c311e8753ecd32c
540 Author: Elan Ruusamäe <glen@delfi.ee>
541 Date: Tue Jun 13 00:54:18 2017 +0300
543 fix MessageId having double brackets
545 this got broken from pull/86 when header lazyloading was omitted
547 diff --git a/src/Header/MessageId.php b/src/Header/MessageId.php
548 index 850757ea..03c52a75 100644
549 --- a/src/Header/MessageId.php
550 +++ b/src/Header/MessageId.php
551 @@ -27,7 +27,7 @@ class MessageId implements HeaderInterface
554 $header = new static();
555 - $header->setId($value);
556 + $header->setId(trim($value, '<>'));
561 commit 63ac228e6f883ed8e26a7b8a02130348e2332edf
562 Author: Elan Ruusamäe <glen@delfi.ee>
563 Date: Wed Jun 28 00:19:11 2017 +0300
565 skip lazy-load for array as well
571 this should be sent upstream, but upstream doesn't seem to respond even
572 to trivial pull-requests.
574 diff --git a/src/Headers.php b/src/Headers.php
575 index eac2bf9f..d4f9a9e7 100644
576 --- a/src/Headers.php
577 +++ b/src/Headers.php
578 @@ -207,9 +207,6 @@ class Headers implements Countable, Iterator
580 * Add a raw header line, either in name => value, or as a single string 'name: value'
582 - * This method allows for lazy-loading in that the parsing and instantiation of HeaderInterface object
583 - * will be delayed until they are retrieved by either get() or current()
585 * @throws Exception\InvalidArgumentException
586 * @param string $headerFieldNameOrLine
587 * @param string $fieldValue optional
588 @@ -238,7 +235,9 @@ class Headers implements Countable, Iterator
589 $this->addHeader(Header\GenericMultiHeader::fromString($headerFieldNameOrLine . ':' . $i));
592 - $this->addHeader(Header\GenericHeader::fromString($headerFieldNameOrLine . ':' . $fieldValue));
593 + $class = $this->getPluginClassLoader()->load($headerFieldNameOrLine) ?: Header\GenericHeader::class;
594 + $header = $class::fromString($headerFieldNameOrLine . ':' . $fieldValue);
595 + $this->addHeader($header);