X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/53f4633144ed68c8b8fb5861f992b5489894a940..refs/tags/wordpress-4.7.1:/wp-includes/class-smtp.php diff --git a/wp-includes/class-smtp.php b/wp-includes/class-smtp.php index 1eae77a1..3ad08192 100644 --- a/wp-includes/class-smtp.php +++ b/wp-includes/class-smtp.php @@ -28,25 +28,25 @@ class SMTP { /** * The PHPMailer SMTP version number. - * @type string + * @var string */ - const VERSION = '5.2.10'; + const VERSION = '5.2.22'; /** * SMTP line break constant. - * @type string + * @var string */ const CRLF = "\r\n"; /** * The SMTP port to use if one is not specified. - * @type integer + * @var integer */ const DEFAULT_SMTP_PORT = 25; /** * The maximum line length allowed by RFC 2822 section 2.1.1 - * @type integer + * @var integer */ const MAX_LINE_LENGTH = 998; @@ -77,15 +77,15 @@ class SMTP /** * The PHPMailer SMTP Version number. - * @type string + * @var string * @deprecated Use the `VERSION` constant instead * @see SMTP::VERSION */ - public $Version = '5.2.10'; + public $Version = '5.2.22'; /** * SMTP server port number. - * @type integer + * @var integer * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead * @see SMTP::DEFAULT_SMTP_PORT */ @@ -93,7 +93,7 @@ class SMTP /** * SMTP reply line ending. - * @type string + * @var string * @deprecated Use the `CRLF` constant instead * @see SMTP::CRLF */ @@ -107,7 +107,7 @@ class SMTP * * self::DEBUG_SERVER (`2`) Client commands and server responses * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages - * @type integer + * @var integer */ public $do_debug = self::DEBUG_OFF; @@ -122,7 +122,7 @@ class SMTP * * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; * - * @type string|callable + * @var string|callable */ public $Debugoutput = 'echo'; @@ -130,7 +130,7 @@ class SMTP * Whether to use VERP. * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path * @link http://www.postfix.org/VERP_README.html Info on VERP - * @type boolean + * @var boolean */ public $do_verp = false; @@ -139,26 +139,37 @@ class SMTP * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 - * @type integer + * @var integer */ public $Timeout = 300; /** * How long to wait for commands to complete, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * @type integer + * @var integer */ public $Timelimit = 300; + /** + * @var array patterns to extract smtp transaction id from smtp reply + * Only first capture group will be use, use non-capturing group to deal with it + * Extend this class to override this property to fulfil your needs. + */ + protected $smtp_transaction_id_patterns = array( + 'exim' => '/[0-9]{3} OK id=(.*)/', + 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', + 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' + ); + /** * The socket for the server connection. - * @type resource + * @var resource */ protected $smtp_conn; /** * Error information, if any, for the last SMTP command. - * @type array + * @var array */ protected $error = array( 'error' => '', @@ -170,7 +181,7 @@ class SMTP /** * The reply the server sent to us for HELO. * If null, no HELO string has yet been received. - * @type string|null + * @var string|null */ protected $helo_rply = null; @@ -181,13 +192,13 @@ class SMTP * represents the server name. In case of HELO it is the only element of the array. * Other values can be boolean TRUE or an array containing extension options. * If null, no HELO/EHLO string has yet been received. - * @type array|null + * @var array|null */ protected $server_caps = null; /** * The most recent reply received from the server. - * @type string + * @var string */ protected $last_reply = ''; @@ -206,7 +217,7 @@ class SMTP } //Avoid clash with built-in function names if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { - call_user_func($this->Debugoutput, $str, $this->do_debug); + call_user_func($this->Debugoutput, $str, $level); return; } switch ($this->Debugoutput) { @@ -272,8 +283,8 @@ class SMTP $errstr = ''; if ($streamok) { $socket_context = stream_context_create($options); - //Suppress errors; connection failures are handled at a higher level - $this->smtp_conn = @stream_socket_client( + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = stream_socket_client( $host . ":" . $port, $errno, $errstr, @@ -281,12 +292,14 @@ class SMTP STREAM_CLIENT_CONNECT, $socket_context ); + restore_error_handler(); } else { //Fall back to fsockopen which should work in more places, but is missing some features $this->edebug( "Connection: stream_socket_client not available, falling back to fsockopen", self::DEBUG_CONNECTION ); + set_error_handler(array($this, 'errorHandler')); $this->smtp_conn = fsockopen( $host, $port, @@ -294,6 +307,7 @@ class SMTP $errstr, $timeout ); + restore_error_handler(); } // Verify we connected properly if (!is_resource($this->smtp_conn)) { @@ -336,11 +350,22 @@ class SMTP if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + // Begin encrypted connection if (!stream_socket_enable_crypto( $this->smtp_conn, true, - STREAM_CRYPTO_METHOD_TLS_CLIENT + $crypto_method )) { return false; } @@ -351,20 +376,21 @@ class SMTP * Perform SMTP authentication. * Must be run after hello(). * @see hello() - * @param string $username The user name - * @param string $password The password - * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) - * @param string $realm The auth realm for NTLM + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5) + * @param string $realm The auth realm for NTLM * @param string $workstation The auth workstation for NTLM - * @access public - * @return boolean True if successfully authenticated. + * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) + * @return bool True if successfully authenticated.* @access public */ public function authenticate( $username, $password, $authtype = null, $realm = '', - $workstation = '' + $workstation = '', + $OAuth = null ) { if (!$this->server_caps) { $this->setError('Authentication is not allowed before HELO/EHLO'); @@ -388,7 +414,7 @@ class SMTP ); if (empty($authtype)) { - foreach (array('LOGIN', 'CRAM-MD5', 'PLAIN') as $method) { + foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) { if (in_array($method, $this->server_caps['AUTH'])) { $authtype = $method; break; @@ -672,10 +698,12 @@ class SMTP protected function parseHelloFields($type) { $this->server_caps = array(); - $lines = explode("\n", $this->last_reply); + $lines = explode("\n", $this->helo_rply); + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space $s = trim(substr($s, 4)); - if (!$s) { + if (empty($s)) { continue; } $fields = explode(' ', $s); @@ -685,11 +713,20 @@ class SMTP $fields = $fields[0]; } else { $name = array_shift($fields); - if ($name == 'SIZE') { - $fields = ($fields) ? $fields[0] : 0; + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = array(); + } + break; + default: + $fields = true; } } - $this->server_caps[$name] = ($fields ? $fields : true); + $this->server_caps[$name] = $fields; } } } @@ -739,15 +776,15 @@ class SMTP * Sets the TO argument to $toaddr. * Returns true if the recipient was accepted false if it was rejected. * Implements from rfc 821: RCPT TO: - * @param string $toaddr The address the message is being sent to + * @param string $address The address the message is being sent to * @access public * @return boolean */ - public function recipient($toaddr) + public function recipient($address) { return $this->sendCommand( 'RCPT TO', - 'RCPT TO:<' . $toaddr . '>', + 'RCPT TO:<' . $address . '>', array(250, 251) ); } @@ -766,9 +803,9 @@ class SMTP /** * Send a command to an SMTP server and check its return code. - * @param string $command The command name - not sent to the server + * @param string $command The command name - not sent to the server * @param string $commandstring The actual command to send - * @param integer|array $expect One or more expected integer success codes + * @param integer|array $expect One or more expected integer success codes * @access protected * @return boolean True on success. */ @@ -778,6 +815,11 @@ class SMTP $this->setError("Called $command without being connected"); return false; } + //Reject line breaks in all commands + if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { + $this->setError("Command '$command' contained line breaks"); + return false; + } $this->client_send($commandstring . self::CRLF); $this->last_reply = $this->get_lines(); @@ -981,10 +1023,9 @@ class SMTP } while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { $str = @fgets($this->smtp_conn, 515); - $this->edebug("SMTP -> get_lines(): \$data was \"$data\"", self::DEBUG_LOWLEVEL); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); - $data .= $str; $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); + $data .= $str; // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen if ((isset($str[3]) and $str[3] == ' ')) { break; @@ -1099,4 +1140,47 @@ class SMTP { return $this->Timeout; } + + /** + * Reports an error number and string. + * @param integer $errno The error number returned by PHP. + * @param string $errmsg The error message returned by PHP. + */ + protected function errorHandler($errno, $errmsg) + { + $notice = 'Connection: Failed to connect to server.'; + $this->setError( + $notice, + $errno, + $errmsg + ); + $this->edebug( + $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, + self::DEBUG_CONNECTION + ); + } + + /** + * Will return the ID of the last smtp transaction based on a list of patterns provided + * in SMTP::$smtp_transaction_id_patterns. + * If no reply has been received yet, it will return null. + * If no pattern has been matched, it will return false. + * @return bool|null|string + */ + public function getLastTransactionID() + { + $reply = $this->getLastReply(); + + if (empty($reply)) { + return null; + } + + foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { + if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { + return $matches[1]; + } + } + + return false; + } }