[Mime] allow non-ASCII characters in local part of email
This commit is contained in:
parent
ad94b395d0
commit
d057dffcd6
@ -41,11 +41,11 @@ final class DelayedEnvelope extends Envelope
|
|||||||
|
|
||||||
public function getSender(): Address
|
public function getSender(): Address
|
||||||
{
|
{
|
||||||
if ($this->senderSet) {
|
if (!$this->senderSet) {
|
||||||
return parent::getSender();
|
parent::setSender(self::getSenderFromHeaders($this->message->getHeaders()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::getSenderFromHeaders($this->message->getHeaders());
|
return parent::getSender();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRecipients(array $recipients): void
|
public function setRecipients(array $recipients): void
|
||||||
|
@ -44,6 +44,10 @@ class Envelope
|
|||||||
|
|
||||||
public function setSender(Address $sender): void
|
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());
|
$this->sender = new Address($sender->getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +13,11 @@ namespace Symfony\Component\Mailer\Tests;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Mailer\Envelope;
|
use Symfony\Component\Mailer\Envelope;
|
||||||
|
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\Mailer\Exception\LogicException;
|
use Symfony\Component\Mailer\Exception\LogicException;
|
||||||
use Symfony\Component\Mime\Address;
|
use Symfony\Component\Mime\Address;
|
||||||
use Symfony\Component\Mime\Header\Headers;
|
use Symfony\Component\Mime\Header\Headers;
|
||||||
|
use Symfony\Component\Mime\Header\PathHeader;
|
||||||
use Symfony\Component\Mime\Message;
|
use Symfony\Component\Mime\Message;
|
||||||
use Symfony\Component\Mime\RawMessage;
|
use Symfony\Component\Mime\RawMessage;
|
||||||
|
|
||||||
@ -27,6 +29,13 @@ class EnvelopeTest extends TestCase
|
|||||||
$this->assertEquals(new Address('fabien@symfony.com'), $e->getSender());
|
$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()
|
public function testConstructorWithNamedAddressSender()
|
||||||
{
|
{
|
||||||
$e = new Envelope(new Address('fabien@symfony.com', 'Fabien'), [new Address('thomas@symfony.com')]);
|
$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->addPathHeader('Return-Path', new Address('return@symfony.com', 'return'));
|
||||||
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
||||||
$e = Envelope::create(new Message($headers));
|
$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 = new Headers();
|
||||||
$headers->addMailboxHeader('Sender', new Address('sender@symfony.com', 'sender'));
|
$headers->addMailboxHeader('Sender', new Address('sender@symfony.com', 'sender'));
|
||||||
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
||||||
$e = Envelope::create(new Message($headers));
|
$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 = new Headers();
|
||||||
$headers->addMailboxListHeader('From', [new Address('from@symfony.com', 'from'), 'some@symfony.com']);
|
$headers->addMailboxListHeader('From', [new Address('from@symfony.com', 'from'), 'some@symfony.com']);
|
||||||
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
||||||
$e = Envelope::create(new Message($headers));
|
$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()
|
public function testSenderFromHeadersWithoutFrom()
|
||||||
@ -78,7 +95,7 @@ class EnvelopeTest extends TestCase
|
|||||||
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
$headers->addMailboxListHeader('To', ['from@symfony.com']);
|
||||||
$e = Envelope::create($message = new Message($headers));
|
$e = Envelope::create($message = new Message($headers));
|
||||||
$message->getHeaders()->addMailboxListHeader('From', [new Address('from@symfony.com', 'from')]);
|
$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()
|
public function testRecipientsFromHeaders()
|
||||||
|
@ -11,16 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Mime\Encoder;
|
namespace Symfony\Component\Mime\Encoder;
|
||||||
|
|
||||||
use Symfony\Component\Mime\Exception\AddressEncoderException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IDN email address encoder.
|
* An IDN email address encoder.
|
||||||
*
|
*
|
||||||
* Encodes the domain part of an address using IDN. This is compatible will all
|
* Encodes the domain part of an address using IDN. This is compatible will all
|
||||||
* SMTP servers.
|
* SMTP servers.
|
||||||
*
|
*
|
||||||
* This encoder does not support email addresses with non-ASCII characters in
|
* Note: It leaves the local part as is. In case there are non-ASCII characters
|
||||||
* local-part (the substring before @).
|
* in the local part then it depends on the SMTP Server if this is supported.
|
||||||
*
|
*
|
||||||
* @author Christian Schmidt
|
* @author Christian Schmidt
|
||||||
*/
|
*/
|
||||||
@ -28,8 +26,6 @@ final class IdnAddressEncoder implements AddressEncoderInterface
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Encodes the domain part of an address using IDN.
|
* Encodes the domain part of an address using IDN.
|
||||||
*
|
|
||||||
* @throws AddressEncoderException If local-part contains non-ASCII characters
|
|
||||||
*/
|
*/
|
||||||
public function encodeString(string $address): string
|
public function encodeString(string $address): string
|
||||||
{
|
{
|
||||||
@ -38,10 +34,6 @@ final class IdnAddressEncoder implements AddressEncoderInterface
|
|||||||
$local = substr($address, 0, $i);
|
$local = substr($address, 0, $i);
|
||||||
$domain = substr($address, $i + 1);
|
$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)) {
|
if (preg_match('/[^\x00-\x7F]/', $domain)) {
|
||||||
$address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46));
|
$address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46));
|
||||||
}
|
}
|
||||||
|
@ -58,11 +58,10 @@ class MailboxHeaderTest extends TestCase
|
|||||||
$this->assertEquals('Fabien =?'.$header->getCharset().'?Q?P=8Ftencier?= <fabien@symfony.com>', $header->getBodyAsString());
|
$this->assertEquals('Fabien =?'.$header->getCharset().'?Q?P=8Ftencier?= <fabien@symfony.com>', $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 = new MailboxHeader('Sender', new Address('fabïen@symfony.com'));
|
||||||
$header->getBodyAsString();
|
$this->assertSame('fabïen@symfony.com', $header->getBodyAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToString()
|
public function testToString()
|
||||||
|
@ -55,11 +55,10 @@ class MailboxListHeaderTest extends TestCase
|
|||||||
$this->assertEquals(['Chris Corbyn <chris@xn--swftmailer-78a.org>'], $header->getAddressStrings());
|
$this->assertEquals(['Chris Corbyn <chris@xn--swftmailer-78a.org>'], $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 = new MailboxListHeader('From', [new Address('chrïs@swiftmailer.org', 'Chris Corbyn')]);
|
||||||
$header->getAddressStrings();
|
$this->assertSame(['Chris Corbyn <chrïs@swiftmailer.org>'], $header->getAddressStrings());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMailboxesReturnsNameValuePairs()
|
public function testGetMailboxesReturnsNameValuePairs()
|
||||||
|
@ -49,11 +49,10 @@ class PathHeaderTest extends TestCase
|
|||||||
$this->assertEquals('<chris@xn--swftmailer-78a.org>', $header->getBodyAsString());
|
$this->assertEquals('<chris@xn--swftmailer-78a.org>', $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 = new PathHeader('Return-Path', new Address('chrïs@swiftmailer.org'));
|
||||||
$header->getBodyAsString();
|
$this->assertSame('<chrïs@swiftmailer.org>', $header->getBodyAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetBody()
|
public function testSetBody()
|
||||||
|
Reference in New Issue
Block a user