Make WordPress Core

Ticket #37210: 37210.diff

File 37210.diff, 22.1 KB (added by MattyRob, 8 years ago)
  • src/wp-includes/class-phpmailer.php

     
    3131     * The PHPMailer Version number.
    3232     * @var string
    3333     */
    34     public $Version = '5.2.14';
     34    public $Version = '5.2.16';
    3535
    3636    /**
    3737     * Email priority.
     
    285285
    286286    /**
    287287     * SMTP auth type.
    288      * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
     288     * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
    289289     * @var string
    290290     */
    291291    public $AuthType = '';
     
    352352    /**
    353353     * Whether to split multiple to addresses into multiple messages
    354354     * or send them all in one message.
     355     * Only supported in `mail` and `sendmail` transports, not in SMTP.
    355356     * @var boolean
    356357     */
    357358    public $SingleTo = false;
     
    394395
    395396    /**
    396397     * DKIM Identity.
    397      * Usually the email address used as the source of the email
     398     * Usually the email address used as the source of the email.
    398399     * @var string
    399400     */
    400401    public $DKIM_identity = '';
     
    447448    public $XMailer = '';
    448449
    449450    /**
     451     * Which validator to use by default when validating email addresses.
     452     * May be a callable to inject your own validator, but there are several built-in validators.
     453     * @see PHPMailer::validateAddress()
     454     * @var string|callable
     455     * @static
     456     */
     457    public static $validator = 'auto';
     458
     459    /**
    450460     * An instance of the SMTP sender class.
    451461     * @var SMTP
    452462     * @access protected
     
    634644     * Constructor.
    635645     * @param boolean $exceptions Should we throw external exceptions?
    636646     */
    637     public function __construct($exceptions = false)
     647    public function __construct($exceptions = null)
    638648    {
    639         $this->exceptions = (boolean)$exceptions;
     649        if ($exceptions !== null) {
     650            $this->exceptions = (boolean)$exceptions;
     651        }
    640652    }
    641653
    642654    /**
     
    645657    public function __destruct()
    646658    {
    647659        //Close any open SMTP connection nicely
    648         if ($this->Mailer == 'smtp') {
    649             $this->smtpClose();
    650         }
     660        $this->smtpClose();
    651661    }
    652662
    653663    /**
     
    671681        } else {
    672682            $subject = $this->encodeHeader($this->secureHeader($subject));
    673683        }
    674         if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
     684        //Can't use additional_parameters in safe_mode
     685        //@link http://php.net/manual/en/function.mail.php
     686        if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
    675687            $result = @mail($to, $subject, $body, $header);
    676688        } else {
    677689            $result = @mail($to, $subject, $body, $header, $params);
     
    713725            case 'echo':
    714726            default:
    715727                //Normalize line breaks
    716                 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
     728                $str = preg_replace('/\r\n?/ms', "\n", $str);
    717729                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
    718730                    "\n",
    719731                    "\n                   \t                  ",
     
    850862        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    851863        if (($pos = strrpos($address, '@')) === false) {
    852864            // At-sign is misssing.
    853             $error_message = $this->lang('invalid_address') . $address;
     865            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
    854866            $this->setError($error_message);
    855867            $this->edebug($error_message);
    856868            if ($this->exceptions) {
     
    900912            return false;
    901913        }
    902914        if (!$this->validateAddress($address)) {
    903             $error_message = $this->lang('invalid_address') . $address;
     915            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
    904916            $this->setError($error_message);
    905917            $this->edebug($error_message);
    906918            if ($this->exceptions) {
     
    924936    }
    925937
    926938    /**
     939     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
     940     * of the form "display name <address>" into an array of name/address pairs.
     941     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
     942     * Note that quotes in the name part are removed.
     943     * @param string $addrstr The address list string
     944     * @param bool $useimap Whether to use the IMAP extension to parse the list
     945     * @return array
     946     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
     947     */
     948    public function parseAddresses($addrstr, $useimap = true)
     949    {
     950        $addresses = array();
     951        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
     952            //Use this built-in parser if it's available
     953            $list = imap_rfc822_parse_adrlist($addrstr, '');
     954            foreach ($list as $address) {
     955                if ($address->host != '.SYNTAX-ERROR.') {
     956                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
     957                        $addresses[] = array(
     958                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
     959                            'address' => $address->mailbox . '@' . $address->host
     960                        );
     961                    }
     962                }
     963            }
     964        } else {
     965            //Use this simpler parser
     966            $list = explode(',', $addrstr);
     967            foreach ($list as $address) {
     968                $address = trim($address);
     969                //Is there a separate name part?
     970                if (strpos($address, '<') === false) {
     971                    //No separate name, just use the whole thing
     972                    if ($this->validateAddress($address)) {
     973                        $addresses[] = array(
     974                            'name' => '',
     975                            'address' => $address
     976                        );
     977                    }
     978                } else {
     979                    list($name, $email) = explode('<', $address);
     980                    $email = trim(str_replace('>', '', $email));
     981                    if ($this->validateAddress($email)) {
     982                        $addresses[] = array(
     983                            'name' => trim(str_replace(array('"', "'"), '', $name)),
     984                            'address' => $email
     985                        );
     986                    }
     987                }
     988            }
     989        }
     990        return $addresses;
     991    }
     992
     993    /**
    927994     * Set the From and FromName properties.
    928995     * @param string $address
    929996     * @param string $name
     
    9391006        if (($pos = strrpos($address, '@')) === false or
    9401007            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
    9411008            !$this->validateAddress($address)) {
    942             $error_message = $this->lang('invalid_address') . $address;
     1009            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
    9431010            $this->setError($error_message);
    9441011            $this->edebug($error_message);
    9451012            if ($this->exceptions) {
     
    9721039    /**
    9731040     * Check that a string looks like an email address.
    9741041     * @param string $address The email address to check
    975      * @param string $patternselect A selector for the validation pattern to use :
     1042     * @param string|callable $patternselect A selector for the validation pattern to use :
    9761043     * * `auto` Pick best pattern automatically;
    9771044     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
    9781045     * * `pcre` Use old PCRE implementation;
     
    9791046     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
    9801047     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
    9811048     * * `noregex` Don't use a regex: super fast, really dumb.
     1049     * Alternatively you may pass in a callable to inject your own validator, for example:
     1050     * PHPMailer::validateAddress('user@example.com', function($address) {
     1051     *     return (strpos($address, '@') !== false);
     1052     * });
     1053     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
    9821054     * @return boolean
    9831055     * @static
    9841056     * @access public
    9851057     */
    986     public static function validateAddress($address, $patternselect = 'auto')
     1058    public static function validateAddress($address, $patternselect = null)
    9871059    {
     1060        if (is_null($patternselect)) {
     1061            $patternselect = self::$validator;
     1062        }
     1063        if (is_callable($patternselect)) {
     1064            return call_user_func($patternselect, $address);
     1065        }
    9881066        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
    9891067        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
    9901068            return false;
     
    11611239                }
    11621240                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
    11631241                if (!$this->validateAddress($this->$address_kind)) {
    1164                     $error_message = $this->lang('invalid_address') . $this->$address_kind;
     1242                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
    11651243                    $this->setError($error_message);
    11661244                    $this->edebug($error_message);
    11671245                    if ($this->exceptions) {
     
    11721250            }
    11731251
    11741252            // Set whether the message is multipart/alternative
    1175             if (!empty($this->AltBody)) {
     1253            if ($this->alternativeExists()) {
    11761254                $this->ContentType = 'multipart/alternative';
    11771255            }
    11781256
     
    13491427        }
    13501428        $to = implode(', ', $toArr);
    13511429
    1352         if (empty($this->Sender)) {
    1353             $params = ' ';
    1354         } else {
     1430        $params = null;
     1431        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
     1432        if (!empty($this->Sender)) {
    13551433            $params = sprintf('-f%s', $this->Sender);
    13561434        }
    13571435        if ($this->Sender != '' and !ini_get('safe_mode')) {
     
    13591437            ini_set('sendmail_from', $this->Sender);
    13601438        }
    13611439        $result = false;
    1362         if ($this->SingleTo && count($toArr) > 1) {
     1440        if ($this->SingleTo and count($toArr) > 1) {
    13631441            foreach ($toArr as $toAddr) {
    13641442                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
    13651443                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     
    14661544     * @throws phpmailerException
    14671545     * @return boolean
    14681546     */
    1469     public function smtpConnect($options = array())
     1547    public function smtpConnect($options = null)
    14701548    {
    14711549        if (is_null($this->smtp)) {
    14721550            $this->smtp = $this->getSMTPInstance();
    14731551        }
    14741552
     1553        //If no options are provided, use whatever is set in the instance
     1554        if (is_null($options)) {
     1555            $options = $this->SMTPOptions;
     1556        }
     1557
    14751558        // Already connected?
    14761559        if ($this->smtp->connected()) {
    14771560            return true;
     
    15411624                        if (!$this->smtp->startTLS()) {
    15421625                            throw new phpmailerException($this->lang('connect_host'));
    15431626                        }
    1544                         // We must resend HELO after tls negotiation
     1627                        // We must resend EHLO after TLS negotiation
    15451628                        $this->smtp->hello($hello);
    15461629                    }
    15471630                    if ($this->SMTPAuth) {
     
    15801663     */
    15811664    public function smtpClose()
    15821665    {
    1583         if ($this->smtp !== null) {
     1666        if (is_a($this->smtp, 'SMTP')) {
    15841667            if ($this->smtp->connected()) {
    15851668                $this->smtp->quit();
    15861669                $this->smtp->close();
     
    19182001            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
    19192002        }
    19202003
    1921         if ($this->MessageID != '') {
     2004        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
    19222005            $this->lastMessageID = $this->MessageID;
    19232006        } else {
    19242007            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
     
    20202103     */
    20212104    public function getSentMIMEMessage()
    20222105    {
    2023         return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
     2106        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
    20242107    }
    20252108
    20262109    /**
     
    20502133        //Can we do a 7-bit downgrade?
    20512134        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
    20522135            $bodyEncoding = '7bit';
     2136            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
    20532137            $bodyCharSet = 'us-ascii';
    20542138        }
    2055         //If lines are too long, change to quoted-printable transfer encoding
    2056         if (self::hasLineLongerThanMax($this->Body)) {
    2057             $this->Encoding = 'quoted-printable';
     2139        //If lines are too long, and we're not already using an encoding that will shorten them,
     2140        //change to quoted-printable transfer encoding for the body part only
     2141        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
    20582142            $bodyEncoding = 'quoted-printable';
    20592143        }
    20602144
     
    20632147        //Can we do a 7-bit downgrade?
    20642148        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
    20652149            $altBodyEncoding = '7bit';
     2150            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
    20662151            $altBodyCharSet = 'us-ascii';
    20672152        }
    2068         //If lines are too long, change to quoted-printable transfer encoding
    2069         if (self::hasLineLongerThanMax($this->AltBody)) {
     2153        //If lines are too long, and we're not already using an encoding that will shorten them,
     2154        //change to quoted-printable transfer encoding for the alt body part only
     2155        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
    20702156            $altBodyEncoding = 'quoted-printable';
    20712157        }
    20722158        //Use this as a preamble in all multipart message types
     
    21692255                $body .= $this->attachAll('attachment', $this->boundary[1]);
    21702256                break;
    21712257            default:
    2172                 // catch case 'plain' and case ''
    2173                 $body .= $this->encodeString($this->Body, $bodyEncoding);
     2258                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
     2259                //Reset the `Encoding` property in case we changed it for line length reasons
     2260                $this->Encoding = $bodyEncoding;
     2261                $body .= $this->encodeString($this->Body, $this->Encoding);
    21742262                break;
    21752263        }
    21762264
     
    22762364
    22772365    /**
    22782366     * Set the message type.
    2279      * PHPMailer only supports some preset message types,
    2280      * not arbitrary MIME structures.
     2367     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
    22812368     * @access protected
    22822369     * @return void
    22832370     */
     
    22952382        }
    22962383        $this->message_type = implode('_', $type);
    22972384        if ($this->message_type == '') {
     2385            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
    22982386            $this->message_type = 'plain';
    22992387        }
    23002388    }
     
    32413329                            $message
    32423330                        );
    32433331                    }
    3244                 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
     3332                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
    32453333                    // Do not change urls for absolute images (thanks to corvuscorax)
    32463334                    // Do not change urls that are already inline images
    32473335                    $filename = basename($url);
     
    32773365        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
    32783366        $this->Body = $this->normalizeBreaks($message);
    32793367        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
    3280         if (empty($this->AltBody)) {
     3368        if (!$this->alternativeExists()) {
    32813369            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
    32823370                self::CRLF . self::CRLF;
    32833371        }
     
    36023690        if ($this->DKIM_passphrase != '') {
    36033691            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
    36043692        } else {
    3605             $privKey = $privKeyStr;
     3693            $privKey = openssl_pkey_get_private($privKeyStr);
    36063694        }
    3607         if (openssl_sign($signHeader, $signature, $privKey)) {
     3695        if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption
     3696            openssl_pkey_free($privKey);
    36083697            return base64_encode($signature);
    36093698        }
     3699        openssl_pkey_free($privKey);
    36103700        return '';
    36113701    }
    36123702
     
    36233713        foreach ($lines as $key => $line) {
    36243714            list($heading, $value) = explode(':', $line, 2);
    36253715            $heading = strtolower($heading);
    3626             $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
     3716            $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
    36273717            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
    36283718        }
    36293719        $signHeader = implode("\r\n", $lines);
     
    36613751     */
    36623752    public function DKIM_Add($headers_line, $subject, $body)
    36633753    {
    3664         $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
     3754        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
    36653755        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
    36663756        $DKIMquery = 'dns/txt'; // Query method
    36673757        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
     
    36693759        $headers = explode($this->LE, $headers_line);
    36703760        $from_header = '';
    36713761        $to_header = '';
     3762        $date_header = '';
    36723763        $current = '';
    36733764        foreach ($headers as $header) {
    36743765            if (strpos($header, 'From:') === 0) {
     
    36773768            } elseif (strpos($header, 'To:') === 0) {
    36783769                $to_header = $header;
    36793770                $current = 'to_header';
     3771            } elseif (strpos($header, 'Date:') === 0) {
     3772                $date_header = $header;
     3773                $current = 'date_header';
    36803774            } else {
    36813775                if (!empty($$current) && strpos($header, ' =?') === 0) {
    36823776                    $$current .= $header;
     
    36873781        }
    36883782        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
    36893783        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
     3784        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
    36903785        $subject = str_replace(
    36913786            '|',
    36923787            '=7C',
     
    36943789        ); // Copied header fields (dkim-quoted-printable)
    36953790        $body = $this->DKIM_BodyC($body);
    36963791        $DKIMlen = strlen($body); // Length of body
    3697         $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
     3792        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
    36983793        if ('' == $this->DKIM_identity) {
    36993794            $ident = '';
    37003795        } else {
     
    37073802            $this->DKIM_selector .
    37083803            ";\r\n" .
    37093804            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
    3710             "\th=From:To:Subject;\r\n" .
     3805            "\th=From:To:Date:Subject;\r\n" .
    37113806            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
    37123807            "\tz=$from\r\n" .
    37133808            "\t|$to\r\n" .
     3809            "\t|$date\r\n" .
    37143810            "\t|$subject;\r\n" .
    37153811            "\tbh=" . $DKIMb64 . ";\r\n" .
    37163812            "\tb=";
     
    37173813        $toSign = $this->DKIM_HeaderC(
    37183814            $from_header . "\r\n" .
    37193815            $to_header . "\r\n" .
     3816            $date_header . "\r\n" .
    37203817            $subject_header . "\r\n" .
    37213818            $dkimhdrs
    37223819        );
  • src/wp-includes/class-smtp.php

     
    3030     * The PHPMailer SMTP version number.
    3131     * @var string
    3232     */
    33     const VERSION = '5.2.14';
     33    const VERSION = '5.2.16';
    3434
    3535    /**
    3636     * SMTP line break constant.
     
    8181     * @deprecated Use the `VERSION` constant instead
    8282     * @see SMTP::VERSION
    8383     */
    84     public $Version = '5.2.14';
     84    public $Version = '5.2.16';
    8585
    8686    /**
    8787     * SMTP server port number.
     
    336336        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
    337337            return false;
    338338        }
     339
     340        //Allow the best TLS version(s) we can
     341        $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
     342
     343        //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
     344        //so add them back in manually if we can
     345        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
     346            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
     347            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
     348        }
     349
    339350        // Begin encrypted connection
    340351        if (!stream_socket_enable_crypto(
    341352            $this->smtp_conn,
    342353            true,
    343             STREAM_CRYPTO_METHOD_TLS_CLIENT
     354            $crypto_method
    344355        )) {
    345356            return false;
    346357        }
     
    673684    protected function parseHelloFields($type)
    674685    {
    675686        $this->server_caps = array();
    676         $lines = explode("\n", $this->last_reply);
     687        $lines = explode("\n", $this->helo_rply);
    677688
    678689        foreach ($lines as $n => $s) {
    679690            //First 4 chars contain response code followed by - or space