Update phpMailer to 5.2.26

Signed-off-by: Michael Kaufmann (d00p) <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann (d00p)
2018-01-26 17:18:12 +01:00
parent 3fb92259a8
commit fcd0dddfd5
2 changed files with 590 additions and 544 deletions

View File

@@ -31,7 +31,7 @@ class PHPMailer
* The PHPMailer Version number. * The PHPMailer Version number.
* @var string * @var string
*/ */
public $Version = '5.2.21'; public $Version = '5.2.26';
/** /**
* Email priority. * Email priority.
@@ -440,9 +440,9 @@ class PHPMailer
* *
* Parameters: * Parameters:
* boolean $result result of the send action * boolean $result result of the send action
* string $to email address of the recipient * array $to email addresses of the recipients
* string $cc cc email addresses * array $cc cc email addresses
* string $bcc bcc email addresses * array $bcc bcc email addresses
* string $subject the subject * string $subject the subject
* string $body the email body * string $body the email body
* string $from email address of sender * string $from email address of sender
@@ -659,6 +659,8 @@ class PHPMailer
if ($exceptions !== null) { if ($exceptions !== null) {
$this->exceptions = (boolean)$exceptions; $this->exceptions = (boolean)$exceptions;
} }
//Pick an appropriate debug output format automatically
$this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
} }
/** /**
@@ -1622,8 +1624,13 @@ class PHPMailer
foreach ($hosts as $hostentry) { foreach ($hosts as $hostentry) {
$hostinfo = array(); $hostinfo = array();
if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { if (!preg_match(
'/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/',
trim($hostentry),
$hostinfo
)) {
// Not a valid host entry // Not a valid host entry
$this->edebug('Ignoring invalid host: ' . $hostentry);
continue; continue;
} }
// $hostinfo[2]: optional ssl or tls prefix // $hostinfo[2]: optional ssl or tls prefix
@@ -1742,6 +1749,7 @@ class PHPMailer
'dk' => 'da', 'dk' => 'da',
'no' => 'nb', 'no' => 'nb',
'se' => 'sv', 'se' => 'sv',
'sr' => 'rs'
); );
if (isset($renamed_langcodes[$langcode])) { if (isset($renamed_langcodes[$langcode])) {
@@ -2024,10 +2032,7 @@ class PHPMailer
{ {
$result = ''; $result = '';
if ($this->MessageDate == '') { $result .= $this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate);
$this->MessageDate = self::rfcDate();
}
$result .= $this->headerLine('Date', $this->MessageDate);
// To be created automatically by mail() // To be created automatically by mail()
if ($this->SingleTo) { if ($this->SingleTo) {
@@ -2492,6 +2497,7 @@ class PHPMailer
/** /**
* Add an attachment from a path on the filesystem. * Add an attachment from a path on the filesystem.
* Never use a user-supplied path to a file!
* Returns false if the file could not be found or read. * Returns false if the file could not be found or read.
* @param string $path Path to the attachment. * @param string $path Path to the attachment.
* @param string $name Overrides the attachment name. * @param string $name Overrides the attachment name.
@@ -3017,6 +3023,7 @@ class PHPMailer
* displayed inline with the message, not just attached for download. * displayed inline with the message, not just attached for download.
* This is used in HTML messages that embed the images * This is used in HTML messages that embed the images
* the HTML refers to using the $cid value. * the HTML refers to using the $cid value.
* Never use a user-supplied path to a file!
* @param string $path Path to the attachment. * @param string $path Path to the attachment.
* @param string $cid Content ID of the attachment; Use this to reference * @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML. * the content when using an embedded image in HTML.
@@ -3380,12 +3387,14 @@ class PHPMailer
* Create a message body from an HTML string. * Create a message body from an HTML string.
* Automatically inlines images and creates a plain-text version by converting the HTML, * Automatically inlines images and creates a plain-text version by converting the HTML,
* overwriting any existing values in Body and AltBody. * overwriting any existing values in Body and AltBody.
* $basedir is used when handling relative image paths, e.g. <img src="images/a.png"> * Do not source $message content from user input!
* $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
* will look for an image file in $basedir/images/a.png and convert it to inline. * will look for an image file in $basedir/images/a.png and convert it to inline.
* If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
* If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
* @access public * @access public
* @param string $message HTML message string * @param string $message HTML message string
* @param string $basedir base directory for relative paths to images * @param string $basedir Absolute path to a base directory to prepend to relative paths to images
* @param boolean|callable $advanced Whether to use the internal HTML to text converter * @param boolean|callable $advanced Whether to use the internal HTML to text converter
* or your own custom converter @see PHPMailer::html2text() * or your own custom converter @see PHPMailer::html2text()
* @return string $message The transformed message Body * @return string $message The transformed message Body
@@ -3394,6 +3403,10 @@ class PHPMailer
{ {
preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
if (array_key_exists(2, $images)) { if (array_key_exists(2, $images)) {
if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
// Ensure $basedir has a trailing /
$basedir .= '/';
}
foreach ($images[2] as $imgindex => $url) { foreach ($images[2] as $imgindex => $url) {
// Convert data URIs into embedded images // Convert data URIs into embedded images
if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
@@ -3411,18 +3424,24 @@ class PHPMailer
$message $message
); );
} }
} elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { continue;
// Do not change urls for absolute images (thanks to corvuscorax) }
if (
// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
!empty($basedir)
// Ignore URLs containing parent dir traversal (..)
&& (strpos($url, '..') === false)
// Do not change urls that are already inline images // Do not change urls that are already inline images
&& substr($url, 0, 4) !== 'cid:'
// Do not change absolute URLs, including anonymous protocol
&& !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
) {
$filename = basename($url); $filename = basename($url);
$directory = dirname($url); $directory = dirname($url);
if ($directory == '.') { if ($directory == '.') {
$directory = ''; $directory = '';
} }
$cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
$basedir .= '/';
}
if (strlen($directory) > 1 && substr($directory, -1) != '/') { if (strlen($directory) > 1 && substr($directory, -1) != '/') {
$directory .= '/'; $directory .= '/';
} }
@@ -4019,7 +4038,7 @@ class phpmailerException extends Exception
*/ */
public function errorMessage() public function errorMessage()
{ {
$errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
return $errorMsg; return $errorMsg;
} }
} }

