Skip to content
Permalink
Browse files

Basic IDN support

  • Loading branch information...
c960657 authored and fabpot committed Jan 22, 2018
1 parent fc2749d commit 6a87efd39bb3d950d85b69f807e3852f485cbe30
@@ -1,10 +1,10 @@
Changelog
=========

6.0.3 (2017-XX-XX)
6.1.0 (2017-XX-XX)
------------------

* n/a
* added support for IDN domains in email addresses

6.0.2 (2017-09-30)
------------------
@@ -22,6 +22,10 @@
"mockery/mockery": "~0.9.1",
"symfony/phpunit-bridge": "~3.3@dev"
},
"suggest": {
"ext-intl": "Needed to support internationalized email addresses",
"true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed"
},
"autoload": {
"files": ["lib/swift_required.php"]
},
@@ -383,6 +383,19 @@ following::

*/

Internationalized domains are automatically converted to IDN encoding::

$to = $message->getHeaders()->get('To');
$to->setAddresses('joe@ëxämple.org');

echo $to->toString();

/*

To: joe@xn--xmple-gra1c.org

*/

ID Headers
~~~~~~~~~~

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Email address encoder.
*
* @author Christian Schmidt
*/
interface Swift_AddressEncoder
{
/**
* Encodes an email address.
*
* @throws Swift_AddressEncoderException If the email cannot be represented in
* the encoding implemented by this class.
*/
public function encodeString(string $address): string;
}
@@ -0,0 +1,60 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An IDN email address encoder.
*
* @author Christian Schmidt
*/
class Swift_AddressEncoder_IdnAddressEncoder implements Swift_AddressEncoder
{
/**
* Encodes the domain part of an address using IDN.
*
* @throws Swift_AddressEncoderException If local-part contains non-ASCII characters
*/
public function encodeString(string $address): string
{
$i = strrpos($address, '@');
if (false !== $i) {
$local = substr($address, 0, $i);
$domain = substr($address, $i + 1);
if (preg_match('/[^\x00-\x7F]/', $local)) {
throw new Swift_AddressEncoderException('Non-ASCII characters not supported in local-part', $address);
}
$address = sprintf('%s@%s', $local, $this->idnToAscii($domain));
}
return $address;
}
/**
* IDN-encodes a UTF-8 string to ASCII.
*
* @param string $string
*
* @return string
*/
protected function idnToAscii(string $string): string
{
if (function_exists('idn_to_ascii')) {
return idn_to_ascii($string, 0, INTL_IDNA_VARIANT_UTS46);
}
if (class_exists('TrueBV\Punycode')) {
$punycode = new \TrueBV\Punycode();
return $punycode->encode($string);
}
throw new Swift_SwiftException('No IDN encoder found (install the intl extension or the true/punycode package');
}
}
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* AddressEncoderException when the specified email address is in a format that
* cannot be encoded by a given address encoder.
*
* @author Christian Schmidt
*/
class Swift_AddressEncoderException extends Swift_RfcComplianceException
{
/** The address that could not be encoded */
protected $address;
public function __construct(string $message, string $address)
{
parent::__construct($message);
$this->address = $address;
}
/**
* Returns the address that could not be encoded.
*/
public function getAddress(): string
{
return $this->address;
}
}
@@ -34,16 +34,20 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new IdentificationHeader with the given $name and $id.
*
* @param string $name
* @param EmailValidator $emailValidator
* @param string $name
* @param emailValidator $emailValidator
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct($name, EmailValidator $emailValidator)
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
@@ -159,7 +163,7 @@ public function getFieldBody()
$angleAddrs = array();
foreach ($this->ids as $id) {
$angleAddrs[] = '<'.$id.'>';
$angleAddrs[] = '<'.$this->addressEncoder->encodeString($id).'>';
}
$this->setCachedValue(implode(' ', $angleAddrs));
@@ -32,18 +32,22 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new MailboxHeader with $name.
*
* @param string $name of Header
* @param Swift_Mime_HeaderEncoder $encoder
* @param EmailValidator $emailValidator
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator)
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->setEncoder($encoder);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
@@ -330,7 +334,7 @@ private function createNameAddressStrings(array $mailboxes)
$strings = array();
foreach ($mailboxes as $email => $name) {
$mailboxStr = $email;
$mailboxStr = $this->addressEncoder->encodeString($email);
if (null !== $name) {
$nameStr = $this->createDisplayNameString($name, empty($strings));
$mailboxStr = $nameStr.' <'.$mailboxStr.'>';
@@ -32,16 +32,20 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new PathHeader with the given $name.
*
* @param string $name
* @param EmailValidator $emailValidator
* @param string $name
* @param EmailValidator $emailValidator
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct($name, EmailValidator $emailValidator)
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
@@ -127,7 +131,8 @@ public function getFieldBody()
{
if (!$this->getCachedValue()) {
if (isset($this->address)) {
$this->setCachedValue('<'.$this->address.'>');
$address = $this->addressEncoder->encodeString($this->address);
$this->setCachedValue('<'.$address.'>');
}
}
@@ -36,13 +36,15 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_CharsetObserver
* @param Swift_Encoder $paramEncoder
* @param EmailValidator $emailValidator
* @param string|null $charset
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, EmailValidator $emailValidator, $charset = null)
public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, EmailValidator $emailValidator, $charset = null, Swift_AddressEncoder $addressEncoder = null)
{
$this->encoder = $encoder;
$this->paramEncoder = $paramEncoder;
$this->emailValidator = $emailValidator;
$this->charset = $charset;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
@@ -55,7 +57,7 @@ public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $pa
*/
public function createMailboxHeader($name, $addresses = null)
{
$header = new Swift_Mime_Headers_MailboxHeader($name, $this->encoder, $this->emailValidator);
$header = new Swift_Mime_Headers_MailboxHeader($name, $this->encoder, $this->emailValidator, $this->addressEncoder);
if (isset($addresses)) {
$header->setFieldBodyModel($addresses);
}
@@ -27,6 +27,8 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
/** The event dispatching layer */
protected $eventDispatcher;
protected $addressEncoder;
/** Source Ip */
protected $sourceIp;
@@ -39,11 +41,13 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
* @param Swift_Transport_IoBuffer $buf
* @param Swift_Events_EventDispatcher $dispatcher
* @param string $localDomain
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1')
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', Swift_AddressEncoder $addressEncoder = null)
{
$this->eventDispatcher = $dispatcher;
$this->buffer = $buf;
$this->eventDispatcher = $dispatcher;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
$this->setLocalDomain($localDomain);
}
@@ -336,6 +340,7 @@ protected function doHeloCommand()
/** Send the MAIL FROM command */
protected function doMailFromCommand($address)
{
$address = $this->addressEncoder->encodeString($address);
$this->executeCommand(
sprintf("MAIL FROM:<%s>\r\n", $address), array(250)
);
@@ -344,6 +349,7 @@ protected function doMailFromCommand($address)
/** Send the RCPT TO command */
protected function doRcptToCommand($address)
{
$address = $this->addressEncoder->encodeString($address);
$this->executeCommand(
sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252)
);
@@ -52,10 +52,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo
* @param Swift_Transport_EsmtpHandler[] $extensionHandlers
* @param Swift_Events_EventDispatcher $dispatcher
* @param string $localDomain
* @param Swift_AddressEncoder $addressEncoder
*/
public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1')
public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', Swift_AddressEncoder $addressEncoder = null)
{
parent::__construct($buf, $dispatcher, $localDomain);
parent::__construct($buf, $dispatcher, $localDomain, $addressEncoder);
$this->setExtensionHandlers($extensionHandlers);
}
@@ -338,6 +339,7 @@ protected function doHeloCommand()
/** Overridden to add Extension support */
protected function doMailFromCommand($address)
{
$address = $this->addressEncoder->encodeString($address);
$handlers = $this->getActiveHandlers();
$params = array();
foreach ($handlers as $handler) {
@@ -352,6 +354,7 @@ protected function doMailFromCommand($address)
/** Overridden to add Extension support */
protected function doRcptToCommand($address)
{
$address = $this->addressEncoder->encodeString($address);
$handlers = $this->getActiveHandlers();
$params = array();
foreach ($handlers as $handler) {
@@ -68,6 +68,7 @@
'mime.rfc2231encoder',
'email.validator',
'properties.charset',
'mime.addressencoder',
))
->register('mime.headerset')
@@ -125,6 +126,9 @@
->register('mime.rfc2231encoder')
->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder')
->withDependencies(array('mime.charstream'))
->register('mime.addressencoder')
->asNewInstanceOf('Swift_AddressEncoder_IdnAddressEncoder')
;
unset($swift_mime_types);
@@ -14,6 +14,7 @@
array('transport.authhandler'),
'transport.eventdispatcher',
'transport.localdomain',
'transport.addressencoder',
))
->register('transport.sendmail')
@@ -72,6 +73,9 @@
->register('transport.eventdispatcher')
->asNewInstanceOf('Swift_Events_SimpleEventDispatcher')
->register('transport.addressencoder')
->asNewInstanceOf('Swift_AddressEncoder_IdnAddressEncoder')
->register('transport.replacementfactory')
->asSharedInstanceOf('Swift_StreamFilters_StringReplacementFilterFactory')
;

0 comments on commit 6a87efd

Please sign in to comment.
You can’t perform that action at this time.