diff --git a/src/composer.json b/src/composer.json index 6225bcb3..596b3ead 100644 --- a/src/composer.json +++ b/src/composer.json @@ -29,7 +29,8 @@ "canteen/html5": "1.1.*", "vierbergenlars/php-semver": "3.0.*", "swiftmailer/swiftmailer": "5.3.*", - "ua-parser/uap-php": "~3.4.1" + "ua-parser/uap-php": "~3.4.1", + "true/punycode": "~1.1" }, "require-dev": { "squizlabs/php_codesniffer": "2.*" diff --git a/src/mibew/libs/classes/Mibew/Mail/Utils.php b/src/mibew/libs/classes/Mibew/Mail/Utils.php index 91b505c0..566aff13 100644 --- a/src/mibew/libs/classes/Mibew/Mail/Utils.php +++ b/src/mibew/libs/classes/Mibew/Mail/Utils.php @@ -20,6 +20,7 @@ namespace Mibew\Mail; use Symfony\Component\Yaml\Parser as YamlParser; +use True\Punycode; /** * Contains a set of utility methods related with emails. @@ -29,17 +30,29 @@ class Utils /** * Checks if the passed in e-mail address is valid. * + * The method play nice with addresses that have national characters in the + * domain part. + * * @param string $address E-mail address to check. * @return boolean */ public static function isValidAddress($address) { - return (bool)filter_var($address, FILTER_VALIDATE_EMAIL); + // Email address can contain UTF8 characters in the domain part, but + // PHP's validator does not allow IDN. Thus address normalization is + // used here to convert domain part of the address to punycode. + $normalized_address = self::normalizeAddress($address); + + return (bool)filter_var($normalized_address, FILTER_VALIDATE_EMAIL); } /** * Builds an instance of \Swift_message. * + * The method assumes that $to_addr and $reply_to arguments are valid email + * addresses. One can use {@link Utils::isValidAddress} for address + * validation. + * * @param string $to_addr Address the message should be send to. * @param string $reply_to The address which will be used in "Reply-to" * mail header. @@ -54,8 +67,8 @@ class Utils ->setCharset('utf-8') ->setMaxLineLength(70) ->setFrom(MIBEW_MAILBOX) - ->setTo($to_addr) - ->setReplyTo($reply_to) + ->setTo(self::normalizeAddress($to_addr)) + ->setReplyTo(self::normalizeAddress($reply_to)) ->setSubject($subject) ->setBody(preg_replace("/\n/", "\r\n", $body)); } @@ -99,6 +112,30 @@ class Utils } } + /** + * Converts domain part of the address to punycode if needed. + * + * @param string $address The address that should be normalized. + * @return string + */ + private static function normalizeAddress($address) + { + $chunks = explode('@', $address); + if (count($chunks) < 2) { + // The address has no "@" character thus it's not a real email + // address and should not be normalized at all. + return $address; + } + + $punycode = new Punycode(); + // Domain part should be converted to punycode to play nice with IDN. + $domain = $punycode->encode(array_pop($chunks)); + // Local part should be left as is. + $local_part = implode('@', $chunks); + + return $local_part . '@' . $domain; + } + /** * This class should not be instantiated */