diff --git a/lib/classes/phpmailer/class.PHPMailer.php b/lib/classes/phpmailer/class.PHPMailer.php
index cd102a28..e4dd00bf 100644
--- a/lib/classes/phpmailer/class.PHPMailer.php
+++ b/lib/classes/phpmailer/class.PHPMailer.php
@@ -29,65 +29,66 @@ class PHPMailer
{
/**
* The PHPMailer Version number.
- * @type string
+ * @var string
*/
- public $Version = '5.2.9';
+ public $Version = '5.2.14';
/**
* Email priority.
- * Options: 1 = High, 3 = Normal, 5 = low.
- * @type integer
+ * Options: null (default), 1 = High, 3 = Normal, 5 = low.
+ * When null, the header is not set at all.
+ * @var integer
*/
- public $Priority = 3;
+ public $Priority = null;
/**
* The character set of the message.
- * @type string
+ * @var string
*/
public $CharSet = 'iso-8859-1';
/**
* The MIME Content-type of the message.
- * @type string
+ * @var string
*/
public $ContentType = 'text/plain';
/**
* The message encoding.
* Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
- * @type string
+ * @var string
*/
public $Encoding = '8bit';
/**
* Holds the most recent mailer error message.
- * @type string
+ * @var string
*/
public $ErrorInfo = '';
/**
* The From email address for the message.
- * @type string
+ * @var string
*/
public $From = 'root@localhost';
/**
* The From name of the message.
- * @type string
+ * @var string
*/
public $FromName = 'Root User';
/**
* The Sender email (Return-Path) of the message.
* If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
- * @type string
+ * @var string
*/
public $Sender = '';
/**
* The Return-Path of the message.
* If empty, it will be set to either From or Sender.
- * @type string
+ * @var string
* @deprecated Email senders should never set a return-path header;
* it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
* @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
@@ -96,14 +97,14 @@ class PHPMailer
/**
* The Subject of the message.
- * @type string
+ * @var string
*/
public $Subject = '';
/**
* An HTML or plain text message body.
* If HTML then call isHTML(true).
- * @type string
+ * @var string
*/
public $Body = '';
@@ -112,7 +113,7 @@ class PHPMailer
* This body can be read by mail clients that do not have HTML email
* capability such as mutt & Eudora.
* Clients that can read HTML will view the normal Body.
- * @type string
+ * @var string
*/
public $AltBody = '';
@@ -122,91 +123,92 @@ class PHPMailer
* To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
* @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
* @link http://kigkonsult.se/iCalcreator/
- * @type string
+ * @var string
*/
public $Ical = '';
/**
* The complete compiled MIME message body.
* @access protected
- * @type string
+ * @var string
*/
protected $MIMEBody = '';
/**
* The complete compiled MIME message headers.
- * @type string
+ * @var string
* @access protected
*/
protected $MIMEHeader = '';
/**
* Extra headers that createHeader() doesn't fold in.
- * @type string
+ * @var string
* @access protected
*/
protected $mailHeader = '';
/**
* Word-wrap the message body to this number of chars.
- * @type integer
+ * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
+ * @var integer
*/
public $WordWrap = 0;
/**
* Which method to use to send mail.
* Options: "mail", "sendmail", or "smtp".
- * @type string
+ * @var string
*/
public $Mailer = 'mail';
/**
* The path to the sendmail program.
- * @type string
+ * @var string
*/
public $Sendmail = '/usr/sbin/sendmail';
/**
* Whether mail() uses a fully sendmail-compatible MTA.
* One which supports sendmail's "-oi -f" options.
- * @type boolean
+ * @var boolean
*/
public $UseSendmailOptions = true;
/**
* Path to PHPMailer plugins.
* Useful if the SMTP class is not in the PHP include path.
- * @type string
+ * @var string
* @deprecated Should not be needed now there is an autoloader.
*/
public $PluginDir = '';
/**
- * The email address that a reading confirmation should be sent to.
- * @type string
+ * The email address that a reading confirmation should be sent to, also known as read receipt.
+ * @var string
*/
public $ConfirmReadingTo = '';
/**
- * The hostname to use in Message-Id and Received headers
- * and as default HELO string.
- * If empty, the value returned
- * by SERVER_NAME is used or 'localhost.localdomain'.
- * @type string
+ * The hostname to use in the Message-ID header and as default HELO string.
+ * If empty, PHPMailer attempts to find one with, in order,
+ * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
+ * 'localhost.localdomain'.
+ * @var string
*/
public $Hostname = '';
/**
- * 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.
- * @type string
+ * @var string
*/
public $MessageID = '';
/**
* The message Date to be used in the Date header.
* If empty, the current date will be added.
- * @type string
+ * @var string
*/
public $MessageDate = '';
@@ -219,79 +221,95 @@ class PHPMailer
* You can also specify encryption type, for example:
* (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
* Hosts will be tried in order.
- * @type string
+ * @var string
*/
public $Host = 'localhost';
/**
* The default SMTP server port.
- * @type integer
+ * @var integer
* @TODO Why is this needed when the SMTP class takes care of it?
*/
public $Port = 25;
/**
* The SMTP HELO of the message.
- * Default is $Hostname.
- * @type string
+ * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
+ * one with the same method described above for $Hostname.
+ * @var string
* @see PHPMailer::$Hostname
*/
public $Helo = '';
/**
- * The secure connection prefix.
- * Options: "", "ssl" or "tls"
- * @type string
+ * What kind of encryption to use on the SMTP connection.
+ * Options: '', 'ssl' or 'tls'
+ * @var string
*/
public $SMTPSecure = '';
+ /**
+ * Whether to enable TLS encryption automatically if a server supports it,
+ * even if `SMTPSecure` is not set to 'tls'.
+ * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
+ * @var boolean
+ */
+ public $SMTPAutoTLS = true;
+
/**
* Whether to use SMTP authentication.
* Uses the Username and Password properties.
- * @type boolean
+ * @var boolean
* @see PHPMailer::$Username
* @see PHPMailer::$Password
*/
public $SMTPAuth = false;
+ /**
+ * Options array passed to stream_context_create when connecting via SMTP.
+ * @var array
+ */
+ public $SMTPOptions = array();
+
/**
* SMTP username.
- * @type string
+ * @var string
*/
public $Username = '';
/**
* SMTP password.
- * @type string
+ * @var string
*/
public $Password = '';
/**
* SMTP auth type.
* Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
- * @type string
+ * @var string
*/
public $AuthType = '';
/**
* SMTP realm.
* Used for NTLM auth
- * @type string
+ * @var string
*/
public $Realm = '';
/**
* SMTP workstation.
* Used for NTLM auth
- * @type string
+ * @var string
*/
public $Workstation = '';
/**
* The SMTP server timeout in seconds.
- * @type integer
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
*/
- public $Timeout = 10;
+ public $Timeout = 300;
/**
* SMTP class debug output mode.
@@ -302,7 +320,7 @@ class PHPMailer
* * `2` Data and commands
* * `3` As 2 plus connection status
* * `4` Low-level data output
- * @type integer
+ * @var integer
* @see SMTP::$do_debug
*/
public $SMTPDebug = 0;
@@ -318,7 +336,7 @@ class PHPMailer
*
* $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
*
- * @type string|callable
+ * @var string|callable
* @see SMTP::$Debugoutput
*/
public $Debugoutput = 'echo';
@@ -327,20 +345,20 @@ class PHPMailer
* Whether to keep SMTP connection open after each message.
* If this is set to true then to close the connection
* requires an explicit call to smtpClose().
- * @type boolean
+ * @var boolean
*/
public $SMTPKeepAlive = false;
/**
* Whether to split multiple to addresses into multiple messages
* or send them all in one message.
- * @type boolean
+ * @var boolean
*/
public $SingleTo = false;
/**
* Storage for addresses when SingleTo is enabled.
- * @type array
+ * @var array
* @TODO This should really not be public
*/
public $SingleToArray = array();
@@ -348,15 +366,15 @@ class PHPMailer
/**
* Whether to generate VERP addresses on send.
* Only applicable when sending via SMTP.
- * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
* @link http://www.postfix.org/VERP_README.html Postfix VERP info
- * @type boolean
+ * @var boolean
*/
public $do_verp = false;
/**
* Whether to allow sending messages with an empty body.
- * @type boolean
+ * @var boolean
*/
public $AllowEmpty = false;
@@ -364,40 +382,40 @@ class PHPMailer
* The default line ending.
* @note The default remains "\n". We force CRLF where we know
* it must be used via self::CRLF.
- * @type string
+ * @var string
*/
public $LE = "\n";
/**
* DKIM selector.
- * @type string
+ * @var string
*/
public $DKIM_selector = '';
/**
* DKIM Identity.
* Usually the email address used as the source of the email
- * @type string
+ * @var string
*/
public $DKIM_identity = '';
/**
* DKIM passphrase.
* Used if your key is encrypted.
- * @type string
+ * @var string
*/
public $DKIM_passphrase = '';
/**
* DKIM signing domain name.
* @example 'example.com'
- * @type string
+ * @var string
*/
public $DKIM_domain = '';
/**
* DKIM private key file path.
- * @type string
+ * @var string
*/
public $DKIM_private = '';
@@ -417,138 +435,175 @@ class PHPMailer
* string $subject the subject
* string $body the email body
* string $from email address of sender
- * @type string
+ * @var string
*/
public $action_function = '';
/**
- * What to use in the X-Mailer header.
- * Options: null for default, whitespace for none, or a string to use
- * @type string
+ * What to put in the X-Mailer header.
+ * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
+ * @var string
*/
public $XMailer = '';
/**
* An instance of the SMTP sender class.
- * @type SMTP
+ * @var SMTP
* @access protected
*/
protected $smtp = null;
/**
- * The array of 'to' addresses.
- * @type array
+ * The array of 'to' names and addresses.
+ * @var array
* @access protected
*/
protected $to = array();
/**
- * The array of 'cc' addresses.
- * @type array
+ * The array of 'cc' names and addresses.
+ * @var array
* @access protected
*/
protected $cc = array();
/**
- * The array of 'bcc' addresses.
- * @type array
+ * The array of 'bcc' names and addresses.
+ * @var array
* @access protected
*/
protected $bcc = array();
/**
* The array of reply-to names and addresses.
- * @type array
+ * @var array
* @access protected
*/
protected $ReplyTo = array();
/**
* An array of all kinds of addresses.
- * Includes all of $to, $cc, $bcc, $replyto
- * @type array
+ * Includes all of $to, $cc, $bcc
+ * @var array
* @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
*/
protected $all_recipients = array();
+ /**
+ * An array of names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $all_recipients
+ * and one of $to, $cc, or $bcc.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ * @see PHPMailer::$all_recipients
+ */
+ protected $RecipientsQueue = array();
+
+ /**
+ * An array of reply-to names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $ReplyTo.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$ReplyTo
+ */
+ protected $ReplyToQueue = array();
+
/**
* The array of attachments.
- * @type array
+ * @var array
* @access protected
*/
protected $attachment = array();
/**
* The array of custom headers.
- * @type array
+ * @var array
* @access protected
*/
protected $CustomHeader = array();
/**
* The most recent Message-ID (including angular brackets).
- * @type string
+ * @var string
* @access protected
*/
protected $lastMessageID = '';
/**
* The message's MIME type.
- * @type string
+ * @var string
* @access protected
*/
protected $message_type = '';
/**
* The array of MIME boundary strings.
- * @type array
+ * @var array
* @access protected
*/
protected $boundary = array();
/**
* The array of available languages.
- * @type array
+ * @var array
* @access protected
*/
protected $language = array();
/**
* The number of errors encountered.
- * @type integer
+ * @var integer
* @access protected
*/
protected $error_count = 0;
/**
* The S/MIME certificate file path.
- * @type string
+ * @var string
* @access protected
*/
protected $sign_cert_file = '';
/**
* The S/MIME key file path.
- * @type string
+ * @var string
* @access protected
*/
protected $sign_key_file = '';
+ /**
+ * The optional S/MIME extra certificates ("CA Chain") file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_extracerts_file = '';
+
/**
* The S/MIME password for the key.
* Used only if the key is encrypted.
- * @type string
+ * @var string
* @access protected
*/
protected $sign_key_pass = '';
/**
* Whether to throw exceptions for errors.
- * @type boolean
+ * @var boolean
* @access protected
*/
protected $exceptions = false;
+ /**
+ * Unique ID used for message ID and boundaries.
+ * @var string
+ * @access protected
+ */
+ protected $uniqueid = '';
+
/**
* Error severity: message only, continue processing.
*/
@@ -569,13 +624,19 @@ class PHPMailer
*/
const CRLF = "\r\n";
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+
/**
* Constructor.
* @param boolean $exceptions Should we throw external exceptions?
*/
public function __construct($exceptions = false)
{
- $this->exceptions = ($exceptions == true);
+ $this->exceptions = (boolean)$exceptions;
}
/**
@@ -583,7 +644,8 @@ class PHPMailer
*/
public function __destruct()
{
- if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
+ //Close any open SMTP connection nicely
+ if ($this->Mailer == 'smtp') {
$this->smtpClose();
}
}
@@ -629,7 +691,8 @@ class PHPMailer
if ($this->SMTPDebug <= 0) {
return;
}
- if (is_callable($this->Debugoutput)) {
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
return;
}
@@ -725,55 +788,101 @@ class PHPMailer
/**
* Add a "To" address.
- * @param string $address
+ * @param string $address The email address to send to
* @param string $name
- * @return boolean true on success, false if address already used
+ * @return boolean true on success, false if address already used or invalid in some way
*/
public function addAddress($address, $name = '')
{
- return $this->addAnAddress('to', $address, $name);
+ return $this->addOrEnqueueAnAddress('to', $address, $name);
}
/**
* Add a "CC" address.
* @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- * @param string $address
+ * @param string $address The email address to send to
* @param string $name
- * @return boolean true on success, false if address already used
+ * @return boolean true on success, false if address already used or invalid in some way
*/
public function addCC($address, $name = '')
{
- return $this->addAnAddress('cc', $address, $name);
+ return $this->addOrEnqueueAnAddress('cc', $address, $name);
}
/**
* Add a "BCC" address.
* @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- * @param string $address
+ * @param string $address The email address to send to
* @param string $name
- * @return boolean true on success, false if address already used
+ * @return boolean true on success, false if address already used or invalid in some way
*/
public function addBCC($address, $name = '')
{
- return $this->addAnAddress('bcc', $address, $name);
+ return $this->addOrEnqueueAnAddress('bcc', $address, $name);
}
/**
- * Add a "Reply-to" address.
- * @param string $address
+ * Add a "Reply-To" address.
+ * @param string $address The email address to reply to
* @param string $name
- * @return boolean
+ * @return boolean true on success, false if address already used or invalid in some way
*/
public function addReplyTo($address, $name = '')
{
- return $this->addAnAddress('Reply-To', $address, $name);
+ return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
}
/**
- * Add an address to one of the recipient arrays.
- * Addresses that have been added already return false, but do not throw exceptions
- * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
- * @param string $address The email address to send to
+ * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
+ * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
+ * be modified after calling this function), addition of such addresses is delayed until send().
+ * Addresses that have been added already return false, but do not throw exceptions.
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
+ * @param string $name
+ * @throws phpmailerException
+ * @return boolean true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addOrEnqueueAnAddress($kind, $address, $name)
+ {
+ $address = trim($address);
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ if (($pos = strrpos($address, '@')) === false) {
+ // At-sign is misssing.
+ $error_message = $this->lang('invalid_address') . $address;
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ $params = array($kind, $address, $name);
+ // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+ if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
+ if ($kind != 'Reply-To') {
+ if (!array_key_exists($address, $this->RecipientsQueue)) {
+ $this->RecipientsQueue[$address] = $params;
+ return true;
+ }
+ } else {
+ if (!array_key_exists($address, $this->ReplyToQueue)) {
+ $this->ReplyToQueue[$address] = $params;
+ return true;
+ }
+ }
+ return false;
+ }
+ // Immediately add standard addresses without IDN.
+ return call_user_func_array(array($this, 'addAnAddress'), $params);
+ }
+
+ /**
+ * Add an address to one of the recipient arrays or to the ReplyTo array.
+ * Addresses that have been added already return false, but do not throw exceptions.
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
* @param string $name
* @throws phpmailerException
* @return boolean true on success, false if address already used or invalid in some way
@@ -781,26 +890,26 @@ class PHPMailer
*/
protected function addAnAddress($kind, $address, $name = '')
{
- if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
- $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
- $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
+ if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
+ $error_message = $this->lang('Invalid recipient kind: ') . $kind;
+ $this->setError($error_message);
+ $this->edebug($error_message);
if ($this->exceptions) {
- throw new phpmailerException('Invalid recipient array: ' . $kind);
+ throw new phpmailerException($error_message);
}
return false;
}
- $address = trim($address);
- $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
if (!$this->validateAddress($address)) {
- $this->setError($this->lang('invalid_address') . ': ' . $address);
- $this->edebug($this->lang('invalid_address') . ': ' . $address);
+ $error_message = $this->lang('invalid_address') . $address;
+ $this->setError($error_message);
+ $this->edebug($error_message);
if ($this->exceptions) {
- throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
+ throw new phpmailerException($error_message);
}
return false;
}
if ($kind != 'Reply-To') {
- if (!isset($this->all_recipients[strtolower($address)])) {
+ if (!array_key_exists(strtolower($address), $this->all_recipients)) {
array_push($this->$kind, array($address, $name));
$this->all_recipients[strtolower($address)] = true;
return true;
@@ -814,6 +923,61 @@ class PHPMailer
return false;
}
+ /**
+ * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
+ * of the form "display name
' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "
\n"; + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: '. $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } } } $this->ErrorInfo = $msg; @@ -2826,10 +3193,17 @@ class PHPMailer $this->setLanguage('en'); // set the default language } - if (isset($this->language[$key])) { + if (array_key_exists($key, $this->language)) { + if ($key == 'smtp_connect_failed') { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } return $this->language[$key]; } else { - return 'Language string failed to load: ' . $key; + //Return the key as a fallback + return $key; } } @@ -2880,6 +3254,15 @@ class PHPMailer } } + /** + * Returns all custom headers. + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + /** * Create a message from an HTML string. * Automatically makes modifications for inline images and backgrounds @@ -2888,13 +3271,14 @@ class PHPMailer * @access public * @param string $message HTML message string * @param string $basedir baseline directory for path - * @param boolean $advanced Whether to use the advanced 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() * @return string $message */ public function msgHTML($message, $basedir = '', $advanced = false) { preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); - if (isset($images[2])) { + if (array_key_exists(2, $images)) { foreach ($images[2] as $imgindex => $url) { // Convert data URIs into embedded images if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { @@ -2905,15 +3289,16 @@ class PHPMailer $data = rawurldecode($data); } $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 - if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) { - $message = preg_replace( - '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { + $message = str_replace( + $images[0][$imgindex], $images[1][$imgindex] . '="cid:' . $cid . '"', $message ); } - } elseif (!preg_match('#^[A-z]+://#', $url)) { + } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) { // Do not change urls for absolute images (thanks to corvuscorax) + // Do not change urls that are already inline images $filename = basename($url); $directory = dirname($url); if ($directory == '.') { @@ -2931,7 +3316,7 @@ class PHPMailer $cid, $filename, 'base64', - self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) ) ) { $message = preg_replace( @@ -2956,16 +3341,28 @@ class PHPMailer /** * Convert an HTML string into plain text. + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was been removed for license reasons in #232 + * Example usage: + *
+ * // Use default conversion
+ * $plain = $mail->html2text($html);
+ * // Use your own custom converter
+ * $plain = $mail->html2text($html, function($html) {
+ * $converter = new MyHtml2text($html);
+ * return $converter->get_text();
+ * });
+ *
* @param string $html The HTML text to convert
- * @param boolean $advanced Should this use the more complex html2text converter or just a simple one?
+ * @param boolean|callable $advanced Any boolean value to use the internal converter,
+ * or provide your own callable for custom conversion.
* @return string
*/
public function html2text($html, $advanced = false)
{
- if ($advanced) {
- require_once 'extras/class.html2text.php';
- $htmlconverter = new html2text($html);
- return $htmlconverter->get_text();
+ if (is_callable($advanced)) {
+ return call_user_func($advanced, $html);
}
return html_entity_decode(
trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
@@ -2984,96 +3381,109 @@ class PHPMailer
public static function _mime_types($ext = '')
{
$mimes = array(
- 'xl' => 'application/excel',
- 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'bin' => 'application/macbinary',
- 'doc' => 'application/msword',
- 'word' => 'application/msword',
+ 'xl' => 'application/excel',
+ 'js' => 'application/javascript',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'bin' => 'application/macbinary',
+ 'doc' => 'application/msword',
+ 'word' => 'application/msword',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'class' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'dms' => 'application/octet-stream',
- 'exe' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'psd' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'so' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => 'application/pdf',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
+ 'dll' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'exe' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'psd' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'so' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
'wbxml' => 'application/vnd.wap.wbxml',
- 'wmlc' => 'application/vnd.wap.wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'php3' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php' => 'application/x-httpd-php',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'php' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'js' => 'application/x-javascript',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => 'application/x-tar',
- 'xht' => 'application/xhtml+xml',
+ 'phps' => 'application/x-httpd-php-source',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => 'application/x-tar',
+ 'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'mpga' => 'audio/mpeg',
- 'aif' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'wav' => 'audio/x-wav',
- 'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpe' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'png' => 'image/png',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'eml' => 'message/rfc822',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
+ 'zip' => 'application/zip',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'wav' => 'audio/x-wav',
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'eml' => 'message/rfc822',
+ 'css' => 'text/css',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
'shtml' => 'text/html',
- 'log' => 'text/plain',
- 'text' => 'text/plain',
- 'txt' => 'text/plain',
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'vcf' => 'text/vcard',
+ 'log' => 'text/plain',
+ 'text' => 'text/plain',
+ 'txt' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'vcf' => 'text/vcard',
'vcard' => 'text/vcard',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mov' => 'video/quicktime',
- 'qt' => 'video/quicktime',
- 'rv' => 'video/vnd.rn-realvideo',
- 'avi' => 'video/x-msvideo',
+ 'xml' => 'text/xml',
+ 'xsl' => 'text/xml',
+ 'mpeg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'qt' => 'video/quicktime',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie'
);
- return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
+ if (array_key_exists(strtolower($ext), $mimes)) {
+ return $mimes[strtolower($ext)];
+ }
+ return 'application/octet-stream';
}
/**
@@ -3087,7 +3497,7 @@ class PHPMailer
{
// In case the path is a URL, strip any query string before getting extension
$qpos = strpos($filename, '?');
- if ($qpos !== false) {
+ if (false !== $qpos) {
$filename = substr($filename, 0, $qpos);
}
$pathinfo = self::mb_pathinfo($filename);
@@ -3143,33 +3553,27 @@ class PHPMailer
/**
* Set or reset instance properties.
- *
+ * You should avoid this function - it's more verbose, less efficient, more error-prone and
+ * harder to debug than setting properties directly.
* Usage Example:
- * $page->set('X-Priority', '3');
- *
+ * `$mail->set('SMTPSecure', 'tls');`
+ * is the same as:
+ * `$mail->SMTPSecure = 'tls';`
* @access public
- * @param string $name
- * @param mixed $value
- * NOTE: will not work with arrays, there are no arrays to set/reset
- * @throws phpmailerException
+ * @param string $name The property name to set
+ * @param mixed $value The value to set the property to
* @return boolean
- * @TODO Should this not be using __set() magic function?
+ * @TODO Should this not be using the __set() magic function?
*/
public function set($name, $value = '')
{
- try {
- if (isset($this->$name)) {
- $this->$name = $value;
- } else {
- throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
- }
- } catch (Exception $exc) {
- $this->setError($exc->getMessage());
- if ($exc->getCode() == self::STOP_CRITICAL) {
- return false;
- }
+ if (property_exists($this, $name)) {
+ $this->$name = $value;
+ return true;
+ } else {
+ $this->setError($this->lang('variable_set') . $name);
+ return false;
}
- return true;
}
/**
@@ -3198,19 +3602,20 @@ class PHPMailer
return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
}
-
/**
* Set the public and private key files and password for S/MIME signing.
* @access public
* @param string $cert_filename
* @param string $key_filename
* @param string $key_pass Password for private key
+ * @param string $extracerts_filename Optional path to chain certificate
*/
- public function sign($cert_filename, $key_filename, $key_pass)
+ public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
{
$this->sign_cert_file = $cert_filename;
$this->sign_key_file = $key_filename;
$this->sign_key_pass = $key_pass;
+ $this->sign_extracerts_file = $extracerts_filename;
}
/**
@@ -3244,7 +3649,7 @@ class PHPMailer
{
if (!defined('PKCS7_TEXT')) {
if ($this->exceptions) {
- throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
+ throw new phpmailerException($this->lang('extension_missing') . 'openssl');
}
return '';
}
@@ -3328,8 +3733,8 @@ class PHPMailer
$to_header = $header;
$current = 'to_header';
} else {
- if ($current && strpos($header, ' =?') === 0) {
- $current .= $header;
+ if (!empty($$current) && strpos($header, ' =?') === 0) {
+ $$current .= $header;
} else {
$current = '';
}
@@ -3345,7 +3750,11 @@ class PHPMailer
$body = $this->DKIM_BodyC($body);
$DKIMlen = strlen($body); // Length of body
$DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
- $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
+ if ('' == $this->DKIM_identity) {
+ $ident = '';
+ } else {
+ $ident = ' i=' . $this->DKIM_identity . ';';
+ }
$dkimhdrs = 'DKIM-Signature: v=1; a=' .
$DKIMsignatureType . '; q=' .
$DKIMquery . '; l=' .
@@ -3361,14 +3770,30 @@ class PHPMailer
"\tbh=" . $DKIMb64 . ";\r\n" .
"\tb=";
$toSign = $this->DKIM_HeaderC(
- $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
+ $from_header . "\r\n" .
+ $to_header . "\r\n" .
+ $subject_header . "\r\n" .
+ $dkimhdrs
);
$signed = $this->DKIM_Sign($toSign);
return $dkimhdrs . $signed . "\r\n";
}
+ /**
+ * Detect if a string contains a line longer than the maximum line length allowed.
+ * @param string $str
+ * @return boolean
+ * @static
+ */
+ public static function hasLineLongerThanMax($str)
+ {
+ //+2 to include CRLF line break for a 1000 total
+ return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
+ }
+
/**
* Allows for public read access to 'to' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
@@ -3379,6 +3804,7 @@ class PHPMailer
/**
* Allows for public read access to 'cc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
@@ -3389,6 +3815,7 @@ class PHPMailer
/**
* Allows for public read access to 'bcc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
@@ -3399,6 +3826,7 @@ class PHPMailer
/**
* Allows for public read access to 'ReplyTo' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
@@ -3409,6 +3837,7 @@ class PHPMailer
/**
* Allows for public read access to 'all_recipients' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
diff --git a/lib/classes/phpmailer/class.SMTP.php b/lib/classes/phpmailer/class.SMTP.php
index 9d1f2283..2e32e2fc 100644
--- a/lib/classes/phpmailer/class.SMTP.php
+++ b/lib/classes/phpmailer/class.SMTP.php
@@ -21,32 +21,32 @@
* PHPMailer RFC821 SMTP email transport class.
* Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
* @package PHPMailer
- * @author Chris Ryan
* $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
*
- * @type string|callable
+ * @var string|callable
*/
public $Debugoutput = 'echo';
@@ -130,7 +130,7 @@ class SMTP
* Whether to use VERP.
* @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
* @link http://www.postfix.org/VERP_README.html Info on VERP
- * @type boolean
+ * @var boolean
*/
public $do_verp = false;
@@ -139,38 +139,55 @@ class SMTP
* Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
* This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
* @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
- * @type integer
+ * @var integer
*/
public $Timeout = 300;
/**
- * The SMTP timelimit value for reads, in seconds.
- * @type integer
+ * How long to wait for commands to complete, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
*/
- public $Timelimit = 30;
+ public $Timelimit = 300;
/**
* The socket for the server connection.
- * @type resource
+ * @var resource
*/
protected $smtp_conn;
/**
- * Error message, if any, for the last call.
- * @type array
+ * Error information, if any, for the last SMTP command.
+ * @var array
*/
- protected $error = array();
+ protected $error = array(
+ 'error' => '',
+ 'detail' => '',
+ 'smtp_code' => '',
+ 'smtp_code_ex' => ''
+ );
/**
* The reply the server sent to us for HELO.
* If null, no HELO string has yet been received.
- * @type string|null
+ * @var string|null
*/
protected $helo_rply = null;
+ /**
+ * The set of SMTP extensions sent in reply to EHLO command.
+ * Indexes of the array are extension names.
+ * Value at index 'HELO' or 'EHLO' (according to command that was sent)
+ * represents the server name. In case of HELO it is the only element of the array.
+ * Other values can be boolean TRUE or an array containing extension options.
+ * If null, no HELO/EHLO string has yet been received.
+ * @var array|null
+ */
+ protected $server_caps = null;
+
/**
* The most recent reply received from the server.
- * @type string
+ * @var string
*/
protected $last_reply = '';
@@ -187,7 +204,8 @@ class SMTP
if ($level > $this->do_debug) {
return;
}
- if (is_callable($this->Debugoutput)) {
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
call_user_func($this->Debugoutput, $str, $this->do_debug);
return;
}
@@ -235,11 +253,11 @@ class SMTP
$streamok = function_exists('stream_socket_client');
}
// Clear errors to avoid confusion
- $this->error = array();
+ $this->setError('');
// Make sure we are __not__ connected
if ($this->connected()) {
// Already connected, generate error
- $this->error = array('error' => 'Already connected to a server');
+ $this->setError('Already connected to a server');
return false;
}
if (empty($port)) {
@@ -247,7 +265,7 @@ class SMTP
}
// Connect to the SMTP server
$this->edebug(
- "Connection: opening to $host:$port, t=$timeout, opt=".var_export($options, true),
+ "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),
self::DEBUG_CONNECTION
);
$errno = 0;
@@ -279,10 +297,10 @@ class SMTP
}
// Verify we connected properly
if (!is_resource($this->smtp_conn)) {
- $this->error = array(
- 'error' => 'Failed to connect to server',
- 'errno' => $errno,
- 'errstr' => $errstr
+ $this->setError(
+ 'Failed to connect to server',
+ $errno,
+ $errstr
);
$this->edebug(
'SMTP ERROR: ' . $this->error['error']
@@ -296,7 +314,8 @@ class SMTP
// Windows does not have support for this timeout function
if (substr(PHP_OS, 0, 3) != 'WIN') {
$max = ini_get('max_execution_time');
- if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
+ // Don't bother if unlimited
+ if ($max != 0 && $timeout > $max) {
@set_time_limit($timeout);
}
stream_set_timeout($this->smtp_conn, $timeout, 0);
@@ -332,22 +351,62 @@ class SMTP
* Perform SMTP authentication.
* Must be run after hello().
* @see hello()
- * @param string $username The user name
- * @param string $password The password
- * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
- * @param string $realm The auth realm for NTLM
+ * @param string $username The user name
+ * @param string $password The password
+ * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
+ * @param string $realm The auth realm for NTLM
* @param string $workstation The auth workstation for NTLM
- * @access public
- * @return boolean True if successfully authenticated.
+ * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
+ * @return bool True if successfully authenticated.* @access public
*/
public function authenticate(
$username,
$password,
- $authtype = 'LOGIN',
+ $authtype = null,
$realm = '',
- $workstation = ''
+ $workstation = '',
+ $OAuth = null
) {
- if (empty($authtype)) {
+ if (!$this->server_caps) {
+ $this->setError('Authentication is not allowed before HELO/EHLO');
+ return false;
+ }
+
+ if (array_key_exists('EHLO', $this->server_caps)) {
+ // SMTP extensions are available. Let's 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
+ return false;
+ }
+
+ self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
+ self::edebug(
+ 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
+ self::DEBUG_LOWLEVEL
+ );
+
+ if (empty($authtype)) {
+ foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2') as $method) {
+ if (in_array($method, $this->server_caps['AUTH'])) {
+ $authtype = $method;
+ break;
+ }
+ }
+ if (empty($authtype)) {
+ $this->setError('No supported authentication methods found');
+ return false;
+ }
+ self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
+ }
+
+ if (!in_array($authtype, $this->server_caps['AUTH'])) {
+ $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
+ return false;
+ }
+ } elseif (empty($authtype)) {
$authtype = 'LOGIN';
}
switch ($authtype) {
@@ -378,6 +437,19 @@ class SMTP
return false;
}
break;
+ case 'XOAUTH2':
+ //If the OAuth Instance is not set. Can be a case when PHPMailer is used
+ //instead of PHPMailerOAuth
+ if (is_null($OAuth)) {
+ return false;
+ }
+ $oauth = $OAuth->getOauth64();
+
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
+ return false;
+ }
+ break;
case 'NTLM':
/*
* ntlm_sasl_client.php
@@ -388,11 +460,11 @@ class SMTP
* PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
*/
require_once 'extras/ntlm_sasl_client.php';
- $temp = new stdClass();
+ $temp = new stdClass;
$ntlm_client = new ntlm_sasl_client_class;
//Check that functions are available
if (!$ntlm_client->Initialize($temp)) {
- $this->error = array('error' => $temp->error);
+ $this->setError($temp->error);
$this->edebug(
'You need to enable some modules in your php.ini file: '
. $this->error['error'],
@@ -441,6 +513,9 @@ class SMTP
// send encoded credentials
return $this->sendCommand('Username', base64_encode($response), 235);
+ default:
+ $this->setError("Authentication method \"$authtype\" is not supported");
+ return false;
}
return true;
}
@@ -513,7 +588,8 @@ class SMTP
*/
public function close()
{
- $this->error = array();
+ $this->setError('');
+ $this->server_caps = null;
$this->helo_rply = null;
if (is_resource($this->smtp_conn)) {
// close the connection and cleanup
@@ -537,9 +613,11 @@ class SMTP
*/
public function data($msg_data)
{
+ //This will use the standard timelimit
if (!$this->sendCommand('DATA', 'DATA', 354)) {
return false;
}
+
/* The server is ready to accept data!
* According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
* so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
@@ -567,13 +645,14 @@ class SMTP
if ($in_headers and $line == '') {
$in_headers = false;
}
- // ok we need to break this line up into several smaller lines
- //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
+ //Break this line up into several smaller lines if it's too long
+ //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
while (isset($line[self::MAX_LINE_LENGTH])) {
//Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
//so as to avoid breaking in the middle of a word
$pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
- if (!$pos) { //Deliberately matches both false and 0
+ //Deliberately matches both false and 0
+ if (!$pos) {
//No nice break found, add a hard break
$pos = self::MAX_LINE_LENGTH - 1;
$lines_out[] = substr($line, 0, $pos);
@@ -584,16 +663,14 @@ class SMTP
//Move along by the amount we dealt with
$line = substr($line, $pos + 1);
}
- /* If processing headers add a LWSP-char to the front of new line
- * RFC822 section 3.1.1
- */
+ //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
if ($in_headers) {
$line = "\t" . $line;
}
}
$lines_out[] = $line;
- // Send the lines to the server
+ //Send the lines to the server
foreach ($lines_out as $line_out) {
//RFC2821 section 4.5.2
if (!empty($line_out) and $line_out[0] == '.') {
@@ -603,8 +680,14 @@ class SMTP
}
}
- // Message data has been sent, complete the command
- return $this->sendCommand('DATA END', '.', 250);
+ //Message data has been sent, complete the command
+ //Increase timelimit for end of DATA command
+ $savetimelimit = $this->Timelimit;
+ $this->Timelimit = $this->Timelimit * 2;
+ $result = $this->sendCommand('DATA END', '.', 250);
+ //Restore timelimit
+ $this->Timelimit = $savetimelimit;
+ return $result;
}
/**
@@ -619,7 +702,7 @@ class SMTP
*/
public function hello($host = '')
{
- // Try extended hello first (RFC 2821)
+ //Try extended hello first (RFC 2821)
return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
}
@@ -636,9 +719,56 @@ class SMTP
{
$noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
$this->helo_rply = $this->last_reply;
+ if ($noerror) {
+ $this->parseHelloFields($hello);
+ } else {
+ $this->server_caps = null;
+ }
return $noerror;
}
+ /**
+ * Parse a reply to HELO/EHLO command to discover server extensions.
+ * In case of HELO, the only parameter that can be discovered is a server name.
+ * @access protected
+ * @param string $type - 'HELO' or 'EHLO'
+ */
+ protected function parseHelloFields($type)
+ {
+ $this->server_caps = array();
+ $lines = explode("\n", $this->last_reply);
+
+ foreach ($lines as $n => $s) {
+ //First 4 chars contain response code followed by - or space
+ $s = trim(substr($s, 4));
+ if (empty($s)) {
+ continue;
+ }
+ $fields = explode(' ', $s);
+ if (!empty($fields)) {
+ if (!$n) {
+ $name = $type;
+ $fields = $fields[0];
+ } else {
+ $name = array_shift($fields);
+ switch ($name) {
+ case 'SIZE':
+ $fields = ($fields ? $fields[0] : 0);
+ break;
+ case 'AUTH':
+ if (!is_array($fields)) {
+ $fields = array();
+ }
+ break;
+ default:
+ $fields = true;
+ }
+ }
+ $this->server_caps[$name] = $fields;
+ }
+ }
+ }
+
/**
* Send an SMTP MAIL command.
* Starts a mail transaction from the email address specified in
@@ -684,15 +814,15 @@ class SMTP
* Sets the TO argument to $toaddr.
* Returns true if the recipient was accepted false if it was rejected.
* Implements from rfc 821: RCPT