View File

@@ -30,7 +30,7 @@ class SMTP
* The PHPMailer SMTP version number. * The PHPMailer SMTP version number.
* @var string * @var string
*/ */
const VERSION = '5.2.21'; const VERSION = '5.2.26';
/** /**
* SMTP line break constant. * SMTP line break constant.
@@ -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.21'; public $Version = '5.2.26';
/** /**
* SMTP server port number. * SMTP server port number.
@@ -151,9 +151,8 @@ class SMTP
public $Timelimit = 300; public $Timelimit = 300;
/** /**
* @var array patterns to extract smtp transaction id from smtp reply * @var array Patterns to extract an SMTP transaction id from reply to a DATA command.
* Only first capture group will be use, use non-capturing group to deal with it * The first capture group in each regex will be used as the ID.
* Extend this class to override this property to fulfil your needs.
*/ */
protected $smtp_transaction_id_patterns = array( protected $smtp_transaction_id_patterns = array(
'exim' => '/[0-9]{3} OK id=(.*)/', 'exim' => '/[0-9]{3} OK id=(.*)/',
@@ -161,6 +160,12 @@ class SMTP
'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/'
); );
/**
* @var string The last transaction ID issued in response to a DATA command,
* if one was detected
*/
protected $last_smtp_transaction_id;
/** /**
* The socket for the server connection. * The socket for the server connection.
* @var resource * @var resource
@@ -227,12 +232,11 @@ class SMTP
break; break;
case 'html': case 'html':
//Cleans up output a bit for a better looking, HTML-safe output //Cleans up output a bit for a better looking, HTML-safe output
echo htmlentities( echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities(
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:
@@ -276,7 +280,8 @@ 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;
@@ -362,14 +367,14 @@ class SMTP
} }
// 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,
$crypto_method $crypto_method
)) { );
return false; restore_error_handler();
} return $crypto_ok;
return true;
} }
/** /**
@@ -398,8 +403,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
@@ -710,6 +714,7 @@ class SMTP
$savetimelimit = $this->Timelimit; $savetimelimit = $this->Timelimit;
$this->Timelimit = $this->Timelimit * 2; $this->Timelimit = $this->Timelimit * 2;
$result = $this->sendCommand('DATA END', '.', 250); $result = $this->sendCommand('DATA END', '.', 250);
$this->recordLastTransactionID();
//Restore timelimit //Restore timelimit
$this->Timelimit = $savetimelimit; $this->Timelimit = $savetimelimit;
return $result; return $result;
@@ -893,7 +898,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
); );
@@ -989,7 +995,10 @@ class SMTP
public function client_send($data) public function client_send($data)
{ {
$this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
return fwrite($this->smtp_conn, $data); set_error_handler(array($this, 'errorHandler'));
$result = fwrite($this->smtp_conn, $data);
restore_error_handler();
return $result;
} }
/** /**
@@ -1089,8 +1098,10 @@ class SMTP
$this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
$this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL);
$data .= $str; $data .= $str;
// If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
if ((isset($str[3]) and $str[3] == ' ')) { // or 4th character is a space, we are done reading, break the loop,
// string array access is a micro-optimisation over strlen
if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) {
break; break;
} }
// Timed-out? Log and break // Timed-out? Log and break
@@ -1208,42 +1219,58 @@ class SMTP
* Reports an error number and string. * Reports an error number and string.
* @param integer $errno The error number returned by PHP. * @param integer $errno The error number returned by PHP.
* @param string $errmsg The error message 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) protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
{ {
$notice = 'Connection: Failed to connect to server.'; $notice = 'Connection failed.';
$this->setError( $this->setError(
$notice, $notice,
$errno, $errno,
$errmsg $errmsg
); );
$this->edebug( $this->edebug(
$notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]",
self::DEBUG_CONNECTION self::DEBUG_CONNECTION
); );
} }
/** /**
* Will return the ID of the last smtp transaction based on a list of patterns provided * Extract and return the ID of the last SMTP transaction based on
* in SMTP::$smtp_transaction_id_patterns. * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
* Relies on the host providing the ID in response to a DATA command.
* If no reply has been received yet, it will return null. * If no reply has been received yet, it will return null.
* If no pattern has been matched, it will return false. * If no pattern was matched, it will return false.
* @return bool|null|string * @return bool|null|string
*/ */
public function getLastTransactionID() protected function recordLastTransactionID()
{ {
$reply = $this->getLastReply(); $reply = $this->getLastReply();
if (empty($reply)) { if (empty($reply)) {
return null; $this->last_smtp_transaction_id = null;
} } else {
$this->last_smtp_transaction_id = false;
foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
return $matches[1]; $this->last_smtp_transaction_id = $matches[1];
}
} }
} }
return false; return $this->last_smtp_transaction_id;
}
/**
* Get the queue/transaction ID of the last SMTP transaction
* If no reply has been received yet, it will return null.
* If no pattern was matched, it will return false.
* @return bool|null|string
* @see recordLastTransactionID()
*/
public function getLastTransactionID()
{
return $this->last_smtp_transaction_id;
} }
} }