Commit 81a6e450 by ProThoughts

Updated to v5.2.23

parent 438e4513
...@@ -30,7 +30,7 @@ class SMTP ...@@ -30,7 +30,7 @@ class SMTP
* The PHPMailer SMTP version number. * The PHPMailer SMTP version number.
* @var string * @var string
*/ */
const VERSION = '5.2.14'; const VERSION = '5.2.23';
/** /**
* SMTP line break constant. * SMTP line break constant.
...@@ -81,7 +81,7 @@ class SMTP ...@@ -81,7 +81,7 @@ class SMTP
* @deprecated Use the `VERSION` constant instead * @deprecated Use the `VERSION` constant instead
* @see SMTP::VERSION * @see SMTP::VERSION
*/ */
public $Version = '5.2.14'; public $Version = '5.2.23';
/** /**
* SMTP server port number. * SMTP server port number.
...@@ -151,6 +151,17 @@ class SMTP ...@@ -151,6 +151,17 @@ class SMTP
public $Timelimit = 300; 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. * The socket for the server connection.
* @var resource * @var resource
*/ */
...@@ -206,7 +217,7 @@ class SMTP ...@@ -206,7 +217,7 @@ class SMTP
} }
//Avoid clash with built-in function names //Avoid clash with built-in function names
if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 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; return;
} }
switch ($this->Debugoutput) { switch ($this->Debugoutput) {
...@@ -220,8 +231,7 @@ class SMTP ...@@ -220,8 +231,7 @@ class SMTP
preg_replace('/[\r\n]+/', '', $str), preg_replace('/[\r\n]+/', '', $str),
ENT_QUOTES, ENT_QUOTES,
'UTF-8' 'UTF-8'
) ) . "<br>\n";
. "<br>\n";
break; break;
case 'echo': case 'echo':
default: default:
...@@ -231,7 +241,7 @@ class SMTP ...@@ -231,7 +241,7 @@ class SMTP
"\n", "\n",
"\n \t ", "\n \t ",
trim($str) trim($str)
)."\n"; ) . "\n";
} }
} }
...@@ -265,15 +275,16 @@ class SMTP ...@@ -265,15 +275,16 @@ class SMTP
} }
// Connect to the SMTP server // Connect to the SMTP server
$this->edebug( $this->edebug(
"Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), "Connection: opening to $host:$port, timeout=$timeout, options=" .
var_export($options, true),
self::DEBUG_CONNECTION self::DEBUG_CONNECTION
); );
$errno = 0; $errno = 0;
$errstr = ''; $errstr = '';
if ($streamok) { if ($streamok) {
$socket_context = stream_context_create($options); $socket_context = stream_context_create($options);
//Suppress errors; connection failures are handled at a higher level set_error_handler(array($this, 'errorHandler'));
$this->smtp_conn = @stream_socket_client( $this->smtp_conn = stream_socket_client(
$host . ":" . $port, $host . ":" . $port,
$errno, $errno,
$errstr, $errstr,
...@@ -281,12 +292,14 @@ class SMTP ...@@ -281,12 +292,14 @@ class SMTP
STREAM_CLIENT_CONNECT, STREAM_CLIENT_CONNECT,
$socket_context $socket_context
); );
restore_error_handler();
} else { } else {
//Fall back to fsockopen which should work in more places, but is missing some features //Fall back to fsockopen which should work in more places, but is missing some features
$this->edebug( $this->edebug(
"Connection: stream_socket_client not available, falling back to fsockopen", "Connection: stream_socket_client not available, falling back to fsockopen",
self::DEBUG_CONNECTION self::DEBUG_CONNECTION
); );
set_error_handler(array($this, 'errorHandler'));
$this->smtp_conn = fsockopen( $this->smtp_conn = fsockopen(
$host, $host,
$port, $port,
...@@ -294,6 +307,7 @@ class SMTP ...@@ -294,6 +307,7 @@ class SMTP
$errstr, $errstr,
$timeout $timeout
); );
restore_error_handler();
} }
// Verify we connected properly // Verify we connected properly
if (!is_resource($this->smtp_conn)) { if (!is_resource($this->smtp_conn)) {
...@@ -336,15 +350,26 @@ class SMTP ...@@ -336,15 +350,26 @@ class SMTP
if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
return false; 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 // Begin encrypted connection
if (!stream_socket_enable_crypto( set_error_handler(array($this, 'errorHandler'));
$crypto_ok = stream_socket_enable_crypto(
$this->smtp_conn, $this->smtp_conn,
true, true,
STREAM_CRYPTO_METHOD_TLS_CLIENT $crypto_method
)) { );
return false; restore_error_handler();
} return $crypto_ok;
return true;
} }
/** /**
...@@ -373,8 +398,7 @@ class SMTP ...@@ -373,8 +398,7 @@ class SMTP
} }
if (array_key_exists('EHLO', $this->server_caps)) { if (array_key_exists('EHLO', $this->server_caps)) {
// SMTP extensions are available. Let's try to find a proper authentication method // SMTP extensions are available; try to find a proper authentication method
if (!array_key_exists('AUTH', $this->server_caps)) { if (!array_key_exists('AUTH', $this->server_caps)) {
$this->setError('Authentication is not allowed at this stage'); $this->setError('Authentication is not allowed at this stage');
// 'at this stage' means that auth may be allowed after the stage changes // 'at this stage' means that auth may be allowed after the stage changes
...@@ -389,7 +413,7 @@ class SMTP ...@@ -389,7 +413,7 @@ class SMTP
); );
if (empty($authtype)) { if (empty($authtype)) {
foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2') as $method) { foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) {
if (in_array($method, $this->server_caps['AUTH'])) { if (in_array($method, $this->server_caps['AUTH'])) {
$authtype = $method; $authtype = $method;
break; break;
...@@ -399,7 +423,7 @@ class SMTP ...@@ -399,7 +423,7 @@ class SMTP
$this->setError('No supported authentication methods found'); $this->setError('No supported authentication methods found');
return false; return false;
} }
self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
} }
if (!in_array($authtype, $this->server_caps['AUTH'])) { if (!in_array($authtype, $this->server_caps['AUTH'])) {
...@@ -463,7 +487,7 @@ class SMTP ...@@ -463,7 +487,7 @@ class SMTP
$temp = new stdClass; $temp = new stdClass;
$ntlm_client = new ntlm_sasl_client_class; $ntlm_client = new ntlm_sasl_client_class;
//Check that functions are available //Check that functions are available
if (!$ntlm_client->Initialize($temp)) { if (!$ntlm_client->initialize($temp)) {
$this->setError($temp->error); $this->setError($temp->error);
$this->edebug( $this->edebug(
'You need to enable some modules in your php.ini file: ' 'You need to enable some modules in your php.ini file: '
...@@ -473,7 +497,7 @@ class SMTP ...@@ -473,7 +497,7 @@ class SMTP
return false; return false;
} }
//msg1 //msg1
$msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1
if (!$this->sendCommand( if (!$this->sendCommand(
'AUTH NTLM', 'AUTH NTLM',
...@@ -492,7 +516,7 @@ class SMTP ...@@ -492,7 +516,7 @@ class SMTP
$password $password
); );
//msg3 //msg3
$msg3 = $ntlm_client->TypeMsg3( $msg3 = $ntlm_client->typeMsg3(
$ntlm_res, $ntlm_res,
$username, $username,
$realm, $realm,
...@@ -736,7 +760,7 @@ class SMTP ...@@ -736,7 +760,7 @@ class SMTP
protected function parseHelloFields($type) protected function parseHelloFields($type)
{ {
$this->server_caps = array(); $this->server_caps = array();
$lines = explode("\n", $this->last_reply); $lines = explode("\n", $this->helo_rply);
foreach ($lines as $n => $s) { foreach ($lines as $n => $s) {
//First 4 chars contain response code followed by - or space //First 4 chars contain response code followed by - or space
...@@ -868,7 +892,8 @@ class SMTP ...@@ -868,7 +892,8 @@ class SMTP
$code_ex = (count($matches) > 2 ? $matches[2] : null); $code_ex = (count($matches) > 2 ? $matches[2] : null);
// Cut off error code from each response line // Cut off error code from each response line
$detail = preg_replace( $detail = preg_replace(
"/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", "/{$code}[ -]" .
($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m",
'', '',
$this->last_reply $this->last_reply
); );
...@@ -1080,7 +1105,7 @@ class SMTP ...@@ -1080,7 +1105,7 @@ class SMTP
// Now check if reads took too long // Now check if reads took too long
if ($endtime and time() > $endtime) { if ($endtime and time() > $endtime) {
$this->edebug( $this->edebug(
'SMTP -> get_lines(): timelimit reached ('. 'SMTP -> get_lines(): timelimit reached (' .
$this->Timelimit . ' sec)', $this->Timelimit . ' sec)',
self::DEBUG_LOWLEVEL self::DEBUG_LOWLEVEL
); );
...@@ -1178,4 +1203,49 @@ class SMTP ...@@ -1178,4 +1203,49 @@ class SMTP
{ {
return $this->Timeout; 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.
* @param string $errfile The file the error occurred in
* @param integer $errline The line number the error occurred on
*/
protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
{
$notice = 'Connection failed.';
$this->setError(
$notice,
$errno,
$errmsg
);
$this->edebug(
$notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]",
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;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment