WordPress.org

Make WordPress Core


Ignore:
Timestamp:
06/12/2020 03:45:30 PM (16 months ago)
Author:
desrosj
Message:

External Libraries: Upgrade PHPMailer to version 6.1.6.

Now that WordPress Core supports PHP >= 5.6, the PHPMailer library can be updated to the latest version.

The PHPMailer files now reside in a new directory, wp-includes/PHPMailer. These files are copied verbatim from the library upstream and will make updating in the future easier. For backwards compatibility, the old files will remain and trigger deprecated file warnings.

The PHPMailer class is also now under the PHPMailer\PHPMailer\PHPMailer namespace. The PHPMailer class in the global namespace has been aliased for a seamless transition.

This upgrade also clears up a handful of PHP compatibility issues detailed in #49922.

For a full list of changes, see the PHPMailer GitHub: https://github.com/PHPMailer/PHPMailer/compare/v5.2.27...v6.1.6.

Props Synchro, SergeyBiryukov, desrosj, donmhico, ayeshrajans.
Fixes #41750.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-smtp.php

    r46097 r48033  
    11<?php
    2 /**
    3  * PHPMailer RFC821 SMTP email transport class.
    4  * PHP Version 5
    5  * @package PHPMailer
    6  * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
    7  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    8  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    9  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    10  * @author Brent R. Matzelle (original founder)
    11  * @copyright 2014 Marcus Bointon
    12  * @copyright 2010 - 2012 Jim Jagielski
    13  * @copyright 2004 - 2009 Andy Prevost
    14  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
    15  * @note This program is distributed in the hope that it will be useful - WITHOUT
    16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    17  * FITNESS FOR A PARTICULAR PURPOSE.
    18  */
    192
    203/**
    21  * PHPMailer RFC821 SMTP email transport class.
    22  * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
    23  * @package PHPMailer
    24  * @author Chris Ryan
    25  * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
     4 * The SMTP class has been moved to the wp-includes/PHPMailer subdirectory and now uses the PHPMailer\PHPMailer namespace.
    265 */
    27 class SMTP
    28 {
    29     /**
    30      * The PHPMailer SMTP version number.
    31      * @var string
    32      */
    33     const VERSION = '5.2.27';
    34 
    35     /**
    36      * SMTP line break constant.
    37      * @var string
    38      */
    39     const CRLF = "\r\n";
    40 
    41     /**
    42      * The SMTP port to use if one is not specified.
    43      * @var integer
    44      */
    45     const DEFAULT_SMTP_PORT = 25;
    46 
    47     /**
    48      * The maximum line length allowed by RFC 2822 section 2.1.1
    49      * @var integer
    50      */
    51     const MAX_LINE_LENGTH = 998;
    52 
    53     /**
    54      * Debug level for no output
    55      */
    56     const DEBUG_OFF = 0;
    57 
    58     /**
    59      * Debug level to show client -> server messages
    60      */
    61     const DEBUG_CLIENT = 1;
    62 
    63     /**
    64      * Debug level to show client -> server and server -> client messages
    65      */
    66     const DEBUG_SERVER = 2;
    67 
    68     /**
    69      * Debug level to show connection status, client -> server and server -> client messages
    70      */
    71     const DEBUG_CONNECTION = 3;
    72 
    73     /**
    74      * Debug level to show all messages
    75      */
    76     const DEBUG_LOWLEVEL = 4;
    77 
    78     /**
    79      * The PHPMailer SMTP Version number.
    80      * @var string
    81      * @deprecated Use the `VERSION` constant instead
    82      * @see SMTP::VERSION
    83      */
    84     public $Version = '5.2.27';
    85 
    86     /**
    87      * SMTP server port number.
    88      * @var integer
    89      * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
    90      * @see SMTP::DEFAULT_SMTP_PORT
    91      */
    92     public $SMTP_PORT = 25;
    93 
    94     /**
    95      * SMTP reply line ending.
    96      * @var string
    97      * @deprecated Use the `CRLF` constant instead
    98      * @see SMTP::CRLF
    99      */
    100     public $CRLF = "\r\n";
    101 
    102     /**
    103      * Debug output level.
    104      * Options:
    105      * * self::DEBUG_OFF (`0`) No debug output, default
    106      * * self::DEBUG_CLIENT (`1`) Client commands
    107      * * self::DEBUG_SERVER (`2`) Client commands and server responses
    108      * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
    109      * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
    110      * @var integer
    111      */
    112     public $do_debug = self::DEBUG_OFF;
    113 
    114     /**
    115      * How to handle debug output.
    116      * Options:
    117      * * `echo` Output plain-text as-is, appropriate for CLI
    118      * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
    119      * * `error_log` Output to error log as configured in php.ini
    120      *
    121      * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
    122      * <code>
    123      * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
    124      * </code>
    125      * @var string|callable
    126      */
    127     public $Debugoutput = 'echo';
    128 
    129     /**
    130      * Whether to use VERP.
    131      * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
    132      * @link http://www.postfix.org/VERP_README.html Info on VERP
    133      * @var boolean
    134      */
    135     public $do_verp = false;
    136 
    137     /**
    138      * The timeout value for connection, in seconds.
    139      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
    140      * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
    141      * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
    142      * @var integer
    143      */
    144     public $Timeout = 300;
    145 
    146     /**
    147      * How long to wait for commands to complete, in seconds.
    148      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
    149      * @var integer
    150      */
    151     public $Timelimit = 300;
    152 
    153     /**
    154      * @var array Patterns to extract an SMTP transaction id from reply to a DATA command.
    155      * The first capture group in each regex will be used as the ID.
    156      */
    157     protected $smtp_transaction_id_patterns = array(
    158         'exim' => '/[0-9]{3} OK id=(.*)/',
    159         'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
    160         'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/'
    161     );
    162 
    163     /**
    164      * @var string The last transaction ID issued in response to a DATA command,
    165      * if one was detected
    166      */
    167     protected $last_smtp_transaction_id;
    168 
    169     /**
    170      * The socket for the server connection.
    171      * @var resource
    172      */
    173     protected $smtp_conn;
    174 
    175     /**
    176      * Error information, if any, for the last SMTP command.
    177      * @var array
    178      */
    179     protected $error = array(
    180         'error' => '',
    181         'detail' => '',
    182         'smtp_code' => '',
    183         'smtp_code_ex' => ''
    184     );
    185 
    186     /**
    187      * The reply the server sent to us for HELO.
    188      * If null, no HELO string has yet been received.
    189      * @var string|null
    190      */
    191     protected $helo_rply = null;
    192 
    193     /**
    194      * The set of SMTP extensions sent in reply to EHLO command.
    195      * Indexes of the array are extension names.
    196      * Value at index 'HELO' or 'EHLO' (according to command that was sent)
    197      * represents the server name. In case of HELO it is the only element of the array.
    198      * Other values can be boolean TRUE or an array containing extension options.
    199      * If null, no HELO/EHLO string has yet been received.
    200      * @var array|null
    201      */
    202     protected $server_caps = null;
    203 
    204     /**
    205      * The most recent reply received from the server.
    206      * @var string
    207      */
    208     protected $last_reply = '';
    209 
    210     /**
    211      * Output debugging info via a user-selected method.
    212      * @see SMTP::$Debugoutput
    213      * @see SMTP::$do_debug
    214      * @param string $str Debug string to output
    215      * @param integer $level The debug level of this message; see DEBUG_* constants
    216      * @return void
    217      */
    218     protected function edebug($str, $level = 0)
    219     {
    220         if ($level > $this->do_debug) {
    221             return;
    222         }
    223         //Avoid clash with built-in function names
    224         if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
    225             call_user_func($this->Debugoutput, $str, $level);
    226             return;
    227         }
    228         switch ($this->Debugoutput) {
    229             case 'error_log':
    230                 //Don't output, just log
    231                 error_log($str);
    232                 break;
    233             case 'html':
    234                 //Cleans up output a bit for a better looking, HTML-safe output
    235                 echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities(
    236                     preg_replace('/[\r\n]+/', '', $str),
    237                     ENT_QUOTES,
    238                     'UTF-8'
    239                 ) . "<br>\n";
    240                 break;
    241             case 'echo':
    242             default:
    243                 //Normalize line breaks
    244                 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
    245                 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
    246                     "\n",
    247                     "\n                   \t                  ",
    248                     trim($str)
    249                 ) . "\n";
    250         }
    251     }
    252 
    253     /**
    254      * Connect to an SMTP server.
    255      * @param string $host SMTP server IP or host name
    256      * @param integer $port The port number to connect to
    257      * @param integer $timeout How long to wait for the connection to open
    258      * @param array $options An array of options for stream_context_create()
    259      * @access public
    260      * @return boolean
    261      */
    262     public function connect($host, $port = null, $timeout = 30, $options = array())
    263     {
    264         static $streamok;
    265         //This is enabled by default since 5.0.0 but some providers disable it
    266         //Check this once and cache the result
    267         if (is_null($streamok)) {
    268             $streamok = function_exists('stream_socket_client');
    269         }
    270         // Clear errors to avoid confusion
    271         $this->setError('');
    272         // Make sure we are __not__ connected
    273         if ($this->connected()) {
    274             // Already connected, generate error
    275             $this->setError('Already connected to a server');
    276             return false;
    277         }
    278         if (empty($port)) {
    279             $port = self::DEFAULT_SMTP_PORT;
    280         }
    281         // Connect to the SMTP server
    282         $this->edebug(
    283             "Connection: opening to $host:$port, timeout=$timeout, options=" .
    284             var_export($options, true),
    285             self::DEBUG_CONNECTION
    286         );
    287         $errno = 0;
    288         $errstr = '';
    289         if ($streamok) {
    290             $socket_context = stream_context_create($options);
    291             set_error_handler(array($this, 'errorHandler'));
    292             $this->smtp_conn = stream_socket_client(
    293                 $host . ":" . $port,
    294                 $errno,
    295                 $errstr,
    296                 $timeout,
    297                 STREAM_CLIENT_CONNECT,
    298                 $socket_context
    299             );
    300             restore_error_handler();
    301         } else {
    302             //Fall back to fsockopen which should work in more places, but is missing some features
    303             $this->edebug(
    304                 "Connection: stream_socket_client not available, falling back to fsockopen",
    305                 self::DEBUG_CONNECTION
    306             );
    307             set_error_handler(array($this, 'errorHandler'));
    308             $this->smtp_conn = fsockopen(
    309                 $host,
    310                 $port,
    311                 $errno,
    312                 $errstr,
    313                 $timeout
    314             );
    315             restore_error_handler();
    316         }
    317         // Verify we connected properly
    318         if (!is_resource($this->smtp_conn)) {
    319             $this->setError(
    320                 'Failed to connect to server',
    321                 $errno,
    322                 $errstr
    323             );
    324             $this->edebug(
    325                 'SMTP ERROR: ' . $this->error['error']
    326                 . ": $errstr ($errno)",
    327                 self::DEBUG_CLIENT
    328             );
    329             return false;
    330         }
    331         $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
    332         // SMTP server can take longer to respond, give longer timeout for first read
    333         // Windows does not have support for this timeout function
    334         if (substr(PHP_OS, 0, 3) != 'WIN') {
    335             $max = ini_get('max_execution_time');
    336             // Don't bother if unlimited
    337             if ($max != 0 && $timeout > $max) {
    338                 @set_time_limit($timeout);
    339             }
    340             stream_set_timeout($this->smtp_conn, $timeout, 0);
    341         }
    342         // Get any announcement
    343         $announce = $this->get_lines();
    344         $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
    345         return true;
    346     }
    347 
    348     /**
    349      * Initiate a TLS (encrypted) session.
    350      * @access public
    351      * @return boolean
    352      */
    353     public function startTLS()
    354     {
    355         if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
    356             return false;
    357         }
    358 
    359         //Allow the best TLS version(s) we can
    360         $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
    361 
    362         //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
    363         //so add them back in manually if we can
    364         if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
    365             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
    366             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
    367         }
    368 
    369         // Begin encrypted connection
    370         set_error_handler(array($this, 'errorHandler'));
    371         $crypto_ok = stream_socket_enable_crypto(
    372             $this->smtp_conn,
    373             true,
    374             $crypto_method
    375         );
    376         restore_error_handler();
    377         return $crypto_ok;
    378     }
    379 
    380     /**
    381      * Perform SMTP authentication.
    382      * Must be run after hello().
    383      * @see hello()
    384      * @param string $username The user name
    385      * @param string $password The password
    386      * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5)
    387      * @param string $realm The auth realm for NTLM
    388      * @param string $workstation The auth workstation for NTLM
    389      * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
    390      * @return bool True if successfully authenticated.* @access public
    391      */
    392     public function authenticate(
    393         $username,
    394         $password,
    395         $authtype = null,
    396         $realm = '',
    397         $workstation = '',
    398         $OAuth = null
    399     ) {
    400         if (!$this->server_caps) {
    401             $this->setError('Authentication is not allowed before HELO/EHLO');
    402             return false;
    403         }
    404 
    405         if (array_key_exists('EHLO', $this->server_caps)) {
    406             // SMTP extensions are available; try to find a proper authentication method
    407             if (!array_key_exists('AUTH', $this->server_caps)) {
    408                 $this->setError('Authentication is not allowed at this stage');
    409                 // 'at this stage' means that auth may be allowed after the stage changes
    410                 // e.g. after STARTTLS
    411                 return false;
    412             }
    413 
    414             self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
    415             self::edebug(
    416                 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
    417                 self::DEBUG_LOWLEVEL
    418             );
    419 
    420             if (empty($authtype)) {
    421                 foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) {
    422                     if (in_array($method, $this->server_caps['AUTH'])) {
    423                         $authtype = $method;
    424                         break;
    425                     }
    426                 }
    427                 if (empty($authtype)) {
    428                     $this->setError('No supported authentication methods found');
    429                     return false;
    430                 }
    431                 self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
    432             }
    433 
    434             if (!in_array($authtype, $this->server_caps['AUTH'])) {
    435                 $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
    436                 return false;
    437             }
    438         } elseif (empty($authtype)) {
    439             $authtype = 'LOGIN';
    440         }
    441         switch ($authtype) {
    442             case 'PLAIN':
    443                 // Start authentication
    444                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
    445                     return false;
    446                 }
    447                 // Send encoded username and password
    448                 if (!$this->sendCommand(
    449                     'User & Password',
    450                     base64_encode("\0" . $username . "\0" . $password),
    451                     235
    452                 )
    453                 ) {
    454                     return false;
    455                 }
    456                 break;
    457             case 'LOGIN':
    458                 // Start authentication
    459                 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
    460                     return false;
    461                 }
    462                 if (!$this->sendCommand("Username", base64_encode($username), 334)) {
    463                     return false;
    464                 }
    465                 if (!$this->sendCommand("Password", base64_encode($password), 235)) {
    466                     return false;
    467                 }
    468                 break;
    469             case 'CRAM-MD5':
    470                 // Start authentication
    471                 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
    472                     return false;
    473                 }
    474                 // Get the challenge
    475                 $challenge = base64_decode(substr($this->last_reply, 4));
    476 
    477                 // Build the response
    478                 $response = $username . ' ' . $this->hmac($challenge, $password);
    479 
    480                 // send encoded credentials
    481                 return $this->sendCommand('Username', base64_encode($response), 235);
    482             default:
    483                 $this->setError("Authentication method \"$authtype\" is not supported");
    484                 return false;
    485         }
    486         return true;
    487     }
    488 
    489     /**
    490      * Calculate an MD5 HMAC hash.
    491      * Works like hash_hmac('md5', $data, $key)
    492      * in case that function is not available
    493      * @param string $data The data to hash
    494      * @param string $key The key to hash with
    495      * @access protected
    496      * @return string
    497      */
    498     protected function hmac($data, $key)
    499     {
    500         if (function_exists('hash_hmac')) {
    501             return hash_hmac('md5', $data, $key);
    502         }
    503 
    504         // The following borrowed from
    505         // http://php.net/manual/en/function.mhash.php#27225
    506 
    507         // RFC 2104 HMAC implementation for php.
    508         // Creates an md5 HMAC.
    509         // Eliminates the need to install mhash to compute a HMAC
    510         // by Lance Rushing
    511 
    512         $bytelen = 64; // byte length for md5
    513         if (strlen($key) > $bytelen) {
    514             $key = pack('H*', md5($key));
    515         }
    516         $key = str_pad($key, $bytelen, chr(0x00));
    517         $ipad = str_pad('', $bytelen, chr(0x36));
    518         $opad = str_pad('', $bytelen, chr(0x5c));
    519         $k_ipad = $key ^ $ipad;
    520         $k_opad = $key ^ $opad;
    521 
    522         return md5($k_opad . pack('H*', md5($k_ipad . $data)));
    523     }
    524 
    525     /**
    526      * Check connection state.
    527      * @access public
    528      * @return boolean True if connected.
    529      */
    530     public function connected()
    531     {
    532         if (is_resource($this->smtp_conn)) {
    533             $sock_status = stream_get_meta_data($this->smtp_conn);
    534             if ($sock_status['eof']) {
    535                 // The socket is valid but we are not connected
    536                 $this->edebug(
    537                     'SMTP NOTICE: EOF caught while checking if connected',
    538                     self::DEBUG_CLIENT
    539                 );
    540                 $this->close();
    541                 return false;
    542             }
    543             return true; // everything looks good
    544         }
    545         return false;
    546     }
    547 
    548     /**
    549      * Close the socket and clean up the state of the class.
    550      * Don't use this function without first trying to use QUIT.
    551      * @see quit()
    552      * @access public
    553      * @return void
    554      */
    555     public function close()
    556     {
    557         $this->setError('');
    558         $this->server_caps = null;
    559         $this->helo_rply = null;
    560         if (is_resource($this->smtp_conn)) {
    561             // close the connection and cleanup
    562             fclose($this->smtp_conn);
    563             $this->smtp_conn = null; //Makes for cleaner serialization
    564             $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
    565         }
    566     }
    567 
    568     /**
    569      * Send an SMTP DATA command.
    570      * Issues a data command and sends the msg_data to the server,
    571      * finializing the mail transaction. $msg_data is the message
    572      * that is to be send with the headers. Each header needs to be
    573      * on a single line followed by a <CRLF> with the message headers
    574      * and the message body being separated by and additional <CRLF>.
    575      * Implements rfc 821: DATA <CRLF>
    576      * @param string $msg_data Message data to send
    577      * @access public
    578      * @return boolean
    579      */
    580     public function data($msg_data)
    581     {
    582         //This will use the standard timelimit
    583         if (!$this->sendCommand('DATA', 'DATA', 354)) {
    584             return false;
    585         }
    586 
    587         /* The server is ready to accept data!
    588          * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
    589          * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
    590          * smaller lines to fit within the limit.
    591          * We will also look for lines that start with a '.' and prepend an additional '.'.
    592          * NOTE: this does not count towards line-length limit.
    593          */
    594 
    595         // Normalize line breaks before exploding
    596         $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
    597 
    598         /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
    599          * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
    600          * process all lines before a blank line as headers.
    601          */
    602 
    603         $field = substr($lines[0], 0, strpos($lines[0], ':'));
    604         $in_headers = false;
    605         if (!empty($field) && strpos($field, ' ') === false) {
    606             $in_headers = true;
    607         }
    608 
    609         foreach ($lines as $line) {
    610             $lines_out = array();
    611             if ($in_headers and $line == '') {
    612                 $in_headers = false;
    613             }
    614             //Break this line up into several smaller lines if it's too long
    615             //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
    616             while (isset($line[self::MAX_LINE_LENGTH])) {
    617                 //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
    618                 //so as to avoid breaking in the middle of a word
    619                 $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
    620                 //Deliberately matches both false and 0
    621                 if (!$pos) {
    622                     //No nice break found, add a hard break
    623                     $pos = self::MAX_LINE_LENGTH - 1;
    624                     $lines_out[] = substr($line, 0, $pos);
    625                     $line = substr($line, $pos);
    626                 } else {
    627                     //Break at the found point
    628                     $lines_out[] = substr($line, 0, $pos);
    629                     //Move along by the amount we dealt with
    630                     $line = substr($line, $pos + 1);
    631                 }
    632                 //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
    633                 if ($in_headers) {
    634                     $line = "\t" . $line;
    635                 }
    636             }
    637             $lines_out[] = $line;
    638 
    639             //Send the lines to the server
    640             foreach ($lines_out as $line_out) {
    641                 //RFC2821 section 4.5.2
    642                 if (!empty($line_out) and $line_out[0] == '.') {
    643                     $line_out = '.' . $line_out;
    644                 }
    645                 $this->client_send($line_out . self::CRLF);
    646             }
    647         }
    648 
    649         //Message data has been sent, complete the command
    650         //Increase timelimit for end of DATA command
    651         $savetimelimit = $this->Timelimit;
    652         $this->Timelimit = $this->Timelimit * 2;
    653         $result = $this->sendCommand('DATA END', '.', 250);
    654         $this->recordLastTransactionID();
    655         //Restore timelimit
    656         $this->Timelimit = $savetimelimit;
    657         return $result;
    658     }
    659 
    660     /**
    661      * Send an SMTP HELO or EHLO command.
    662      * Used to identify the sending server to the receiving server.
    663      * This makes sure that client and server are in a known state.
    664      * Implements RFC 821: HELO <SP> <domain> <CRLF>
    665      * and RFC 2821 EHLO.
    666      * @param string $host The host name or IP to connect to
    667      * @access public
    668      * @return boolean
    669      */
    670     public function hello($host = '')
    671     {
    672         //Try extended hello first (RFC 2821)
    673         return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
    674     }
    675 
    676     /**
    677      * Send an SMTP HELO or EHLO command.
    678      * Low-level implementation used by hello()
    679      * @see hello()
    680      * @param string $hello The HELO string
    681      * @param string $host The hostname to say we are
    682      * @access protected
    683      * @return boolean
    684      */
    685     protected function sendHello($hello, $host)
    686     {
    687         $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
    688         $this->helo_rply = $this->last_reply;
    689         if ($noerror) {
    690             $this->parseHelloFields($hello);
    691         } else {
    692             $this->server_caps = null;
    693         }
    694         return $noerror;
    695     }
    696 
    697     /**
    698      * Parse a reply to HELO/EHLO command to discover server extensions.
    699      * In case of HELO, the only parameter that can be discovered is a server name.
    700      * @access protected
    701      * @param string $type - 'HELO' or 'EHLO'
    702      */
    703     protected function parseHelloFields($type)
    704     {
    705         $this->server_caps = array();
    706         $lines = explode("\n", $this->helo_rply);
    707 
    708         foreach ($lines as $n => $s) {
    709             //First 4 chars contain response code followed by - or space
    710             $s = trim(substr($s, 4));
    711             if (empty($s)) {
    712                 continue;
    713             }
    714             $fields = explode(' ', $s);
    715             if (!empty($fields)) {
    716                 if (!$n) {
    717                     $name = $type;
    718                     $fields = $fields[0];
    719                 } else {
    720                     $name = array_shift($fields);
    721                     switch ($name) {
    722                         case 'SIZE':
    723                             $fields = ($fields ? $fields[0] : 0);
    724                             break;
    725                         case 'AUTH':
    726                             if (!is_array($fields)) {
    727                                 $fields = array();
    728                             }
    729                             break;
    730                         default:
    731                             $fields = true;
    732                     }
    733                 }
    734                 $this->server_caps[$name] = $fields;
    735             }
    736         }
    737     }
    738 
    739     /**
    740      * Send an SMTP MAIL command.
    741      * Starts a mail transaction from the email address specified in
    742      * $from. Returns true if successful or false otherwise. If True
    743      * the mail transaction is started and then one or more recipient
    744      * commands may be called followed by a data command.
    745      * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
    746      * @param string $from Source address of this message
    747      * @access public
    748      * @return boolean
    749      */
    750     public function mail($from)
    751     {
    752         $useVerp = ($this->do_verp ? ' XVERP' : '');
    753         return $this->sendCommand(
    754             'MAIL FROM',
    755             'MAIL FROM:<' . $from . '>' . $useVerp,
    756             250
    757         );
    758     }
    759 
    760     /**
    761      * Send an SMTP QUIT command.
    762      * Closes the socket if there is no error or the $close_on_error argument is true.
    763      * Implements from rfc 821: QUIT <CRLF>
    764      * @param boolean $close_on_error Should the connection close if an error occurs?
    765      * @access public
    766      * @return boolean
    767      */
    768     public function quit($close_on_error = true)
    769     {
    770         $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
    771         $err = $this->error; //Save any error
    772         if ($noerror or $close_on_error) {
    773             $this->close();
    774             $this->error = $err; //Restore any error from the quit command
    775         }
    776         return $noerror;
    777     }
    778 
    779     /**
    780      * Send an SMTP RCPT command.
    781      * Sets the TO argument to $toaddr.
    782      * Returns true if the recipient was accepted false if it was rejected.
    783      * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
    784      * @param string $address The address the message is being sent to
    785      * @access public
    786      * @return boolean
    787      */
    788     public function recipient($address)
    789     {
    790         return $this->sendCommand(
    791             'RCPT TO',
    792             'RCPT TO:<' . $address . '>',
    793             array(250, 251)
    794         );
    795     }
    796 
    797     /**
    798      * Send an SMTP RSET command.
    799      * Abort any transaction that is currently in progress.
    800      * Implements rfc 821: RSET <CRLF>
    801      * @access public
    802      * @return boolean True on success.
    803      */
    804     public function reset()
    805     {
    806         return $this->sendCommand('RSET', 'RSET', 250);
    807     }
    808 
    809     /**
    810      * Send a command to an SMTP server and check its return code.
    811      * @param string $command The command name - not sent to the server
    812      * @param string $commandstring The actual command to send
    813      * @param integer|array $expect One or more expected integer success codes
    814      * @access protected
    815      * @return boolean True on success.
    816      */
    817     protected function sendCommand($command, $commandstring, $expect)
    818     {
    819         if (!$this->connected()) {
    820             $this->setError("Called $command without being connected");
    821             return false;
    822         }
    823         //Reject line breaks in all commands
    824         if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
    825             $this->setError("Command '$command' contained line breaks");
    826             return false;
    827         }
    828         $this->client_send($commandstring . self::CRLF);
    829 
    830         $this->last_reply = $this->get_lines();
    831         // Fetch SMTP code and possible error code explanation
    832         $matches = array();
    833         if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
    834             $code = $matches[1];
    835             $code_ex = (count($matches) > 2 ? $matches[2] : null);
    836             // Cut off error code from each response line
    837             $detail = preg_replace(
    838                 "/{$code}[ -]" .
    839                 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m",
    840                 '',
    841                 $this->last_reply
    842             );
    843         } else {
    844             // Fall back to simple parsing if regex fails
    845             $code = substr($this->last_reply, 0, 3);
    846             $code_ex = null;
    847             $detail = substr($this->last_reply, 4);
    848         }
    849 
    850         $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
    851 
    852         if (!in_array($code, (array)$expect)) {
    853             $this->setError(
    854                 "$command command failed",
    855                 $detail,
    856                 $code,
    857                 $code_ex
    858             );
    859             $this->edebug(
    860                 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
    861                 self::DEBUG_CLIENT
    862             );
    863             return false;
    864         }
    865 
    866         $this->setError('');
    867         return true;
    868     }
    869 
    870     /**
    871      * Send an SMTP SAML command.
    872      * Starts a mail transaction from the email address specified in $from.
    873      * Returns true if successful or false otherwise. If True
    874      * the mail transaction is started and then one or more recipient
    875      * commands may be called followed by a data command. This command
    876      * will send the message to the users terminal if they are logged
    877      * in and send them an email.
    878      * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
    879      * @param string $from The address the message is from
    880      * @access public
    881      * @return boolean
    882      */
    883     public function sendAndMail($from)
    884     {
    885         return $this->sendCommand('SAML', "SAML FROM:$from", 250);
    886     }
    887 
    888     /**
    889      * Send an SMTP VRFY command.
    890      * @param string $name The name to verify
    891      * @access public
    892      * @return boolean
    893      */
    894     public function verify($name)
    895     {
    896         return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
    897     }
    898 
    899     /**
    900      * Send an SMTP NOOP command.
    901      * Used to keep keep-alives alive, doesn't actually do anything
    902      * @access public
    903      * @return boolean
    904      */
    905     public function noop()
    906     {
    907         return $this->sendCommand('NOOP', 'NOOP', 250);
    908     }
    909 
    910     /**
    911      * Send an SMTP TURN command.
    912      * This is an optional command for SMTP that this class does not support.
    913      * This method is here to make the RFC821 Definition complete for this class
    914      * and _may_ be implemented in future
    915      * Implements from rfc 821: TURN <CRLF>
    916      * @access public
    917      * @return boolean
    918      */
    919     public function turn()
    920     {
    921         $this->setError('The SMTP TURN command is not implemented');
    922         $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
    923         return false;
    924     }
    925 
    926     /**
    927      * Send raw data to the server.
    928      * @param string $data The data to send
    929      * @access public
    930      * @return integer|boolean The number of bytes sent to the server or false on error
    931      */
    932     public function client_send($data)
    933     {
    934         $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
    935         set_error_handler(array($this, 'errorHandler'));
    936         $result = fwrite($this->smtp_conn, $data);
    937         restore_error_handler();
    938         return $result;
    939     }
    940 
    941     /**
    942      * Get the latest error.
    943      * @access public
    944      * @return array
    945      */
    946     public function getError()
    947     {
    948         return $this->error;
    949     }
    950 
    951     /**
    952      * Get SMTP extensions available on the server
    953      * @access public
    954      * @return array|null
    955      */
    956     public function getServerExtList()
    957     {
    958         return $this->server_caps;
    959     }
    960 
    961     /**
    962      * A multipurpose method
    963      * The method works in three ways, dependent on argument value and current state
    964      *   1. HELO/EHLO was not sent - returns null and set up $this->error
    965      *   2. HELO was sent
    966      *     $name = 'HELO': returns server name
    967      *     $name = 'EHLO': returns boolean false
    968      *     $name = any string: returns null and set up $this->error
    969      *   3. EHLO was sent
    970      *     $name = 'HELO'|'EHLO': returns server name
    971      *     $name = any string: if extension $name exists, returns boolean True
    972      *       or its options. Otherwise returns boolean False
    973      * In other words, one can use this method to detect 3 conditions:
    974      *  - null returned: handshake was not or we don't know about ext (refer to $this->error)
    975      *  - false returned: the requested feature exactly not exists
    976      *  - positive value returned: the requested feature exists
    977      * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
    978      * @return mixed
    979      */
    980     public function getServerExt($name)
    981     {
    982         if (!$this->server_caps) {
    983             $this->setError('No HELO/EHLO was sent');
    984             return null;
    985         }
    986 
    987         // the tight logic knot ;)
    988         if (!array_key_exists($name, $this->server_caps)) {
    989             if ($name == 'HELO') {
    990                 return $this->server_caps['EHLO'];
    991             }
    992             if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
    993                 return false;
    994             }
    995             $this->setError('HELO handshake was used. Client knows nothing about server extensions');
    996             return null;
    997         }
    998 
    999         return $this->server_caps[$name];
    1000     }
    1001 
    1002     /**
    1003      * Get the last reply from the server.
    1004      * @access public
    1005      * @return string
    1006      */
    1007     public function getLastReply()
    1008     {
    1009         return $this->last_reply;
    1010     }
    1011 
    1012     /**
    1013      * Read the SMTP server's response.
    1014      * Either before eof or socket timeout occurs on the operation.
    1015      * With SMTP we can tell if we have more lines to read if the
    1016      * 4th character is '-' symbol. If it is a space then we don't
    1017      * need to read anything else.
    1018      * @access protected
    1019      * @return string
    1020      */
    1021     protected function get_lines()
    1022     {
    1023         // If the connection is bad, give up straight away
    1024         if (!is_resource($this->smtp_conn)) {
    1025             return '';
    1026         }
    1027         $data = '';
    1028         $endtime = 0;
    1029         stream_set_timeout($this->smtp_conn, $this->Timeout);
    1030         if ($this->Timelimit > 0) {
    1031             $endtime = time() + $this->Timelimit;
    1032         }
    1033         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
    1034             $str = @fgets($this->smtp_conn, 515);
    1035             $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
    1036             $this->edebug("SMTP -> get_lines(): \$str is  \"$str\"", self::DEBUG_LOWLEVEL);
    1037             $data .= $str;
    1038             // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
    1039             // or 4th character is a space, we are done reading, break the loop,
    1040             // string array access is a micro-optimisation over strlen
    1041             if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) {
    1042                 break;
    1043             }
    1044             // Timed-out? Log and break
    1045             $info = stream_get_meta_data($this->smtp_conn);
    1046             if ($info['timed_out']) {
    1047                 $this->edebug(
    1048                     'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
    1049                     self::DEBUG_LOWLEVEL
    1050                 );
    1051                 break;
    1052             }
    1053             // Now check if reads took too long
    1054             if ($endtime and time() > $endtime) {
    1055                 $this->edebug(
    1056                     'SMTP -> get_lines(): timelimit reached (' .
    1057                     $this->Timelimit . ' sec)',
    1058                     self::DEBUG_LOWLEVEL
    1059                 );
    1060                 break;
    1061             }
    1062         }
    1063         return $data;
    1064     }
    1065 
    1066     /**
    1067      * Enable or disable VERP address generation.
    1068      * @param boolean $enabled
    1069      */
    1070     public function setVerp($enabled = false)
    1071     {
    1072         $this->do_verp = $enabled;
    1073     }
    1074 
    1075     /**
    1076      * Get VERP address generation mode.
    1077      * @return boolean
    1078      */
    1079     public function getVerp()
    1080     {
    1081         return $this->do_verp;
    1082     }
    1083 
    1084     /**
    1085      * Set error messages and codes.
    1086      * @param string $message The error message
    1087      * @param string $detail Further detail on the error
    1088      * @param string $smtp_code An associated SMTP error code
    1089      * @param string $smtp_code_ex Extended SMTP code
    1090      */
    1091     protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
    1092     {
    1093         $this->error = array(
    1094             'error' => $message,
    1095             'detail' => $detail,
    1096             'smtp_code' => $smtp_code,
    1097             'smtp_code_ex' => $smtp_code_ex
    1098         );
    1099     }
    1100 
    1101     /**
    1102      * Set debug output method.
    1103      * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
    1104      */
    1105     public function setDebugOutput($method = 'echo')
    1106     {
    1107         $this->Debugoutput = $method;
    1108     }
    1109 
    1110     /**
    1111      * Get debug output method.
    1112      * @return string
    1113      */
    1114     public function getDebugOutput()
    1115     {
    1116         return $this->Debugoutput;
    1117     }
    1118 
    1119     /**
    1120      * Set debug output level.
    1121      * @param integer $level
    1122      */
    1123     public function setDebugLevel($level = 0)
    1124     {
    1125         $this->do_debug = $level;
    1126     }
    1127 
    1128     /**
    1129      * Get debug output level.
    1130      * @return integer
    1131      */
    1132     public function getDebugLevel()
    1133     {
    1134         return $this->do_debug;
    1135     }
    1136 
    1137     /**
    1138      * Set SMTP timeout.
    1139      * @param integer $timeout
    1140      */
    1141     public function setTimeout($timeout = 0)
    1142     {
    1143         $this->Timeout = $timeout;
    1144     }
    1145 
    1146     /**
    1147      * Get SMTP timeout.
    1148      * @return integer
    1149      */
    1150     public function getTimeout()
    1151     {
    1152         return $this->Timeout;
    1153     }
    1154 
    1155     /**
    1156      * Reports an error number and string.
    1157      * @param integer $errno The error number returned by PHP.
    1158      * @param string $errmsg The error message returned by PHP.
    1159      * @param string $errfile The file the error occurred in
    1160      * @param integer $errline The line number the error occurred on
    1161      */
    1162     protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
    1163     {
    1164         $notice = 'Connection failed.';
    1165         $this->setError(
    1166             $notice,
    1167             $errno,
    1168             $errmsg
    1169         );
    1170         $this->edebug(
    1171             $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]",
    1172             self::DEBUG_CONNECTION
    1173         );
    1174     }
    1175 
    1176     /**
    1177      * Extract and return the ID of the last SMTP transaction based on
    1178      * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
    1179      * Relies on the host providing the ID in response to a DATA command.
    1180      * If no reply has been received yet, it will return null.
    1181      * If no pattern was matched, it will return false.
    1182      * @return bool|null|string
    1183      */
    1184     protected function recordLastTransactionID()
    1185     {
    1186         $reply = $this->getLastReply();
    1187 
    1188         if (empty($reply)) {
    1189             $this->last_smtp_transaction_id = null;
    1190         } else {
    1191             $this->last_smtp_transaction_id = false;
    1192             foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
    1193                 if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
    1194                     $this->last_smtp_transaction_id = $matches[1];
    1195                 }
    1196             }
    1197         }
    1198 
    1199         return $this->last_smtp_transaction_id;
    1200     }
    1201 
    1202     /**
    1203      * Get the queue/transaction ID of the last SMTP transaction
    1204      * If no reply has been received yet, it will return null.
    1205      * If no pattern was matched, it will return false.
    1206      * @return bool|null|string
    1207      * @see recordLastTransactionID()
    1208      */
    1209     public function getLastTransactionID()
    1210     {
    1211         return $this->last_smtp_transaction_id;
    1212     }
    1213 }
     6_deprecated_file( basename( __FILE__ ), '5.5.0', WPINC . '/PHPMailer/SMTP.php', __( 'The SMTP class has been moved to the wp-includes/PHPMailer subdirectory and now uses the PHPMailer\PHPMailer namespace.' ) );
     7require __DIR__ . '/PHPMailer/SMTP.php';
Note: See TracChangeset for help on using the changeset viewer.