* @author Brent R. Matzelle (original founder)
- * @copyright 2012 - 2019 Marcus Bointon
+ * @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
@@ -45,7 +46,7 @@ class POP3
*
* @var string
*/
- const VERSION = '6.1.4';
+ const VERSION = '6.5.0';
/**
* Default POP3 port number.
@@ -62,12 +63,16 @@ class POP3
const DEFAULT_TIMEOUT = 30;
/**
- * Debug display level.
- * Options: 0 = no, 1+ = yes.
+ * POP3 class debug output mode.
+ * Debug output level.
+ * Options:
+ * @see POP3::DEBUG_OFF: No output
+ * @see POP3::DEBUG_SERVER: Server messages, connection/server errors
+ * @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors
*
* @var int
*/
- public $do_debug = 0;
+ public $do_debug = self::DEBUG_OFF;
/**
* POP3 mail server hostname.
@@ -130,6 +135,28 @@ class POP3
*/
const LE = "\r\n";
+ /**
+ * Debug level for no output.
+ *
+ * @var int
+ */
+ const DEBUG_OFF = 0;
+
+ /**
+ * Debug level to show server -> client messages
+ * also shows clients connection errors or errors from server
+ *
+ * @var int
+ */
+ const DEBUG_SERVER = 1;
+
+ /**
+ * Debug level to show client -> server and server -> client messages.
+ *
+ * @var int
+ */
+ const DEBUG_CLIENT = 2;
+
/**
* Simple static wrapper for all-in-one POP before SMTP.
*
@@ -172,13 +199,13 @@ class POP3
public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
{
$this->host = $host;
- // If no port value provided, use default
+ //If no port value provided, use default
if (false === $port) {
$this->port = static::DEFAULT_PORT;
} else {
$this->port = (int) $port;
}
- // If no timeout value provided, use default
+ //If no timeout value provided, use default
if (false === $timeout) {
$this->tval = static::DEFAULT_TIMEOUT;
} else {
@@ -187,9 +214,9 @@ class POP3
$this->do_debug = $debug_level;
$this->username = $username;
$this->password = $password;
- // Reset the error log
+ //Reset the error log
$this->errors = [];
- // connect
+ //Connect
$result = $this->connect($this->host, $this->port, $this->tval);
if ($result) {
$login_result = $this->login($this->username, $this->password);
@@ -199,7 +226,7 @@ class POP3
return true;
}
}
- // We need to disconnect regardless of whether the login succeeded
+ //We need to disconnect regardless of whether the login succeeded
$this->disconnect();
return false;
@@ -216,7 +243,7 @@ class POP3
*/
public function connect($host, $port = false, $tval = 30)
{
- // Are we already connected?
+ //Are we already connected?
if ($this->connected) {
return true;
}
@@ -229,20 +256,22 @@ class POP3
$port = static::DEFAULT_PORT;
}
- // connect to the POP3 server
+ //Connect to the POP3 server
+ $errno = 0;
+ $errstr = '';
$this->pop_conn = fsockopen(
- $host, // POP3 Host
- $port, // Port #
- $errno, // Error Number
- $errstr, // Error Message
+ $host, //POP3 Host
+ $port, //Port #
+ $errno, //Error Number
+ $errstr, //Error Message
$tval
- ); // Timeout (seconds)
- // Restore the error handler
+ ); //Timeout (seconds)
+ //Restore the error handler
restore_error_handler();
- // Did we connect?
+ //Did we connect?
if (false === $this->pop_conn) {
- // It would appear not...
+ //It would appear not...
$this->setError(
"Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
);
@@ -250,14 +279,14 @@ class POP3
return false;
}
- // Increase the stream time-out
+ //Increase the stream time-out
stream_set_timeout($this->pop_conn, $tval, 0);
- // Get the POP3 server response
+ //Get the POP3 server response
$pop3_response = $this->getResponse();
- // Check for the +OK
+ //Check for the +OK
if ($this->checkResponse($pop3_response)) {
- // The connection is established and the POP3 server is talking
+ //The connection is established and the POP3 server is talking
$this->connected = true;
return true;
@@ -287,11 +316,11 @@ class POP3
$password = $this->password;
}
- // Send the Username
+ //Send the Username
$this->sendString("USER $username" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
- // Send the Password
+ //Send the Password
$this->sendString("PASS $password" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
@@ -327,7 +356,7 @@ class POP3
protected function getResponse($size = 128)
{
$response = fgets($this->pop_conn, $size);
- if ($this->do_debug >= 1) {
+ if ($this->do_debug >= self::DEBUG_SERVER) {
echo 'Server -> Client: ', $response;
}
@@ -344,7 +373,7 @@ class POP3
protected function sendString($string)
{
if ($this->pop_conn) {
- if ($this->do_debug >= 2) { //Show client messages when debug >= 2
+ if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
echo 'Client -> Server: ', $string;
}
@@ -382,7 +411,7 @@ class POP3
protected function setError($error)
{
$this->errors[] = $error;
- if ($this->do_debug >= 1) {
+ if ($this->do_debug >= self::DEBUG_SERVER) {
echo '';
foreach ($this->errors as $e) {
print_r($e);
diff --git a/phpmailer/vendor/phpmailer/phpmailer/src/SMTP.php b/phpmailer/vendor/phpmailer/phpmailer/src/SMTP.php
index c693f4d46..a4a91ed0d 100644
--- a/phpmailer/vendor/phpmailer/phpmailer/src/SMTP.php
+++ b/phpmailer/vendor/phpmailer/phpmailer/src/SMTP.php
@@ -1,4 +1,5 @@
* @author Andy Prevost (codeworxtech)
* @author Brent R. Matzelle (original founder)
- * @copyright 2012 - 2019 Marcus Bointon
+ * @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
@@ -34,7 +35,7 @@ class SMTP
*
* @var string
*/
- const VERSION = '6.1.4';
+ const VERSION = '6.5.0';
/**
* SMTP line break constant.
@@ -185,6 +186,7 @@ class SMTP
'Amazon_SES' => '/[\d]{3} Ok (.*)/',
'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
+ 'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
];
/**
@@ -311,17 +313,11 @@ class SMTP
*/
public function connect($host, $port = null, $timeout = 30, $options = [])
{
- static $streamok;
- //This is enabled by default since 5.0.0 but some providers disable it
- //Check this once and cache the result
- if (null === $streamok) {
- $streamok = function_exists('stream_socket_client');
- }
- // Clear errors to avoid confusion
+ //Clear errors to avoid confusion
$this->setError('');
- // Make sure we are __not__ connected
+ //Make sure we are __not__ connected
if ($this->connected()) {
- // Already connected, generate error
+ //Already connected, generate error
$this->setError('Already connected to a server');
return false;
@@ -329,18 +325,66 @@ class SMTP
if (empty($port)) {
$port = self::DEFAULT_PORT;
}
- // Connect to the SMTP server
+ //Connect to the SMTP server
$this->edebug(
"Connection: opening to $host:$port, timeout=$timeout, options=" .
(count($options) > 0 ? var_export($options, true) : 'array()'),
self::DEBUG_CONNECTION
);
+
+ $this->smtp_conn = $this->getSMTPConnection($host, $port, $timeout, $options);
+
+ if ($this->smtp_conn === false) {
+ //Error info already set inside `getSMTPConnection()`
+ return false;
+ }
+
+ $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
+
+ //Get any announcement
+ $this->last_reply = $this->get_lines();
+ $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
+ $responseCode = (int)substr($this->last_reply, 0, 3);
+ if ($responseCode === 220) {
+ return true;
+ }
+ //Anything other than a 220 response means something went wrong
+ //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
+ //https://tools.ietf.org/html/rfc5321#section-3.1
+ if ($responseCode === 554) {
+ $this->quit();
+ }
+ //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down)
+ $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION);
+ $this->close();
+ return false;
+ }
+
+ /**
+ * Create connection to the SMTP server.
+ *
+ * @param string $host SMTP server IP or host name
+ * @param int $port The port number to connect to
+ * @param int $timeout How long to wait for the connection to open
+ * @param array $options An array of options for stream_context_create()
+ *
+ * @return false|resource
+ */
+ protected function getSMTPConnection($host, $port = null, $timeout = 30, $options = [])
+ {
+ static $streamok;
+ //This is enabled by default since 5.0.0 but some providers disable it
+ //Check this once and cache the result
+ if (null === $streamok) {
+ $streamok = function_exists('stream_socket_client');
+ }
+
$errno = 0;
$errstr = '';
if ($streamok) {
$socket_context = stream_context_create($options);
set_error_handler([$this, 'errorHandler']);
- $this->smtp_conn = stream_socket_client(
+ $connection = stream_socket_client(
$host . ':' . $port,
$errno,
$errstr,
@@ -356,7 +400,7 @@ class SMTP
self::DEBUG_CONNECTION
);
set_error_handler([$this, 'errorHandler']);
- $this->smtp_conn = fsockopen(
+ $connection = fsockopen(
$host,
$port,
$errno,
@@ -365,8 +409,9 @@ class SMTP
);
restore_error_handler();
}
- // Verify we connected properly
- if (!is_resource($this->smtp_conn)) {
+
+ //Verify we connected properly
+ if (!is_resource($connection)) {
$this->setError(
'Failed to connect to server',
'',
@@ -381,22 +426,19 @@ class SMTP
return false;
}
- $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
- // SMTP server can take longer to respond, give longer timeout for first read
- // Windows does not have support for this timeout function
+
+ //SMTP server can take longer to respond, give longer timeout for first read
+ //Windows does not have support for this timeout function
if (strpos(PHP_OS, 'WIN') !== 0) {
- $max = (int) ini_get('max_execution_time');
- // Don't bother if unlimited
- if (0 !== $max && $timeout > $max) {
+ $max = (int)ini_get('max_execution_time');
+ //Don't bother if unlimited, or if set_time_limit is disabled
+ if (0 !== $max && $timeout > $max && strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
@set_time_limit($timeout);
}
- stream_set_timeout($this->smtp_conn, $timeout, 0);
+ stream_set_timeout($connection, $timeout, 0);
}
- // Get any announcement
- $announce = $this->get_lines();
- $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
- return true;
+ return $connection;
}
/**
@@ -420,7 +462,7 @@ class SMTP
$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
- // Begin encrypted connection
+ //Begin encrypted connection
set_error_handler([$this, 'errorHandler']);
$crypto_ok = stream_socket_enable_crypto(
$this->smtp_conn,
@@ -458,11 +500,11 @@ class SMTP
}
if (array_key_exists('EHLO', $this->server_caps)) {
- // SMTP extensions are available; 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)) {
$this->setError('Authentication is not allowed at this stage');
- // 'at this stage' means that auth may be allowed after the stage changes
- // e.g. after STARTTLS
+ //'at this stage' means that auth may be allowed after the stage changes
+ //e.g. after STARTTLS
return false;
}
@@ -506,22 +548,25 @@ class SMTP
}
switch ($authtype) {
case 'PLAIN':
- // Start authentication
+ //Start authentication
if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
return false;
}
- // Send encoded username and password
- if (!$this->sendCommand(
- 'User & Password',
- base64_encode("\0" . $username . "\0" . $password),
- 235
- )
+ //Send encoded username and password
+ if (
+ //Format from https://tools.ietf.org/html/rfc4616#section-2
+ //We skip the first field (it's forgery), so the string starts with a null byte
+ !$this->sendCommand(
+ 'User & Password',
+ base64_encode("\0" . $username . "\0" . $password),
+ 235
+ )
) {
return false;
}
break;
case 'LOGIN':
- // Start authentication
+ //Start authentication
if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
return false;
}
@@ -533,17 +578,17 @@ class SMTP
}
break;
case 'CRAM-MD5':
- // Start authentication
+ //Start authentication
if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
return false;
}
- // Get the challenge
+ //Get the challenge
$challenge = base64_decode(substr($this->last_reply, 4));
- // Build the response
+ //Build the response
$response = $username . ' ' . $this->hmac($challenge, $password);
- // send encoded credentials
+ //send encoded credentials
return $this->sendCommand('Username', base64_encode($response), 235);
case 'XOAUTH2':
//The OAuth instance must be set up prior to requesting auth.
@@ -552,7 +597,7 @@ class SMTP
}
$oauth = $OAuth->getOauth64();
- // Start authentication
+ //Start authentication
if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
return false;
}
@@ -582,15 +627,15 @@ class SMTP
return hash_hmac('md5', $data, $key);
}
- // The following borrowed from
- // http://php.net/manual/en/function.mhash.php#27225
+ //The following borrowed from
+ //http://php.net/manual/en/function.mhash.php#27225
- // RFC 2104 HMAC implementation for php.
- // Creates an md5 HMAC.
- // Eliminates the need to install mhash to compute a HMAC
- // by Lance Rushing
+ //RFC 2104 HMAC implementation for php.
+ //Creates an md5 HMAC.
+ //Eliminates the need to install mhash to compute a HMAC
+ //by Lance Rushing
- $bytelen = 64; // byte length for md5
+ $bytelen = 64; //byte length for md5
if (strlen($key) > $bytelen) {
$key = pack('H*', md5($key));
}
@@ -613,7 +658,7 @@ class SMTP
if (is_resource($this->smtp_conn)) {
$sock_status = stream_get_meta_data($this->smtp_conn);
if ($sock_status['eof']) {
- // The socket is valid but we are not connected
+ //The socket is valid but we are not connected
$this->edebug(
'SMTP NOTICE: EOF caught while checking if connected',
self::DEBUG_CLIENT
@@ -623,7 +668,7 @@ class SMTP
return false;
}
- return true; // everything looks good
+ return true; //everything looks good
}
return false;
@@ -641,7 +686,7 @@ class SMTP
$this->server_caps = null;
$this->helo_rply = null;
if (is_resource($this->smtp_conn)) {
- // close the connection and cleanup
+ //Close the connection and cleanup
fclose($this->smtp_conn);
$this->smtp_conn = null; //Makes for cleaner serialization
$this->edebug('Connection: closed', self::DEBUG_CONNECTION);
@@ -676,7 +721,7 @@ class SMTP
* NOTE: this does not count towards line-length limit.
*/
- // Normalize line breaks before exploding
+ //Normalize line breaks before exploding
$lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
/* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
@@ -722,7 +767,8 @@ class SMTP
//Send the lines to the server
foreach ($lines_out as $line_out) {
- //RFC2821 section 4.5.2
+ //Dot-stuffing as per RFC5321 section 4.5.2
+ //https://tools.ietf.org/html/rfc5321#section-4.5.2
if (!empty($line_out) && $line_out[0] === '.') {
$line_out = '.' . $line_out;
}
@@ -756,7 +802,16 @@ class SMTP
public function hello($host = '')
{
//Try extended hello first (RFC 2821)
- return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host);
+ if ($this->sendHello('EHLO', $host)) {
+ return true;
+ }
+
+ //Some servers shut down the SMTP service here (RFC 5321)
+ if (substr($this->helo_rply, 0, 3) == '421') {
+ return false;
+ }
+
+ return $this->sendHello('HELO', $host);
}
/**
@@ -946,12 +1001,12 @@ class SMTP
$this->client_send($commandstring . static::LE, $command);
$this->last_reply = $this->get_lines();
- // Fetch SMTP code and possible error code explanation
+ //Fetch SMTP code and possible error code explanation
$matches = [];
if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) {
$code = (int) $matches[1];
$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(
"/{$code}[ -]" .
($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
@@ -959,7 +1014,7 @@ class SMTP
$this->last_reply
);
} else {
- // Fall back to simple parsing if regex fails
+ //Fall back to simple parsing if regex fails
$code = (int) substr($this->last_reply, 0, 3);
$code_ex = null;
$detail = substr($this->last_reply, 4);
@@ -1058,8 +1113,10 @@ class SMTP
{
//If SMTP transcripts are left enabled, or debug output is posted online
//it can leak credentials, so hide credentials in all but lowest level
- if (self::DEBUG_LOWLEVEL > $this->do_debug &&
- in_array($command, ['User & Password', 'Username', 'Password'], true)) {
+ if (
+ self::DEBUG_LOWLEVEL > $this->do_debug &&
+ in_array($command, ['User & Password', 'Username', 'Password'], true)
+ ) {
$this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
} else {
$this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
@@ -1152,7 +1209,7 @@ class SMTP
*/
protected function get_lines()
{
- // If the connection is bad, give up straight away
+ //If the connection is bad, give up straight away
if (!is_resource($this->smtp_conn)) {
return '';
}
@@ -1166,33 +1223,61 @@ class SMTP
$selW = null;
while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
//Must pass vars in here as params are by reference
- if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
+ //solution for signals inspired by https://github.com/symfony/symfony/pull/6540
+ set_error_handler([$this, 'errorHandler']);
+ $n = stream_select($selR, $selW, $selW, $this->Timelimit);
+ restore_error_handler();
+
+ if ($n === false) {
+ $message = $this->getError()['detail'];
+
$this->edebug(
- 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
+ 'SMTP -> get_lines(): select failed (' . $message . ')',
+ self::DEBUG_LOWLEVEL
+ );
+
+ //stream_select returns false when the `select` system call is interrupted
+ //by an incoming signal, try the select again
+ if (stripos($message, 'interrupted system call') !== false) {
+ $this->edebug(
+ 'SMTP -> get_lines(): retrying stream_select',
+ self::DEBUG_LOWLEVEL
+ );
+ $this->setError('');
+ continue;
+ }
+
+ break;
+ }
+
+ if (!$n) {
+ $this->edebug(
+ 'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)',
self::DEBUG_LOWLEVEL
);
break;
}
+
//Deliberate noise suppression - errors are handled afterwards
$str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH);
$this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
$data .= $str;
- // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
- // or 4th character is a space or a line break char, we are done reading, break the loop.
- // String array access is a significant micro-optimisation over strlen
+ //If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
+ //or 4th character is a space or a line break char, we are done reading, break the loop.
+ //String array access is a significant micro-optimisation over strlen
if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") {
break;
}
- // Timed-out? Log and break
+ //Timed-out? Log and break
$info = stream_get_meta_data($this->smtp_conn);
if ($info['timed_out']) {
$this->edebug(
- 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
+ 'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)',
self::DEBUG_LOWLEVEL
);
break;
}
- // Now check if reads took too long
+ //Now check if reads took too long
if ($endtime && time() > $endtime) {
$this->edebug(
'SMTP -> get_lines(): timelimit reached (' .
@@ -1344,6 +1429,7 @@ class SMTP
} else {
$this->last_smtp_transaction_id = false;
foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
+ $matches = [];
if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
$this->last_smtp_transaction_id = trim($matches[1]);
break;