From d2ba3e0dc7680cc8e8d28a281986b86850153de5 Mon Sep 17 00:00:00 2001 From: Sergey Romanenko Date: Thu, 27 Jun 2013 13:04:28 +0300 Subject: [PATCH] Update PHPMailer to 5.2.6 --- libraries/PHPMailer/PHPMailer.php | 1131 +++++++++++++++-------------- 1 file changed, 567 insertions(+), 564 deletions(-) diff --git a/libraries/PHPMailer/PHPMailer.php b/libraries/PHPMailer/PHPMailer.php index f76ecac..5df9730 100644 --- a/libraries/PHPMailer/PHPMailer.php +++ b/libraries/PHPMailer/PHPMailer.php @@ -2,12 +2,13 @@ /*~ class.phpmailer.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.2.4 | -| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | +| Version: 5.2.6 | +| Site: https://github.com/PHPMailer/PHPMailer/ | | ------------------------------------------------------------------------- | -| Admin: Jim Jagielski (project admininistrator) | +| Admins: Marcus Bointon | +| Admins: Jim Jagielski | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | -| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk | | : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | @@ -34,14 +35,16 @@ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ -if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); +if (version_compare(PHP_VERSION, '5.0.0', '<') ) { + exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n"); +} /** * PHP email creation and transport class * @package PHPMailer */ -class PHPMailer -{ +class PHPMailer { + ///////////////////////////////////////////////// // PROPERTIES, PUBLIC ///////////////////////////////////////////////// @@ -90,8 +93,8 @@ class PHPMailer public $FromName = 'Root User'; /** - * Sets 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. + * Sets 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. * @var string */ public $Sender = ''; @@ -110,21 +113,31 @@ class PHPMailer public $Subject = ''; /** - * Sets the Body of the message. This can be either an HTML or text body. - * If HTML then run IsHTML(true). + * An HTML or plain text message body. + * If HTML then call IsHTML(true). * @var string */ public $Body = ''; /** - * Sets the text-only body of the message. This automatically sets the - * email to multipart/alternative. This body can be read by mail - * clients that do not have HTML email capability such as mutt. Clients - * that can read HTML will view the normal Body. + * The plain-text message body. + * 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. * @var string */ public $AltBody = ''; + /** + * An iCal message part body + * Only supported in simple alt or alt_inline message types + * 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/ + * @var string + */ + public $Ical = ''; + /** * Stores the complete compiled MIME message body. * @var string @@ -143,7 +156,7 @@ class PHPMailer * Stores the extra header list which CreateHeader() doesn't fold in * @var string * @access protected - */ + */ protected $mailHeader = ''; /** @@ -260,7 +273,7 @@ class PHPMailer public $Password = ''; /** - * Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM (default LOGIN) + * Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM | CRAM-MD5 (default LOGIN) * @var string */ public $AuthType = ''; @@ -312,13 +325,26 @@ class PHPMailer */ public $SingleTo = false; - /** + /** + * Should we generate VERP addresses when sending via SMTP? + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @var bool + */ + public $do_verp = false; + + /** * If SingleTo is true, this provides the array to hold the email addresses * @var bool */ public $SingleToArray = array(); - /** + /** + * Should we allow sending messages with empty body? + * @var bool + */ + public $AllowEmpty = false; + + /** * Provides the ability to change the generic line ending * NOTE: The default remains '\n'. We force CRLF where we KNOW * it must be used via self::CRLF @@ -390,7 +416,7 @@ class PHPMailer * Sets the PHPMailer Version number * @var string */ - public $Version = '5.2.4'; + public $Version = '5.2.6'; /** * What to use in the X-Mailer header @@ -406,82 +432,82 @@ class PHPMailer * @var SMTP An instance of the SMTP sender class * @access protected */ - protected $smtp = null; + protected $smtp = null; /** * @var array An array of 'to' addresses * @access protected */ - protected $to = array(); + protected $to = array(); /** * @var array An array of 'cc' addresses * @access protected */ - protected $cc = array(); + protected $cc = array(); /** * @var array An array of 'bcc' addresses * @access protected */ - protected $bcc = array(); + protected $bcc = array(); /** * @var array An array of reply-to name and address * @access protected */ - protected $ReplyTo = array(); + protected $ReplyTo = array(); /** * @var array An array of all kinds of addresses: to, cc, bcc, replyto * @access protected */ - protected $all_recipients = array(); + protected $all_recipients = array(); /** * @var array An array of attachments * @access protected */ - protected $attachment = array(); + protected $attachment = array(); /** * @var array An array of custom headers * @access protected */ - protected $CustomHeader = array(); + protected $CustomHeader = array(); /** * @var string The message's MIME type * @access protected */ - protected $message_type = ''; + protected $message_type = ''; /** * @var array An array of MIME boundary strings * @access protected */ - protected $boundary = array(); + protected $boundary = array(); /** * @var array An array of available languages * @access protected */ - protected $language = array(); + protected $language = array(); /** * @var integer The number of errors encountered * @access protected */ - protected $error_count = 0; + protected $error_count = 0; /** * @var string The filename of a DKIM certificate file * @access protected */ - protected $sign_cert_file = ''; + protected $sign_cert_file = ''; /** * @var string The filename of a DKIM key file * @access protected */ - protected $sign_key_file = ''; + protected $sign_key_file = ''; /** * @var string The password of a DKIM key * @access protected */ - protected $sign_key_pass = ''; + protected $sign_key_pass = ''; /** * @var boolean Whether to throw exceptions for errors * @access protected */ - protected $exceptions = false; + protected $exceptions = false; ///////////////////////////////////////////////// // CONSTANTS @@ -509,14 +535,12 @@ class PHPMailer * @access private * @return bool */ - private function mail_passthru($to, $subject, $body, $header, $params) - { + private function mail_passthru($to, $subject, $body, $header, $params) { if ( ini_get('safe_mode') || !($this->UseSendmailOptions) ) { $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); } else { $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); } - return $rt; } @@ -524,11 +548,18 @@ class PHPMailer * Outputs debugging info via user-defined method * @param string $str */ - private function edebug($str) - { - if ($this->Debugoutput == "error_log") { + protected function edebug($str) { + switch ($this->Debugoutput) { + case 'error_log': error_log($str); - } else { + break; + case 'html': + //Cleans up output a bit for a better looking display that's HTML-safe + echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."
\n"; + break; + case 'echo': + default: + //Just echoes exactly what was received echo $str; } } @@ -537,18 +568,25 @@ class PHPMailer * Constructor * @param boolean $exceptions Should we throw external exceptions? */ - public function __construct($exceptions = false) - { + public function __construct($exceptions = false) { $this->exceptions = ($exceptions == true); } + /** + * Destructor + */ + public function __destruct() { + if ($this->Mailer == 'smtp') { //Close any open SMTP connection nicely + $this->SmtpClose(); + } + } + /** * Sets message type to HTML. * @param bool $ishtml * @return void */ - public function IsHTML($ishtml = true) - { + public function IsHTML($ishtml = true) { if ($ishtml) { $this->ContentType = 'text/html'; } else { @@ -560,8 +598,7 @@ class PHPMailer * Sets Mailer to send message using SMTP. * @return void */ - public function IsSMTP() - { + public function IsSMTP() { $this->Mailer = 'smtp'; } @@ -569,8 +606,7 @@ class PHPMailer * Sets Mailer to send message using PHP mail() function. * @return void */ - public function IsMail() - { + public function IsMail() { $this->Mailer = 'mail'; } @@ -578,8 +614,7 @@ class PHPMailer * Sets Mailer to send message using the $Sendmail program. * @return void */ - public function IsSendmail() - { + public function IsSendmail() { if (!stristr(ini_get('sendmail_path'), 'sendmail')) { $this->Sendmail = '/var/qmail/bin/sendmail'; } @@ -590,8 +625,7 @@ class PHPMailer * Sets Mailer to send message using the qmail MTA. * @return void */ - public function IsQmail() - { + public function IsQmail() { if (stristr(ini_get('sendmail_path'), 'qmail')) { $this->Sendmail = '/var/qmail/bin/sendmail'; } @@ -608,8 +642,7 @@ class PHPMailer * @param string $name * @return boolean true on success, false if address already used */ - public function AddAddress($address, $name = '') - { + public function AddAddress($address, $name = '') { return $this->AddAnAddress('to', $address, $name); } @@ -620,8 +653,7 @@ class PHPMailer * @param string $name * @return boolean true on success, false if address already used */ - public function AddCC($address, $name = '') - { + public function AddCC($address, $name = '') { return $this->AddAnAddress('cc', $address, $name); } @@ -632,8 +664,7 @@ class PHPMailer * @param string $name * @return boolean true on success, false if address already used */ - public function AddBCC($address, $name = '') - { + public function AddBCC($address, $name = '') { return $this->AddAnAddress('bcc', $address, $name); } @@ -643,8 +674,7 @@ class PHPMailer * @param string $name * @return boolean */ - public function AddReplyTo($address, $name = '') - { + public function AddReplyTo($address, $name = '') { return $this->AddAnAddress('Reply-To', $address, $name); } @@ -658,8 +688,7 @@ class PHPMailer * @return boolean true on success, false if address already used or invalid in some way * @access protected */ - protected function AddAnAddress($kind, $address, $name = '') - { + protected function AddAnAddress($kind, $address, $name = '') { if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { $this->SetError($this->Lang('Invalid recipient array').': '.$kind); if ($this->exceptions) { @@ -668,7 +697,6 @@ class PHPMailer if ($this->SMTPDebug) { $this->edebug($this->Lang('Invalid recipient array').': '.$kind); } - return false; } $address = trim($address); @@ -681,37 +709,32 @@ class PHPMailer if ($this->SMTPDebug) { $this->edebug($this->Lang('invalid_address').': '.$address); } - return false; } if ($kind != 'Reply-To') { if (!isset($this->all_recipients[strtolower($address)])) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; - return true; } } else { if (!array_key_exists(strtolower($address), $this->ReplyTo)) { $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; } } - return false; } -/** - * Set the From and FromName properties - * @param string $address - * @param string $name - * @param int $auto Also set Reply-To and Sender + /** + * Set the From and FromName properties + * @param string $address + * @param string $name + * @param boolean $auto Whether to also set the Sender address, defaults to true * @throws phpmailerException - * @return boolean - */ - public function SetFrom($address, $name = '', $auto = 1) - { + * @return boolean + */ + public function SetFrom($address, $name = '', $auto = true) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (!$this->ValidateAddress($address)) { @@ -722,20 +745,15 @@ class PHPMailer if ($this->SMTPDebug) { $this->edebug($this->Lang('invalid_address').': '.$address); } - return false; } $this->From = $address; $this->FromName = $name; if ($auto) { - if (empty($this->ReplyTo)) { - $this->AddAnAddress('Reply-To', $address, $name); - } if (empty($this->Sender)) { $this->Sender = $address; } } - return true; } @@ -745,8 +763,6 @@ class PHPMailer * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to * not allow a@b type valid addresses :( - * Some Versions of PHP break on the regex though, likely due to PCRE, so use - * the older validation method for those users. (http://php.net/manual/en/pcre.installation.php) * @link http://squiloople.com/2009/12/20/email-address-validation/ * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice. * @param string $address The email address to check @@ -754,19 +770,19 @@ class PHPMailer * @static * @access public */ - public static function ValidateAddress($address) - { - if ((defined('PCRE_VERSION')) && (version_compare(PCRE_VERSION, '8.0') >= 0)) { - return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[ ])+|(?>[ ]*\x0D\x0A)?[ ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){7,})((?6)(?>:(?6)){0,5})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){5,})(?8)?::(?>((?6)(?>:(?6)){0,3}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); - } elseif (function_exists('filter_var')) { //Introduced in PHP 5.2 - if (filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { - return false; - } else { - return true; - } - } else { - return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); - } + public static function ValidateAddress($address) { + if (defined('PCRE_VERSION')) { //Check this instead of extension_loaded so it works when that function is disabled + if (version_compare(PCRE_VERSION, '8.0') >= 0) { + return (boolean)preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); + } else { + //Fall back to an older regex that doesn't need a recent PCRE + return (boolean)preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address); + } + } else { + //No PCRE! Do something _very_ approximate! + //Check the address is 3 chars or longer and contains an @ that's not the first or last char + return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1); + } } ///////////////////////////////////////////////// @@ -780,11 +796,9 @@ class PHPMailer * @throws phpmailerException * @return bool */ - public function Send() - { + public function Send() { try { if(!$this->PreSend()) return false; - return $this->PostSend(); } catch (phpmailerException $e) { $this->mailHeader = ''; @@ -792,7 +806,6 @@ class PHPMailer if ($this->exceptions) { throw $e; } - return false; } } @@ -802,8 +815,7 @@ class PHPMailer * @throws phpmailerException * @return bool */ - public function PreSend() - { + public function PreSend() { try { $this->mailHeader = ""; if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { @@ -811,14 +823,14 @@ class PHPMailer } // Set whether the message is multipart/alternative - if (!empty($this->AltBody)) { + if(!empty($this->AltBody)) { $this->ContentType = 'multipart/alternative'; } $this->error_count = 0; // reset errors $this->SetMessageType(); - //Refuse to send an empty message - if (empty($this->Body)) { + //Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); } @@ -834,14 +846,11 @@ class PHPMailer $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); } $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); - // if (count($this->cc) > 0) { - // $this->mailHeader .= $this->AddrAppend("Cc", $this->cc); - // } } // digitally sign with DKIM if enabled if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) { - $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); + $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; } @@ -852,7 +861,6 @@ class PHPMailer if ($this->exceptions) { throw $e; } - return false; } } @@ -863,11 +871,10 @@ class PHPMailer * @throws phpmailerException * @return bool */ - public function PostSend() - { + public function PostSend() { try { // Choose the mailer and send through it - switch ($this->Mailer) { + switch($this->Mailer) { case 'sendmail': return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': @@ -886,7 +893,6 @@ class PHPMailer $this->edebug($e->getMessage()."\n"); } } - return false; } @@ -898,8 +904,7 @@ class PHPMailer * @access protected * @return bool */ - protected function SendmailSend($header, $body) - { + protected function SendmailSend($header, $body) { if ($this->Sender != '') { $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } else { @@ -907,7 +912,7 @@ class PHPMailer } if ($this->SingleTo === true) { foreach ($this->SingleToArray as $val) { - if (!@$mail = popen($sendmail, 'w')) { + if(!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fputs($mail, "To: " . $val . "\n"); @@ -917,12 +922,12 @@ class PHPMailer // implement call back function if it exists $isSent = ($result == 0) ? 1 : 0; $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); - if ($result != 0) { + if($result != 0) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } } else { - if (!@$mail = popen($sendmail, 'w')) { + if(!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fputs($mail, $header); @@ -931,11 +936,10 @@ class PHPMailer // implement call back function if it exists $isSent = ($result == 0) ? 1 : 0; $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); - if ($result != 0) { + if($result != 0) { throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } - return true; } @@ -943,22 +947,21 @@ class PHPMailer * Sends mail using the PHP mail() function. * @param string $header The message headers * @param string $body The message body - * @throws phpmailerException + * @throws phpmailerException * @access protected * @return bool */ - protected function MailSend($header, $body) - { + protected function MailSend($header, $body) { $toArr = array(); - foreach ($this->to as $t) { + foreach($this->to as $t) { $toArr[] = $this->AddrFormat($t); } $to = implode(', ', $toArr); if (empty($this->Sender)) { - $params = "-oi "; + $params = " "; } else { - $params = sprintf("-oi -f%s", $this->Sender); + $params = sprintf("-f%s", $this->Sender); } if ($this->Sender != '' and !ini_get('safe_mode')) { $old_from = ini_get('sendmail_from'); @@ -981,10 +984,9 @@ class PHPMailer if (isset($old_from)) { ini_set('sendmail_from', $old_from); } - if (!$rt) { + if(!$rt) { throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); } - return true; } @@ -998,22 +1000,21 @@ class PHPMailer * @access protected * @return bool */ - protected function SmtpSend($header, $body) - { + protected function SmtpSend($header, $body) { require_once $this->PluginDir . 'class.smtp.php'; $bad_rcpt = array(); - if (!$this->SmtpConnect()) { + if(!$this->SmtpConnect()) { throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); } $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; - if (!$this->smtp->Mail($smtp_from)) { - $this->SetError($this->Lang('from_failed') . $smtp_from . " : " . implode(",",$this->smtp->getError())) ; + if(!$this->smtp->Mail($smtp_from)) { + $this->SetError($this->Lang('from_failed') . $smtp_from . ' : ' .implode(',', $this->smtp->getError())); throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } // Attempt to send attach all recipients - foreach ($this->to as $to) { + foreach($this->to as $to) { if (!$this->smtp->Recipient($to[0])) { $bad_rcpt[] = $to[0]; // implement call back function if it exists @@ -1025,7 +1026,7 @@ class PHPMailer $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } } - foreach ($this->cc as $cc) { + foreach($this->cc as $cc) { if (!$this->smtp->Recipient($cc[0])) { $bad_rcpt[] = $cc[0]; // implement call back function if it exists @@ -1037,7 +1038,7 @@ class PHPMailer $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } } - foreach ($this->bcc as $bcc) { + foreach($this->bcc as $bcc) { if (!$this->smtp->Recipient($bcc[0])) { $bad_rcpt[] = $bcc[0]; // implement call back function if it exists @@ -1055,99 +1056,100 @@ class PHPMailer $badaddresses = implode(', ', $bad_rcpt); throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); } - if (!$this->smtp->Data($header . $body)) { + if(!$this->smtp->Data($header . $body)) { throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); } - if ($this->SMTPKeepAlive == true) { + if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); } else { $this->smtp->Quit(); $this->smtp->Close(); } - return true; } /** * Initiates a connection to an SMTP server. * Returns false if the operation failed. + * @param array $options An array of options compatible with stream_context_create() * @uses SMTP * @access public * @throws phpmailerException * @return bool */ - public function SmtpConnect() - { - if (is_null($this->smtp)) { + public function SmtpConnect($options = array()) { + if(is_null($this->smtp)) { $this->smtp = new SMTP; } + //Already connected? + if ($this->smtp->Connected()) { + return true; + } + $this->smtp->Timeout = $this->Timeout; $this->smtp->do_debug = $this->SMTPDebug; - $hosts = explode(';', $this->Host); + $this->smtp->Debugoutput = $this->Debugoutput; + $this->smtp->do_verp = $this->do_verp; $index = 0; - $connection = $this->smtp->Connected(); + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); + $hosts = explode(';', $this->Host); + $lastexception = null; - // Retry while there is no connection - try { - while ($index < count($hosts) && !$connection) { - $hostinfo = array(); - if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } - - $tls = ($this->SMTPSecure == 'tls'); - $ssl = ($this->SMTPSecure == 'ssl'); - - if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { - - $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); + foreach ($hosts as $hostentry) { + $hostinfo = array(); + $host = $hostentry; + $port = $this->Port; + if (preg_match('/^(.+):([0-9]+)$/', $hostentry, $hostinfo)) { //If $hostentry contains 'address:port', override default + $host = $hostinfo[1]; + $port = $hostinfo[2]; + } + if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->ServerHostname(); + } $this->smtp->Hello($hello); if ($tls) { if (!$this->smtp->StartTLS()) { throw new phpmailerException($this->Lang('connect_host')); } - //We must resend HELO after tls negotiation $this->smtp->Hello($hello); } - - $connection = true; if ($this->SMTPAuth) { - if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, - $this->Realm, $this->Workstation)) { + if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation)) { throw new phpmailerException($this->Lang('authenticate')); } } + return true; + } catch (phpmailerException $e) { + $lastexception = $e; + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->Quit(); } - $index++; - if (!$connection) { - throw new phpmailerException($this->Lang('connect_host')); - } - } - } catch (phpmailerException $e) { - $this->smtp->Reset(); - if ($this->exceptions) { - throw $e; } } - - return true; + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->Close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; } /** * Closes the active SMTP session if one exists. * @return void */ - public function SmtpClose() - { + public function SmtpClose() { if ($this->smtp !== null) { - if ($this->smtp->Connected()) { + if($this->smtp->Connected()) { $this->smtp->Quit(); $this->smtp->Close(); } @@ -1155,15 +1157,14 @@ class PHPMailer } /** - * Sets the language for all class error messages. - * Returns false if it cannot load the language file. The default language is English. - * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory + * Sets the language for all class error messages. + * Returns false if it cannot load the language file. The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") + * @param string $lang_path Path to the language file directory * @return bool - * @access public - */ - public function SetLanguage($langcode = 'en', $lang_path = 'language/') - { + * @access public + */ + function SetLanguage($langcode = 'en', $lang_path = 'language/') { //Define full set of translatable strings $PHPMAILER_LANG = array( 'authenticate' => 'SMTP Error: Could not authenticate.', @@ -1191,7 +1192,6 @@ class PHPMailer $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; } $this->language = $PHPMAILER_LANG; - return ($l == true); //Returns false if language not found } @@ -1199,8 +1199,7 @@ class PHPMailer * Return the current array of language strings * @return array */ - public function GetTranslations() - { + public function GetTranslations() { return $this->language; } @@ -1215,8 +1214,7 @@ class PHPMailer * @param array $addr * @return string */ - public function AddrAppend($type, $addr) - { + public function AddrAppend($type, $addr) { $addr_str = $type . ': '; $addresses = array(); foreach ($addr as $a) { @@ -1234,8 +1232,7 @@ class PHPMailer * @param string $addr * @return string */ - public function AddrFormat($addr) - { + public function AddrFormat($addr) { if (empty($addr[1])) { return $this->SecureHeader($addr[0]); } else { @@ -1253,8 +1250,7 @@ class PHPMailer * @access public * @return string */ - public function WrapText($message, $length, $qp_mode = false) - { + public function WrapText($message, $length, $qp_mode = false) { $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap @@ -1296,6 +1292,9 @@ class PHPMailer $buf = ''; } while (strlen($word) > 0) { + if ($length <= 0) { + break; + } $len = $length; if ($is_utf8) { $len = $this->UTF8CharBoundary($word, $len); @@ -1338,8 +1337,7 @@ class PHPMailer * @param int $maxLength find last character boundary prior to this length * @return int */ - public function UTF8CharBoundary($encodedText, $maxLength) - { + public function UTF8CharBoundary($encodedText, $maxLength) { $foundSplitPos = false; $lookBack = 3; while (!$foundSplitPos) { @@ -1368,7 +1366,6 @@ class PHPMailer $foundSplitPos = true; } } - return $maxLength; } @@ -1378,13 +1375,12 @@ class PHPMailer * @access public * @return void */ - public function SetWordWrap() - { - if ($this->WordWrap < 1) { + public function SetWordWrap() { + if($this->WordWrap < 1) { return; } - switch ($this->message_type) { + switch($this->message_type) { case 'alt': case 'alt_inline': case 'alt_attach': @@ -1402,8 +1398,7 @@ class PHPMailer * @access public * @return string The assembled header */ - public function CreateHeader() - { + public function CreateHeader() { $result = ''; // Set the boundaries @@ -1419,21 +1414,21 @@ class PHPMailer } if ($this->ReturnPath) { - $result .= $this->HeaderLine('Return-Path', trim($this->ReturnPath)); + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->ReturnPath).'>'); } elseif ($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', trim($this->From)); + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->From).'>'); } else { - $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); + $result .= $this->HeaderLine('Return-Path', '<'.trim($this->Sender).'>'); } // To be created automatically by mail() - if ($this->Mailer != 'mail') { + if($this->Mailer != 'mail') { if ($this->SingleTo === true) { - foreach ($this->to as $t) { + foreach($this->to as $t) { $this->SingleToArray[] = $this->AddrFormat($t); } } else { - if (count($this->to) > 0) { + if(count($this->to) > 0) { $result .= $this->AddrAppend('To', $this->to); } elseif (count($this->cc) == 0) { $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); @@ -1447,32 +1442,32 @@ class PHPMailer $result .= $this->AddrAppend('From', $from); // sendmail and mail() extract Cc from the header before sending - if (count($this->cc) > 0) { + if(count($this->cc) > 0) { $result .= $this->AddrAppend('Cc', $this->cc); } // sendmail and mail() extract Bcc from the header before sending - if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { + if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { $result .= $this->AddrAppend('Bcc', $this->bcc); } - if (count($this->ReplyTo) > 0) { + if(count($this->ReplyTo) > 0) { $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); } // mail() sets the subject itself - if ($this->Mailer != 'mail') { + if($this->Mailer != 'mail') { $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); } - if ($this->MessageID != '') { + if($this->MessageID != '') { $result .= $this->HeaderLine('Message-ID', $this->MessageID); } else { $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); } $result .= $this->HeaderLine('X-Priority', $this->Priority); if ($this->XMailer == '') { - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)'); } else { $myXmailer = trim($this->XMailer); if ($myXmailer) { @@ -1480,12 +1475,12 @@ class PHPMailer } } - if ($this->ConfirmReadingTo != '') { + if($this->ConfirmReadingTo != '') { $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); } // Add custom headers - for ($index = 0; $index < count($this->CustomHeader); $index++) { + for($index = 0; $index < count($this->CustomHeader); $index++) { $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); } if (!$this->sign_key_file) { @@ -1501,34 +1496,36 @@ class PHPMailer * @access public * @return string */ - public function GetMailMIME() - { + public function GetMailMIME() { $result = ''; - switch ($this->message_type) { + switch($this->message_type) { case 'inline': $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; case 'attach': case 'inline_attach': case 'alt_attach': case 'alt_inline_attach': $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; case 'alt': case 'alt_inline': $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"'); break; default: // Catches case 'plain': and case '': - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet); break; } + //RFC1341 part 5 says 7bit is assumed if not specified + if ($this->Encoding != '7bit') { + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); + } - if ($this->Mailer != 'mail') { + if($this->Mailer != 'mail') { $result .= $this->LE; } @@ -1540,19 +1537,18 @@ class PHPMailer * @access public * @return string */ - public function GetSentMIMEMessage() - { + public function GetSentMIMEMessage() { return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; } + /** * Assembles the message body. Returns an empty string on failure. * @access public * @throws phpmailerException * @return string The assembled message body */ - public function CreateBody() - { + public function CreateBody() { $body = ''; if ($this->sign_key_file) { @@ -1561,30 +1557,30 @@ class PHPMailer $this->SetWordWrap(); - switch ($this->message_type) { + switch($this->message_type) { case 'inline': $body .= $this->GetBoundary($this->boundary[1], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[1]); + $body .= $this->AttachAll('inline', $this->boundary[1]); break; case 'attach': $body .= $this->GetBoundary($this->boundary[1], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', '', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->AttachAll('inline', $this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'alt': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); @@ -1593,27 +1589,32 @@ class PHPMailer $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; + if(!empty($this->Ical)) { + $body .= $this->GetBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); + $body .= $this->EncodeString($this->Ical, $this->Encoding); + $body .= $this->LE.$this->LE; + } $body .= $this->EndBoundary($this->boundary[1]); break; case 'alt_inline': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->AttachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->EndBoundary($this->boundary[1]); break; case 'alt_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); @@ -1623,28 +1624,28 @@ class PHPMailer $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; case 'alt_inline_attach': - $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->TextLine('--' . $this->boundary[1]); $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->TextLine("--" . $this->boundary[2]); + $body .= $this->TextLine('--' . $this->boundary[2]); $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); - $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3].'"'); $body .= $this->LE; $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->AttachAll("inline", $this->boundary[3]); + $body .= $this->AttachAll('inline', $this->boundary[3]); $body .= $this->LE; $body .= $this->EndBoundary($this->boundary[2]); $body .= $this->LE; - $body .= $this->AttachAll("attachment", $this->boundary[1]); + $body .= $this->AttachAll('attachment', $this->boundary[1]); break; default: // catch case 'plain' and case '' @@ -1656,17 +1657,20 @@ class PHPMailer $body = ''; } elseif ($this->sign_key_file) { try { - $file = tempnam('', 'mail'); + if (!defined('PKCS7_TEXT')) { + throw new phpmailerException($this->Lang('signing').' OpenSSL extension missing.'); + } + $file = tempnam(sys_get_temp_dir(), 'mail'); file_put_contents($file, $body); //TODO check this worked - $signed = tempnam("", "signed"); - if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { + $signed = tempnam(sys_get_temp_dir(), 'signed'); + if (@openssl_pkcs7_sign($file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null)) { @unlink($file); $body = file_get_contents($signed); @unlink($signed); } else { @unlink($file); @unlink($signed); - throw new phpmailerException($this->Lang("signing").openssl_error_string()); + throw new phpmailerException($this->Lang('signing').openssl_error_string()); } } catch (phpmailerException $e) { $body = ''; @@ -1675,7 +1679,6 @@ class PHPMailer } } } - return $body; } @@ -1688,16 +1691,15 @@ class PHPMailer * @param string $encoding * @return string */ - protected function GetBoundary($boundary, $charSet, $contentType, $encoding) - { + protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; - if ($charSet == '') { + if($charSet == '') { $charSet = $this->CharSet; } - if ($contentType == '') { + if($contentType == '') { $contentType = $this->ContentType; } - if ($encoding == '') { + if($encoding == '') { $encoding = $this->Encoding; } $result .= $this->TextLine('--' . $boundary); @@ -1715,8 +1717,7 @@ class PHPMailer * @param string $boundary * @return string */ - protected function EndBoundary($boundary) - { + protected function EndBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } @@ -1725,8 +1726,7 @@ class PHPMailer * @access protected * @return void */ - protected function SetMessageType() - { + protected function SetMessageType() { $this->message_type = array(); if($this->AlternativeExists()) $this->message_type[] = "alt"; if($this->InlineImageExists()) $this->message_type[] = "inline"; @@ -1736,14 +1736,13 @@ class PHPMailer } /** - * Returns a formatted header line. + * Returns a formatted header line. * @access public * @param string $name * @param string $value * @return string */ - public function HeaderLine($name, $value) - { + public function HeaderLine($name, $value) { return $name . ': ' . $value . $this->LE; } @@ -1753,8 +1752,7 @@ class PHPMailer * @param string $value * @return string */ - public function TextLine($value) - { + public function TextLine($value) { return $value . $this->LE; } @@ -1773,14 +1771,19 @@ class PHPMailer * @throws phpmailerException * @return bool */ - public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') - { + public function AddAttachment($path, $name = '', $encoding = 'base64', $type = '') { try { if ( !@is_file($path) ) { throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); } + + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + $filename = basename($path); - if ($name == '') { + if ( $name == '' ) { $name = $filename; } @@ -1803,11 +1806,8 @@ class PHPMailer if ($this->SMTPDebug) { $this->edebug($e->getMessage()."\n"); } - if ( $e->getCode() == self::STOP_CRITICAL ) { - return false; - } + return false; } - return true; } @@ -1815,8 +1815,7 @@ class PHPMailer * Return the current array of attachments * @return array */ - public function GetAttachments() - { + public function GetAttachments() { return $this->attachment; } @@ -1828,8 +1827,7 @@ class PHPMailer * @param string $boundary * @return string */ - protected function AttachAll($disposition_type, $boundary) - { + protected function AttachAll($disposition_type, $boundary) { // Return text of body $mime = array(); $cidUniq = array(); @@ -1838,7 +1836,7 @@ class PHPMailer // Add all attachments foreach ($this->attachment as $attachment) { // CHECK IF IT IS A VALID DISPOSITION_FILTER - if ($attachment[6] == $disposition_type) { + if($attachment[6] == $disposition_type) { // Check for string attachment $string = ''; $path = ''; @@ -1865,22 +1863,28 @@ class PHPMailer $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - if ($disposition == 'inline') { + if($disposition == 'inline') { $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); } - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + //If a filename contains any of these chars, it should be quoted, but not otherwise: RFC2183 & RFC2045 5.1 + //Fixes a warning in IETF's msglint MIME checker + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + } else { + $mime[] = sprintf("Content-Disposition: %s; filename=%s%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + } // Encode as string attachment - if ($bString) { + if($bString) { $mime[] = $this->EncodeString($string, $encoding); - if ($this->IsError()) { + if($this->IsError()) { return ''; } $mime[] = $this->LE.$this->LE; } else { $mime[] = $this->EncodeFile($path, $encoding); - if ($this->IsError()) { + if($this->IsError()) { return ''; } $mime[] = $this->LE.$this->LE; @@ -1903,17 +1907,11 @@ class PHPMailer * @access protected * @return string */ - protected function EncodeFile($path, $encoding = 'base64') - { + protected function EncodeFile($path, $encoding = 'base64') { try { if (!is_readable($path)) { throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); } - // if (!function_exists('get_magic_quotes')) { - // function get_magic_quotes() { - // return false; - // } - // } $magic_quotes = get_magic_quotes_runtime(); if ($magic_quotes) { if (version_compare(PHP_VERSION, '5.3.0', '<')) { @@ -1931,11 +1929,9 @@ class PHPMailer ini_set('magic_quotes_runtime', $magic_quotes); } } - return $file_buffer; } catch (Exception $e) { $this->SetError($e->getMessage()); - return ''; } } @@ -1948,10 +1944,9 @@ class PHPMailer * @access public * @return string */ - public function EncodeString($str, $encoding = 'base64') - { + public function EncodeString($str, $encoding = 'base64') { $encoded = ''; - switch (strtolower($encoding)) { + switch(strtolower($encoding)) { case 'base64': $encoded = chunk_split(base64_encode($str), 76, $this->LE); break; @@ -1972,7 +1967,6 @@ class PHPMailer $this->SetError($this->Lang('encoding') . $encoding); break; } - return $encoded; } @@ -1983,8 +1977,7 @@ class PHPMailer * @param string $position * @return string */ - public function EncodeHeader($str, $position = 'text') - { + public function EncodeHeader($str, $position = 'text') { $x = 0; switch (strtolower($position)) { @@ -2009,13 +2002,13 @@ class PHPMailer break; } - if ($x == 0) { + if ($x == 0) { //There are no chars that need encoding return ($str); } $maxlen = 75 - 7 - strlen($this->CharSet); // Try to select the encoding which should produce the shortest output - if (strlen($str)/3 < $x) { + if ($x > strlen($str)/3) { //More than a third of the content will need encoding, so B encoding will be most efficient $encoding = 'B'; if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { // Use a custom function which correctly encodes and wraps long @@ -2045,12 +2038,10 @@ class PHPMailer * @param string $str multi-byte text to wrap encode * @return bool */ - public function HasMultiBytes($str) - { + public function HasMultiBytes($str) { if (function_exists('mb_strlen')) { return (strlen($str) > mb_strlen($str, $this->CharSet)); } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return false; } } @@ -2064,8 +2055,7 @@ class PHPMailer * @param string $lf string to use as linefeed/end-of-line * @return string */ - public function Base64EncodeWrapMB($str, $lf=null) - { + public function Base64EncodeWrapMB($str, $lf=null) { $start = "=?".$this->CharSet."?B?"; $end = "?="; $encoded = ""; @@ -2089,105 +2079,46 @@ class PHPMailer $chunk = mb_substr($str, $i, $offset, $this->CharSet); $chunk = base64_encode($chunk); $lookBack++; - } while (strlen($chunk) > $length); + } + while (strlen($chunk) > $length); $encoded .= $chunk . $lf; } // Chomp the last linefeed $encoded = substr($encoded, 0, -strlen($lf)); - return $encoded; } /** - * Encode string to quoted-printable. - * Only uses standard PHP, slow, but will always work - * @access public - * @param string $input - * @param integer $line_max Number of chars allowed on a line before wrapping - * @param bool $space_conv - * @internal param string $string the text to encode - * @return string - */ - public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) - { - $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); - $lines = preg_split('/(?:\r\n|\r|\n)/', $input); - $eol = "\r\n"; - $escape = '='; - $output = ''; - while ( list(, $line) = each($lines) ) { - $linlen = strlen($line); - $newline = ''; - for ($i = 0; $i < $linlen; $i++) { - $c = substr( $line, $i, 1 ); - $dec = ord( $c ); - if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E - $c = '=2E'; - } - if ($dec == 32) { - if ( $i == ( $linlen - 1 ) ) { // convert space at eol only - $c = '=20'; - } elseif ($space_conv) { - $c = '=20'; - } - } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required - $h2 = (integer) floor($dec/16); - $h1 = (integer) floor($dec%16); - $c = $escape.$hex[$h2].$hex[$h1]; - } - if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted - $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay - $newline = ''; - // check if newline first character will be point or not - if ($dec == 46) { - $c = '=2E'; - } - } - $newline .= $c; - } // end of for - $output .= $newline.$eol; - } // end of while - - return $output; + * Encode string to RFC2045 (6.7) quoted-printable format + * @access public + * @param string $string The text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 + */ + public function EncodeQP($string, $line_max = 76) { + if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) + return quoted_printable_encode($string); + } + //Fall back to a pure PHP implementation + $string = str_replace(array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string)); + $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string); + return $string; } /** - * Encode string to RFC2045 (6.7) quoted-printable format - * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version - * Also results in same content as you started with after decoding - * @see EncodeQPphp() - * @access public - * @param string $string the text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function - * @return string - * @author Marcus Bointon - */ - public function EncodeQP($string, $line_max = 76, $space_conv = false) - { - if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) - - return quoted_printable_encode($string); - } - $filters = stream_get_filters(); - if (!in_array('convert.*', $filters)) { //Got convert stream filter? - - return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation - } - $fp = fopen('php://temp/', 'r+'); - $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks - $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); - $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); - fputs($fp, $string); - rewind($fp); - $out = stream_get_contents($fp); - stream_filter_remove($s); - $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange - fclose($fp); - - return $out; + * Wrapper to preserve BC for old QP encoding function that was removed + * @see EncodeQP() + * @access public + * @param string $string + * @param integer $line_max + * @param bool $space_conv + * @return string + */ + public function EncodeQPphp($string, $line_max = 76, $space_conv = false) { + return $this->EncodeQP($string, $line_max); } /** @@ -2198,10 +2129,9 @@ class PHPMailer * @access public * @return string */ - public function EncodeQ($str, $position = 'text') - { + public function EncodeQ($str, $position = 'text') { //There should not be any EOL in the string - $pattern=""; + $pattern = ''; $encoded = str_replace(array("\r", "\n"), '', $str); switch (strtolower($position)) { case 'phrase': @@ -2210,8 +2140,8 @@ class PHPMailer case 'comment': $pattern = '\(\)"'; - //note that we dont break here! - //for this reason we build the $pattern withoud including delimiters and [] + //note that we don't break here! + //for this reason we build the $pattern without including delimiters and [] case 'text': default: @@ -2242,8 +2172,11 @@ class PHPMailer * @param string $type File extension (MIME) type. * @return void */ - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') - { + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = '') { + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($filename); + } // Append to $attachment array $this->attachment[] = array( 0 => $string, @@ -2258,28 +2191,29 @@ class PHPMailer } /** - * Adds an embedded attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". + * Add an embedded attachment from a file. + * This can include images, sounds, and just about any other document type. * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool + * @param string $type File MIME type. + * @return bool True on successfully adding an attachment */ - public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') - { + public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '') { if ( !@is_file($path) ) { $this->SetError($this->Lang('file_access') . $path); - return false; } + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + $filename = basename($path); - if ($name == '') { + if ( $name == '' ) { $name = $filename; } @@ -2294,25 +2228,29 @@ class PHPMailer 6 => 'inline', 7 => $cid ); - return true; } + /** - * Adds an embedded stringified attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". - * @param string $string The attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. - * @param string $name Overrides the attachment name. + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * Be sure to set the $type to an image type for images: + * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool + * @param string $type MIME type. + * @return bool True on successfully adding an attachment */ - public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') - { + public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = '') { + //If a MIME type is not specified, try to work it out from the name + if ($type == '') { + $type = self::filenameToType($name); + } + // Append to $attachment array $this->attachment[] = array( 0 => $string, @@ -2324,6 +2262,7 @@ class PHPMailer 6 => 'inline', 7 => $cid ); + return true; } /** @@ -2331,14 +2270,12 @@ class PHPMailer * @access public * @return bool */ - public function InlineImageExists() - { - foreach ($this->attachment as $attachment) { + public function InlineImageExists() { + foreach($this->attachment as $attachment) { if ($attachment[6] == 'inline') { return true; } } - return false; } @@ -2346,14 +2283,12 @@ class PHPMailer * Returns true if an attachment (non-inline) is present. * @return bool */ - public function AttachmentExists() - { - foreach ($this->attachment as $attachment) { + public function AttachmentExists() { + foreach($this->attachment as $attachment) { if ($attachment[6] == 'attachment') { return true; } } - return false; } @@ -2361,8 +2296,7 @@ class PHPMailer * Does this message have an alternative body set? * @return bool */ - public function AlternativeExists() - { + public function AlternativeExists() { return !empty($this->AltBody); } @@ -2374,9 +2308,8 @@ class PHPMailer * Clears all recipients assigned in the TO array. Returns void. * @return void */ - public function ClearAddresses() - { - foreach ($this->to as $to) { + public function ClearAddresses() { + foreach($this->to as $to) { unset($this->all_recipients[strtolower($to[0])]); } $this->to = array(); @@ -2386,9 +2319,8 @@ class PHPMailer * Clears all recipients assigned in the CC array. Returns void. * @return void */ - public function ClearCCs() - { - foreach ($this->cc as $cc) { + public function ClearCCs() { + foreach($this->cc as $cc) { unset($this->all_recipients[strtolower($cc[0])]); } $this->cc = array(); @@ -2398,9 +2330,8 @@ class PHPMailer * Clears all recipients assigned in the BCC array. Returns void. * @return void */ - public function ClearBCCs() - { - foreach ($this->bcc as $bcc) { + public function ClearBCCs() { + foreach($this->bcc as $bcc) { unset($this->all_recipients[strtolower($bcc[0])]); } $this->bcc = array(); @@ -2410,8 +2341,7 @@ class PHPMailer * Clears all recipients assigned in the ReplyTo array. Returns void. * @return void */ - public function ClearReplyTos() - { + public function ClearReplyTos() { $this->ReplyTo = array(); } @@ -2420,8 +2350,7 @@ class PHPMailer * array. Returns void. * @return void */ - public function ClearAllRecipients() - { + public function ClearAllRecipients() { $this->to = array(); $this->cc = array(); $this->bcc = array(); @@ -2433,8 +2362,7 @@ class PHPMailer * attachments. Returns void. * @return void */ - public function ClearAttachments() - { + public function ClearAttachments() { $this->attachment = array(); } @@ -2442,8 +2370,7 @@ class PHPMailer * Clears all custom headers. Returns void. * @return void */ - public function ClearCustomHeaders() - { + public function ClearCustomHeaders() { $this->CustomHeader = array(); } @@ -2457,8 +2384,7 @@ class PHPMailer * @param string $msg * @return void */ - protected function SetError($msg) - { + protected function SetError($msg) { $this->error_count++; if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { $lasterror = $this->smtp->getError(); @@ -2475,15 +2401,11 @@ class PHPMailer * @return string * @static */ - public static function RFCDate() - { - $tz = date('Z'); - $tzs = ($tz < 0) ? '-' : '+'; - $tz = abs($tz); - $tz = (int) ($tz/3600)*100 + ($tz%3600)/60; - $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); - - return $result; + public static function RFCDate() { + //Set the time zone to whatever the default is to avoid 500 errors + //Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + return date('D, j M Y H:i:s O'); } /** @@ -2491,8 +2413,7 @@ class PHPMailer * @access protected * @return string */ - protected function ServerHostname() - { + protected function ServerHostname() { if (!empty($this->Hostname)) { $result = $this->Hostname; } elseif (isset($_SERVER['SERVER_NAME'])) { @@ -2510,13 +2431,12 @@ class PHPMailer * @param string $key * @return string */ - protected function Lang($key) - { - if (count($this->language) < 1) { + protected function Lang($key) { + if(count($this->language) < 1) { $this->SetLanguage('en'); // set the default language } - if (isset($this->language[$key])) { + if(isset($this->language[$key])) { return $this->language[$key]; } else { return 'Language string failed to load: ' . $key; @@ -2528,8 +2448,7 @@ class PHPMailer * @access public * @return bool */ - public function IsError() - { + public function IsError() { return ($this->error_count > 0); } @@ -2539,15 +2458,13 @@ class PHPMailer * @param string $str String to FixEOL * @return string */ - public function FixEOL($str) - { - // condense down to \n - $nstr = str_replace(array("\r\n", "\r"), "\n", $str); - // Now convert LE as needed - if ($this->LE !== "\n") { - $nstr = str_replace("\n", $this->LE, $nstr); - } - + public function FixEOL($str) { + // condense down to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } return $nstr; } @@ -2559,25 +2476,26 @@ class PHPMailer * @param string $value header value * @return void */ - public function AddCustomHeader($name, $value=null) - { - if ($value === null) { - // Value passed in as name:value - $this->CustomHeader[] = explode(':', $name, 2); - } else { - $this->CustomHeader[] = array($name, $value); - } + public function AddCustomHeader($name, $value=null) { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } } /** - * Evaluates the message and returns modifications for inline images and backgrounds + * Creates a message from an HTML string, making modifications for inline images and backgrounds + * and creates a plain-text version by converting the HTML + * Overwrites any existing values in $this->Body and $this->AltBody * @access public - * @param string $message Text to be HTML modified + * @param string $message HTML message string * @param string $basedir baseline directory for path + * @param bool $advanced Whether to use the advanced HTML to text converter * @return string $message */ - public function MsgHTML($message, $basedir = '') - { + public function MsgHTML($message, $basedir = '', $advanced = false) { preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); if (isset($images[2])) { foreach ($images[2] as $i => $url) { @@ -2588,30 +2506,42 @@ class PHPMailer if ($directory == '.') { $directory = ''; } - $cid = 'cid:' . md5($url); - $ext = pathinfo($filename, PATHINFO_EXTENSION); - $mimeType = self::_mime_types($ext); - if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } - if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($url), $filename, 'base64', $mimeType) ) { - $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message); + $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2 + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + $basedir .= '/'; + } + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->AddEmbeddedImage($basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)))) { + $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"cid:".$cid."\"", $message); } } } } $this->IsHTML(true); - $this->Body = $message; - if (empty($this->AltBody)) { - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); - if (!empty($textMsg)) { - $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet); - } - } if (empty($this->AltBody)) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } + //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + $this->Body = $this->NormalizeBreaks($message); + $this->AltBody = $this->NormalizeBreaks($this->html2text($message, $advanced)); + return $this->Body; + } - return $message; + /** + * Convert an HTML string into a plain text version + * @param string $html The HTML text to convert + * @param bool $advanced Should this use the more complex html2text converter or just a simple one? + * @return string + */ + public function html2text($html, $advanced = false) { + if ($advanced) { + require_once 'extras/class.html2text.php'; + $h = new html2text($html); + return $h->get_text(); + } + return html_entity_decode(trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet); } /** @@ -2621,8 +2551,7 @@ class PHPMailer * @return string MIME type of ext * @static */ - public static function _mime_types($ext = '') - { + public static function _mime_types($ext = '') { $mimes = array( 'xl' => 'application/excel', 'hqx' => 'application/mac-binhex40', @@ -2711,26 +2640,87 @@ class PHPMailer 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie' ); - return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; } /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name Parameter Name - * @param mixed $value Parameter Value - * NOTE: will not work with arrays, there are no arrays to set/reset + * Try to map a file name to a MIME type, default to application/octet-stream + * @param string $filename A file name or full path, does not need to exist as a file + * @return string + * @static + */ + public static function filenameToType($filename) { + //In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if ($qpos !== false) { + $filename = substr($filename, 0, $qpos); + } + $pathinfo = self::mb_pathinfo($filename); + return self::_mime_types($pathinfo['extension']); + } + + /** + * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. + * Works similarly to the one in PHP >= 5.2.0 + * @link http://www.php.net/manual/en/function.pathinfo.php#107461 + * @param string $path A filename or path, does not need to exist as a file + * @param integer|string $options Either a PATHINFO_* constant, or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * @return string|array + * @static + */ + public static function mb_pathinfo($path, $options = null) { + $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); + $m = array(); + preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); + if(array_key_exists(1, $m)) { + $ret['dirname'] = $m[1]; + } + if(array_key_exists(2, $m)) { + $ret['basename'] = $m[2]; + } + if(array_key_exists(5, $m)) { + $ret['extension'] = $m[5]; + } + if(array_key_exists(3, $m)) { + $ret['filename'] = $m[3]; + } + switch($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + break; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + break; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + break; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + break; + default: + return $ret; + } + } + + /** + * Set (or reset) Class Objects (variables) + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name + * @param mixed $value + * NOTE: will not work with arrays, there are no arrays to set/reset * @throws phpmailerException * @return bool - * @todo Should this not be using __set() magic function? - */ - public function set($name, $value = '') - { + * @todo Should this not be using __set() magic function? + */ + public function set($name, $value = '') { try { if (isset($this->$name) ) { $this->$name = $value; @@ -2743,31 +2733,42 @@ class PHPMailer return false; } } - return true; } /** * Strips newlines to prevent header injection. * @access public - * @param string $str String + * @param string $str * @return string */ - public function SecureHeader($str) - { + public function SecureHeader($str) { return trim(str_replace(array("\r", "\n"), '', $str)); } /** + * Normalize UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format + * Defaults to CRLF (for message bodies) and preserves consecutive breaks + * @param string $text + * @param string $breaktype What kind of line break to use, defaults to CRLF + * @return string + * @access public + * @static + */ + public static function NormalizeBreaks($text, $breaktype = "\r\n") { + return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); + } + + + /** * Set the private key file and password to sign the message. * * @access public - * @param $cert_filename - * @param string $key_filename Parameter File Name + * @param string $cert_filename + * @param string $key_filename * @param string $key_pass Password for private key */ - public function Sign($cert_filename, $key_filename, $key_pass) - { + public function Sign($cert_filename, $key_filename, $key_pass) { $this->sign_cert_file = $cert_filename; $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; @@ -2780,8 +2781,7 @@ class PHPMailer * @param string $txt * @return string */ - public function DKIM_QP($txt) - { + public function DKIM_QP($txt) { $line = ''; for ($i = 0; $i < strlen($txt); $i++) { $ord = ord($txt[$i]); @@ -2791,7 +2791,6 @@ class PHPMailer $line .= "=".sprintf("%02X", $ord); } } - return $line; } @@ -2800,10 +2799,16 @@ class PHPMailer * * @access public * @param string $s Header + * @throws phpmailerException * @return string */ - public function DKIM_Sign($s) - { + public function DKIM_Sign($s) { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new phpmailerException($this->Lang("signing").' OpenSSL extension missing.'); + } + return ''; + } $privKeyStr = file_get_contents($this->DKIM_private); if ($this->DKIM_passphrase != '') { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); @@ -2813,7 +2818,6 @@ class PHPMailer if (openssl_sign($s, $signature, $privKey)) { return base64_encode($signature); } - return ''; } @@ -2824,8 +2828,7 @@ class PHPMailer * @param string $s Header * @return string */ - public function DKIM_HeaderC($s) - { + public function DKIM_HeaderC($s) { $s = preg_replace("/\r\n\s+/", " ", $s); $lines = explode("\r\n", $s); foreach ($lines as $key => $line) { @@ -2835,7 +2838,6 @@ class PHPMailer $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value } $s = implode("\r\n", $lines); - return $s; } @@ -2846,8 +2848,7 @@ class PHPMailer * @param string $body Message Body * @return string */ - public function DKIM_BodyC($body) - { + public function DKIM_BodyC($body) { if ($body == '') return "\r\n"; // stabilize line endings $body = str_replace("\r\n", "\n", $body); @@ -2856,7 +2857,6 @@ class PHPMailer while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { $body = substr($body, 0, strlen($body) - 2); } - return $body; } @@ -2869,21 +2869,29 @@ class PHPMailer * @param string $body Body * @return string */ - public function DKIM_Add($headers_line, $subject, $body) - { + public function DKIM_Add($headers_line, $subject, $body) { $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) $subject_header = "Subject: $subject"; $headers = explode($this->LE, $headers_line); - $from_header = ""; - $to_header = ""; - foreach ($headers as $header) { + $from_header = ''; + $to_header = ''; + $current = ''; + foreach($headers as $header) { if (strpos($header, 'From:') === 0) { $from_header = $header; + $current = 'from_header'; } elseif (strpos($header, 'To:') === 0) { $to_header = $header; + $current = 'to_header'; + } else { + if($current && strpos($header, ' =?') === 0){ + $current .= $header; + } else { + $current = ''; + } } } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); @@ -2904,8 +2912,7 @@ class PHPMailer "\tb="; $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); $signed = $this->DKIM_Sign($toSign); - - return "X-PHPMAILER-DKIM: code.google.com/a/apache-extras.org/p/phpmailer/\r\n".$dkimhdrs.$signed."\r\n"; + return $dkimhdrs.$signed."\r\n"; } /** @@ -2918,8 +2925,7 @@ class PHPMailer * @param string $body * @param string $from */ - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) - { + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) { if (!empty($this->action_function) && is_callable($this->action_function)) { $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); call_user_func_array($this->action_function, $params); @@ -2931,16 +2937,13 @@ class PHPMailer * Exception handler for PHPMailer * @package PHPMailer */ -class phpmailerException extends Exception -{ +class phpmailerException extends Exception { /** * Prettify error message output * @return string */ - public function errorMessage() - { + public function errorMessage() { $errorMsg = '' . $this->getMessage() . "
\n"; - return $errorMsg; } }