diff --git a/lib/classes/phpmailer/class.PHPMailer.php b/lib/classes/phpmailer/class.PHPMailer.php index e4dd00bf..89d97316 100644 --- a/lib/classes/phpmailer/class.PHPMailer.php +++ b/lib/classes/phpmailer/class.PHPMailer.php @@ -31,7 +31,7 @@ class PHPMailer * The PHPMailer Version number. * @var string */ - public $Version = '5.2.14'; + public $Version = '5.2.15'; /** * Email priority. @@ -352,6 +352,7 @@ class PHPMailer /** * Whether to split multiple to addresses into multiple messages * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. * @var boolean */ public $SingleTo = false; @@ -446,6 +447,15 @@ class PHPMailer */ public $XMailer = ''; + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * @see PHPMailer::validateAddress() + * @var string|callable + * @static + */ + public static $validator = 'auto'; + /** * An instance of the SMTP sender class. * @var SMTP @@ -634,9 +644,11 @@ class PHPMailer * Constructor. * @param boolean $exceptions Should we throw external exceptions? */ - public function __construct($exceptions = false) + public function __construct($exceptions = null) { - $this->exceptions = (boolean)$exceptions; + if ($exceptions !== null) { + $this->exceptions = (boolean)$exceptions; + } } /** @@ -645,9 +657,7 @@ class PHPMailer public function __destruct() { //Close any open SMTP connection nicely - if ($this->Mailer == 'smtp') { - $this->smtpClose(); - } + $this->smtpClose(); } /** @@ -713,7 +723,7 @@ class PHPMailer case 'echo': default: //Normalize line breaks - $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); + $str = preg_replace('/\r\n?/ms', "\n", $str); echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( "\n", "\n \t ", @@ -850,7 +860,7 @@ class PHPMailer $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (($pos = strrpos($address, '@')) === false) { // At-sign is misssing. - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -900,7 +910,7 @@ class PHPMailer return false; } if (!$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -994,7 +1004,7 @@ class PHPMailer if (($pos = strrpos($address, '@')) === false or (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and !$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (setFrom) $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -1027,19 +1037,30 @@ class PHPMailer /** * Check that a string looks like an email address. * @param string $address The email address to check - * @param string $patternselect A selector for the validation pattern to use : + * @param string|callable $patternselect A selector for the validation pattern to use : * * `auto` Pick best pattern automatically; * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; * * `pcre` Use old PCRE implementation; * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. * @return boolean * @static * @access public */ - public static function validateAddress($address, $patternselect = 'auto') + public static function validateAddress($address, $patternselect = null) { + if (is_null($patternselect)) { + $patternselect = self::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { return false; @@ -1216,7 +1237,7 @@ class PHPMailer } $this->$address_kind = $this->punyencodeAddress($this->$address_kind); if (!$this->validateAddress($this->$address_kind)) { - $error_message = $this->lang('invalid_address') . $this->$address_kind; + $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -1227,7 +1248,7 @@ class PHPMailer } // Set whether the message is multipart/alternative - if (!empty($this->AltBody)) { + if ($this->alternativeExists()) { $this->ContentType = 'multipart/alternative'; } @@ -1634,7 +1655,7 @@ class PHPMailer */ public function smtpClose() { - if ($this->smtp !== null) { + if (is_a($this->smtp, 'SMTP')) { if ($this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); @@ -1972,7 +1993,7 @@ class PHPMailer $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } - if ($this->MessageID != '') { + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { $this->lastMessageID = $this->MessageID; } else { $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); @@ -2074,7 +2095,7 @@ class PHPMailer */ public function getSentMIMEMessage() { - return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; } /** @@ -2120,8 +2141,9 @@ class PHPMailer $altBodyEncoding = '7bit'; $altBodyCharSet = 'us-ascii'; } - //If lines are too long, change to quoted-printable transfer encoding - if (self::hasLineLongerThanMax($this->AltBody)) { + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding + if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { $altBodyEncoding = 'quoted-printable'; } //Use this as a preamble in all multipart message types @@ -3296,7 +3318,7 @@ class PHPMailer $message ); } - } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) { + } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { // Do not change urls for absolute images (thanks to corvuscorax) // Do not change urls that are already inline images $filename = basename($url); @@ -3332,7 +3354,7 @@ class PHPMailer // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better $this->Body = $this->normalizeBreaks($message); $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); - if (empty($this->AltBody)) { + if (!$this->alternativeExists()) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . self::CRLF . self::CRLF; } @@ -3657,11 +3679,13 @@ class PHPMailer if ($this->DKIM_passphrase != '') { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); } else { - $privKey = $privKeyStr; + $privKey = openssl_pkey_get_private($privKeyStr); } - if (openssl_sign($signHeader, $signature, $privKey)) { + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption + openssl_pkey_free($privKey); return base64_encode($signature); } + openssl_pkey_free($privKey); return ''; } @@ -3678,7 +3702,7 @@ class PHPMailer foreach ($lines as $key => $line) { list($heading, $value) = explode(':', $line, 2); $heading = strtolower($heading); - $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces + $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value } $signHeader = implode("\r\n", $lines); @@ -3716,7 +3740,7 @@ class PHPMailer */ public function DKIM_Add($headers_line, $subject, $body) { - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) @@ -3724,6 +3748,7 @@ class PHPMailer $headers = explode($this->LE, $headers_line); $from_header = ''; $to_header = ''; + $date_header = ''; $current = ''; foreach ($headers as $header) { if (strpos($header, 'From:') === 0) { @@ -3732,6 +3757,9 @@ class PHPMailer } elseif (strpos($header, 'To:') === 0) { $to_header = $header; $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; } else { if (!empty($$current) && strpos($header, ' =?') === 0) { $$current .= $header; @@ -3742,6 +3770,7 @@ class PHPMailer } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); $subject = str_replace( '|', '=7C', @@ -3749,7 +3778,7 @@ class PHPMailer ); // Copied header fields (dkim-quoted-printable) $body = $this->DKIM_BodyC($body); $DKIMlen = strlen($body); // Length of body - $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body if ('' == $this->DKIM_identity) { $ident = ''; } else { @@ -3762,16 +3791,18 @@ class PHPMailer $this->DKIM_selector . ";\r\n" . "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . - "\th=From:To:Subject;\r\n" . + "\th=From:To:Date:Subject;\r\n" . "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . "\tz=$from\r\n" . "\t|$to\r\n" . + "\t|$date\r\n" . "\t|$subject;\r\n" . "\tbh=" . $DKIMb64 . ";\r\n" . "\tb="; $toSign = $this->DKIM_HeaderC( $from_header . "\r\n" . $to_header . "\r\n" . + $date_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs ); diff --git a/lib/classes/phpmailer/class.SMTP.php b/lib/classes/phpmailer/class.SMTP.php index 2e32e2fc..039f8beb 100644 --- a/lib/classes/phpmailer/class.SMTP.php +++ b/lib/classes/phpmailer/class.SMTP.php @@ -30,7 +30,7 @@ class SMTP * The PHPMailer SMTP version number. * @var string */ - const VERSION = '5.2.14'; + const VERSION = '5.2.15'; /** * SMTP line break constant. @@ -81,7 +81,7 @@ class SMTP * @deprecated Use the `VERSION` constant instead * @see SMTP::VERSION */ - public $Version = '5.2.14'; + public $Version = '5.2.15'; /** * SMTP server port number. @@ -336,11 +336,22 @@ class SMTP if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + // Begin encrypted connection if (!stream_socket_enable_crypto( $this->smtp_conn, true, - STREAM_CRYPTO_METHOD_TLS_CLIENT + $crypto_method )) { return false; } @@ -736,7 +747,7 @@ class SMTP protected function parseHelloFields($type) { $this->server_caps = array(); - $lines = explode("\n", $this->last_reply); + $lines = explode("\n", $this->helo_rply); foreach ($lines as $n => $s) { //First 4 chars contain response code followed by - or space