wp/wp-includes/PHPMailer/SMTP.php
changeset 22 8c2e4d02f4ef
parent 21 48c4eec2b7e6
equal deleted inserted replaced
21:48c4eec2b7e6 22:8c2e4d02f4ef
    11  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    11  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    12  * @author    Brent R. Matzelle (original founder)
    12  * @author    Brent R. Matzelle (original founder)
    13  * @copyright 2012 - 2020 Marcus Bointon
    13  * @copyright 2012 - 2020 Marcus Bointon
    14  * @copyright 2010 - 2012 Jim Jagielski
    14  * @copyright 2010 - 2012 Jim Jagielski
    15  * @copyright 2004 - 2009 Andy Prevost
    15  * @copyright 2004 - 2009 Andy Prevost
    16  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
    16  * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
    17  * @note      This program is distributed in the hope that it will be useful - WITHOUT
    17  * @note      This program is distributed in the hope that it will be useful - WITHOUT
    18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    19  * FITNESS FOR A PARTICULAR PURPOSE.
    19  * FITNESS FOR A PARTICULAR PURPOSE.
    20  */
    20  */
    21 
    21 
    33     /**
    33     /**
    34      * The PHPMailer SMTP version number.
    34      * The PHPMailer SMTP version number.
    35      *
    35      *
    36      * @var string
    36      * @var string
    37      */
    37      */
    38     const VERSION = '6.9.1';
    38     const VERSION = '6.9.3';
    39 
    39 
    40     /**
    40     /**
    41      * SMTP line break constant.
    41      * SMTP line break constant.
    42      *
    42      *
    43      * @var string
    43      * @var string
    60 
    60 
    61     /**
    61     /**
    62      * The maximum line length allowed by RFC 5321 section 4.5.3.1.6,
    62      * The maximum line length allowed by RFC 5321 section 4.5.3.1.6,
    63      * *excluding* a trailing CRLF break.
    63      * *excluding* a trailing CRLF break.
    64      *
    64      *
    65      * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.6
    65      * @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.6
    66      *
    66      *
    67      * @var int
    67      * @var int
    68      */
    68      */
    69     const MAX_LINE_LENGTH = 998;
    69     const MAX_LINE_LENGTH = 998;
    70 
    70 
    71     /**
    71     /**
    72      * The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5,
    72      * The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5,
    73      * *including* a trailing CRLF line break.
    73      * *including* a trailing CRLF line break.
    74      *
    74      *
    75      * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.5
    75      * @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.5
    76      *
    76      *
    77      * @var int
    77      * @var int
    78      */
    78      */
    79     const MAX_REPLY_LENGTH = 512;
    79     const MAX_REPLY_LENGTH = 512;
    80 
    80 
   150     public $Debugoutput = 'echo';
   150     public $Debugoutput = 'echo';
   151 
   151 
   152     /**
   152     /**
   153      * Whether to use VERP.
   153      * Whether to use VERP.
   154      *
   154      *
   155      * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
   155      * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
   156      * @see http://www.postfix.org/VERP_README.html Info on VERP
   156      * @see https://www.postfix.org/VERP_README.html Info on VERP
   157      *
   157      *
   158      * @var bool
   158      * @var bool
   159      */
   159      */
   160     public $do_verp = false;
   160     public $do_verp = false;
   161 
   161 
   162     /**
   162     /**
   163      * The timeout value for connection, in seconds.
   163      * The timeout value for connection, in seconds.
   164      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
   164      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
   165      * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
   165      * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
   166      *
   166      *
   167      * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
   167      * @see https://www.rfc-editor.org/rfc/rfc2821#section-4.5.3.2
   168      *
   168      *
   169      * @var int
   169      * @var int
   170      */
   170      */
   171     public $Timeout = 300;
   171     public $Timeout = 300;
   172 
   172 
   185      *
   185      *
   186      * @var string[]
   186      * @var string[]
   187      */
   187      */
   188     protected $smtp_transaction_id_patterns = [
   188     protected $smtp_transaction_id_patterns = [
   189         'exim' => '/[\d]{3} OK id=(.*)/',
   189         'exim' => '/[\d]{3} OK id=(.*)/',
   190         'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/',
   190         'sendmail' => '/[\d]{3} 2\.0\.0 (.*) Message/',
   191         'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/',
   191         'postfix' => '/[\d]{3} 2\.0\.0 Ok: queued as (.*)/',
   192         'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/',
   192         'Microsoft_ESMTP' => '/[0-9]{3} 2\.[\d]\.0 (.*)@(?:.*) Queued mail for delivery/',
   193         'Amazon_SES' => '/[\d]{3} Ok (.*)/',
   193         'Amazon_SES' => '/[\d]{3} Ok (.*)/',
   194         'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
   194         'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
   195         'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
   195         'CampaignMonitor' => '/[\d]{3} 2\.0\.0 OK:([a-zA-Z\d]{48})/',
   196         'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
   196         'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
   197         'ZoneMTA' => '/[\d]{3} Message queued as (.*)/',
   197         'ZoneMTA' => '/[\d]{3} Message queued as (.*)/',
   198         'Mailjet' => '/[\d]{3} OK queued as (.*)/',
   198         'Mailjet' => '/[\d]{3} OK queued as (.*)/',
   199     ];
   199     ];
   200 
   200 
   278         if ($level > $this->do_debug) {
   278         if ($level > $this->do_debug) {
   279             return;
   279             return;
   280         }
   280         }
   281         //Is this a PSR-3 logger?
   281         //Is this a PSR-3 logger?
   282         if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
   282         if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
   283             $this->Debugoutput->debug($str);
   283             //Remove trailing line breaks potentially added by calls to SMTP::client_send()
       
   284             $this->Debugoutput->debug(rtrim($str, "\r\n"));
   284 
   285 
   285             return;
   286             return;
   286         }
   287         }
   287         //Avoid clash with built-in function names
   288         //Avoid clash with built-in function names
   288         if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
   289         if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
   291             return;
   292             return;
   292         }
   293         }
   293         switch ($this->Debugoutput) {
   294         switch ($this->Debugoutput) {
   294             case 'error_log':
   295             case 'error_log':
   295                 //Don't output, just log
   296                 //Don't output, just log
       
   297                 /** @noinspection ForgottenDebugOutputInspection */
   296                 error_log($str);
   298                 error_log($str);
   297                 break;
   299                 break;
   298             case 'html':
   300             case 'html':
   299                 //Cleans up output a bit for a better looking, HTML-safe output
   301                 //Cleans up output a bit for a better looking, HTML-safe output
   300                 echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
   302                 echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
   369         if ($responseCode === 220) {
   371         if ($responseCode === 220) {
   370             return true;
   372             return true;
   371         }
   373         }
   372         //Anything other than a 220 response means something went wrong
   374         //Anything other than a 220 response means something went wrong
   373         //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
   375         //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
   374         //https://tools.ietf.org/html/rfc5321#section-3.1
   376         //https://www.rfc-editor.org/rfc/rfc5321#section-3.1
   375         if ($responseCode === 554) {
   377         if ($responseCode === 554) {
   376             $this->quit();
   378             $this->quit();
   377         }
   379         }
   378         //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down)
   380         //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down)
   379         $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION);
   381         $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION);
   402 
   404 
   403         $errno = 0;
   405         $errno = 0;
   404         $errstr = '';
   406         $errstr = '';
   405         if ($streamok) {
   407         if ($streamok) {
   406             $socket_context = stream_context_create($options);
   408             $socket_context = stream_context_create($options);
   407             set_error_handler([$this, 'errorHandler']);
   409             set_error_handler(function () {
       
   410                 call_user_func_array([$this, 'errorHandler'], func_get_args());
       
   411             });
   408             $connection = stream_socket_client(
   412             $connection = stream_socket_client(
   409                 $host . ':' . $port,
   413                 $host . ':' . $port,
   410                 $errno,
   414                 $errno,
   411                 $errstr,
   415                 $errstr,
   412                 $timeout,
   416                 $timeout,
   417             //Fall back to fsockopen which should work in more places, but is missing some features
   421             //Fall back to fsockopen which should work in more places, but is missing some features
   418             $this->edebug(
   422             $this->edebug(
   419                 'Connection: stream_socket_client not available, falling back to fsockopen',
   423                 'Connection: stream_socket_client not available, falling back to fsockopen',
   420                 self::DEBUG_CONNECTION
   424                 self::DEBUG_CONNECTION
   421             );
   425             );
   422             set_error_handler([$this, 'errorHandler']);
   426             set_error_handler(function () {
       
   427                 call_user_func_array([$this, 'errorHandler'], func_get_args());
       
   428             });
   423             $connection = fsockopen(
   429             $connection = fsockopen(
   424                 $host,
   430                 $host,
   425                 $port,
   431                 $port,
   426                 $errno,
   432                 $errno,
   427                 $errstr,
   433                 $errstr,
   481             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
   487             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
   482             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
   488             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
   483         }
   489         }
   484 
   490 
   485         //Begin encrypted connection
   491         //Begin encrypted connection
   486         set_error_handler([$this, 'errorHandler']);
   492             set_error_handler(function () {
       
   493                 call_user_func_array([$this, 'errorHandler'], func_get_args());
       
   494             });
   487         $crypto_ok = stream_socket_enable_crypto(
   495         $crypto_ok = stream_socket_enable_crypto(
   488             $this->smtp_conn,
   496             $this->smtp_conn,
   489             true,
   497             true,
   490             $crypto_method
   498             $crypto_method
   491         );
   499         );
   572                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
   580                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
   573                     return false;
   581                     return false;
   574                 }
   582                 }
   575                 //Send encoded username and password
   583                 //Send encoded username and password
   576                 if (
   584                 if (
   577                     //Format from https://tools.ietf.org/html/rfc4616#section-2
   585                     //Format from https://www.rfc-editor.org/rfc/rfc4616#section-2
   578                     //We skip the first field (it's forgery), so the string starts with a null byte
   586                     //We skip the first field (it's forgery), so the string starts with a null byte
   579                     !$this->sendCommand(
   587                     !$this->sendCommand(
   580                         'User & Password',
   588                         'User & Password',
   581                         base64_encode("\0" . $username . "\0" . $password),
   589                         base64_encode("\0" . $username . "\0" . $password),
   582                         235
   590                         235
   646         if (function_exists('hash_hmac')) {
   654         if (function_exists('hash_hmac')) {
   647             return hash_hmac('md5', $data, $key);
   655             return hash_hmac('md5', $data, $key);
   648         }
   656         }
   649 
   657 
   650         //The following borrowed from
   658         //The following borrowed from
   651         //http://php.net/manual/en/function.mhash.php#27225
   659         //https://www.php.net/manual/en/function.mhash.php#27225
   652 
   660 
   653         //RFC 2104 HMAC implementation for php.
   661         //RFC 2104 HMAC implementation for php.
   654         //Creates an md5 HMAC.
   662         //Creates an md5 HMAC.
   655         //Eliminates the need to install mhash to compute a HMAC
   663         //Eliminates the need to install mhash to compute a HMAC
   656         //by Lance Rushing
   664         //by Lance Rushing
   785             $lines_out[] = $line;
   793             $lines_out[] = $line;
   786 
   794 
   787             //Send the lines to the server
   795             //Send the lines to the server
   788             foreach ($lines_out as $line_out) {
   796             foreach ($lines_out as $line_out) {
   789                 //Dot-stuffing as per RFC5321 section 4.5.2
   797                 //Dot-stuffing as per RFC5321 section 4.5.2
   790                 //https://tools.ietf.org/html/rfc5321#section-4.5.2
   798                 //https://www.rfc-editor.org/rfc/rfc5321#section-4.5.2
   791                 if (!empty($line_out) && $line_out[0] === '.') {
   799                 if (!empty($line_out) && $line_out[0] === '.') {
   792                     $line_out = '.' . $line_out;
   800                     $line_out = '.' . $line_out;
   793                 }
   801                 }
   794                 $this->client_send($line_out . static::LE, 'DATA');
   802                 $this->client_send($line_out . static::LE, 'DATA');
   795             }
   803             }
  1160         ) {
  1168         ) {
  1161             $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
  1169             $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
  1162         } else {
  1170         } else {
  1163             $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
  1171             $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
  1164         }
  1172         }
  1165         set_error_handler([$this, 'errorHandler']);
  1173         set_error_handler(function () {
       
  1174             call_user_func_array([$this, 'errorHandler'], func_get_args());
       
  1175         });
  1166         $result = fwrite($this->smtp_conn, $data);
  1176         $result = fwrite($this->smtp_conn, $data);
  1167         restore_error_handler();
  1177         restore_error_handler();
  1168 
  1178 
  1169         return $result;
  1179         return $result;
  1170     }
  1180     }
  1263         $selR = [$this->smtp_conn];
  1273         $selR = [$this->smtp_conn];
  1264         $selW = null;
  1274         $selW = null;
  1265         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  1275         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  1266             //Must pass vars in here as params are by reference
  1276             //Must pass vars in here as params are by reference
  1267             //solution for signals inspired by https://github.com/symfony/symfony/pull/6540
  1277             //solution for signals inspired by https://github.com/symfony/symfony/pull/6540
  1268             set_error_handler([$this, 'errorHandler']);
  1278             set_error_handler(function () {
       
  1279                 call_user_func_array([$this, 'errorHandler'], func_get_args());
       
  1280             });
  1269             $n = stream_select($selR, $selW, $selW, $this->Timelimit);
  1281             $n = stream_select($selR, $selW, $selW, $this->Timelimit);
  1270             restore_error_handler();
  1282             restore_error_handler();
  1271 
  1283 
  1272             if ($n === false) {
  1284             if ($n === false) {
  1273                 $message = $this->getError()['detail'];
  1285                 $message = $this->getError()['detail'];