Commit e3732603 by Scott Committed by GitHub

Merge pull request #513 from ProThoughts/1.8

Updated PHPMailer to v5.2.23 (from v5.2.14)
parents c93f249a 81a6e450
...@@ -31,7 +31,7 @@ class PHPMailer ...@@ -31,7 +31,7 @@ class PHPMailer
* The PHPMailer Version number. * The PHPMailer Version number.
* @var string * @var string
*/ */
public $Version = '5.2.14'; public $Version = '5.2.23';
/** /**
* Email priority. * Email priority.
...@@ -201,6 +201,9 @@ class PHPMailer ...@@ -201,6 +201,9 @@ class PHPMailer
/** /**
* An ID to be used in the Message-ID header. * An ID to be used in the Message-ID header.
* If empty, a unique id will be generated. * If empty, a unique id will be generated.
* You can set your own, but it must be in the format "<id@domain>",
* as defined in RFC5322 section 3.6.4 or it will be ignored.
* @see https://tools.ietf.org/html/rfc5322#section-3.6.4
* @var string * @var string
*/ */
public $MessageID = ''; public $MessageID = '';
...@@ -285,7 +288,7 @@ class PHPMailer ...@@ -285,7 +288,7 @@ class PHPMailer
/** /**
* SMTP auth type. * SMTP auth type.
* Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified
* @var string * @var string
*/ */
public $AuthType = ''; public $AuthType = '';
...@@ -352,6 +355,7 @@ class PHPMailer ...@@ -352,6 +355,7 @@ class PHPMailer
/** /**
* Whether to split multiple to addresses into multiple messages * Whether to split multiple to addresses into multiple messages
* or send them all in one message. * or send them all in one message.
* Only supported in `mail` and `sendmail` transports, not in SMTP.
* @var boolean * @var boolean
*/ */
public $SingleTo = false; public $SingleTo = false;
...@@ -394,7 +398,7 @@ class PHPMailer ...@@ -394,7 +398,7 @@ class PHPMailer
/** /**
* DKIM Identity. * DKIM Identity.
* Usually the email address used as the source of the email * Usually the email address used as the source of the email.
* @var string * @var string
*/ */
public $DKIM_identity = ''; public $DKIM_identity = '';
...@@ -420,6 +424,13 @@ class PHPMailer ...@@ -420,6 +424,13 @@ class PHPMailer
public $DKIM_private = ''; public $DKIM_private = '';
/** /**
* DKIM private key string.
* If set, takes precedence over `$DKIM_private`.
* @var string
*/
public $DKIM_private_string = '';
/**
* Callback Action function name. * Callback Action function name.
* *
* The function that handles the result of the send email action. * The function that handles the result of the send email action.
...@@ -447,6 +458,15 @@ class PHPMailer ...@@ -447,6 +458,15 @@ class PHPMailer
public $XMailer = ''; public $XMailer = '';
/** /**
* Which validator to use by default when validating email addresses.
* May be a callable to inject your own validator, but there are several built-in validators.
* @see PHPMailer::validateAddress()
* @var string|callable
* @static
*/
public static $validator = 'auto';
/**
* An instance of the SMTP sender class. * An instance of the SMTP sender class.
* @var SMTP * @var SMTP
* @access protected * @access protected
...@@ -634,10 +654,12 @@ class PHPMailer ...@@ -634,10 +654,12 @@ class PHPMailer
* Constructor. * Constructor.
* @param boolean $exceptions Should we throw external exceptions? * @param boolean $exceptions Should we throw external exceptions?
*/ */
public function __construct($exceptions = false) public function __construct($exceptions = null)
{ {
if ($exceptions !== null) {
$this->exceptions = (boolean)$exceptions; $this->exceptions = (boolean)$exceptions;
} }
}
/** /**
* Destructor. * Destructor.
...@@ -645,10 +667,8 @@ class PHPMailer ...@@ -645,10 +667,8 @@ class PHPMailer
public function __destruct() public function __destruct()
{ {
//Close any open SMTP connection nicely //Close any open SMTP connection nicely
if ($this->Mailer == 'smtp') {
$this->smtpClose(); $this->smtpClose();
} }
}
/** /**
* Call mail() in a safe_mode-aware fashion. * Call mail() in a safe_mode-aware fashion.
...@@ -671,14 +691,16 @@ class PHPMailer ...@@ -671,14 +691,16 @@ class PHPMailer
} else { } else {
$subject = $this->encodeHeader($this->secureHeader($subject)); $subject = $this->encodeHeader($this->secureHeader($subject));
} }
if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
//Can't use additional_parameters in safe_mode, calling mail() with null params breaks
//@link http://php.net/manual/en/function.mail.php
if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
$result = @mail($to, $subject, $body, $header); $result = @mail($to, $subject, $body, $header);
} else { } else {
$result = @mail($to, $subject, $body, $header, $params); $result = @mail($to, $subject, $body, $header, $params);
} }
return $result; return $result;
} }
/** /**
* Output debugging info via user-defined method. * Output debugging info via user-defined method.
* Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
...@@ -713,7 +735,7 @@ class PHPMailer ...@@ -713,7 +735,7 @@ class PHPMailer
case 'echo': case 'echo':
default: default:
//Normalize line breaks //Normalize line breaks
$str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); $str = preg_replace('/\r\n?/ms', "\n", $str);
echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
"\n", "\n",
"\n \t ", "\n \t ",
...@@ -850,7 +872,7 @@ class PHPMailer ...@@ -850,7 +872,7 @@ class PHPMailer
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
if (($pos = strrpos($address, '@')) === false) { if (($pos = strrpos($address, '@')) === false) {
// At-sign is misssing. // At-sign is misssing.
$error_message = $this->lang('invalid_address') . $address; $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
$this->setError($error_message); $this->setError($error_message);
$this->edebug($error_message); $this->edebug($error_message);
if ($this->exceptions) { if ($this->exceptions) {
...@@ -900,7 +922,7 @@ class PHPMailer ...@@ -900,7 +922,7 @@ class PHPMailer
return false; return false;
} }
if (!$this->validateAddress($address)) { if (!$this->validateAddress($address)) {
$error_message = $this->lang('invalid_address') . $address; $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
$this->setError($error_message); $this->setError($error_message);
$this->edebug($error_message); $this->edebug($error_message);
if ($this->exceptions) { if ($this->exceptions) {
...@@ -994,7 +1016,7 @@ class PHPMailer ...@@ -994,7 +1016,7 @@ class PHPMailer
if (($pos = strrpos($address, '@')) === false or if (($pos = strrpos($address, '@')) === false or
(!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
!$this->validateAddress($address)) { !$this->validateAddress($address)) {
$error_message = $this->lang('invalid_address') . $address; $error_message = $this->lang('invalid_address') . " (setFrom) $address";
$this->setError($error_message); $this->setError($error_message);
$this->edebug($error_message); $this->edebug($error_message);
if ($this->exceptions) { if ($this->exceptions) {
...@@ -1027,19 +1049,30 @@ class PHPMailer ...@@ -1027,19 +1049,30 @@ class PHPMailer
/** /**
* Check that a string looks like an email address. * Check that a string looks like an email address.
* @param string $address The email address to check * @param string $address The email address to check
* @param string $patternselect A selector for the validation pattern to use : * @param string|callable $patternselect A selector for the validation pattern to use :
* * `auto` Pick best pattern automatically; * * `auto` Pick best pattern automatically;
* * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
* * `pcre` Use old PCRE implementation; * * `pcre` Use old PCRE implementation;
* * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
* * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
* * `noregex` Don't use a regex: super fast, really dumb. * * `noregex` Don't use a regex: super fast, really dumb.
* Alternatively you may pass in a callable to inject your own validator, for example:
* PHPMailer::validateAddress('user@example.com', function($address) {
* return (strpos($address, '@') !== false);
* });
* You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
* @return boolean * @return boolean
* @static * @static
* @access public * @access public
*/ */
public static function validateAddress($address, $patternselect = 'auto') public static function validateAddress($address, $patternselect = null)
{ {
if (is_null($patternselect)) {
$patternselect = self::$validator;
}
if (is_callable($patternselect)) {
return call_user_func($patternselect, $address);
}
//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
return false; return false;
...@@ -1216,7 +1249,7 @@ class PHPMailer ...@@ -1216,7 +1249,7 @@ class PHPMailer
} }
$this->$address_kind = $this->punyencodeAddress($this->$address_kind); $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
if (!$this->validateAddress($this->$address_kind)) { if (!$this->validateAddress($this->$address_kind)) {
$error_message = $this->lang('invalid_address') . $this->$address_kind; $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
$this->setError($error_message); $this->setError($error_message);
$this->edebug($error_message); $this->edebug($error_message);
if ($this->exceptions) { if ($this->exceptions) {
...@@ -1227,7 +1260,7 @@ class PHPMailer ...@@ -1227,7 +1260,7 @@ class PHPMailer
} }
// Set whether the message is multipart/alternative // Set whether the message is multipart/alternative
if (!empty($this->AltBody)) { if ($this->alternativeExists()) {
$this->ContentType = 'multipart/alternative'; $this->ContentType = 'multipart/alternative';
} }
...@@ -1261,9 +1294,11 @@ class PHPMailer ...@@ -1261,9 +1294,11 @@ class PHPMailer
// Sign with DKIM if enabled // Sign with DKIM if enabled
if (!empty($this->DKIM_domain) if (!empty($this->DKIM_domain)
&& !empty($this->DKIM_private)
&& !empty($this->DKIM_selector) && !empty($this->DKIM_selector)
&& file_exists($this->DKIM_private)) { && (!empty($this->DKIM_private_string)
|| (!empty($this->DKIM_private) && file_exists($this->DKIM_private))
)
) {
$header_dkim = $this->DKIM_Add( $header_dkim = $this->DKIM_Add(
$this->MIMEHeader . $this->mailHeader, $this->MIMEHeader . $this->mailHeader,
$this->encodeHeader($this->secureHeader($this->Subject)), $this->encodeHeader($this->secureHeader($this->Subject)),
...@@ -1329,19 +1364,24 @@ class PHPMailer ...@@ -1329,19 +1364,24 @@ class PHPMailer
*/ */
protected function sendmailSend($header, $body) protected function sendmailSend($header, $body)
{ {
if ($this->Sender != '') { // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
if ($this->Mailer == 'qmail') { if ($this->Mailer == 'qmail') {
$sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); $sendmailFmt = '%s -f%s';
} else { } else {
$sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); $sendmailFmt = '%s -oi -f%s -t';
} }
} else { } else {
if ($this->Mailer == 'qmail') { if ($this->Mailer == 'qmail') {
$sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); $sendmailFmt = '%s';
} else { } else {
$sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); $sendmailFmt = '%s -oi -t';
} }
} }
// TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing.
$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
if ($this->SingleTo) { if ($this->SingleTo) {
foreach ($this->SingleToArray as $toAddr) { foreach ($this->SingleToArray as $toAddr) {
if (!@$mail = popen($sendmail, 'w')) { if (!@$mail = popen($sendmail, 'w')) {
...@@ -1388,6 +1428,40 @@ class PHPMailer ...@@ -1388,6 +1428,40 @@ class PHPMailer
} }
/** /**
* Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
*
* Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
* @param string $string The string to be validated
* @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
* @access protected
* @return boolean
*/
protected static function isShellSafe($string)
{
// Future-proof
if (escapeshellcmd($string) !== $string
or !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))
) {
return false;
}
$length = strlen($string);
for ($i = 0; $i < $length; $i++) {
$c = $string[$i];
// All other characters have a special meaning in at least one common shell, including = and +.
// Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
// Note that this does permit non-Latin alphanumeric characters based on the current locale.
if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
return false;
}
}
return true;
}
/**
* Send mail using the PHP mail() function. * Send mail using the PHP mail() function.
* @param string $header The message headers * @param string $header The message headers
* @param string $body The message body * @param string $body The message body
...@@ -1404,17 +1478,20 @@ class PHPMailer ...@@ -1404,17 +1478,20 @@ class PHPMailer
} }
$to = implode(', ', $toArr); $to = implode(', ', $toArr);
if (empty($this->Sender)) { $params = null;
$params = ' '; //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
} else { if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
if (self::isShellSafe($this->Sender)) {
$params = sprintf('-f%s', $this->Sender); $params = sprintf('-f%s', $this->Sender);
} }
if ($this->Sender != '' and !ini_get('safe_mode')) { }
if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
$old_from = ini_get('sendmail_from'); $old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender); ini_set('sendmail_from', $this->Sender);
} }
$result = false; $result = false;
if ($this->SingleTo && count($toArr) > 1) { if ($this->SingleTo and count($toArr) > 1) {
foreach ($toArr as $toAddr) { foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
$this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
...@@ -1463,10 +1540,10 @@ class PHPMailer ...@@ -1463,10 +1540,10 @@ class PHPMailer
if (!$this->smtpConnect($this->SMTPOptions)) { if (!$this->smtpConnect($this->SMTPOptions)) {
throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
} }
if ('' == $this->Sender) { if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
$smtp_from = $this->From;
} else {
$smtp_from = $this->Sender; $smtp_from = $this->Sender;
} else {
$smtp_from = $this->From;
} }
if (!$this->smtp->mail($smtp_from)) { if (!$this->smtp->mail($smtp_from)) {
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
...@@ -1520,12 +1597,17 @@ class PHPMailer ...@@ -1520,12 +1597,17 @@ class PHPMailer
* @throws phpmailerException * @throws phpmailerException
* @return boolean * @return boolean
*/ */
public function smtpConnect($options = array()) public function smtpConnect($options = null)
{ {
if (is_null($this->smtp)) { if (is_null($this->smtp)) {
$this->smtp = $this->getSMTPInstance(); $this->smtp = $this->getSMTPInstance();
} }
//If no options are provided, use whatever is set in the instance
if (is_null($options)) {
$options = $this->SMTPOptions;
}
// Already connected? // Already connected?
if ($this->smtp->connected()) { if ($this->smtp->connected()) {
return true; return true;
...@@ -1595,7 +1677,7 @@ class PHPMailer ...@@ -1595,7 +1677,7 @@ class PHPMailer
if (!$this->smtp->startTLS()) { if (!$this->smtp->startTLS()) {
throw new phpmailerException($this->lang('connect_host')); throw new phpmailerException($this->lang('connect_host'));
} }
// We must resend HELO after tls negotiation // We must resend EHLO after TLS negotiation
$this->smtp->hello($hello); $this->smtp->hello($hello);
} }
if ($this->SMTPAuth) { if ($this->SMTPAuth) {
...@@ -1634,7 +1716,7 @@ class PHPMailer ...@@ -1634,7 +1716,7 @@ class PHPMailer
*/ */
public function smtpClose() public function smtpClose()
{ {
if ($this->smtp !== null) { if (is_a($this->smtp, 'SMTP')) {
if ($this->smtp->connected()) { if ($this->smtp->connected()) {
$this->smtp->quit(); $this->smtp->quit();
$this->smtp->close(); $this->smtp->close();
...@@ -1653,6 +1735,19 @@ class PHPMailer ...@@ -1653,6 +1735,19 @@ class PHPMailer
*/ */
public function setLanguage($langcode = 'en', $lang_path = '') public function setLanguage($langcode = 'en', $lang_path = '')
{ {
// Backwards compatibility for renamed language codes
$renamed_langcodes = array(
'br' => 'pt_br',
'cz' => 'cs',
'dk' => 'da',
'no' => 'nb',
'se' => 'sv',
);
if (isset($renamed_langcodes[$langcode])) {
$langcode = $renamed_langcodes[$langcode];
}
// Define full set of translatable strings in English // Define full set of translatable strings in English
$PHPMAILER_LANG = array( $PHPMAILER_LANG = array(
'authenticate' => 'SMTP Error: Could not authenticate.', 'authenticate' => 'SMTP Error: Could not authenticate.',
...@@ -1679,6 +1774,10 @@ class PHPMailer ...@@ -1679,6 +1774,10 @@ class PHPMailer
// Calculate an absolute path so it can work if CWD is not here // Calculate an absolute path so it can work if CWD is not here
$lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
} }
//Validate $langcode
if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
$langcode = 'en';
}
$foundlang = true; $foundlang = true;
$lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
// There is no English translation file // There is no English translation file
...@@ -1972,7 +2071,9 @@ class PHPMailer ...@@ -1972,7 +2071,9 @@ class PHPMailer
$result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
} }
if ($this->MessageID != '') { // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
// https://tools.ietf.org/html/rfc5322#section-3.6.4
if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
$this->lastMessageID = $this->MessageID; $this->lastMessageID = $this->MessageID;
} else { } else {
$this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
...@@ -2074,7 +2175,15 @@ class PHPMailer ...@@ -2074,7 +2175,15 @@ class PHPMailer
*/ */
public function getSentMIMEMessage() public function getSentMIMEMessage()
{ {
return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
}
/**
* Create unique ID
* @return string
*/
protected function generateId() {
return md5(uniqid(time()));
} }
/** /**
...@@ -2088,7 +2197,7 @@ class PHPMailer ...@@ -2088,7 +2197,7 @@ class PHPMailer
{ {
$body = ''; $body = '';
//Create unique IDs and preset boundaries //Create unique IDs and preset boundaries
$this->uniqueid = md5(uniqid(time())); $this->uniqueid = $this->generateId();
$this->boundary[1] = 'b1_' . $this->uniqueid; $this->boundary[1] = 'b1_' . $this->uniqueid;
$this->boundary[2] = 'b2_' . $this->uniqueid; $this->boundary[2] = 'b2_' . $this->uniqueid;
$this->boundary[3] = 'b3_' . $this->uniqueid; $this->boundary[3] = 'b3_' . $this->uniqueid;
...@@ -2104,12 +2213,12 @@ class PHPMailer ...@@ -2104,12 +2213,12 @@ class PHPMailer
//Can we do a 7-bit downgrade? //Can we do a 7-bit downgrade?
if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
$bodyEncoding = '7bit'; $bodyEncoding = '7bit';
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
$bodyCharSet = 'us-ascii'; $bodyCharSet = 'us-ascii';
} }
//If lines are too long, and we're not already using an encoding that will shorten them, //If lines are too long, and we're not already using an encoding that will shorten them,
//change to quoted-printable transfer encoding //change to quoted-printable transfer encoding for the body part only
if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
$this->Encoding = 'quoted-printable';
$bodyEncoding = 'quoted-printable'; $bodyEncoding = 'quoted-printable';
} }
...@@ -2118,10 +2227,12 @@ class PHPMailer ...@@ -2118,10 +2227,12 @@ class PHPMailer
//Can we do a 7-bit downgrade? //Can we do a 7-bit downgrade?
if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
$altBodyEncoding = '7bit'; $altBodyEncoding = '7bit';
//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
$altBodyCharSet = 'us-ascii'; $altBodyCharSet = 'us-ascii';
} }
//If lines are too long, change to quoted-printable transfer encoding //If lines are too long, and we're not already using an encoding that will shorten them,
if (self::hasLineLongerThanMax($this->AltBody)) { //change to quoted-printable transfer encoding for the alt body part only
if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
$altBodyEncoding = 'quoted-printable'; $altBodyEncoding = 'quoted-printable';
} }
//Use this as a preamble in all multipart message types //Use this as a preamble in all multipart message types
...@@ -2224,8 +2335,10 @@ class PHPMailer ...@@ -2224,8 +2335,10 @@ class PHPMailer
$body .= $this->attachAll('attachment', $this->boundary[1]); $body .= $this->attachAll('attachment', $this->boundary[1]);
break; break;
default: default:
// catch case 'plain' and case '' // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
$body .= $this->encodeString($this->Body, $bodyEncoding); //Reset the `Encoding` property in case we changed it for line length reasons
$this->Encoding = $bodyEncoding;
$body .= $this->encodeString($this->Body, $this->Encoding);
break; break;
} }
...@@ -2331,8 +2444,7 @@ class PHPMailer ...@@ -2331,8 +2444,7 @@ class PHPMailer
/** /**
* Set the message type. * Set the message type.
* PHPMailer only supports some preset message types, * PHPMailer only supports some preset message types, not arbitrary MIME structures.
* not arbitrary MIME structures.
* @access protected * @access protected
* @return void * @return void
*/ */
...@@ -2350,6 +2462,7 @@ class PHPMailer ...@@ -2350,6 +2462,7 @@ class PHPMailer
} }
$this->message_type = implode('_', $type); $this->message_type = implode('_', $type);
if ($this->message_type == '') { if ($this->message_type == '') {
//The 'plain' message_type refers to the message having a single body element, not that it is plain-text
$this->message_type = 'plain'; $this->message_type = 'plain';
} }
} }
...@@ -2379,6 +2492,7 @@ class PHPMailer ...@@ -2379,6 +2492,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.
...@@ -2904,6 +3018,7 @@ class PHPMailer ...@@ -2904,6 +3018,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.
...@@ -3264,21 +3379,29 @@ class PHPMailer ...@@ -3264,21 +3379,29 @@ class PHPMailer
} }
/** /**
* Create a message from an HTML string. * Create a message body from an HTML string.
* Automatically makes modifications for inline images and backgrounds * Automatically inlines images and creates a plain-text version by converting the HTML,
* and creates a plain-text version by converting the HTML. * overwriting any existing values in Body and AltBody.
* Overwrites any existing values in $this->Body and $this->AltBody * 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.
* 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 baseline directory for path * @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 * @return string $message The transformed message Body
*/ */
public function msgHTML($message, $basedir = '', $advanced = false) public function msgHTML($message, $basedir = '', $advanced = false)
{ {
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)) {
...@@ -3296,18 +3419,24 @@ class PHPMailer ...@@ -3296,18 +3419,24 @@ class PHPMailer
$message $message
); );
} }
} elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $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 .= '/';
} }
...@@ -3332,7 +3461,7 @@ class PHPMailer ...@@ -3332,7 +3461,7 @@ class PHPMailer
// Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
$this->Body = $this->normalizeBreaks($message); $this->Body = $this->normalizeBreaks($message);
$this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
if (empty($this->AltBody)) { if (!$this->alternativeExists()) {
$this->AltBody = 'To view this email message, open it in a program that understands HTML!' . $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
self::CRLF . self::CRLF; self::CRLF . self::CRLF;
} }
...@@ -3343,7 +3472,7 @@ class PHPMailer ...@@ -3343,7 +3472,7 @@ class PHPMailer
* Convert an HTML string into plain text. * Convert an HTML string into plain text.
* This is used by msgHTML(). * This is used by msgHTML().
* Note - older versions of this function used a bundled advanced converter * Note - older versions of this function used a bundled advanced converter
* which was been removed for license reasons in #232 * which was been removed for license reasons in #232.
* Example usage: * Example usage:
* <code> * <code>
* // Use default conversion * // Use default conversion
...@@ -3643,7 +3772,7 @@ class PHPMailer ...@@ -3643,7 +3772,7 @@ class PHPMailer
* @access public * @access public
* @param string $signHeader * @param string $signHeader
* @throws phpmailerException * @throws phpmailerException
* @return string * @return string The DKIM signature value
*/ */
public function DKIM_Sign($signHeader) public function DKIM_Sign($signHeader)
{ {
...@@ -3653,15 +3782,35 @@ class PHPMailer ...@@ -3653,15 +3782,35 @@ class PHPMailer
} }
return ''; return '';
} }
$privKeyStr = file_get_contents($this->DKIM_private); $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
if ($this->DKIM_passphrase != '') { if ('' != $this->DKIM_passphrase) {
$privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
} else { } else {
$privKey = $privKeyStr; $privKey = openssl_pkey_get_private($privKeyStr);
}
//Workaround for missing digest algorithms in old PHP & OpenSSL versions
//@link http://stackoverflow.com/a/11117338/333340
if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
openssl_pkey_free($privKey);
return base64_encode($signature);
} }
if (openssl_sign($signHeader, $signature, $privKey)) { } else {
$pinfo = openssl_pkey_get_details($privKey);
$hash = hash('sha256', $signHeader);
//'Magic' constant for SHA256 from RFC3447
//@link https://tools.ietf.org/html/rfc3447#page-43
$t = '3031300d060960864801650304020105000420' . $hash;
$pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
$eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
openssl_pkey_free($privKey);
return base64_encode($signature); return base64_encode($signature);
} }
}
openssl_pkey_free($privKey);
return ''; return '';
} }
...@@ -3678,7 +3827,7 @@ class PHPMailer ...@@ -3678,7 +3827,7 @@ class PHPMailer
foreach ($lines as $key => $line) { foreach ($lines as $key => $line) {
list($heading, $value) = explode(':', $line, 2); list($heading, $value) = explode(':', $line, 2);
$heading = strtolower($heading); $heading = strtolower($heading);
$value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
$lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
} }
$signHeader = implode("\r\n", $lines); $signHeader = implode("\r\n", $lines);
...@@ -3716,7 +3865,7 @@ class PHPMailer ...@@ -3716,7 +3865,7 @@ class PHPMailer
*/ */
public function DKIM_Add($headers_line, $subject, $body) public function DKIM_Add($headers_line, $subject, $body)
{ {
$DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
$DKIMquery = 'dns/txt'; // Query method $DKIMquery = 'dns/txt'; // Query method
$DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
...@@ -3724,6 +3873,7 @@ class PHPMailer ...@@ -3724,6 +3873,7 @@ class PHPMailer
$headers = explode($this->LE, $headers_line); $headers = explode($this->LE, $headers_line);
$from_header = ''; $from_header = '';
$to_header = ''; $to_header = '';
$date_header = '';
$current = ''; $current = '';
foreach ($headers as $header) { foreach ($headers as $header) {
if (strpos($header, 'From:') === 0) { if (strpos($header, 'From:') === 0) {
...@@ -3732,6 +3882,9 @@ class PHPMailer ...@@ -3732,6 +3882,9 @@ class PHPMailer
} elseif (strpos($header, 'To:') === 0) { } elseif (strpos($header, 'To:') === 0) {
$to_header = $header; $to_header = $header;
$current = 'to_header'; $current = 'to_header';
} elseif (strpos($header, 'Date:') === 0) {
$date_header = $header;
$current = 'date_header';
} else { } else {
if (!empty($$current) && strpos($header, ' =?') === 0) { if (!empty($$current) && strpos($header, ' =?') === 0) {
$$current .= $header; $$current .= $header;
...@@ -3742,6 +3895,7 @@ class PHPMailer ...@@ -3742,6 +3895,7 @@ class PHPMailer
} }
$from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
$to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
$date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
$subject = str_replace( $subject = str_replace(
'|', '|',
'=7C', '=7C',
...@@ -3749,7 +3903,7 @@ class PHPMailer ...@@ -3749,7 +3903,7 @@ class PHPMailer
); // Copied header fields (dkim-quoted-printable) ); // Copied header fields (dkim-quoted-printable)
$body = $this->DKIM_BodyC($body); $body = $this->DKIM_BodyC($body);
$DKIMlen = strlen($body); // Length of body $DKIMlen = strlen($body); // Length of body
$DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
if ('' == $this->DKIM_identity) { if ('' == $this->DKIM_identity) {
$ident = ''; $ident = '';
} else { } else {
...@@ -3762,16 +3916,18 @@ class PHPMailer ...@@ -3762,16 +3916,18 @@ class PHPMailer
$this->DKIM_selector . $this->DKIM_selector .
";\r\n" . ";\r\n" .
"\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
"\th=From:To:Subject;\r\n" . "\th=From:To:Date:Subject;\r\n" .
"\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
"\tz=$from\r\n" . "\tz=$from\r\n" .
"\t|$to\r\n" . "\t|$to\r\n" .
"\t|$date\r\n" .
"\t|$subject;\r\n" . "\t|$subject;\r\n" .
"\tbh=" . $DKIMb64 . ";\r\n" . "\tbh=" . $DKIMb64 . ";\r\n" .
"\tb="; "\tb=";
$toSign = $this->DKIM_HeaderC( $toSign = $this->DKIM_HeaderC(
$from_header . "\r\n" . $from_header . "\r\n" .
$to_header . "\r\n" . $to_header . "\r\n" .
$date_header . "\r\n" .
$subject_header . "\r\n" . $subject_header . "\r\n" .
$dkimhdrs $dkimhdrs
); );
......
...@@ -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