WordPress.org

Make WordPress Core

Ticket #39397: 39397.patch

File 39397.patch, 42.3 KB (added by sebastian.pisula, 8 months ago)
  • wp-includes/class-smtp.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    3030     * The PHPMailer SMTP version number. 
    3131     * @var string 
    3232     */ 
    33     const VERSION = '5.2.14'; 
     33    const VERSION = '5.2.19'; 
    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.19'; 
    8585 
    8686    /** 
    8787     * SMTP server port number. 
     
    150150     */ 
    151151    public $Timelimit = 300; 
    152152 
     153    /** 
     154     * @var array patterns to extract smtp transaction id from smtp reply 
     155     * Only first capture group will be use, use non-capturing group to deal with it 
     156     * Extend this class to override this property to fulfil your needs. 
     157     */ 
     158    protected $smtp_transaction_id_patterns = array( 
     159        'exim' => '/[0-9]{3} OK id=(.*)/', 
     160        'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 
     161        'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 
     162    ); 
     163 
    153164    /** 
    154165     * The socket for the server connection. 
    155166     * @var resource 
     
    206217        } 
    207218        //Avoid clash with built-in function names 
    208219        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 
    209             call_user_func($this->Debugoutput, $str, $this->do_debug); 
     220            call_user_func($this->Debugoutput, $str, $level); 
    210221            return; 
    211222        } 
    212223        switch ($this->Debugoutput) { 
     
    217228            case 'html': 
    218229                //Cleans up output a bit for a better looking, HTML-safe output 
    219230                echo htmlentities( 
    220                     preg_replace('/[\r\n]+/', '', $str), 
    221                     ENT_QUOTES, 
    222                     'UTF-8' 
    223                 ) 
    224                 . "<br>\n"; 
     231                        preg_replace('/[\r\n]+/', '', $str), 
     232                        ENT_QUOTES, 
     233                        'UTF-8' 
     234                    ) 
     235                    . "<br>\n"; 
    225236                break; 
    226237            case 'echo': 
    227238            default: 
    228239                //Normalize line breaks 
    229240                $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 
    230241                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 
    231                     "\n", 
    232                     "\n                   \t                  ", 
    233                     trim($str) 
    234                 )."\n"; 
     242                        "\n", 
     243                        "\n                   \t                  ", 
     244                        trim($str) 
     245                    )."\n"; 
    235246        } 
    236247    } 
    237248 
     
    272283        $errstr = ''; 
    273284        if ($streamok) { 
    274285            $socket_context = stream_context_create($options); 
    275             //Suppress errors; connection failures are handled at a higher level 
    276             $this->smtp_conn = @stream_socket_client( 
     286            set_error_handler(array($this, 'errorHandler')); 
     287            $this->smtp_conn = stream_socket_client( 
    277288                $host . ":" . $port, 
    278289                $errno, 
    279290                $errstr, 
     
    281292                STREAM_CLIENT_CONNECT, 
    282293                $socket_context 
    283294            ); 
     295            restore_error_handler(); 
    284296        } else { 
    285297            //Fall back to fsockopen which should work in more places, but is missing some features 
    286298            $this->edebug( 
    287299                "Connection: stream_socket_client not available, falling back to fsockopen", 
    288300                self::DEBUG_CONNECTION 
    289301            ); 
     302            set_error_handler(array($this, 'errorHandler')); 
    290303            $this->smtp_conn = fsockopen( 
    291304                $host, 
    292305                $port, 
     
    294307                $errstr, 
    295308                $timeout 
    296309            ); 
     310            restore_error_handler(); 
    297311        } 
    298312        // Verify we connected properly 
    299313        if (!is_resource($this->smtp_conn)) { 
     
    336350        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 
    337351            return false; 
    338352        } 
     353 
     354        //Allow the best TLS version(s) we can 
     355        $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 
     356 
     357        //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT 
     358        //so add them back in manually if we can 
     359        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { 
     360            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 
     361            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 
     362        } 
     363 
    339364        // Begin encrypted connection 
    340365        if (!stream_socket_enable_crypto( 
    341366            $this->smtp_conn, 
    342367            true, 
    343             STREAM_CRYPTO_METHOD_TLS_CLIENT 
     368            $crypto_method 
    344369        )) { 
    345370            return false; 
    346371        } 
     
    373398        } 
    374399 
    375400        if (array_key_exists('EHLO', $this->server_caps)) { 
    376         // SMTP extensions are available. Let's try to find a proper authentication method 
     401            // SMTP extensions are available. Let's try to find a proper authentication method 
    377402 
    378403            if (!array_key_exists('AUTH', $this->server_caps)) { 
    379404                $this->setError('Authentication is not allowed at this stage'); 
     
    389414            ); 
    390415 
    391416            if (empty($authtype)) { 
    392                 foreach (array('LOGIN', 'CRAM-MD5', 'PLAIN') as $method) { 
     417                foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { 
    393418                    if (in_array($method, $this->server_caps['AUTH'])) { 
    394419                        $authtype = $method; 
    395420                        break; 
     
    437462                    return false; 
    438463                } 
    439464                break; 
     465            case 'XOAUTH2': 
     466                //If the OAuth Instance is not set. Can be a case when PHPMailer is used 
     467                //instead of PHPMailerOAuth 
     468                if (is_null($OAuth)) { 
     469                    return false; 
     470                } 
     471                $oauth = $OAuth->getOauth64(); 
     472 
     473                // Start authentication 
     474                if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { 
     475                    return false; 
     476                } 
     477                break; 
     478            case 'NTLM': 
     479                /* 
     480                 * ntlm_sasl_client.php 
     481                 * Bundled with Permission 
     482                 * 
     483                 * How to telnet in windows: 
     484                 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 
     485                 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 
     486                 */ 
     487                require_once 'extras/ntlm_sasl_client.php'; 
     488                $temp = new stdClass; 
     489                $ntlm_client = new ntlm_sasl_client_class; 
     490                //Check that functions are available 
     491                if (!$ntlm_client->initialize($temp)) { 
     492                    $this->setError($temp->error); 
     493                    $this->edebug( 
     494                        'You need to enable some modules in your php.ini file: ' 
     495                        . $this->error['error'], 
     496                        self::DEBUG_CLIENT 
     497                    ); 
     498                    return false; 
     499                } 
     500                //msg1 
     501                $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 
     502 
     503                if (!$this->sendCommand( 
     504                    'AUTH NTLM', 
     505                    'AUTH NTLM ' . base64_encode($msg1), 
     506                    334 
     507                ) 
     508                ) { 
     509                    return false; 
     510                } 
     511                //Though 0 based, there is a white space after the 3 digit number 
     512                //msg2 
     513                $challenge = substr($this->last_reply, 3); 
     514                $challenge = base64_decode($challenge); 
     515                $ntlm_res = $ntlm_client->NTLMResponse( 
     516                    substr($challenge, 24, 8), 
     517                    $password 
     518                ); 
     519                //msg3 
     520                $msg3 = $ntlm_client->typeMsg3( 
     521                    $ntlm_res, 
     522                    $username, 
     523                    $realm, 
     524                    $workstation 
     525                ); 
     526                // send encoded username 
     527                return $this->sendCommand('Username', base64_encode($msg3), 235); 
    440528            case 'CRAM-MD5': 
    441529                // Start authentication 
    442530                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { 
     
    673761    protected function parseHelloFields($type) 
    674762    { 
    675763        $this->server_caps = array(); 
    676         $lines = explode("\n", $this->last_reply); 
     764        $lines = explode("\n", $this->helo_rply); 
    677765 
    678766        foreach ($lines as $n => $s) { 
    679767            //First 4 chars contain response code followed by - or space 
     
    11151203    { 
    11161204        return $this->Timeout; 
    11171205    } 
    1118 } 
     1206 
     1207    /** 
     1208     * Reports an error number and string. 
     1209     * @param integer $errno The error number returned by PHP. 
     1210     * @param string $errmsg The error message returned by PHP. 
     1211     */ 
     1212    protected function errorHandler($errno, $errmsg) 
     1213    { 
     1214        $notice = 'Connection: Failed to connect to server.'; 
     1215        $this->setError( 
     1216            $notice, 
     1217            $errno, 
     1218            $errmsg 
     1219        ); 
     1220        $this->edebug( 
     1221            $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, 
     1222            self::DEBUG_CONNECTION 
     1223        ); 
     1224    } 
     1225 
     1226    /** 
     1227     * Will return the ID of the last smtp transaction based on a list of patterns provided 
     1228     * in SMTP::$smtp_transaction_id_patterns. 
     1229     * If no reply has been received yet, it will return null. 
     1230     * If no pattern has been matched, it will return false. 
     1231     * @return bool|null|string 
     1232     */ 
     1233    public function getLastTransactionID() 
     1234    { 
     1235        $reply = $this->getLastReply(); 
     1236 
     1237        if (empty($reply)) { 
     1238            return null; 
     1239        } 
     1240 
     1241        foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 
     1242            if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 
     1243                return $matches[1]; 
     1244            } 
     1245        } 
     1246 
     1247        return false; 
     1248    } 
     1249} 
     1250 No newline at end of file 
  • wp-includes/class-phpmailer.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    3131     * The PHPMailer Version number. 
    3232     * @var string 
    3333     */ 
    34     public $Version = '5.2.14'; 
     34    public $Version = '5.2.19'; 
    3535 
    3636    /** 
    3737     * Email priority. 
     
    201201    /** 
    202202     * An ID to be used in the Message-ID header. 
    203203     * If empty, a unique id will be generated. 
     204     * You can set your own, but it must be in the format "<id@domain>", 
     205     * as defined in RFC5322 section 3.6.4 or it will be ignored. 
     206     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 
    204207     * @var string 
    205208     */ 
    206209    public $MessageID = ''; 
     
    285288 
    286289    /** 
    287290     * SMTP auth type. 
    288      * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 
     291     * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified 
    289292     * @var string 
    290293     */ 
    291294    public $AuthType = ''; 
     
    352355    /** 
    353356     * Whether to split multiple to addresses into multiple messages 
    354357     * or send them all in one message. 
     358     * Only supported in `mail` and `sendmail` transports, not in SMTP. 
    355359     * @var boolean 
    356360     */ 
    357361    public $SingleTo = false; 
     
    394398 
    395399    /** 
    396400     * DKIM Identity. 
    397      * Usually the email address used as the source of the email 
     401     * Usually the email address used as the source of the email. 
    398402     * @var string 
    399403     */ 
    400404    public $DKIM_identity = ''; 
     
    419423     */ 
    420424    public $DKIM_private = ''; 
    421425 
     426    /** 
     427     * DKIM private key string. 
     428     * If set, takes precedence over `$DKIM_private`. 
     429     * @var string 
     430     */ 
     431    public $DKIM_private_string = ''; 
     432 
    422433    /** 
    423434     * Callback Action function name. 
    424435     * 
     
    446457     */ 
    447458    public $XMailer = ''; 
    448459 
     460    /** 
     461     * Which validator to use by default when validating email addresses. 
     462     * May be a callable to inject your own validator, but there are several built-in validators. 
     463     * @see PHPMailer::validateAddress() 
     464     * @var string|callable 
     465     * @static 
     466     */ 
     467    public static $validator = 'auto'; 
     468 
    449469    /** 
    450470     * An instance of the SMTP sender class. 
    451471     * @var SMTP 
     
    634654     * Constructor. 
    635655     * @param boolean $exceptions Should we throw external exceptions? 
    636656     */ 
    637     public function __construct($exceptions = false) 
     657    public function __construct($exceptions = null) 
    638658    { 
    639         $this->exceptions = (boolean)$exceptions; 
     659        if ($exceptions !== null) { 
     660            $this->exceptions = (boolean)$exceptions; 
     661        } 
    640662    } 
    641663 
    642664    /** 
     
    645667    public function __destruct() 
    646668    { 
    647669        //Close any open SMTP connection nicely 
    648         if ($this->Mailer == 'smtp') { 
    649             $this->smtpClose(); 
    650         } 
     670        $this->smtpClose(); 
    651671    } 
    652672 
    653673    /** 
     
    671691        } else { 
    672692            $subject = $this->encodeHeader($this->secureHeader($subject)); 
    673693        } 
    674         if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { 
     694 
     695        //Can't use additional_parameters in safe_mode, calling mail() with null params breaks 
     696        //@link http://php.net/manual/en/function.mail.php 
     697        if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { 
    675698            $result = @mail($to, $subject, $body, $header); 
    676699        } else { 
    677700            $result = @mail($to, $subject, $body, $header, $params); 
    678701        } 
    679702        return $result; 
    680703    } 
    681  
    682704    /** 
    683705     * Output debugging info via user-defined method. 
    684706     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). 
     
    704726            case 'html': 
    705727                //Cleans up output a bit for a better looking, HTML-safe output 
    706728                echo htmlentities( 
    707                     preg_replace('/[\r\n]+/', '', $str), 
    708                     ENT_QUOTES, 
    709                     'UTF-8' 
    710                 ) 
    711                 . "<br>\n"; 
     729                        preg_replace('/[\r\n]+/', '', $str), 
     730                        ENT_QUOTES, 
     731                        'UTF-8' 
     732                    ) 
     733                    . "<br>\n"; 
    712734                break; 
    713735            case 'echo': 
    714736            default: 
    715737                //Normalize line breaks 
    716                 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 
     738                $str = preg_replace('/\r\n?/ms', "\n", $str); 
    717739                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 
    718                     "\n", 
    719                     "\n                   \t                  ", 
    720                     trim($str) 
    721                 ) . "\n"; 
     740                        "\n", 
     741                        "\n                   \t                  ", 
     742                        trim($str) 
     743                    ) . "\n"; 
    722744        } 
    723745    } 
    724746 
     
    850872        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 
    851873        if (($pos = strrpos($address, '@')) === false) { 
    852874            // At-sign is misssing. 
    853             $error_message = $this->lang('invalid_address') . $address; 
     875            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 
    854876            $this->setError($error_message); 
    855877            $this->edebug($error_message); 
    856878            if ($this->exceptions) { 
     
    900922            return false; 
    901923        } 
    902924        if (!$this->validateAddress($address)) { 
    903             $error_message = $this->lang('invalid_address') . $address; 
     925            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 
    904926            $this->setError($error_message); 
    905927            $this->edebug($error_message); 
    906928            if ($this->exceptions) { 
     
    923945        return false; 
    924946    } 
    925947 
     948    /** 
     949     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses 
     950     * of the form "display name <address>" into an array of name/address pairs. 
     951     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. 
     952     * Note that quotes in the name part are removed. 
     953     * @param string $addrstr The address list string 
     954     * @param bool $useimap Whether to use the IMAP extension to parse the list 
     955     * @return array 
     956     * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation 
     957     */ 
     958    public function parseAddresses($addrstr, $useimap = true) 
     959    { 
     960        $addresses = array(); 
     961        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { 
     962            //Use this built-in parser if it's available 
     963            $list = imap_rfc822_parse_adrlist($addrstr, ''); 
     964            foreach ($list as $address) { 
     965                if ($address->host != '.SYNTAX-ERROR.') { 
     966                    if ($this->validateAddress($address->mailbox . '@' . $address->host)) { 
     967                        $addresses[] = array( 
     968                            'name' => (property_exists($address, 'personal') ? $address->personal : ''), 
     969                            'address' => $address->mailbox . '@' . $address->host 
     970                        ); 
     971                    } 
     972                } 
     973            } 
     974        } else { 
     975            //Use this simpler parser 
     976            $list = explode(',', $addrstr); 
     977            foreach ($list as $address) { 
     978                $address = trim($address); 
     979                //Is there a separate name part? 
     980                if (strpos($address, '<') === false) { 
     981                    //No separate name, just use the whole thing 
     982                    if ($this->validateAddress($address)) { 
     983                        $addresses[] = array( 
     984                            'name' => '', 
     985                            'address' => $address 
     986                        ); 
     987                    } 
     988                } else { 
     989                    list($name, $email) = explode('<', $address); 
     990                    $email = trim(str_replace('>', '', $email)); 
     991                    if ($this->validateAddress($email)) { 
     992                        $addresses[] = array( 
     993                            'name' => trim(str_replace(array('"', "'"), '', $name)), 
     994                            'address' => $email 
     995                        ); 
     996                    } 
     997                } 
     998            } 
     999        } 
     1000        return $addresses; 
     1001    } 
     1002 
    9261003    /** 
    9271004     * Set the From and FromName properties. 
    9281005     * @param string $address 
     
    9391016        if (($pos = strrpos($address, '@')) === false or 
    9401017            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and 
    9411018            !$this->validateAddress($address)) { 
    942             $error_message = $this->lang('invalid_address') . $address; 
     1019            $error_message = $this->lang('invalid_address') . " (setFrom) $address"; 
    9431020            $this->setError($error_message); 
    9441021            $this->edebug($error_message); 
    9451022            if ($this->exceptions) { 
     
    9721049    /** 
    9731050     * Check that a string looks like an email address. 
    9741051     * @param string $address The email address to check 
    975      * @param string $patternselect A selector for the validation pattern to use : 
     1052     * @param string|callable $patternselect A selector for the validation pattern to use : 
    9761053     * * `auto` Pick best pattern automatically; 
    9771054     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; 
    9781055     * * `pcre` Use old PCRE implementation; 
    9791056     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; 
    9801057     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. 
    9811058     * * `noregex` Don't use a regex: super fast, really dumb. 
     1059     * Alternatively you may pass in a callable to inject your own validator, for example: 
     1060     * PHPMailer::validateAddress('user@example.com', function($address) { 
     1061     *     return (strpos($address, '@') !== false); 
     1062     * }); 
     1063     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. 
    9821064     * @return boolean 
    9831065     * @static 
    9841066     * @access public 
    9851067     */ 
    986     public static function validateAddress($address, $patternselect = 'auto') 
     1068    public static function validateAddress($address, $patternselect = null) 
    9871069    { 
     1070        if (is_null($patternselect)) { 
     1071            $patternselect = self::$validator; 
     1072        } 
     1073        if (is_callable($patternselect)) { 
     1074            return call_user_func($patternselect, $address); 
     1075        } 
    9881076        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 
    9891077        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { 
    9901078            return false; 
     
    11011189            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { 
    11021190                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); 
    11031191                if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? 
    1104                     idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : 
    1105                     idn_to_ascii($domain)) !== false) { 
     1192                        idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : 
     1193                        idn_to_ascii($domain)) !== false) { 
    11061194                    return substr($address, 0, $pos) . $punycode; 
    11071195                } 
    11081196            } 
     
    11611249                } 
    11621250                $this->$address_kind = $this->punyencodeAddress($this->$address_kind); 
    11631251                if (!$this->validateAddress($this->$address_kind)) { 
    1164                     $error_message = $this->lang('invalid_address') . $this->$address_kind; 
     1252                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; 
    11651253                    $this->setError($error_message); 
    11661254                    $this->edebug($error_message); 
    11671255                    if ($this->exceptions) { 
     
    11721260            } 
    11731261 
    11741262            // Set whether the message is multipart/alternative 
    1175             if (!empty($this->AltBody)) { 
     1263            if ($this->alternativeExists()) { 
    11761264                $this->ContentType = 'multipart/alternative'; 
    11771265            } 
    11781266 
     
    12061294 
    12071295            // Sign with DKIM if enabled 
    12081296            if (!empty($this->DKIM_domain) 
    1209                 && !empty($this->DKIM_private) 
    12101297                && !empty($this->DKIM_selector) 
    1211                 && file_exists($this->DKIM_private)) { 
     1298                && (!empty($this->DKIM_private_string) 
     1299                    || (!empty($this->DKIM_private) && file_exists($this->DKIM_private)) 
     1300                ) 
     1301            ) { 
    12121302                $header_dkim = $this->DKIM_Add( 
    12131303                    $this->MIMEHeader . $this->mailHeader, 
    12141304                    $this->encodeHeader($this->secureHeader($this->Subject)), 
     
    12741364     */ 
    12751365    protected function sendmailSend($header, $body) 
    12761366    { 
    1277         if ($this->Sender != '') { 
     1367        if (!empty($this->Sender)) { 
    12781368            if ($this->Mailer == 'qmail') { 
    12791369                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 
    12801370            } else { 
     
    13491439        } 
    13501440        $to = implode(', ', $toArr); 
    13511441 
    1352         if (empty($this->Sender)) { 
    1353             $params = ' '; 
    1354         } else { 
    1355             $params = sprintf('-f%s', $this->Sender); 
     1442        $params = null; 
     1443        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver 
     1444        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 
     1445            $params = sprintf('-f%s', escapeshellarg($this->Sender)); 
    13561446        } 
    1357         if ($this->Sender != '' and !ini_get('safe_mode')) { 
     1447        if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { 
    13581448            $old_from = ini_get('sendmail_from'); 
    13591449            ini_set('sendmail_from', $this->Sender); 
    13601450        } 
    13611451        $result = false; 
    1362         if ($this->SingleTo && count($toArr) > 1) { 
     1452        if ($this->SingleTo and count($toArr) > 1) { 
    13631453            foreach ($toArr as $toAddr) { 
    13641454                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 
    13651455                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); 
     
    13851475    public function getSMTPInstance() 
    13861476    { 
    13871477        if (!is_object($this->smtp)) { 
    1388                         require_once( 'class-smtp.php' ); 
    13891478            $this->smtp = new SMTP; 
    13901479        } 
    13911480        return $this->smtp; 
     
    14091498        if (!$this->smtpConnect($this->SMTPOptions)) { 
    14101499            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 
    14111500        } 
    1412         if ('' == $this->Sender) { 
    1413             $smtp_from = $this->From; 
    1414         } else { 
     1501        if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 
    14151502            $smtp_from = $this->Sender; 
     1503        } else { 
     1504            $smtp_from = $this->From; 
    14161505        } 
    14171506        if (!$this->smtp->mail($smtp_from)) { 
    14181507            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); 
     
    14661555     * @throws phpmailerException 
    14671556     * @return boolean 
    14681557     */ 
    1469     public function smtpConnect($options = array()) 
     1558    public function smtpConnect($options = null) 
    14701559    { 
    14711560        if (is_null($this->smtp)) { 
    14721561            $this->smtp = $this->getSMTPInstance(); 
    14731562        } 
    14741563 
     1564        //If no options are provided, use whatever is set in the instance 
     1565        if (is_null($options)) { 
     1566            $options = $this->SMTPOptions; 
     1567        } 
     1568 
    14751569        // Already connected? 
    14761570        if ($this->smtp->connected()) { 
    14771571            return true; 
     
    15411635                        if (!$this->smtp->startTLS()) { 
    15421636                            throw new phpmailerException($this->lang('connect_host')); 
    15431637                        } 
    1544                         // We must resend HELO after tls negotiation 
     1638                        // We must resend EHLO after TLS negotiation 
    15451639                        $this->smtp->hello($hello); 
    15461640                    } 
    15471641                    if ($this->SMTPAuth) { 
     
    15801674     */ 
    15811675    public function smtpClose() 
    15821676    { 
    1583         if ($this->smtp !== null) { 
     1677        if (is_a($this->smtp, 'SMTP')) { 
    15841678            if ($this->smtp->connected()) { 
    15851679                $this->smtp->quit(); 
    15861680                $this->smtp->close(); 
     
    15991693     */ 
    16001694    public function setLanguage($langcode = 'en', $lang_path = '') 
    16011695    { 
     1696        // Backwards compatibility for renamed language codes 
     1697        $renamed_langcodes = array( 
     1698            'br' => 'pt_br', 
     1699            'cz' => 'cs', 
     1700            'dk' => 'da', 
     1701            'no' => 'nb', 
     1702            'se' => 'sv', 
     1703        ); 
     1704 
     1705        if (isset($renamed_langcodes[$langcode])) { 
     1706            $langcode = $renamed_langcodes[$langcode]; 
     1707        } 
     1708 
    16021709        // Define full set of translatable strings in English 
    16031710        $PHPMAILER_LANG = array( 
    16041711            'authenticate' => 'SMTP Error: Could not authenticate.', 
     
    16251732            // Calculate an absolute path so it can work if CWD is not here 
    16261733            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 
    16271734        } 
     1735        //Validate $langcode 
     1736        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { 
     1737            $langcode = 'en'; 
     1738        } 
    16281739        $foundlang = true; 
    16291740        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 
    16301741        // There is no English translation file 
     
    16831794            return $this->secureHeader($addr[0]); 
    16841795        } else { 
    16851796            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 
    1686                 $addr[0] 
    1687             ) . '>'; 
     1797                    $addr[0] 
     1798                ) . '>'; 
    16881799        } 
    16891800    } 
    16901801 
     
    19182029            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 
    19192030        } 
    19202031 
    1921         if ($this->MessageID != '') { 
     2032        // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 
     2033        // https://tools.ietf.org/html/rfc5322#section-3.6.4 
     2034        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { 
    19222035            $this->lastMessageID = $this->MessageID; 
    19232036        } else { 
    19242037            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); 
     
    20202133     */ 
    20212134    public function getSentMIMEMessage() 
    20222135    { 
    2023         return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 
     2136        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; 
     2137    } 
     2138 
     2139    /** 
     2140     * Create unique ID 
     2141     * @return string 
     2142     */ 
     2143    protected function generateId() { 
     2144        return md5(uniqid(time())); 
    20242145    } 
    20252146 
    20262147    /** 
     
    20342155    { 
    20352156        $body = ''; 
    20362157        //Create unique IDs and preset boundaries 
    2037         $this->uniqueid = md5(uniqid(time())); 
     2158        $this->uniqueid = $this->generateId(); 
    20382159        $this->boundary[1] = 'b1_' . $this->uniqueid; 
    20392160        $this->boundary[2] = 'b2_' . $this->uniqueid; 
    20402161        $this->boundary[3] = 'b3_' . $this->uniqueid; 
     
    20502171        //Can we do a 7-bit downgrade? 
    20512172        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 
    20522173            $bodyEncoding = '7bit'; 
     2174            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 
    20532175            $bodyCharSet = 'us-ascii'; 
    20542176        } 
    2055         //If lines are too long, change to quoted-printable transfer encoding 
    2056         if (self::hasLineLongerThanMax($this->Body)) { 
    2057             $this->Encoding = 'quoted-printable'; 
     2177        //If lines are too long, and we're not already using an encoding that will shorten them, 
     2178        //change to quoted-printable transfer encoding for the body part only 
     2179        if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { 
    20582180            $bodyEncoding = 'quoted-printable'; 
    20592181        } 
    20602182 
     
    20632185        //Can we do a 7-bit downgrade? 
    20642186        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 
    20652187            $altBodyEncoding = '7bit'; 
     2188            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 
    20662189            $altBodyCharSet = 'us-ascii'; 
    20672190        } 
    2068         //If lines are too long, change to quoted-printable transfer encoding 
    2069         if (self::hasLineLongerThanMax($this->AltBody)) { 
     2191        //If lines are too long, and we're not already using an encoding that will shorten them, 
     2192        //change to quoted-printable transfer encoding for the alt body part only 
     2193        if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { 
    20702194            $altBodyEncoding = 'quoted-printable'; 
    20712195        } 
    20722196        //Use this as a preamble in all multipart message types 
     
    21692293                $body .= $this->attachAll('attachment', $this->boundary[1]); 
    21702294                break; 
    21712295            default: 
    2172                 // catch case 'plain' and case '' 
    2173                 $body .= $this->encodeString($this->Body, $bodyEncoding); 
     2296                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types 
     2297                //Reset the `Encoding` property in case we changed it for line length reasons 
     2298                $this->Encoding = $bodyEncoding; 
     2299                $body .= $this->encodeString($this->Body, $this->Encoding); 
    21742300                break; 
    21752301        } 
    21762302 
     
    22762402 
    22772403    /** 
    22782404     * Set the message type. 
    2279      * PHPMailer only supports some preset message types, 
    2280      * not arbitrary MIME structures. 
     2405     * PHPMailer only supports some preset message types, not arbitrary MIME structures. 
    22812406     * @access protected 
    22822407     * @return void 
    22832408     */ 
     
    22952420        } 
    22962421        $this->message_type = implode('_', $type); 
    22972422        if ($this->message_type == '') { 
     2423            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text 
    22982424            $this->message_type = 'plain'; 
    22992425        } 
    23002426    } 
     
    26102736            /** @noinspection PhpMissingBreakStatementInspection */ 
    26112737            case 'comment': 
    26122738                $matchcount = preg_match_all('/[()"]/', $str, $matches); 
    2613                 // Intentional fall-through 
     2739            // Intentional fall-through 
    26142740            case 'text': 
    26152741            default: 
    26162742                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 
     
    27812907            case 'comment': 
    27822908                // RFC 2047 section 5.2 
    27832909                $pattern = '\(\)"'; 
    2784                 // intentional fall-through 
    2785                 // for this reason we build the $pattern without including delimiters and [] 
     2910            // intentional fall-through 
     2911            // for this reason we build the $pattern without including delimiters and [] 
    27862912            case 'text': 
    27872913            default: 
    27882914                // RFC 2047 section 5.1 
     
    32093335    } 
    32103336 
    32113337    /** 
    3212      * Create a message from an HTML string. 
    3213      * Automatically makes modifications for inline images and backgrounds 
    3214      * and creates a plain-text version by converting the HTML. 
    3215      * Overwrites any existing values in $this->Body and $this->AltBody 
     3338     * Create a message body from an HTML string. 
     3339     * Automatically inlines images and creates a plain-text version by converting the HTML, 
     3340     * overwriting any existing values in Body and AltBody. 
     3341     * $basedir is used when handling relative image paths, e.g. <img src="images/a.png"> 
     3342     * will look for an image file in $basedir/images/a.png and convert it to inline. 
     3343     * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. 
    32163344     * @access public 
    32173345     * @param string $message HTML message string 
    3218      * @param string $basedir baseline directory for path 
     3346     * @param string $basedir base directory for relative paths to images 
    32193347     * @param boolean|callable $advanced Whether to use the internal HTML to text converter 
    32203348     *    or your own custom converter @see PHPMailer::html2text() 
    3221      * @return string $message 
     3349     * @return string $message The transformed message Body 
    32223350     */ 
    32233351    public function msgHTML($message, $basedir = '', $advanced = false) 
    32243352    { 
     
    32413369                            $message 
    32423370                        ); 
    32433371                    } 
    3244                 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) { 
     3372                } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { 
    32453373                    // Do not change urls for absolute images (thanks to corvuscorax) 
    32463374                    // Do not change urls that are already inline images 
    32473375                    $filename = basename($url); 
     
    32773405        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 
    32783406        $this->Body = $this->normalizeBreaks($message); 
    32793407        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 
    3280         if (empty($this->AltBody)) { 
     3408        if (!$this->alternativeExists()) { 
    32813409            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 
    32823410                self::CRLF . self::CRLF; 
    32833411        } 
     
    32883416     * Convert an HTML string into plain text. 
    32893417     * This is used by msgHTML(). 
    32903418     * Note - older versions of this function used a bundled advanced converter 
    3291      * which was been removed for license reasons in #232 
     3419     * which was been removed for license reasons in #232. 
    32923420     * Example usage: 
    32933421     * <code> 
    32943422     * // Use default conversion 
     
    35883716     * @access public 
    35893717     * @param string $signHeader 
    35903718     * @throws phpmailerException 
    3591      * @return string 
     3719     * @return string The DKIM signature value 
    35923720     */ 
    35933721    public function DKIM_Sign($signHeader) 
    35943722    { 
     
    35983726            } 
    35993727            return ''; 
    36003728        } 
    3601         $privKeyStr = file_get_contents($this->DKIM_private); 
    3602         if ($this->DKIM_passphrase != '') { 
     3729        $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); 
     3730        if ('' != $this->DKIM_passphrase) { 
    36033731            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 
    36043732        } else { 
    3605             $privKey = $privKeyStr; 
     3733            $privKey = openssl_pkey_get_private($privKeyStr); 
    36063734        } 
    3607         if (openssl_sign($signHeader, $signature, $privKey)) { 
    3608             return base64_encode($signature); 
     3735        //Workaround for missing digest algorithms in old PHP & OpenSSL versions 
     3736        //@link http://stackoverflow.com/a/11117338/333340 
     3737        if (version_compare(PHP_VERSION, '5.3.0') >= 0 and 
     3738            in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 
     3739            if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 
     3740                openssl_pkey_free($privKey); 
     3741                return base64_encode($signature); 
     3742            } 
     3743        } else { 
     3744            $pinfo = openssl_pkey_get_details($privKey); 
     3745            $hash = hash('sha256', $signHeader); 
     3746            //'Magic' constant for SHA256 from RFC3447 
     3747            //@link https://tools.ietf.org/html/rfc3447#page-43 
     3748            $t = '3031300d060960864801650304020105000420' . $hash; 
     3749            $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); 
     3750            $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); 
     3751 
     3752            if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { 
     3753                openssl_pkey_free($privKey); 
     3754                return base64_encode($signature); 
     3755            } 
    36093756        } 
     3757        openssl_pkey_free($privKey); 
    36103758        return ''; 
    36113759    } 
    36123760 
     
    36233771        foreach ($lines as $key => $line) { 
    36243772            list($heading, $value) = explode(':', $line, 2); 
    36253773            $heading = strtolower($heading); 
    3626             $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces 
     3774            $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces 
    36273775            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 
    36283776        } 
    36293777        $signHeader = implode("\r\n", $lines); 
     
    36613809     */ 
    36623810    public function DKIM_Add($headers_line, $subject, $body) 
    36633811    { 
    3664         $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 
     3812        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms 
    36653813        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 
    36663814        $DKIMquery = 'dns/txt'; // Query method 
    36673815        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 
     
    36693817        $headers = explode($this->LE, $headers_line); 
    36703818        $from_header = ''; 
    36713819        $to_header = ''; 
     3820        $date_header = ''; 
    36723821        $current = ''; 
    36733822        foreach ($headers as $header) { 
    36743823            if (strpos($header, 'From:') === 0) { 
     
    36773826            } elseif (strpos($header, 'To:') === 0) { 
    36783827                $to_header = $header; 
    36793828                $current = 'to_header'; 
     3829            } elseif (strpos($header, 'Date:') === 0) { 
     3830                $date_header = $header; 
     3831                $current = 'date_header'; 
    36803832            } else { 
    36813833                if (!empty($$current) && strpos($header, ' =?') === 0) { 
    36823834                    $$current .= $header; 
     
    36873839        } 
    36883840        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 
    36893841        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 
     3842        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); 
    36903843        $subject = str_replace( 
    36913844            '|', 
    36923845            '=7C', 
     
    36943847        ); // Copied header fields (dkim-quoted-printable) 
    36953848        $body = $this->DKIM_BodyC($body); 
    36963849        $DKIMlen = strlen($body); // Length of body 
    3697         $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body 
     3850        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body 
    36983851        if ('' == $this->DKIM_identity) { 
    36993852            $ident = ''; 
    37003853        } else { 
     
    37073860            $this->DKIM_selector . 
    37083861            ";\r\n" . 
    37093862            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 
    3710             "\th=From:To:Subject;\r\n" . 
     3863            "\th=From:To:Date:Subject;\r\n" . 
    37113864            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 
    37123865            "\tz=$from\r\n" . 
    37133866            "\t|$to\r\n" . 
     3867            "\t|$date\r\n" . 
    37143868            "\t|$subject;\r\n" . 
    37153869            "\tbh=" . $DKIMb64 . ";\r\n" . 
    37163870            "\tb="; 
    37173871        $toSign = $this->DKIM_HeaderC( 
    37183872            $from_header . "\r\n" . 
    37193873            $to_header . "\r\n" . 
     3874            $date_header . "\r\n" . 
    37203875            $subject_header . "\r\n" . 
    37213876            $dkimhdrs 
    37223877        ); 
     
    38253980        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; 
    38263981        return $errorMsg; 
    38273982    } 
    3828 } 
     3983} 
     3984 No newline at end of file