diff --git a/src/Symfony/Component/Mailer/DelayedEnvelope.php b/src/Symfony/Component/Mailer/DelayedEnvelope.php index e892984cb4..41146319c7 100644 --- a/src/Symfony/Component/Mailer/DelayedEnvelope.php +++ b/src/Symfony/Component/Mailer/DelayedEnvelope.php @@ -41,11 +41,11 @@ final class DelayedEnvelope extends Envelope public function getSender(): Address { - if ($this->senderSet) { - return parent::getSender(); + if (!$this->senderSet) { + parent::setSender(self::getSenderFromHeaders($this->message->getHeaders())); } - return self::getSenderFromHeaders($this->message->getHeaders()); + return parent::getSender(); } public function setRecipients(array $recipients): void diff --git a/src/Symfony/Component/Mailer/Envelope.php b/src/Symfony/Component/Mailer/Envelope.php index cdf99d1d2f..fb6d320169 100644 --- a/src/Symfony/Component/Mailer/Envelope.php +++ b/src/Symfony/Component/Mailer/Envelope.php @@ -44,6 +44,10 @@ class Envelope public function setSender(Address $sender): void { + // to ensure deliverability of bounce emails independent of UTF-8 capabilities of SMTP servers + if (!preg_match('/^[^@\x80-\xFF]++@/', $sender->getAddress())) { + throw new InvalidArgumentException(sprintf('Invalid sender "%s": non-ASCII characters not supported in local-part of email.', $sender->getAddress())); + } $this->sender = new Address($sender->getAddress()); } diff --git a/src/Symfony/Component/Mailer/Tests/EnvelopeTest.php b/src/Symfony/Component/Mailer/Tests/EnvelopeTest.php index 33e2fdb09a..1efbef69dd 100644 --- a/src/Symfony/Component/Mailer/Tests/EnvelopeTest.php +++ b/src/Symfony/Component/Mailer/Tests/EnvelopeTest.php @@ -13,9 +13,11 @@ namespace Symfony\Component\Mailer\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Exception\InvalidArgumentException; use Symfony\Component\Mailer\Exception\LogicException; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Header\Headers; +use Symfony\Component\Mime\Header\PathHeader; use Symfony\Component\Mime\Message; use Symfony\Component\Mime\RawMessage; @@ -27,6 +29,13 @@ class EnvelopeTest extends TestCase $this->assertEquals(new Address('fabien@symfony.com'), $e->getSender()); } + public function testConstructorWithAddressSenderAndNonAsciiCharactersInLocalPartOfAddress() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid sender "fabièn@symfony.com": non-ASCII characters not supported in local-part of email.'); + new Envelope(new Address('fabièn@symfony.com'), [new Address('thomas@symfony.com')]); + } + public function testConstructorWithNamedAddressSender() { $e = new Envelope(new Address('fabien@symfony.com', 'Fabien'), [new Address('thomas@symfony.com')]); @@ -57,19 +66,27 @@ class EnvelopeTest extends TestCase $headers->addPathHeader('Return-Path', new Address('return@symfony.com', 'return')); $headers->addMailboxListHeader('To', ['from@symfony.com']); $e = Envelope::create(new Message($headers)); - $this->assertEquals(new Address('return@symfony.com', 'return'), $e->getSender()); + $this->assertEquals(new Address('return@symfony.com'), $e->getSender()); $headers = new Headers(); $headers->addMailboxHeader('Sender', new Address('sender@symfony.com', 'sender')); $headers->addMailboxListHeader('To', ['from@symfony.com']); $e = Envelope::create(new Message($headers)); - $this->assertEquals(new Address('sender@symfony.com', 'sender'), $e->getSender()); + $this->assertEquals(new Address('sender@symfony.com'), $e->getSender()); $headers = new Headers(); $headers->addMailboxListHeader('From', [new Address('from@symfony.com', 'from'), 'some@symfony.com']); $headers->addMailboxListHeader('To', ['from@symfony.com']); $e = Envelope::create(new Message($headers)); - $this->assertEquals(new Address('from@symfony.com', 'from'), $e->getSender()); + $this->assertEquals(new Address('from@symfony.com'), $e->getSender()); + } + + public function testSenderFromHeadersFailsWithNonAsciiCharactersInLocalPart() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid sender "fabièn@symfony.com": non-ASCII characters not supported in local-part of email.'); + $message = new Message(new Headers(new PathHeader('Return-Path', new Address('fabièn@symfony.com')))); + Envelope::create($message)->getSender(); } public function testSenderFromHeadersWithoutFrom() @@ -78,7 +95,7 @@ class EnvelopeTest extends TestCase $headers->addMailboxListHeader('To', ['from@symfony.com']); $e = Envelope::create($message = new Message($headers)); $message->getHeaders()->addMailboxListHeader('From', [new Address('from@symfony.com', 'from')]); - $this->assertEquals(new Address('from@symfony.com', 'from'), $e->getSender()); + $this->assertEquals(new Address('from@symfony.com'), $e->getSender()); } public function testRecipientsFromHeaders() diff --git a/src/Symfony/Component/Mime/Encoder/IdnAddressEncoder.php b/src/Symfony/Component/Mime/Encoder/IdnAddressEncoder.php index cdd5d4cade..21c7a8df3e 100644 --- a/src/Symfony/Component/Mime/Encoder/IdnAddressEncoder.php +++ b/src/Symfony/Component/Mime/Encoder/IdnAddressEncoder.php @@ -11,16 +11,14 @@ namespace Symfony\Component\Mime\Encoder; -use Symfony\Component\Mime\Exception\AddressEncoderException; - /** * An IDN email address encoder. * * Encodes the domain part of an address using IDN. This is compatible will all * SMTP servers. * - * This encoder does not support email addresses with non-ASCII characters in - * local-part (the substring before @). + * Note: It leaves the local part as is. In case there are non-ASCII characters + * in the local part then it depends on the SMTP Server if this is supported. * * @author Christian Schmidt */ @@ -28,8 +26,6 @@ final class IdnAddressEncoder implements AddressEncoderInterface { /** * Encodes the domain part of an address using IDN. - * - * @throws AddressEncoderException If local-part contains non-ASCII characters */ public function encodeString(string $address): string { @@ -38,10 +34,6 @@ final class IdnAddressEncoder implements AddressEncoderInterface $local = substr($address, 0, $i); $domain = substr($address, $i + 1); - if (preg_match('/[^\x00-\x7F]/', $local)) { - throw new AddressEncoderException(sprintf('Non-ASCII characters not supported in local-part os "%s".', $address)); - } - if (preg_match('/[^\x00-\x7F]/', $domain)) { $address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)); } diff --git a/src/Symfony/Component/Mime/Tests/Header/MailboxHeaderTest.php b/src/Symfony/Component/Mime/Tests/Header/MailboxHeaderTest.php index cca27db408..2fc8e1e881 100644 --- a/src/Symfony/Component/Mime/Tests/Header/MailboxHeaderTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/MailboxHeaderTest.php @@ -58,11 +58,10 @@ class MailboxHeaderTest extends TestCase $this->assertEquals('Fabien =?'.$header->getCharset().'?Q?P=8Ftencier?= ', $header->getBodyAsString()); } - public function testUtf8CharsInLocalPartThrows() + public function testUtf8CharsInLocalPart() { - $this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException'); $header = new MailboxHeader('Sender', new Address('fabïen@symfony.com')); - $header->getBodyAsString(); + $this->assertSame('fabïen@symfony.com', $header->getBodyAsString()); } public function testToString() diff --git a/src/Symfony/Component/Mime/Tests/Header/MailboxListHeaderTest.php b/src/Symfony/Component/Mime/Tests/Header/MailboxListHeaderTest.php index 4cace9698b..5acb504bb1 100644 --- a/src/Symfony/Component/Mime/Tests/Header/MailboxListHeaderTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/MailboxListHeaderTest.php @@ -55,11 +55,10 @@ class MailboxListHeaderTest extends TestCase $this->assertEquals(['Chris Corbyn '], $header->getAddressStrings()); } - public function testUtf8CharsInLocalPartThrows() + public function testUtf8CharsInLocalPart() { - $this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException'); $header = new MailboxListHeader('From', [new Address('chrïs@swiftmailer.org', 'Chris Corbyn')]); - $header->getAddressStrings(); + $this->assertSame(['Chris Corbyn '], $header->getAddressStrings()); } public function testGetMailboxesReturnsNameValuePairs() diff --git a/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php b/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php index a8386f8946..947b089138 100644 --- a/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php +++ b/src/Symfony/Component/Mime/Tests/Header/PathHeaderTest.php @@ -49,11 +49,10 @@ class PathHeaderTest extends TestCase $this->assertEquals('', $header->getBodyAsString()); } - public function testAddressMustBeEncodable() + public function testAddressMustBeEncodableWithUtf8CharsInLocalPart() { - $this->expectException('Symfony\Component\Mime\Exception\AddressEncoderException'); $header = new PathHeader('Return-Path', new Address('chrïs@swiftmailer.org')); - $header->getBodyAsString(); + $this->assertSame('', $header->getBodyAsString()); } public function testSetBody()