From 5962076ec1b864336b42f8ade9d9a4b3405b08a8 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Mon, 30 Mar 2009 14:32:28 +0000 Subject: [PATCH] Fairly major cleanup - exceptions system still needs work for consistency Recommit some stuff that got dropped accidentally --- class.phpmailer.php | 1010 ++++++++++---------- class.smtp2.php | 871 +++++++++++++++++ test/message.txt | 382 ++++++++ test/phpmailerTest.php | 10 +- test/testbootstrap.php | 6 + x.php | 2003 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 3757 insertions(+), 525 deletions(-) create mode 100644 class.smtp2.php create mode 100644 test/message.txt create mode 100644 test/testbootstrap.php create mode 100644 x.php diff --git a/class.phpmailer.php b/class.phpmailer.php index 07e536ce..05c328f9 100644 --- a/class.phpmailer.php +++ b/class.phpmailer.php @@ -28,18 +28,16 @@ /** * PHPMailer - PHP email transport class - * NOTE: Designed for use with PHP version 5 and up + * NOTE: Requires PHP version 5 or later * @package PHPMailer * @author Andy Prevost + * @author Marcus Bointon * @copyright 2004 - 2009 Andy Prevost - * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) * @version $Id$ + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ -if ( !defined('STOP_MESSAGE') ) { define("STOP_MESSAGE", 0); } // message only, continue processing -if ( !defined('STOP_CONTINUE') ) { define("STOP_CONTINUE", 1); } // message?, likely ok to continue processing -if ( !defined('STOP_CRITICAL') ) { define("STOP_CRITICAL", 2); } // message, plus full stop, critical error reached -if ( substr(phpversion(),0,1) < 5 ) { exit('Sorry, this version of PHPMailer will only run on PHP version 5 or greater!
'); } +if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); class PHPMailer { @@ -76,7 +74,7 @@ class PHPMailer { * Holds the most recent mailer error message. * @var string */ - protected $ErrorInfo = ''; + public $ErrorInfo = ''; /** * Sets the From email address for the message. @@ -145,12 +143,6 @@ class PHPMailer { */ public $PluginDir = ''; - /** - * Holds PHPMailer version. - * @var string - */ - protected $Version = '5.0.0'; - /** * Sets the email address that a reading confirmation will be sent. * @var string @@ -258,7 +250,7 @@ class PHPMailer { public $LE = "\n"; ///////////////////////////////////////////////// - // PROPERTIES, PRIVATE + // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// private $smtp = NULL; @@ -271,16 +263,34 @@ class PHPMailer { private $CustomHeader = array(); private $message_type = ''; private $boundary = array(); - private $language = array(); + protected $language = array(); private $error_count = 0; private $sign_cert_file = ""; private $sign_key_file = ""; private $sign_key_pass = ""; + private $exceptions = false; + ///////////////////////////////////////////////// + // CONSTANTS + ///////////////////////////////////////////////// + + const VERSION = '5.0.0'; + const STOP_MESSAGE = 0; // message only, continue processing + const STOP_CONTINUE = 1; // message?, likely ok to continue processing + const STOP_CRITICAL = 2; // message, plus full stop, critical error reached + ///////////////////////////////////////////////// // METHODS, VARIABLES ///////////////////////////////////////////////// +/** + * Constructor + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = false) { + $this->exceptions = ($exceptions == true); + } + /** * Sets message type to HTML. * @param bool $bool @@ -315,7 +325,7 @@ class PHPMailer { * @return void */ public function IsSendmail() { - if ( !stristr(ini_get('sendmail_path'),'sendmail') ) { + if (!stristr(ini_get('sendmail_path'), 'sendmail')) { $this->Sendmail = '/var/qmail/bin/sendmail'; } $this->Mailer = 'sendmail'; @@ -326,7 +336,7 @@ class PHPMailer { * @return void */ public function IsQmail() { - if ( stristr(ini_get('sendmail_path'),'qmail') ) { + if (stristr(ini_get('sendmail_path'), 'qmail')) { $this->Sendmail = '/var/qmail/bin/sendmail'; } $this->Mailer = 'sendmail'; @@ -340,17 +350,10 @@ class PHPMailer { * Adds a "To" address. * @param string $address * @param string $name - * @return boolean true on success, false if addressed already used - * @author Marcus Bointon - * @author Carl Corliss + * @return boolean true on success, false if address already used */ public function AddAddress($address, $name = '') { - if (!isset($this->all_recipients[strtolower(trim($address))])) { - $this->to[] = array(trim($address), $name); - $this->all_recipients[strtolower(trim($address))] = true; - return true; - } - return false; + return $this->AddAnAddress('to', $address, $name); } /** @@ -359,15 +362,10 @@ class PHPMailer { * mailer. * @param string $address * @param string $name - * @return boolean true on success, false if addressed already used + * @return boolean true on success, false if address already used */ public function AddCC($address, $name = '') { - if (!isset($this->all_recipients[strtolower(trim($address))])) { - $this->cc[] = array(trim($address), $name); - $this->all_recipients[strtolower(trim($address))] = true; - return true; - } - return false; + return $this->AddAnAddress('cc', $address, $name); } /** @@ -376,16 +374,63 @@ class PHPMailer { * mailer. * @param string $address * @param string $name - * @return boolean true on success, false if addressed already used + * @return boolean true on success, false if address already used */ - public function AddBCC($address, $name = '') { - if (!isset($this->all_recipients[strtolower(trim($address))])) { - $this->bcc[] = array(trim($address), $name); - $this->all_recipients[strtolower(trim($address))] = true; - return true; - } - return false; - } + public function AddBCC($address, $name = '') { + return $this->AddAnAddress('bcc', $address, $name); + } + + /** + * Adds an address to one of the recipient arrays + * @param string $kind One of 'to', 'cc', 'bcc' + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + * @access private + */ + private function AddAnAddress($kind, $address, $name = '') { + $address = trim($address); + if (!preg_match('/^(to|cc|bcc)$/', $kind)) { + echo 'Invalid recipient array: ' . kind; + return false; + } + if (!self::ValidateAddress($address)) { + if ($this->exceptions) { + throw new phpmailerException('Invalid address: '.$address); + } + echo 'Invalid address: '.$address; + return false; + } + if (!isset($this->all_recipients[strtolower($address)])) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + return false; + } + + /** + * Check that a string looks roughly like an email address should + * Static so it can be used without instantiation + * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator + * Conforms approximately to RFC2822 + * @link http://www.hexillion.com/samples/#Regex Original pattern found here + * @param string $address The email address to check + * @return boolean + * @static + * @access public + */ + public static function ValidateAddress($address) { + if (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); + } + } /** * Adds a "Reply-to" address. @@ -394,7 +439,12 @@ class PHPMailer { * @return void */ public function AddReplyTo($address, $name = '') { - $this->ReplyTo[] = array(trim($address), $name); + if (!self::ValidateAddress($address)) { + //throw exception + echo 'Invalid address: '.$address; + return false; + } + $this->ReplyTo[] = array(trim($address), $name); } ///////////////////////////////////////////////// @@ -408,15 +458,9 @@ class PHPMailer { * @return bool */ public function Send() { - $header = ''; - $body = ''; - $result = true; - - try { - if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - $error = $this->Lang('provide_address'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); + try { + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); } // Set whether the message is multipart/alternative @@ -426,93 +470,73 @@ class PHPMailer { $this->error_count = 0; // reset errors $this->SetMessageType(); - $header .= $this->CreateHeader(); + $header = $this->CreateHeader(); $body = $this->CreateBody(); - if($body == '') { - throw new Exception($this->Lang('empty_message'),STOP_CRITICAL); + if (empty($body)) { + throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); } - } catch (Exception $e) { - echo $e->getMessage() . '
'; - if ( $e->getCode() == STOP_CRITICAL ) { - $this->smtp->Reset(); - return false; - } - return $e; - } - // Choose the mailer + // Choose the mailer and send through it switch($this->Mailer) { case 'sendmail': - $result = $this->SendmailSend($header, $body); - break; + return $this->SendmailSend($header, $body); case 'smtp': - $result = $this->SmtpSend($header, $body); - break; + return $this->SmtpSend($header, $body); case 'mail': - $result = $this->MailSend($header, $body); - break; default: - $result = $this->MailSend($header, $body); - break; + return $this->MailSend($header, $body); } - return $result; - } + } catch (Exception $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; + return false; + } + } /** * Sends mail using the $Sendmail program. - * @access public + * @param string $header The message headers + * @param string $body The message body + * @access protected * @return bool */ - public 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 { $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); } - try { - if(!@$mail = popen($sendmail, 'w')) { - $error = $this->Lang('execute') . $this->Sendmail; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - if (version_compare(phpversion(), '4.2.3') == -1) { - $result = $result >> 8 & 0xFF; - } - if($result != 0) { - $error = $this->Lang('execute') . $this->Sendmail; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - } catch (Exception $e) { - echo $e->getMessage() . '
'; - if ( $e->getCode() == STOP_CRITICAL ) { - $this->smtp->Reset(); - return false; - } - return $e; + if(!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + if($result != 0) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } return true; } /** * Sends mail using the PHP mail() function. - * @access public + * @param string $header The message headers + * @param string $body The message body + * @access protected * @return bool */ - public function MailSend($header, $body) { - $to = ''; - for($i = 0; $i < count($this->to); $i++) { - if($i != 0) { $to .= ', '; } - $to .= $this->AddrFormat($this->to[$i]); - } + protected function MailSend($header, $body) { + $toArr = array(); + foreach($this->to as $t) { + $toArr[] = $this->AddrFormat($t); + } + $to = implode(', ', $toArr); - try { - $toArr = explode(',', $to); $params = sprintf("-oi -f %s", $this->Sender); if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { $old_from = ini_get('sendmail_from'); @@ -537,93 +561,58 @@ class PHPMailer { ini_set('sendmail_from', $old_from); } if(!$rt) { - $error = $this->Lang('instantiate'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); + throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); } - } catch (Exception $e) { - echo $e->getMessage() . '
'; - if ( $e->getCode() == STOP_CRITICAL ) { - $this->smtp->Reset(); - return false; - } - return $e; - } return true; } /** * Sends mail via SMTP using PhpSMTP * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * @param string $header The message headers + * @param string $body The message body * @uses SMTP - * @access public + * @access protected * @return bool */ - public function SmtpSend($header, $body) { - include_once($this->PluginDir . 'class.smtp.php'); - $error = ''; + protected function SmtpSend($header, $body) { + require_once $this->PluginDir . 'class.smtp.php'; $bad_rcpt = array(); - try { if(!$this->SmtpConnect()) { - $error = $this->Lang('smtp_connect_failed'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); + 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)) { - $error = $this->Lang('from_failed') . $smtp_from; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); + throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); } - // Attempt to send attach all recipients - for($i = 0; $i < count($this->to); $i++) { - if(!$this->smtp->Recipient($this->to[$i][0])) { - $bad_rcpt[] = $this->to[$i][0]; - } + /* Attempt to send attach all recipients */ + foreach($this->to as $to) { + if (!$this->smtp->Recipient($to[0])) { + $bad_rcpt[] = $to[0]; + } + } + foreach($this->cc as $cc) { + if (!$this->smtp->Recipient($cc[0])) { + $bad_rcpt[] = $cc[0]; + } + } + foreach($this->bcc as $bcc) { + if (!$this->smtp->Recipient($bcc[0])) { + $bad_rcpt[] = $bcc[0]; + } + } + if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses + $badaddresses = implode(', ', $bad_rcpt); + throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); } - for($i = 0; $i < count($this->cc); $i++) { - if(!$this->smtp->Recipient($this->cc[$i][0])) { - $bad_rcpt[] = $this->cc[$i][0]; - } - } - for($i = 0; $i < count($this->bcc); $i++) { - if(!$this->smtp->Recipient($this->bcc[$i][0])) { - $bad_rcpt[] = $this->bcc[$i][0]; - } - } - if(count($bad_rcpt) > 0) { // Create error message - for($i = 0; $i < count($bad_rcpt); $i++) { - if($i != 0) { - $error .= ', '; - } - $error .= $bad_rcpt[$i]; - } - $error = $this->Lang('recipients_failed') . $error; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - if(!$this->smtp->Data($header . $body)) { - $error = $this->Lang('data_not_accepted'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); + throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); } if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); - } else { - $this->SmtpClose(); - } - } catch (Exception $e) { - echo $e->getMessage() . '
'; - if ( $e->getCode() == STOP_CRITICAL ) { - $this->smtp->Reset(); - return false; - } - return $e; } - return true; } @@ -635,91 +624,70 @@ class PHPMailer { * @return bool */ public function SmtpConnect() { - if($this->smtp == NULL) { + if(is_null($this->smtp)) { $this->smtp = new SMTP(); } $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; - $connection = ($this->smtp->Connected()); + $connection = $this->smtp->Connected(); - // Retry while there is no connection - while($index < count($hosts) && $connection == false) { - $hostinfo = array(); - if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } + /* 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'); + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); - try { - if(!$this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { - throw new Exception('SMTP Connect() failed.',STOP_CRITICAL); - } + if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { - $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); - if (!$this->smtp->Hello($hello)) { - throw new Exception('SMTP Hello() failed.',STOP_CRITICAL); - } + $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); + $this->smtp->Hello($hello); - if($tls) { - if(!$this->smtp->StartTLS()) { - $this->smtp->Reset(); - $connection = false; - $error = $this->Lang("tls"); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } + if ($tls) { + if (!$this->smtp->StartTLS()) { + throw new phpmailerException($this->Lang('tls')); + } - //We must resend HELLO after tls negociation - $this->smtp->Hello($hello); - } + //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->smtp->Reset(); - $connection = false; - $error = $this->Lang('authenticate'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - } - $index++; - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; - } - } + $connection = true; + if ($this->SMTPAuth) { + if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + throw new phpmailerException($this->Lang('authenticate')); + } + } + } + $index++; + if (!$connection) { + throw new phpmailerException($this->Lang('connect_host')); + } + } + } + catch (phpmailerException $e) { + $this->smtp->Reset(); + throw $e; } - try { - if(!$connection) { - $error = $this->Lang('connect_host'); - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; - } - } - - return $connection; + return true; } /** * Closes the active SMTP session if one exists. - * @uses SMTP * @return void */ public function SmtpClose() { - if($this->smtp != NULL) { + if(!is_null($this->smtp)) { if($this->smtp->Connected()) { $this->smtp->Quit(); $this->smtp->Close(); @@ -727,40 +695,47 @@ class PHPMailer { } } - /** - * Sets the language for all class error messages. Returns false - * if it cannot load the language file. The default language type - * is English. - * @param string $lang_type Type of language (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @access public - * @return bool - */ - function SetLanguage($lang_type = 'en', $lang_path = 'language/') { - if( !(@include $lang_path.'phpmailer.lang-'.$lang_type.'.php') ) { - $PHPMAILER_LANG = array(); - $PHPMAILER_LANG["authenticate"] = 'SMTP Error: Could not authenticate.'; - $PHPMAILER_LANG["connect_host"] = 'SMTP Error: Could not connect to SMTP host.'; - $PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data not accepted.'; - $PHPMAILER_LANG["empty_message"] = 'Message body empty'; - $PHPMAILER_LANG["encoding"] = 'Unknown encoding: '; - $PHPMAILER_LANG["execute"] = 'Could not execute: '; - $PHPMAILER_LANG["file_access"] = 'Could not access file: '; - $PHPMAILER_LANG["file_open"] = 'File Error: Could not open file: '; - $PHPMAILER_LANG["from_failed"] = 'The following From address failed: '; - $PHPMAILER_LANG["instantiate"] = 'Could not instantiate mail function.'; - $PHPMAILER_LANG["invalid_email"] = 'Not sending, email address is invalid: '; - $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.'; - $PHPMAILER_LANG["provide_address"] = 'You must provide at least one recipient email address.'; - $PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: The following recipients failed: '; - $PHPMAILER_LANG["signing"] = 'Signing Error: '; - $PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; - $PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; - $PHPMAILER_LANG["variable_set"] = 'Cannot set or reset variable: '; - } - $this->language = $PHPMAILER_LANG; - return true; - } + /** + * 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 + * @access public + */ + function SetLanguage($langcode = 'en', $lang_path = 'language/') { + //Define full set of translatable strings + $PHPMAILER_LANG = array( + 'provide_address' => 'You must provide at least one recipient email address.', + 'mailer_not_supported' => ' mailer is not supported.', + 'execute' => 'Could not execute: ', + 'instantiate' => 'Could not instantiate mail function.', + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'from_failed' => 'The following From address failed: ', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'data_not_accepted' => 'SMTP Error: Data not accepted.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'encoding' => 'Unknown encoding: ', + 'signing' => 'Signing Error: ', + 'smtp_error' => 'SMTP server error: ', + 'empty_message' => 'Message body empty', + 'invalid_address' => 'Invalid address', + 'variable_set' => 'Cannot set or reset variable: ' + ); + //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! + $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; + $this->language = $PHPMAILER_LANG; + return ($l == true); //Returns false if language not found + } + + /** + * Return the current array of language strings + * @return array + */ + public function GetTranslations() { + return $this->language; + } ///////////////////////////////////////////////// // METHODS, MESSAGE CREATION @@ -773,13 +748,13 @@ class PHPMailer { */ public function AddrAppend($type, $addr) { $addr_str = $type . ': '; - $addr_str .= $this->AddrFormat($addr[0]); - if(count($addr) > 1) { - for($i = 1; $i < count($addr); $i++) { - $addr_str .= ', ' . $this->AddrFormat($addr[$i]); - } - } + $addresses = array(); + foreach ($addr as $a) { + $addresses[] = $this->AddrFormat($a); + } + $addr_str .= implode(', ', $addresses); $addr_str .= $this->LE; + return $addr_str; } @@ -789,18 +764,20 @@ class PHPMailer { * @return string */ public function AddrFormat($addr) { - if(empty($addr[1])) { - $formatted = $this->SecureHeader($addr[0]); + if (empty($addr[1])) { + return $this->SecureHeader($addr[0]); } else { - $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; + return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; } - return $formatted; } /** * Wraps message for use with mailers that do not * automatically perform wrapping and for quoted-printable. * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode * @access public * @return string */ @@ -931,7 +908,6 @@ class PHPMailer { switch($this->message_type) { case 'alt': - // fall through case 'alt_attachments': $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); break; @@ -944,7 +920,7 @@ class PHPMailer { /** * Assembles message header. * @access public - * @return string + * @return string The assembled header */ public function CreateHeader() { $result = ''; @@ -954,7 +930,7 @@ class PHPMailer { $this->boundary[1] = 'b1_' . $uniq_id; $this->boundary[2] = 'b2_' . $uniq_id; - $result .= $this->HeaderLine('Date', $this->RFCDate()); + $result .= $this->HeaderLine('Date', self::RFCDate()); if($this->Sender == '') { $result .= $this->HeaderLine('Return-Path', trim($this->From)); } else { @@ -1000,7 +976,7 @@ class PHPMailer { $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); } $result .= $this->HeaderLine('X-Priority', $this->Priority); - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.codeworxtech.com) [version ' . $this->Version . ']'); + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer ' . self::VERSION . ' (phpmailer.codeworxtech.com)'); if($this->ConfirmReadingTo != '') { $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); @@ -1031,7 +1007,6 @@ class PHPMailer { $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); break; case 'attachments': - // fall through case 'alt_attachments': if($this->InlineImageExists()){ $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); @@ -1056,88 +1031,84 @@ class PHPMailer { /** * Assembles the message body. Returns an empty string on failure. * @access public - * @return string + * @return string The assembled message body */ public function CreateBody() { - $result = ''; + $body = ''; if ($this->sign_key_file) { - $result .= $this->GetMailMIME(); + $body .= $this->GetMailMIME(); } $this->SetWordWrap(); switch($this->message_type) { case 'alt': - $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[1]); + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[1]); break; case 'plain': - $result .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->EncodeString($this->Body, $this->Encoding); break; case 'attachments': - $result .= $this->GetBoundary($this->boundary[1], '', '', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE; - $result .= $this->AttachAll(); + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE; + $body .= $this->AttachAll(); break; case 'alt_attachments': - $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); - $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); - $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[2]); - $result .= $this->AttachAll(); + $body .= sprintf("--%s%s", $this->boundary[1], $this->LE); + $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->AttachAll(); break; } - try { - if($this->IsError()) { - $result = ''; - } else if ($this->sign_key_file) { - $file = tempnam("", "mail"); - $fp = fopen($file, "w"); - fwrite($fp, $result); - fclose($fp); + if ($this->IsError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + $file = tempnam('', '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)) { - $fp = fopen($signed, "r"); - $result = ''; - while(!feof($fp)){ - $result = $result . fread($fp, 1024); - } - fclose($fp); - } else { - $error = $this->Lang("signing").openssl_error_string(); - $this->SetError($error); - $result = ''; - throw new Exception($error,STOP_CONTINUE); + if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { + @unlink($file); + @unlink($signed); + $body = file_get_contents($signed); + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->Lang("signing").openssl_error_string()); } - unlink($file); - unlink($signed); } - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; + + catch (phpmailerException $e) { + $body = ''; + if ($this->exceptions) { + throw $e; + } } } - return $result; + + return $body; } /** * Returns the start of a message boundary. - * @access public + * @access private */ - public function GetBoundary($boundary, $charSet, $contentType, $encoding) { + private function GetBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; if($charSet == '') { $charSet = $this->CharSet; @@ -1159,18 +1130,18 @@ class PHPMailer { /** * Returns the end of a message boundary. - * @access public + * @access private */ - public function EndBoundary($boundary) { + private function EndBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } /** * Sets the message type. - * @access public + * @access private * @return void */ - public function SetMessageType() { + private function SetMessageType() { if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { $this->message_type = 'plain'; } else { @@ -1219,73 +1190,75 @@ class PHPMailer { */ public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { try { - if(!@is_file($path)) { - $error = $this->Lang('file_access') . $path; - $this->SetError($error); - throw new Exception($error,STOP_CONTINUE); + if ( !@is_file($path) ) { + throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); } - $filename = basename($path); - if($name == '') { - $name = $filename; - } - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; // isStringAttachment - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + + } + catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; + if ( $e->getCode() == self::STOP_CRITICAL ) { return false; } } return true; } - /** - * Gets all current attachments. - * Needed for testability - * @return array - */ - public function GetAttachments() { - return $this->attachment; - } + /** + * Return the current array of attachments + * @return array + */ + public function GetAttachments() { + return $this->attachment; + } /** * Attaches all fs, string, and binary attachments to the message. * Returns an empty string on failure. - * @access public + * @access private * @return string */ - public function AttachAll() { - // Return text of body - $mime = array(); - $cidUniq = array(); + private function AttachAll() { + /* Return text of body */ + $mime = array(); - // Add all attachments - for($i = 0; $i < count($this->attachment); $i++) { - // Check for string attachment - $bString = $this->attachment[$i][5]; + /* Add all attachments */ + foreach ($this->attachment as $attachment) { + /* Check for string attachment */ + $bString = $attachment[5]; if ($bString) { - $string = $this->attachment[$i][0]; + $string = $attachment[0]; } else { - $path = $this->attachment[$i][0]; + $path = $attachment[0]; } - $filename = $this->attachment[$i][1]; - $name = $this->attachment[$i][2]; - $encoding = $this->attachment[$i][3]; - $type = $this->attachment[$i][4]; - $disposition = $this->attachment[$i][6]; - $cid = $this->attachment[$i][7]; - if ( isset($cidUniq[$cid]) ) { continue; } - $cidUniq[$cid] = true; + $filename = $attachment[1]; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); - //$mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); $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); @@ -1293,7 +1266,6 @@ class PHPMailer { $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); } - //$mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE); $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); // Encode as string attachment @@ -1318,42 +1290,43 @@ class PHPMailer { } /** - * Encodes attachment in requested format. Returns an - * empty string on failure. - * @access public + * Encodes attachment in requested format. + * Returns an empty string on failure. + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @see EncodeFile() + * @access private * @return string */ - public function EncodeFile ($path, $encoding = 'base64') { - try { - if(!@$fd = fopen($path, 'rb')) { - $error = $this->Lang('file_open') . $path; - $this->SetError($error); - fclose($fd); - throw new Exception($error,STOP_CONTINUE); - } - if (function_exists('get_magic_quotes')) { - function get_magic_quotes() { - return false; - } - } - if (PHP_VERSION < 6) { - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - } - $file_buffer = file_get_contents($path); - $file_buffer = $this->EncodeString($file_buffer, $encoding); - if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } - return $file_buffer; - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; - } - } - } + private 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; + } + } + if (PHP_VERSION < 6) { + $magic_quotes = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->EncodeString($file_buffer, $encoding); + if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } + return $file_buffer; + } catch (Exception $e) { + $this->SetError($e->getMessage()); + return ''; + } + } /** - * Encodes string to requested format. Returns an - * empty string on failure. + * Encodes string to requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @access public * @return string */ @@ -1366,6 +1339,7 @@ class PHPMailer { case '7bit': case '8bit': $encoded = $this->FixEOL($str); + //Make sure it ends with a line break if (substr($encoded, -(strlen($this->LE))) != $this->LE) $encoded .= $this->LE; break; @@ -1383,11 +1357,11 @@ class PHPMailer { } /** - * Encode a header string to best of Q, B, quoted or none. + * Encode a header string to best (shortest) of Q, B, quoted or none. * @access public * @return string */ - public function EncodeHeader ($str, $position = 'text') { + public function EncodeHeader($str, $position = 'text') { $x = 0; switch (strtolower($position)) { @@ -1452,7 +1426,7 @@ class PHPMailer { 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; + return false; } } @@ -1549,7 +1523,7 @@ class PHPMailer { * 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 - * Note that there is a quoted_printable_encode function due to appear, possibly in PHP 5.3. + * @see EncodeQPphp() * @access public * @param string $string the text to encode * @param integer $line_max Number of chars allowed on a line before wrapping @@ -1580,12 +1554,15 @@ class PHPMailer { /** * Encode string to q encoding. + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means * @access public * @return string */ public function EncodeQ ($str, $position = 'text') { // There should not be any EOL in the string - $encoded = preg_replace("/[\r\n]*/", '', $str); + $encoded = preg_replace('/[\r\n]*/', '', $str); switch (strtolower($position)) { case 'phrase': @@ -1596,6 +1573,7 @@ class PHPMailer { case 'text': default: // Replace every high ascii, control =, ? and _ characters + //TODO using /e (equivalent to eval()) is probably not a good idea $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', "'='.sprintf('%02X', ord('\\1'))", $encoded); break; @@ -1617,18 +1595,19 @@ class PHPMailer { * @param string $type File extension (MIME) type. * @return void */ - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { - // Append to $attachment array - $cur = count($this->attachment); - $this->attachment[$cur][0] = $string; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $filename; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = true; // isString - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; - } + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => $filename, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + } /** * Adds an embedded attachment. This can include images, sounds, and @@ -1644,31 +1623,29 @@ class PHPMailer { * @return bool */ public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - try { - if(!@is_file($path)) { - $error = $this->Lang('file_access') . $path; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - $filename = basename($path); - if($name == '') { - $name = $filename; - } - // Append to $attachment array - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; - $this->attachment[$cur][6] = 'inline'; - $this->attachment[$cur][7] = $cid; - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; - } + + if ( !@is_file($path) ) { + $this->SetError($this->Lang('file_access') . $path); + return false; } + + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + /* Append to $attachment array */ + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + return true; } @@ -1678,15 +1655,12 @@ class PHPMailer { * @return bool */ public function InlineImageExists() { - $result = false; - for($i = 0; $i < count($this->attachment); $i++) { - if($this->attachment[$i][6] == 'inline') { - $result = true; - break; - } - } - - return $result; + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; } ///////////////////////////////////////////////// @@ -1769,12 +1743,10 @@ class PHPMailer { /** * Adds the error message to the error container. - * Also gets SMTP error if there is one - * Returns void. - * @access private + * @access protected * @return void */ - private function SetError($msg) { + protected function SetError($msg) { $this->error_count++; if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { $lasterror = $this->smtp->getError(); @@ -1787,10 +1759,11 @@ class PHPMailer { /** * Returns the proper RFC 822 formatted date. - * @access private + * @access public * @return string + * @static */ - private static function RFCDate() { + public static function RFCDate() { $tz = date('Z'); $tzs = ($tz < 0) ? '-' : '+'; $tz = abs($tz); @@ -1811,7 +1784,7 @@ class PHPMailer { } elseif (isset($_SERVER['SERVER_NAME'])) { $result = $_SERVER['SERVER_NAME']; } else { - $result = "localhost.localdomain"; + $result = 'localhost.localdomain'; } return $result; @@ -1874,14 +1847,12 @@ class PHPMailer { if(isset($images[2])) { foreach($images[2] as $i => $url) { // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) { + if (!preg_match('#^[A-z]+://#',$url)) { $filename = basename($url); $directory = dirname($url); ($directory == '.')?$directory='':''; $cid = 'cid:' . md5($filename); - $fileParts = explode('.', $filename); - $fileParts = array_reverse($fileParts); - $ext = $fileParts[0]; + $ext = pathinfo($filename, PATHINFO_EXTENSION); $mimeType = $this->_mime_types($ext); if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; } if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } @@ -1897,8 +1868,8 @@ class PHPMailer { if ( !empty($textMsg) && empty($this->AltBody) ) { $this->AltBody = html_entity_decode($textMsg); } - if ( empty($this->AltBody) ) { - $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n"; + if (empty($this->AltBody) ) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } } @@ -1997,42 +1968,41 @@ class PHPMailer { 'xl' => 'application/excel', 'eml' => 'message/rfc822' ); - 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 - */ - public function set ( $name, $value = '' ) { - try { - if ( isset($this->$name) ) { - $this->$name = $value; - } else { - $error = $this->Lang('variable_set') . $name; - $this->SetError($error); - throw new Exception($error,STOP_CRITICAL); - } - } catch (Exception $e) { - if ( $e->getCode() == STOP_CRITICAL ) { - return false; - } - } + 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 + */ + 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 $e) { + $this->SetError($e->getMessage()); + if ($e->getCode() == self::STOP_CRITICAL) { + return false; + } + } + } + /** * Read a file from a supplied filename and return it. - * + * TODO this function is a pointless wrapper! * @access public * @param string $filename Parameter File Name - * @return string (or boolean false if it fails to read for any reason) + * @return mixed Either the text of the file or boolean false if it fails to read */ public function getFile($filename) { return @file_get_contents($filename); @@ -2045,10 +2015,9 @@ class PHPMailer { * @return string */ public function SecureHeader($str) { - $str = trim($str); - $str = str_replace("\r", "", $str); - $str = str_replace("\n", "", $str); - return $str; + $str = str_replace("\r", '', $str); + $str = str_replace("\n", '', $str); + return trim($str); } /** @@ -2063,7 +2032,8 @@ class PHPMailer { $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; } - } +class phpmailerException extends Exception { +} ?> \ No newline at end of file diff --git a/class.smtp2.php b/class.smtp2.php new file mode 100644 index 00000000..bdf004fc --- /dev/null +++ b/class.smtp2.php @@ -0,0 +1,871 @@ +' . $this->getMessage() . "
"; + return $errorMsg; + } +} +class smtpCritical extends smtpException { } + +class SMTP { + /** + * SMTP server port + * @var int + */ + public $SMTP_PORT = 25; + + /** + * SMTP reply line ending + * @var string + */ + public $CRLF = "\r\n"; + + /** + * Sets whether debugging is turned on + * 1 = echo errors and messages locally + * 2 = echo messages locally + * note: $do_debug can be equal to a number greater than 2 which are interpreted as 2 + * @var bool + */ + public $do_debug; + + /** + * Sets VERP use on/off (default is off) + * @var bool + */ + public $do_verp = false; + + /** + * Sets error message to pass to PHPMailer + * @var string + */ + public $smtpErrorMessage = array(); + + /** + * Sets compatibility mode with legacy PHPMailer versions true/false (default is true) + * @var bool + */ + public $compatibility_mode = true; + + /**#@+ + * @access private + */ + private $smtp_conn; // the socket to the server + private $error; // error if any on the last call + private $helo_rply; // the reply the server sent to us for HELO + /**#@-*/ + + /** + * Initialize the class so that the data is in a known state. + * @access public + * @return void + */ + public function __construct() { + $this->smtp_conn = 0; + $this->error = array(); + $this->helo_rply = null; + $this->do_debug = 0; + } + + /************************************************************* + * CONNECTION FUNCTIONS * + ***********************************************************/ + + /** + * Common test for SMTP connection + * + * @access private + * @return bool + */ + public function ifNotConnected($string) { + $this->error = null; + + try { + if ( !$this->connected() ) { + throw new smtpException($string); + } + } catch (smtpException $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + //return true; + } + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + public function Connect($host,$port=0,$tval=30) { + $this->error = null; + $returnCode = array(); + + try { + // make sure we are NOT connected + if ($this->connected() ) { + // already connected! - throw exception that we are already connected + throw new smtpCritical("Already connected to a server"); + } + // assign default port if empty + if ( empty($port) ) { + $port = $this->SMTP_PORT; + } + $this->smtp_conn = @fsockopen($host, // the host of the server + $port, // the port to use + $errno, // error number if any + $errstr, // error message if any + $tval); // give up after ? secs - default is 30 seconds + // verify we connected properly + if ( $this->smtp_conn === false || $this->smtp_conn === 0 ) { + $displayErrorString = ''; + if ( empty($errstr) ) { + $smtp_err_str = null; + switch($errno){ + case -3: $smtp_err_str = "Socket creation failed"; break; + case -4: $smtp_err_str = "DNS lookup failure"; break; + case -5: $smtp_err_str = "Connection refused or timed out"; break; + case 1: $smtp_err_str = "Invalid host"; break; + case 111: $smtp_err_str = "Connection refused"; break; + case 113: $smtp_err_str = "No route to host"; break; + case 110: $smtp_err_str = "Connection timed out"; break; + case 104: $smtp_err_str = "Connection reset by client"; break; + default: $smtp_err_str = "Unknown: connection failed"; break; + } + if ( !empty($smtp_err_str) ) { + $displayErrorString = ' (' . $smtp_err_str . ')'; + } + } elseif ( !empty($errstr) ) { + $displayErrorString = ' (' . $errstr . ')'; + } + $error = 'Failed to connect to server. Error: ' . $errno . $displayErrorString; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error . '
' . $this->CRLF; + } + throw new smtpCritical($error); + } + // SMTP server can take longer to respond - give longer timeout for first read + if ( !stream_set_timeout($this->smtp_conn, $tval) ) { + $this->error = 'Extended time out failed.'; + throw new smtpException($this->error); + } + // get any announcement stuff + $announce = $this->get_lines(); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce . '
'; + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } catch (smtpException $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + } else { + $returnCode[0] = RET_MESSAGE; // message only + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + return true; + } + + /** + * Initiate a TSL communication with the server. + * + * SMTP CODE 220 Ready to start TLS + * SMTP CODE 501 Syntax error (no parameters allowed) + * SMTP CODE 454 TLS not available due to temporary reason + * @access public + * @return bool success + */ + public function StartTLS() { + $this->error = null; + + $this->ifNotConnected('Called StartTLS() without being connected.'); + + try { + fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . '
' . $rply . '
'; + } + if ( $code != 220 ) { + $this->error = 'STARTTLS not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + throw new smtpCritical($this->error); + } + //Begin encrypted connection + if ( !stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) ) { + $this->error = 'SMTP encrypted connection error. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $e->errorMessage() . '
' . $this->CRLF; + } + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + public function Authenticate($username, $password) { + $this->error = null; + + // Start authentication + try { + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $code != 334 ) { + $this->error = 'AUTH not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $code != 334 ) { + $this->error = 'Username not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $code != 235 ) { + $this->error = 'Password not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access public + * @return bool + */ + public function Connected() { + $this->error = null; + + try { + if ( $this->smtp_conn !== false && $this->smtp_conn != 0 ) { + $sock_status = socket_get_status($this->smtp_conn); + if ( $sock_status["eof"] ) { + // odd situation - socket is valid but not connected anymore + $this->error = 'NOTICE: EOF caught while checking if connected'; + throw new smtpCritical($this->error); + } + } else { + return false; + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + if ( $this->do_debug >= 1 ) { + echo "SMTP -> NOTICE:" . '
' . $this->CRLF . "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + return true; // everything looks good + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + public function Close() { + $this->error = null; + + $this->helo_rply = null; + if ( !empty($this->smtp_conn) ) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + /*************************************************************** + * SMTP COMMANDS * + *************************************************************/ + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Data($msg_data) { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Data() without being connected'); + + try { + fputs($this->smtp_conn,"DATA" . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 354 ) { + $this->error = 'DATA command not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + /* the server is ready to accept data! + * according to rfc 821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards are limit. + */ + + // normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + /* we need to find a good way to determine if headers are + * in the msg_data or if it is a straight msg body + * currently assuming rfc 822 definitions of msg headers + * and if the first field of the first line (':' separated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if ( !empty($field) && !strstr($field," ") ) { + $in_headers = true; + } + + $max_line_length = 998; // used below; set here for ease in change + + while(list(,$line) = @each($lines)) { + $lines_out = null; + if ( $line == "" && $in_headers ) { + $in_headers = false; + } + // ok we need to break this line up into several smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + // Patch to fix DOS attack + if ( !$pos ) { + $pos = $max_line_length - 1; + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos); + } else { + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + } + + /* if we are processing headers we need to + * add a LWSP-char to the front of the new line + * rfc 822 on long msg headers + */ + if ( $in_headers ) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + // now send the lines to the server + while(list(,$line_out) = @each($lines_out)) { + if ( strlen($line_out) > 0 ) { + if ( substr($line_out, 0, 1) == "." ) { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + try { + // message data has been sent so lets end + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 250 ) { + $this->error = 'DATA not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + return true; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + public function Hello($host="") { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Hello() without being connected'); + + // if hostname for HELO was not specified determine a suitable one to send + if ( empty($host) ) { + // default to send to the server + $host = "localhost"; + } + + try { + // Send extended hello first (RFC 2821) + if ( !$this->SendHello("EHLO", $host) ) { + if ( !$this->SendHello("HELO", $host) ) { + $this->error = 'ERROR: EHLO and/or HELO not accepted by server'; + throw new smtpCritical($this->error); + } + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + private function SendHello($hello, $host) { + $this->error = null; + + try { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER: " . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 250 ) { + $this->error = $hello . ' not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + $this->helo_rply = $rply; + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + public function Mail($from) { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Mail() without being connected'); + + try { + $useVerp = ($this->do_verp ? "XVERP" : ""); + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 250 ) { + $this->error = 'MAIL not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + public function Quit($close_on_error=true) { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Quit() without being connected'); + + try { + // send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + // get any good-bye messages + $byemsg = $this->get_lines(); + + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $byemsg . '
' . $this->CRLF; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if ( $code != 221 ) { + $rval = false; + $this->error = 'QUIT command not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + $e = $this->error; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $e["error"] . " (" . $byemsg . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + if ( empty($e) || $close_on_error ) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Recipient($to) { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Recipient() without being connected'); + + try { + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 250 && $code != 251 ) { + $this->error = 'RCPT command not accepted. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo 'SMTP -> ERROR: ' . $this->error . '
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + public function Reset() { + $this->error = null; + + $this->ifNotConnected('ERROR: Called Reset() without being connected'); + + try { + fputs($this->smtp_conn,"RSET" . $this->CRLF); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + if ( $this->do_debug >= 2 ) { + echo "SMTP -> FROM SERVER:" . '
' . $this->CRLF . $rply . '
' . $this->CRLF; + } + if ( $code != 250 ) { + $this->error = 'RSET failed. Server returned code: ' . $code . ' (' . trim(substr($rply,strpos($rply,' ',5))) . ')'; + if ( $this->do_debug >= 1 ) { + echo "SMTP -> ERROR: " . $this->error . " (" . $rply . ')
' . $this->CRLF; + } + throw new smtpCritical($this->error); + } + } catch (smtpCritical $e) { + $this->smtpErrorMessage[] = $e->errorMessage(); + if ( $this->compatibility_mode ) { + echo $e->errorMessage(); + return false; + } else { + $returnCode[0] = RET_CRITICAL; // critical error, stop processing + $returnCode[1] = $e->errorMessage(); + return $returnCode; + } + } + + return true; + } + + /******************************************************************* + * INTERNAL FUNCTIONS * + ******************************************************************/ + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + private function get_lines() { + $data = ""; + while($str = @fgets($this->smtp_conn,515)) { + if ( $this->do_debug >= 4 ) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . '
' . $this->CRLF; + echo "SMTP -> get_lines(): \$str is \"$str\"" . '
' . $this->CRLF; + } + $data .= $str; + if ( $this->do_debug >= 4 ) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . '
' . $this->CRLF; + } + // if the 4th character is a space then we are done reading + // so just break the loop + if ( substr($str,3,1) == " " ) { + break; + } + } + return $data; + } + +} + +?> \ No newline at end of file diff --git a/test/message.txt b/test/message.txt new file mode 100644 index 00000000..54bb550d --- /dev/null +++ b/test/message.txt @@ -0,0 +1,382 @@ +Date: Sat, 28 Mar 2009 01:58:37 +0000 +Return-Path: unit_test@phpmailer.sf.net +To: Test User +From: Unit Tester +Reply-to: Reply Guy +Subject: Unit Test: AltBody + Attachment +Message-ID: <220194d0bc6c229cb9c5c2899123dba3@phpmailer.mac.bointon.com> +X-Priority: 3 +X-Mailer: PHPMailer 5.0.0 (phpmailer.codeworxtech.com) +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="b1_220194d0bc6c229cb9c5c2899123dba3" + + +--b1_220194d0bc6c229cb9c5c2899123dba3 +Content-Type: multipart/alternative; + boundary="b2_220194d0bc6c229cb9c5c2899123dba3" + +--b2_220194d0bc6c229cb9c5c2899123dba3 +Content-Type: text/plain; charset = "iso-8859-1" +Content-Transfer-Encoding: 8bit + + +This is the text part of the email. + + +--b2_220194d0bc6c229cb9c5c2899123dba3 +Content-Type: text/html; charset = "iso-8859-1" +Content-Transfer-Encoding: 8bit + + +This is the HTML part of the email.

---------------------
Unit Test Information
---------------------
phpmailer version: 5.0.0
Content Type: text/html
Host: mail.synchromedia.co.uk
Attachments:
  • Name: phpmailer_test.php, Encoding: base64, Type: application/octet-stream

Changes
-------
  • Sender was changed to [unit_test@phpmailer.sf.net]
  • Mailer was changed to [smtp]


+ + + +--b2_220194d0bc6c229cb9c5c2899123dba3-- +--b1_220194d0bc6c229cb9c5c2899123dba3 +Content-Type: application/octet-stream; name="test_attach.txt" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="test_attach.txt" + +PD9waHANCi8qKioqKioqKioqKioqKioqKioqDQogIFVuaXQgVGVzdA0KICBUeXBlOiBwaHBtYWls +ZXIgY2xhc3MNCioqKioqKioqKioqKioqKioqKioqLw0KDQokSU5DTFVERV9ESVIgPSAiLi4vIjsN +Cg0KcmVxdWlyZSgicGhwdW5pdC5waHAiKTsNCnJlcXVpcmUoJElOQ0xVREVfRElSIC4gImNsYXNz +LnBocG1haWxlci5waHAiKTsNCmVycm9yX3JlcG9ydGluZyhFX0FMTCk7DQoNCi8qKg0KICogUGVy +Zm9ybXMgYXV0aGVudGljYXRpb24gdGVzdHMNCiAqLw0KY2xhc3MgcGhwbWFpbGVyVGVzdCBleHRl +bmRzIFRlc3RDYXNlDQp7DQogICAgLyoqDQogICAgICogSG9sZHMgdGhlIGRlZmF1bHQgcGhwbWFp +bGVyIGluc3RhbmNlLg0KICAgICAqIEBwcml2YXRlDQogICAgICogQHR5cGUgb2JqZWN0DQogICAg +ICovDQogICAgdmFyICRNYWlsID0gZmFsc2U7DQoNCiAgICAvKioNCiAgICAgKiBIb2xkcyB0aGUg +U01UUCBtYWlsIGhvc3QuDQogICAgICogQHB1YmxpYw0KICAgICAqIEB0eXBlIHN0cmluZw0KICAg +ICAqLw0KICAgIHZhciAkSG9zdCA9ICIiOw0KICAgIA0KICAgIC8qKg0KICAgICAqIEhvbGRzIHRo +ZSBjaGFuZ2UgbG9nLg0KICAgICAqIEBwcml2YXRlDQogICAgICogQHR5cGUgc3RyaW5nIGFycmF5 +DQogICAgICovDQogICAgdmFyICRDaGFuZ2VMb2cgPSBhcnJheSgpOw0KICAgIA0KICAgICAvKioN +CiAgICAgKiBIb2xkcyB0aGUgbm90ZSBsb2cuDQogICAgICogQHByaXZhdGUNCiAgICAgKiBAdHlw +ZSBzdHJpbmcgYXJyYXkNCiAgICAgKi8NCiAgICB2YXIgJE5vdGVMb2cgPSBhcnJheSgpOyAgIA0K +DQogICAgLyoqDQogICAgICogQ2xhc3MgY29uc3R1Y3Rvci4NCiAgICAgKi8NCiAgICBmdW5jdGlv +biBwaHBtYWlsZXJUZXN0KCRuYW1lKSB7DQogICAgICAgIC8qIG11c3QgZGVmaW5lIHRoaXMgY29u +c3RydWN0b3IgKi8NCiAgICAgICAgJHRoaXMtPlRlc3RDYXNlKCAkbmFtZSApOw0KICAgIH0NCiAg +ICANCiAgICAvKioNCiAgICAgKiBSdW4gYmVmb3JlIGVhY2ggdGVzdCBpcyBzdGFydGVkLg0KICAg +ICAqLw0KICAgIGZ1bmN0aW9uIHNldFVwKCkgew0KICAgICAgICBnbG9iYWwgJGdsb2JhbF92YXJz +Ow0KICAgICAgICBnbG9iYWwgJElOQ0xVREVfRElSOw0KDQogICAgICAgICR0aGlzLT5NYWlsID0g +bmV3IFBIUE1haWxlcigpOw0KDQogICAgICAgICR0aGlzLT5NYWlsLT5Qcmlvcml0eSA9IDM7DQog +ICAgICAgICR0aGlzLT5NYWlsLT5FbmNvZGluZyA9ICI4Yml0IjsNCiAgICAgICAgJHRoaXMtPk1h +aWwtPkNoYXJTZXQgPSAiaXNvLTg4NTktMSI7DQogICAgICAgICR0aGlzLT5NYWlsLT5Gcm9tID0g +InVuaXRfdGVzdEBwaHBtYWlsZXIuc2YubmV0IjsNCiAgICAgICAgJHRoaXMtPk1haWwtPkZyb21O +YW1lID0gIlVuaXQgVGVzdGVyIjsNCiAgICAgICAgJHRoaXMtPk1haWwtPlNlbmRlciA9ICIiOw0K +ICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCA9ICJVbml0IFRlc3QiOw0KICAgICAgICAkdGhp +cy0+TWFpbC0+Qm9keSA9ICIiOw0KICAgICAgICAkdGhpcy0+TWFpbC0+QWx0Qm9keSA9ICIiOw0K +ICAgICAgICAkdGhpcy0+TWFpbC0+V29yZFdyYXAgPSAwOw0KICAgICAgICAkdGhpcy0+TWFpbC0+ +SG9zdCA9ICRnbG9iYWxfdmFyc1sibWFpbF9ob3N0Il07DQogICAgICAgICR0aGlzLT5NYWlsLT5Q +b3J0ID0gMjU7DQogICAgICAgICR0aGlzLT5NYWlsLT5IZWxvID0gImxvY2FsaG9zdC5sb2NhbGRv +bWFpbiI7DQogICAgICAgICR0aGlzLT5NYWlsLT5TTVRQQXV0aCA9IGZhbHNlOw0KICAgICAgICAk +dGhpcy0+TWFpbC0+VXNlcm5hbWUgPSAiIjsNCiAgICAgICAgJHRoaXMtPk1haWwtPlBhc3N3b3Jk +ID0gIiI7DQogICAgICAgICR0aGlzLT5NYWlsLT5QbHVnaW5EaXIgPSAkSU5DTFVERV9ESVI7DQoJ +CSR0aGlzLT5NYWlsLT5BZGRSZXBseVRvKCJub19yZXBseUBwaHBtYWlsZXIuc2YubmV0IiwgIlJl +cGx5IEd1eSIpOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U2VuZGVyID0gInVuaXRfdGVzdEBwaHBt +YWlsZXIuc2YubmV0IjsNCg0KICAgICAgICBpZihzdHJsZW4oJHRoaXMtPk1haWwtPkhvc3QpID4g +MCkNCiAgICAgICAgICAgICR0aGlzLT5NYWlsLT5NYWlsZXIgPSAic210cCI7DQogICAgICAgIGVs +c2UNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPk1haWwtPk1haWxlciA9ICJtYWlsIjsN +CiAgICAgICAgICAgICR0aGlzLT5TZW5kZXIgPSAidW5pdF90ZXN0QHBocG1haWxlci5zZi5uZXQi +Ow0KICAgICAgICB9DQogICAgICAgIA0KICAgICAgICBnbG9iYWwgJGdsb2JhbF92YXJzOw0KICAg +ICAgICAkdGhpcy0+U2V0QWRkcmVzcygkZ2xvYmFsX3ZhcnNbIm1haWxfdG8iXSwgIlRlc3QgVXNl +ciIpOw0KICAgICAgICBpZihzdHJsZW4oJGdsb2JhbF92YXJzWyJtYWlsX2NjIl0pID4gMCkNCiAg +ICAgICAgICAgICR0aGlzLT5TZXRBZGRyZXNzKCRnbG9iYWxfdmFyc1sibWFpbF9jYyJdLCAiQ2Fy +Ym9uIFVzZXIiLCAiY2MiKTsNCiAgICB9ICAgICANCg0KICAgIC8qKg0KICAgICAqIFJ1biBhZnRl +ciBlYWNoIHRlc3QgaXMgY29tcGxldGVkLg0KICAgICAqLw0KICAgIGZ1bmN0aW9uIHRlYXJEb3du +KCkgew0KICAgICAgICAvLyBDbGVhbiBnbG9iYWwgdmFyaWFibGVzDQogICAgICAgICR0aGlzLT5N +YWlsID0gTlVMTDsNCiAgICAgICAgJHRoaXMtPkNoYW5nZUxvZyA9IGFycmF5KCk7DQogICAgICAg +ICR0aGlzLT5Ob3RlTG9nID0gYXJyYXkoKTsNCiAgICB9DQoNCg0KICAgIC8qKg0KICAgICAqIEJ1 +aWxkIHRoZSBib2R5IG9mIHRoZSBtZXNzYWdlIGluIHRoZSBhcHByb3ByaWF0ZSBmb3JtYXQuDQog +ICAgICogQHByaXZhdGUNCiAgICAgKiBAcmV0dXJucyB2b2lkDQogICAgICovDQogICAgZnVuY3Rp +b24gQnVpbGRCb2R5KCkgew0KICAgICAgICAkdGhpcy0+Q2hlY2tDaGFuZ2VzKCk7DQogICAgICAg +IA0KICAgICAgICAvLyBEZXRlcm1pbmUgbGluZSBlbmRpbmdzIGZvciBtZXNzYWdlICAgICAgICAN +CiAgICAgICAgaWYoJHRoaXMtPk1haWwtPkNvbnRlbnRUeXBlID09ICJ0ZXh0L2h0bWwiIHx8IHN0 +cmxlbigkdGhpcy0+TWFpbC0+QWx0Qm9keSkgPiAwKQ0KICAgICAgICB7DQogICAgICAgICAgICAk +ZW9sID0gIjxici8+IjsNCiAgICAgICAgICAgICRidWxsZXQgPSAiPGxpPiI7DQogICAgICAgICAg +ICAkYnVsbGV0X3N0YXJ0ID0gIjx1bD4iOw0KICAgICAgICAgICAgJGJ1bGxldF9lbmQgPSAiPC91 +bD4iOw0KICAgICAgICB9DQogICAgICAgIGVsc2UNCiAgICAgICAgew0KICAgICAgICAgICAgJGVv +bCA9ICJcbiI7DQogICAgICAgICAgICAkYnVsbGV0ID0gIiAtICI7DQogICAgICAgICAgICAkYnVs +bGV0X3N0YXJ0ID0gIiI7DQogICAgICAgICAgICAkYnVsbGV0X2VuZCA9ICIiOw0KICAgICAgICB9 +DQogICAgICAgIA0KICAgICAgICAkUmVwb3J0Qm9keSA9ICIiOw0KICAgICAgICANCiAgICAgICAg +JFJlcG9ydEJvZHkgLj0gIi0tLS0tLS0tLS0tLS0tLS0tLS0tLSIgLiAkZW9sOw0KICAgICAgICAk +UmVwb3J0Qm9keSAuPSAiVW5pdCBUZXN0IEluZm9ybWF0aW9uIiAuICRlb2w7DQogICAgICAgICRS +ZXBvcnRCb2R5IC49ICItLS0tLS0tLS0tLS0tLS0tLS0tLS0iIC4gJGVvbDsNCiAgICAgICAgJFJl +cG9ydEJvZHkgLj0gInBocG1haWxlciB2ZXJzaW9uOiAiIC4gUEhQTWFpbGVyOjpWRVJTSU9OIC4g +JGVvbDsNCiAgICAgICAgJFJlcG9ydEJvZHkgLj0gIkNvbnRlbnQgVHlwZTogIiAuICR0aGlzLT5N +YWlsLT5Db250ZW50VHlwZSAuICRlb2w7DQogICAgICAgIA0KICAgICAgICBpZihzdHJsZW4oJHRo +aXMtPk1haWwtPkhvc3QpID4gMCkNCiAgICAgICAgICAgICRSZXBvcnRCb2R5IC49ICJIb3N0OiAi +IC4gJHRoaXMtPk1haWwtPkhvc3QgLiAkZW9sOw0KICAgICAgICANCiAgICAgICAgLy8gSWYgYXR0 +YWNobWVudHMgdGhlbiBjcmVhdGUgYW4gYXR0YWNobWVudCBsaXN0DQogICAgICAgICRhdHRhY2ht +ZW50cyA9ICR0aGlzLT5NYWlsLT5HZXRBdHRhY2htZW50cygpOw0KICAgICAgICBpZihjb3VudCgk +YXR0YWNobWVudHMpID4gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgJFJlcG9ydEJvZHkgLj0g +IkF0dGFjaG1lbnRzOiIgLiAkZW9sOw0KICAgICAgICAgICAgJFJlcG9ydEJvZHkgLj0gJGJ1bGxl +dF9zdGFydDsNCiAgICAgICAgICAgIGZvcmVhY2goJGF0dGFjaG1lbnRzIGFzICRhdHRhY2htZW50 +KSB7DQogICAgICAgICAgICAgICAgJFJlcG9ydEJvZHkgLj0gJGJ1bGxldCAuICJOYW1lOiAiIC4g +JGF0dGFjaG1lbnRbMV0gLiAiLCAiOw0KICAgICAgICAgICAgICAgICRSZXBvcnRCb2R5IC49ICJF +bmNvZGluZzogIiAuICRhdHRhY2htZW50WzNdIC4gIiwgIjsNCiAgICAgICAgICAgICAgICAkUmVw +b3J0Qm9keSAuPSAiVHlwZTogIiAuICRhdHRhY2htZW50WzRdIC4gJGVvbDsNCiAgICAgICAgICAg +IH0NCiAgICAgICAgICAgICRSZXBvcnRCb2R5IC49ICRidWxsZXRfZW5kIC4gJGVvbDsNCiAgICAg +ICAgfQ0KICAgICAgICANCiAgICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMgdGhlbiBsaXN0 +IHRoZW0NCiAgICAgICAgaWYoY291bnQoJHRoaXMtPkNoYW5nZUxvZykgPiAwKQ0KICAgICAgICB7 +DQogICAgICAgICAgICAkUmVwb3J0Qm9keSAuPSAiQ2hhbmdlcyIgLiAkZW9sOw0KICAgICAgICAg +ICAgJFJlcG9ydEJvZHkgLj0gIi0tLS0tLS0iIC4gJGVvbDsNCg0KICAgICAgICAgICAgJFJlcG9y +dEJvZHkgLj0gJGJ1bGxldF9zdGFydDsNCiAgICAgICAgICAgIGZvcigkaSA9IDA7ICRpIDwgY291 +bnQoJHRoaXMtPkNoYW5nZUxvZyk7ICRpKyspDQogICAgICAgICAgICB7DQogICAgICAgICAgICAg +ICAgJFJlcG9ydEJvZHkgLj0gJGJ1bGxldCAuICR0aGlzLT5DaGFuZ2VMb2dbJGldWzBdIC4gIiB3 +YXMgY2hhbmdlZCB0byBbIiAuIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICR0aGlz +LT5DaGFuZ2VMb2dbJGldWzFdIC4gIl0iIC4gJGVvbDsNCiAgICAgICAgICAgIH0NCiAgICAgICAg +ICAgICRSZXBvcnRCb2R5IC49ICRidWxsZXRfZW5kIC4gJGVvbCAuICRlb2w7DQogICAgICAgIH0N +CiAgICAgICAgDQogICAgICAgIC8vIElmIHRoZXJlIGFyZSBub3RlcyB0aGVuIGxpc3QgdGhlbQ0K +ICAgICAgICBpZihjb3VudCgkdGhpcy0+Tm90ZUxvZykgPiAwKQ0KICAgICAgICB7DQogICAgICAg +ICAgICAkUmVwb3J0Qm9keSAuPSAiTm90ZXMiIC4gJGVvbDsNCiAgICAgICAgICAgICRSZXBvcnRC +b2R5IC49ICItLS0tLSIgLiAkZW9sOw0KDQogICAgICAgICAgICAkUmVwb3J0Qm9keSAuPSAkYnVs +bGV0X3N0YXJ0Ow0KICAgICAgICAgICAgZm9yKCRpID0gMDsgJGkgPCBjb3VudCgkdGhpcy0+Tm90 +ZUxvZyk7ICRpKyspDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgJFJlcG9ydEJvZHkg +Lj0gJGJ1bGxldCAuICR0aGlzLT5Ob3RlTG9nWyRpXSAuICRlb2w7DQogICAgICAgICAgICB9DQog +ICAgICAgICAgICAkUmVwb3J0Qm9keSAuPSAkYnVsbGV0X2VuZDsNCiAgICAgICAgfQ0KICAgICAg +ICANCiAgICAgICAgLy8gUmUtYXR0YWNoIHRoZSBvcmlnaW5hbCBib2R5DQogICAgICAgICR0aGlz +LT5NYWlsLT5Cb2R5IC49ICRlb2wgLiAkZW9sIC4gJFJlcG9ydEJvZHk7DQogICAgfQ0KICAgIA0K +ICAgIC8qKg0KICAgICAqIENoZWNrIHdoaWNoIGRlZmF1bHQgc2V0dGluZ3MgaGF2ZSBiZWVuIGNo +YW5nZWQgZm9yIHRoZSByZXBvcnQuDQogICAgICogQHByaXZhdGUNCiAgICAgKiBAcmV0dXJucyB2 +b2lkDQogICAgICovDQogICAgZnVuY3Rpb24gQ2hlY2tDaGFuZ2VzKCkgew0KICAgICAgICBpZigk +dGhpcy0+TWFpbC0+UHJpb3JpdHkgIT0gMykNCiAgICAgICAgICAgICR0aGlzLT5BZGRDaGFuZ2Uo +IlByaW9yaXR5IiwgJHRoaXMtPk1haWwtPlByaW9yaXR5KTsNCiAgICAgICAgaWYoJHRoaXMtPk1h +aWwtPkVuY29kaW5nICE9ICI4Yml0IikNCiAgICAgICAgICAgICR0aGlzLT5BZGRDaGFuZ2UoIkVu +Y29kaW5nIiwgJHRoaXMtPk1haWwtPkVuY29kaW5nKTsNCiAgICAgICAgaWYoJHRoaXMtPk1haWwt +PkNoYXJTZXQgIT0gImlzby04ODU5LTEiKQ0KICAgICAgICAgICAgJHRoaXMtPkFkZENoYW5nZSgi +Q2hhclNldCIsICR0aGlzLT5NYWlsLT5DaGFyU2V0KTsNCiAgICAgICAgaWYoJHRoaXMtPk1haWwt +PlNlbmRlciAhPSAiIikNCiAgICAgICAgICAgICR0aGlzLT5BZGRDaGFuZ2UoIlNlbmRlciIsICR0 +aGlzLT5NYWlsLT5TZW5kZXIpOw0KICAgICAgICBpZigkdGhpcy0+TWFpbC0+V29yZFdyYXAgIT0g +MCkNCiAgICAgICAgICAgICR0aGlzLT5BZGRDaGFuZ2UoIldvcmRXcmFwIiwgJHRoaXMtPk1haWwt +PldvcmRXcmFwKTsNCiAgICAgICAgaWYoJHRoaXMtPk1haWwtPk1haWxlciAhPSAibWFpbCIpDQog +ICAgICAgICAgICAkdGhpcy0+QWRkQ2hhbmdlKCJNYWlsZXIiLCAkdGhpcy0+TWFpbC0+TWFpbGVy +KTsNCiAgICAgICAgaWYoJHRoaXMtPk1haWwtPlBvcnQgIT0gMjUpDQogICAgICAgICAgICAkdGhp +cy0+QWRkQ2hhbmdlKCJQb3J0IiwgJHRoaXMtPk1haWwtPlBvcnQpOw0KICAgICAgICBpZigkdGhp +cy0+TWFpbC0+SGVsbyAhPSAibG9jYWxob3N0LmxvY2FsZG9tYWluIikNCiAgICAgICAgICAgICR0 +aGlzLT5BZGRDaGFuZ2UoIkhlbG8iLCAkdGhpcy0+TWFpbC0+SGVsbyk7DQogICAgICAgIGlmKCR0 +aGlzLT5NYWlsLT5TTVRQQXV0aCkNCiAgICAgICAgICAgICR0aGlzLT5BZGRDaGFuZ2UoIlNNVFBB +dXRoIiwgInRydWUiKTsNCiAgICB9DQogICAgDQogICAgLyoqDQogICAgICogQWRkcyBhIGNoYW5n +ZSBlbnRyeS4NCiAgICAgKiBAcHJpdmF0ZQ0KICAgICAqIEByZXR1cm5zIHZvaWQNCiAgICAgKi8N +CiAgICBmdW5jdGlvbiBBZGRDaGFuZ2UoJHNOYW1lLCAkc05ld1ZhbHVlKSB7DQogICAgICAgICRj +dXIgPSBjb3VudCgkdGhpcy0+Q2hhbmdlTG9nKTsNCiAgICAgICAgJHRoaXMtPkNoYW5nZUxvZ1sk +Y3VyXVswXSA9ICRzTmFtZTsNCiAgICAgICAgJHRoaXMtPkNoYW5nZUxvZ1skY3VyXVsxXSA9ICRz +TmV3VmFsdWU7DQogICAgfQ0KICAgIA0KICAgIC8qKg0KICAgICAqIEFkZHMgYSBzaW1wbGUgbm90 +ZSB0byB0aGUgbWVzc2FnZS4NCiAgICAgKiBAcHVibGljDQogICAgICogQHJldHVybnMgdm9pZA0K +ICAgICAqLw0KICAgIGZ1bmN0aW9uIEFkZE5vdGUoJHNWYWx1ZSkgew0KICAgICAgICAkdGhpcy0+ +Tm90ZUxvZ1tdID0gJHNWYWx1ZTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgKiBBZGRzIGFsbCBv +ZiB0aGUgYWRkcmVzc2VzDQogICAgICogQHB1YmxpYw0KICAgICAqIEByZXR1cm5zIHZvaWQNCiAg +ICAgKi8NCiAgICBmdW5jdGlvbiBTZXRBZGRyZXNzKCRzQWRkcmVzcywgJHNOYW1lID0gIiIsICRz +VHlwZSA9ICJ0byIpIHsNCiAgICAgICAgc3dpdGNoKCRzVHlwZSkNCiAgICAgICAgew0KICAgICAg +ICAgICAgY2FzZSAidG8iOg0KICAgICAgICAgICAgICAgICR0aGlzLT5NYWlsLT5BZGRBZGRyZXNz +KCRzQWRkcmVzcywgJHNOYW1lKTsNCiAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAg +IGNhc2UgImNjIjoNCiAgICAgICAgICAgICAgICAkdGhpcy0+TWFpbC0+QWRkQ0MoJHNBZGRyZXNz +LCAkc05hbWUpOw0KICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgY2FzZSAiYmNj +IjoNCiAgICAgICAgICAgICAgICAkdGhpcy0+TWFpbC0+QWRkQkNDKCRzQWRkcmVzcywgJHNOYW1l +KTsNCiAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgfQ0KICAgIH0NCg0KICAgIC8vLy8v +Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8NCiAgICAvLyBVTklU +IFRFU1RTDQogICAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8v +Ly8vLw0KDQogICAgLyoqDQogICAgICogVHJ5IGEgcGxhaW4gbWVzc2FnZS4NCiAgICAgKi8NCiAg +ICBmdW5jdGlvbiB0ZXN0X1dvcmRXcmFwKCkgew0KDQogICAgICAgICR0aGlzLT5NYWlsLT5Xb3Jk +V3JhcCA9IDQwOw0KICAgICAgICAkbXlfYm9keSA9ICJIZXJlIGlzIHRoZSBtYWluIGJvZHkgb2Yg +dGhpcyBtZXNzYWdlLiAgSXQgc2hvdWxkICIgLg0KICAgICAgICAgICAgICAgICAgICJiZSBxdWl0 +ZSBhIGZldyBsaW5lcy4gIEl0IHNob3VsZCBiZSB3cmFwcGVkIGF0IHRoZSAiIC4NCiAgICAgICAg +ICAgICAgICAgICAiNDAgY2hhcmFjdGVycy4gIE1ha2Ugc3VyZSB0aGF0IGl0IGlzLiI7DQogICAg +ICAgICRuQm9keUxlbiA9IHN0cmxlbigkbXlfYm9keSk7DQogICAgICAgICRteV9ib2R5IC49ICJc +blxuVGhpcyBpcyB0aGUgYWJvdmUgYm9keSBsZW5ndGg6ICIgLiAkbkJvZHlMZW47DQoNCiAgICAg +ICAgJHRoaXMtPk1haWwtPkJvZHkgPSAkbXlfYm9keTsNCiAgICAgICAgJHRoaXMtPk1haWwtPlN1 +YmplY3QgLj0gIjogV29yZHdyYXAiOw0KDQogICAgICAgICR0aGlzLT5CdWlsZEJvZHkoKTsNCiAg +ICAgICAgJHRoaXMtPmFzc2VydCgkdGhpcy0+TWFpbC0+U2VuZCgpLCAkdGhpcy0+TWFpbC0+RXJy +b3JJbmZvKTsNCiAgICB9DQoNCiAgICAvKioNCiAgICAgKiBUcnkgYSBwbGFpbiBtZXNzYWdlLg0K +ICAgICAqLw0KICAgIGZ1bmN0aW9uIHRlc3RfTG93X1ByaW9yaXR5KCkgew0KICAgIA0KICAgICAg +ICAkdGhpcy0+TWFpbC0+UHJpb3JpdHkgPSA1Ow0KICAgICAgICAkdGhpcy0+TWFpbC0+Qm9keSA9 +ICJIZXJlIGlzIHRoZSBtYWluIGJvZHkuICBUaGVyZSBzaG91bGQgYmUgIiAuDQogICAgICAgICAg +ICAgICAgICAgICAgICAgICAgImEgcmVwbHkgdG8gYWRkcmVzcyBpbiB0aGlzIG1lc3NhZ2UuIjsN +CiAgICAgICAgJHRoaXMtPk1haWwtPlN1YmplY3QgLj0gIjogTG93IFByaW9yaXR5IjsNCiAgICAg +ICAgJHRoaXMtPk1haWwtPkFkZFJlcGx5VG8oIm5vYm9keUBub2JvZHkuY29tIiwgIk5vYm9keSAo +VW5pdCBUZXN0KSIpOw0KDQogICAgICAgICR0aGlzLT5CdWlsZEJvZHkoKTsNCiAgICAgICAgJHRo +aXMtPmFzc2VydCgkdGhpcy0+TWFpbC0+U2VuZCgpLCAkdGhpcy0+TWFpbC0+RXJyb3JJbmZvKTsN +CiAgICB9DQoNCiAgICAvKioNCiAgICAgKiBTaW1wbGUgcGxhaW4gZmlsZSBhdHRhY2htZW50IHRl +c3QuDQogICAgICovDQogICAgZnVuY3Rpb24gdGVzdF9NdWx0aXBsZV9QbGFpbl9GaWxlQXR0YWNo +bWVudCgpIHsNCg0KICAgICAgICAkdGhpcy0+TWFpbC0+Qm9keSA9ICJIZXJlIGlzIHRoZSB0ZXh0 +IGJvZHkiOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCAuPSAiOiBQbGFpbiArIE11bHRp +cGxlIEZpbGVBdHRhY2htZW50cyI7DQoNCiAgICAgICAgaWYoISR0aGlzLT5NYWlsLT5BZGRBdHRh +Y2htZW50KCJ0ZXN0LnBuZyIpKQ0KICAgICAgICB7DQogICAgICAgICAgICAkdGhpcy0+YXNzZXJ0 +KGZhbHNlLCAkdGhpcy0+TWFpbC0+RXJyb3JJbmZvKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAg +ICAgICAgfQ0KDQogICAgICAgIGlmKCEkdGhpcy0+TWFpbC0+QWRkQXR0YWNobWVudCgicGhwbWFp +bGVyX3Rlc3QucGhwIiwgInRlc3QudHh0IikpDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlz +LT5hc3NlcnQoZmFsc2UsICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgICAgICAgICAgcmV0 +dXJuOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRoaXMtPkJ1aWxkQm9keSgpOw0KICAgICAgICAk +dGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWlsLT5TZW5kKCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8p +Ow0KICAgIH0NCg0KICAgIC8qKg0KICAgICAqIFNpbXBsZSBwbGFpbiBzdHJpbmcgYXR0YWNobWVu +dCB0ZXN0Lg0KICAgICAqLw0KICAgIGZ1bmN0aW9uIHRlc3RfUGxhaW5fU3RyaW5nQXR0YWNobWVu +dCgpIHsNCg0KICAgICAgICAkdGhpcy0+TWFpbC0+Qm9keSA9ICJIZXJlIGlzIHRoZSB0ZXh0IGJv +ZHkiOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCAuPSAiOiBQbGFpbiArIFN0cmluZ0F0 +dGFjaG1lbnQiOw0KICAgICAgICANCiAgICAgICAgJHNBdHRhY2htZW50ID0gIlRoZXNlIGNoYXJh +Y3RlcnMgYXJlIHRoZSBjb250ZW50IG9mIHRoZSAiIC4NCiAgICAgICAgICAgICAgICAgICAgICAg +InN0cmluZyBhdHRhY2htZW50LlxuVGhpcyBtaWdodCBiZSB0YWtlbiBmcm9tIGEgIi4NCiAgICAg +ICAgICAgICAgICAgICAgICAgImRhdGFiYXNlIG9yIHNvbWUgb3RoZXIgc3VjaCB0aGluZy4gIjsN +CiAgICAgICAgDQogICAgICAgICR0aGlzLT5NYWlsLT5BZGRTdHJpbmdBdHRhY2htZW50KCRzQXR0 +YWNobWVudCwgInN0cmluZ19hdHRhY2gudHh0Iik7DQoNCiAgICAgICAgJHRoaXMtPkJ1aWxkQm9k +eSgpOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWlsLT5TZW5kKCksICR0aGlzLT5N +YWlsLT5FcnJvckluZm8pOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAqIFBsYWluIHF1b3RlZC1w +cmludGFibGUgbWVzc2FnZS4NCiAgICAgKi8NCiAgICBmdW5jdGlvbiB0ZXN0X1F1b3RlZF9Qcmlu +dGFibGUoKSB7DQoNCiAgICAgICAgJHRoaXMtPk1haWwtPkJvZHkgPSAiSGVyZSBpcyB0aGUgbWFp +biBib2R5IjsNCiAgICAgICAgJHRoaXMtPk1haWwtPlN1YmplY3QgLj0gIjogUGxhaW4gKyBRdW90 +ZWQtcHJpbnRhYmxlIjsNCiAgICAgICAgJHRoaXMtPk1haWwtPkVuY29kaW5nID0gInF1b3RlZC1w +cmludGFibGUiOw0KDQogICAgICAgICR0aGlzLT5CdWlsZEJvZHkoKTsNCiAgICAgICAgJHRoaXMt +PmFzc2VydCgkdGhpcy0+TWFpbC0+U2VuZCgpLCAkdGhpcy0+TWFpbC0+RXJyb3JJbmZvKTsNCiAg +ICB9DQoNCiAgICAvKioNCiAgICAgKiBUcnkgYSBwbGFpbiBtZXNzYWdlLg0KICAgICAqLw0KICAg +IGZ1bmN0aW9uIHRlc3RfSHRtbCgpIHsNCiAgICANCiAgICAgICAgJHRoaXMtPk1haWwtPklzSFRN +TCh0cnVlKTsNCiAgICAgICAgJHRoaXMtPk1haWwtPlN1YmplY3QgLj0gIjogSFRNTCBvbmx5IjsN +CiAgICAgICAgDQogICAgICAgICR0aGlzLT5NYWlsLT5Cb2R5ID0gIlRoaXMgaXMgYSA8Yj50ZXN0 +IG1lc3NhZ2U8L2I+IHdyaXR0ZW4gaW4gSFRNTC4gPC9icj4iIC4NCiAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAiR28gdG8gPGEgaHJlZj1cImh0dHA6Ly9waHBtYWlsZXIuc291cmNlZm9yZ2Uu +bmV0L1wiPiIgLg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICJodHRwOi8vcGhwbWFpbGVy +LnNvdXJjZWZvcmdlLm5ldC88L2E+IGZvciBuZXcgdmVyc2lvbnMgb2YgIiAuDQogICAgICAgICAg +ICAgICAgICAgICAgICAgICAgInBocG1haWxlci4gIDxwLz4gVGhhbmsgeW91ISI7DQoNCiAgICAg +ICAgJHRoaXMtPkJ1aWxkQm9keSgpOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWls +LT5TZW5kKCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgIH0NCg0KICAgIC8qKg0KICAg +ICAqIFNpbXBsZSBIVE1MIGFuZCBhdHRhY2htZW50IHRlc3QNCiAgICAgKi8NCiAgICBmdW5jdGlv +biB0ZXN0X0hUTUxfQXR0YWNobWVudCgpIHsNCg0KICAgICAgICAkdGhpcy0+TWFpbC0+Qm9keSA9 +ICJUaGlzIGlzIHRoZSA8Yj5IVE1MPC9iPiBwYXJ0IG9mIHRoZSBlbWFpbC4iOw0KICAgICAgICAk +dGhpcy0+TWFpbC0+U3ViamVjdCAuPSAiOiBIVE1MICsgQXR0YWNobWVudCI7DQogICAgICAgICR0 +aGlzLT5NYWlsLT5Jc0hUTUwodHJ1ZSk7DQogICAgICAgIA0KICAgICAgICBpZighJHRoaXMtPk1h +aWwtPkFkZEF0dGFjaG1lbnQoInBocG1haWxlcl90ZXN0LnBocCIsICJ0ZXN0X2F0dGFjaC50eHQi +KSkNCiAgICAgICAgew0KICAgICAgICAgICAgJHRoaXMtPmFzc2VydChmYWxzZSwgJHRoaXMtPk1h +aWwtPkVycm9ySW5mbyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCg0KICAgICAg +ICAkdGhpcy0+QnVpbGRCb2R5KCk7DQogICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMtPk1haWwt +PlNlbmQoKSwgJHRoaXMtPk1haWwtPkVycm9ySW5mbyk7DQogICAgfQ0KDQogICAgLyoqDQogICAg +ICogQW4gZW1iZWRkZWQgYXR0YWNobWVudCB0ZXN0Lg0KICAgICAqLw0KICAgIGZ1bmN0aW9uIHRl +c3RfRW1iZWRkZWRfSW1hZ2UoKSB7DQoNCiAgICAgICAgJHRoaXMtPk1haWwtPkJvZHkgPSAiRW1i +ZWRkZWQgSW1hZ2U6IDxpbWcgYWx0PVwicGhwbWFpbGVyXCIgc3JjPVwiY2lkOm15LWF0dGFjaFwi +PiIgLg0KICAgICAgICAgICAgICAgICAgICAgIkhlcmUgaXMgYW4gaW1hZ2UhPC9hPiI7DQogICAg +ICAgICR0aGlzLT5NYWlsLT5TdWJqZWN0IC49ICI6IEVtYmVkZGVkIEltYWdlIjsNCiAgICAgICAg +JHRoaXMtPk1haWwtPklzSFRNTCh0cnVlKTsNCiAgICAgICAgDQogICAgICAgIGlmKCEkdGhpcy0+ +TWFpbC0+QWRkRW1iZWRkZWRJbWFnZSgidGVzdC5wbmciLCAibXktYXR0YWNoIiwgInRlc3QucG5n +IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYXNlNjQiLCAi +aW1hZ2UvcG5nIikpDQogICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5hc3NlcnQoZmFsc2Us +ICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgICAgICAgICAgcmV0dXJuOw0KICAgICAgICB9 +DQoNCiAgICAgICAgJHRoaXMtPkJ1aWxkQm9keSgpOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0 +aGlzLT5NYWlsLT5TZW5kKCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgIH0NCg0KICAg +IC8qKg0KICAgICAqIEFuIGVtYmVkZGVkIGF0dGFjaG1lbnQgdGVzdC4NCiAgICAgKi8NCiAgICBm +dW5jdGlvbiB0ZXN0X011bHRpX0VtYmVkZGVkX0ltYWdlKCkgew0KDQogICAgICAgICR0aGlzLT5N +YWlsLT5Cb2R5ID0gIkVtYmVkZGVkIEltYWdlOiA8aW1nIGFsdD1cInBocG1haWxlclwiIHNyYz1c +ImNpZDpteS1hdHRhY2hcIj4iIC4NCiAgICAgICAgICAgICAgICAgICAgICJIZXJlIGlzIGFuIGlt +YWdlITwvYT4iOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCAuPSAiOiBFbWJlZGRlZCBJ +bWFnZSArIEF0dGFjaG1lbnQiOw0KICAgICAgICAkdGhpcy0+TWFpbC0+SXNIVE1MKHRydWUpOw0K +ICAgICAgICANCiAgICAgICAgaWYoISR0aGlzLT5NYWlsLT5BZGRFbWJlZGRlZEltYWdlKCJ0ZXN0 +LnBuZyIsICJteS1hdHRhY2giLCAidGVzdC5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgImJhc2U2NCIsICJpbWFnZS9wbmciKSkNCiAgICAgICAgew0KICAg +ICAgICAgICAgJHRoaXMtPmFzc2VydChmYWxzZSwgJHRoaXMtPk1haWwtPkVycm9ySW5mbyk7DQog +ICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCg0KICAgICAgICBpZighJHRoaXMtPk1haWwt +PkFkZEF0dGFjaG1lbnQoInBocG1haWxlcl90ZXN0LnBocCIsICJ0ZXN0LnR4dCIpKQ0KICAgICAg +ICB7DQogICAgICAgICAgICAkdGhpcy0+YXNzZXJ0KGZhbHNlLCAkdGhpcy0+TWFpbC0+RXJyb3JJ +bmZvKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICANCiAgICAgICAg +JHRoaXMtPkJ1aWxkQm9keSgpOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWlsLT5T +ZW5kKCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgIH0NCg0KICAgIC8qKg0KICAgICAq +IFNpbXBsZSBtdWx0aXBhcnQvYWx0ZXJuYXRpdmUgdGVzdC4NCiAgICAgKi8NCiAgICBmdW5jdGlv +biB0ZXN0X0FsdEJvZHkoKSB7DQoNCiAgICAgICAgJHRoaXMtPk1haWwtPkJvZHkgPSAiVGhpcyBp +cyB0aGUgPGI+SFRNTDwvYj4gcGFydCBvZiB0aGUgZW1haWwuIjsNCiAgICAgICAgJHRoaXMtPk1h +aWwtPkFsdEJvZHkgPSAiSGVyZSBpcyB0aGUgdGV4dCBib2R5IG9mIHRoaXMgbWVzc2FnZS4gICIg +Lg0KICAgICAgICAgICAgICAgICAgICJJdCBzaG91bGQgYmUgcXVpdGUgYSBmZXcgbGluZXMuICBJ +dCBzaG91bGQgYmUgd3JhcHBlZCBhdCB0aGUgIiAuDQogICAgICAgICAgICAgICAgICAgIjQwIGNo +YXJhY3RlcnMuICBNYWtlIHN1cmUgdGhhdCBpdCBpcy4iOw0KICAgICAgICAkdGhpcy0+TWFpbC0+ +V29yZFdyYXAgPSA0MDsNCiAgICAgICAgJHRoaXMtPkFkZE5vdGUoIlRoaXMgaXMgYSBtdWxpcGFy +dCBhbHRlcm5hdGl2ZSBlbWFpbCIpOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCAuPSAi +OiBBbHRCb2R5ICsgV29yZCBXcmFwIjsNCg0KICAgICAgICAkdGhpcy0+QnVpbGRCb2R5KCk7DQog +ICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMtPk1haWwtPlNlbmQoKSwgJHRoaXMtPk1haWwtPkVy +cm9ySW5mbyk7DQogICAgfQ0KDQogICAgLyoqDQogICAgICogU2ltcGxlIEhUTUwgYW5kIGF0dGFj +aG1lbnQgdGVzdA0KICAgICAqLw0KICAgIGZ1bmN0aW9uIHRlc3RfQWx0Qm9keV9BdHRhY2htZW50 +KCkgew0KDQogICAgICAgICR0aGlzLT5NYWlsLT5Cb2R5ID0gIlRoaXMgaXMgdGhlIDxiPkhUTUw8 +L2I+IHBhcnQgb2YgdGhlIGVtYWlsLiI7DQogICAgICAgICR0aGlzLT5NYWlsLT5BbHRCb2R5ID0g +IlRoaXMgaXMgdGhlIHRleHQgcGFydCBvZiB0aGUgZW1haWwuIjsNCiAgICAgICAgJHRoaXMtPk1h +aWwtPlN1YmplY3QgLj0gIjogQWx0Qm9keSArIEF0dGFjaG1lbnQiOw0KICAgICAgICAkdGhpcy0+ +TWFpbC0+SXNIVE1MKHRydWUpOw0KICAgICAgICANCiAgICAgICAgaWYoISR0aGlzLT5NYWlsLT5B +ZGRBdHRhY2htZW50KCJwaHBtYWlsZXJfdGVzdC5waHAiLCAidGVzdF9hdHRhY2gudHh0IikpDQog +ICAgICAgIHsNCiAgICAgICAgICAgICR0aGlzLT5hc3NlcnQoZmFsc2UsICR0aGlzLT5NYWlsLT5F +cnJvckluZm8pOw0KICAgICAgICAgICAgcmV0dXJuOw0KICAgICAgICB9DQoNCiAgICAgICAgJHRo +aXMtPkJ1aWxkQm9keSgpOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWlsLT5TZW5k +KCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KDQogICAgICAgIGZpbGVfcHV0X2NvbnRlbnRz +KCdtZXNzYWdlLnR4dCcsICR0aGlzLT5NYWlsLT5DcmVhdGVIZWFkZXIoKSAuICR0aGlzLT5NYWls +LT5DcmVhdGVCb2R5KCkpOw0KICAgIH0gICAgDQoNCiAgICBmdW5jdGlvbiB0ZXN0X011bHRpcGxl +U2VuZCgpIHsNCiAgICAgICAgJHRoaXMtPk1haWwtPkJvZHkgPSAiU2VuZGluZyB0d28gbWVzc2Fn +ZXMgd2l0aG91dCBrZWVwYWxpdmUiOw0KICAgICAgICAkdGhpcy0+QnVpbGRCb2R5KCk7DQogICAg +ICAgICRzdWJqZWN0ID0gJHRoaXMtPk1haWwtPlN1YmplY3Q7DQoNCiAgICAgICAgJHRoaXMtPk1h +aWwtPlN1YmplY3QgPSAkc3ViamVjdCAuICI6IFNNVFAgMSI7DQogICAgICAgICR0aGlzLT5hc3Nl +cnQoJHRoaXMtPk1haWwtPlNlbmQoKSwgJHRoaXMtPk1haWwtPkVycm9ySW5mbyk7DQogICAgICAg +IA0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCA9ICRzdWJqZWN0IC4gIjogU01UUCAyIjsN +CiAgICAgICAgJHRoaXMtPmFzc2VydCgkdGhpcy0+TWFpbC0+U2VuZCgpLCAkdGhpcy0+TWFpbC0+ +RXJyb3JJbmZvKTsNCiAgICB9DQoNCiAgICBmdW5jdGlvbiB0ZXN0X1NtdHBLZWVwQWxpdmUoKSB7 +DQogICAgICAgICR0aGlzLT5NYWlsLT5Cb2R5ID0gIlRoaXMgd2FzIGRvbmUgdXNpbmcgdGhlIFNN +VFAga2VlcC1hbGl2ZS4iOw0KICAgICAgICAkdGhpcy0+QnVpbGRCb2R5KCk7DQogICAgICAgICRz +dWJqZWN0ID0gJHRoaXMtPk1haWwtPlN1YmplY3Q7DQoNCiAgICAgICAgJHRoaXMtPk1haWwtPlNN +VFBLZWVwQWxpdmUgPSB0cnVlOw0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCA9ICRzdWJq +ZWN0IC4gIjogU01UUCBrZWVwLWFsaXZlIDEiOw0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlz +LT5NYWlsLT5TZW5kKCksICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgICAgICANCiAgICAg +ICAgJHRoaXMtPk1haWwtPlN1YmplY3QgPSAkc3ViamVjdCAuICI6IFNNVFAga2VlcC1hbGl2ZSAy +IjsNCiAgICAgICAgJHRoaXMtPmFzc2VydCgkdGhpcy0+TWFpbC0+U2VuZCgpLCAkdGhpcy0+TWFp +bC0+RXJyb3JJbmZvKTsNCiAgICAgICAgJHRoaXMtPk1haWwtPlNtdHBDbG9zZSgpOw0KICAgIH0N +CiAgICANCiAgICAvKioNCiAgICAgKiBUZXN0cyB0aGlzIGRlbmlhbCBvZiBzZXJ2aWNlIGF0dGFj +azogDQogICAgICogICAgaHR0cDovL3d3dy5jeWJzZWMuY29tL3Z1bG4vUEhQTWFpbGVyLURPUy5w +ZGYNCiAgICAgKi8NCiAgICBmdW5jdGlvbiB0ZXN0X0RlbmlhbE9mU2VydmljZUF0dGFjaygpIHsN +CiAgICAgICAgJHRoaXMtPk1haWwtPkJvZHkgPSAiVGhpcyBzaG91bGQgbm8gbG9uZ2VyIGNhdXNl +IGEgZGVuaWFsIG9mIHNlcnZpY2UuIjsNCiAgICAgICAgJHRoaXMtPkJ1aWxkQm9keSgpOw0KICAg +ICAgIA0KICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCA9IHN0cl9yZXBlYXQoIkEiLCA5OTgp +Ow0KICAgICAgICAkdGhpcy0+YXNzZXJ0KCR0aGlzLT5NYWlsLT5TZW5kKCksICR0aGlzLT5NYWls +LT5FcnJvckluZm8pOw0KICAgIH0NCiAgICANCiAgICBmdW5jdGlvbiB0ZXN0X0Vycm9yKCkgew0K +ICAgICAgICAkdGhpcy0+TWFpbC0+U3ViamVjdCAuPSAiOiBUaGlzIHNob3VsZCBiZSBzZW50Ijsg +DQogICAgICAgICR0aGlzLT5CdWlsZEJvZHkoKTsNCiAgICAgICAgJHRoaXMtPk1haWwtPkNsZWFy +QWxsUmVjaXBpZW50cygpOyAvLyBubyBhZGRyZXNzZXMgc2hvdWxkIGNhdXNlIGFuIGVycm9yDQog +ICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMtPk1haWwtPklzRXJyb3IoKSA9PSBmYWxzZSwgIkVy +cm9yIGZvdW5kIik7DQogICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMtPk1haWwtPlNlbmQoKSA9 +PSBmYWxzZSwgIlNlbmQgc3VjY2VlZGVkIik7DQogICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMt +Pk1haWwtPklzRXJyb3IoKSwgIk5vIGVycm9yIGZvdW5kIik7DQogICAgICAgICR0aGlzLT5hc3Nl +cnRFcXVhbHMoJ1lvdSBtdXN0IHByb3ZpZGUgYXQgbGVhc3Qgb25lIHJlY2lwaWVudCBlbWFpbCBh +ZGRyZXNzLicsICR0aGlzLT5NYWlsLT5FcnJvckluZm8pOw0KICAgICAgICAkdGhpcy0+TWFpbC0+ +QWRkQWRkcmVzcyhnZXQoIm1haWxfdG8iKSk7DQogICAgICAgICR0aGlzLT5hc3NlcnQoJHRoaXMt +Pk1haWwtPlNlbmQoKSwgIlNlbmQgZmFpbGVkIik7DQogICAgfQ0KICAgIA0KCS8qKg0KCSogVGVz +dCBsYW5ndWFnZSBmaWxlcyBmb3IgbWlzc2luZyBhbmQgZXhjZXNzIHRyYW5zbGF0aW9ucw0KCSog +QWxsIGxhbmd1YWdlcyBhcmUgY29tcGFyZWQgd2l0aCBFbmdsaXNoDQoJKi8NCglmdW5jdGlvbiB0 +ZXN0X1RyYW5zbGF0aW9ucygpIHsNCgkJJHRoaXMtPk1haWwtPlNldExhbmd1YWdlKCdlbicpOw0K +CQkkZGVmaW5lZFN0cmluZ3MgPSAkdGhpcy0+TWFpbC0+R2V0VHJhbnNsYXRpb25zKCk7DQoJCWZv +cmVhY2ggKG5ldyBEaXJlY3RvcnlJdGVyYXRvcignLi4vbGFuZ3VhZ2UnKSBhcyAkZmlsZUluZm8p +IHsNCgkJCWlmKCRmaWxlSW5mby0+aXNEb3QoKSkgY29udGludWU7DQoJCQkkbWF0Y2hlcyA9IGFy +cmF5KCk7DQoJCQkvL09ubHkgbG9vayBhdCBsYW5ndWFnZSBmaWxlcywgaWdub3JlIGFueXRoaW5n +IGVsc2UgaW4gdGhlcmUNCgkJCWlmIChwcmVnX21hdGNoKCcvXnBocG1haWxlclwubGFuZy0oW2Et +el9dezIsfSlcLnBocCQvJywgJGZpbGVJbmZvLT5nZXRGaWxlbmFtZSgpLCAkbWF0Y2hlcykpIHsN +CgkJCQkkbGFuZyA9ICRtYXRjaGVzWzFdOyAvL0V4dHJhY3QgbGFuZ3VhZ2UgY29kZQ0KCQkJCSRQ +SFBNQUlMRVJfTEFORyA9IGFycmF5KCk7IC8vTGFuZ3VhZ2Ugc3RyaW5ncyBnZXQgcHV0IGluIGhl +cmUNCgkJCQlpbmNsdWRlIGRpcm5hbWUoZGlybmFtZShfX0ZJTEVfXykpLicvbGFuZ3VhZ2UvJy4k +ZmlsZUluZm8tPmdldEZpbGVuYW1lKCk7IC8vR2V0IGxhbmd1YWdlIHN0cmluZ3MNCgkJCQkkbWlz +c2luZyA9IGFycmF5X2RpZmYoYXJyYXlfa2V5cygkZGVmaW5lZFN0cmluZ3MpLCBhcnJheV9rZXlz +KCRQSFBNQUlMRVJfTEFORykpOw0KCQkJCSRleHRyYSA9IGFycmF5X2RpZmYoYXJyYXlfa2V5cygk +UEhQTUFJTEVSX0xBTkcpLCBhcnJheV9rZXlzKCRkZWZpbmVkU3RyaW5ncykpOw0KCQkJCSR0aGlz +LT5hc3NlcnQoZW1wdHkoJG1pc3NpbmcpLCAiTWlzc2luZyB0cmFuc2xhdGlvbnMgaW4gJGxhbmc6 +ICIuIGltcGxvZGUoJywgJywgJG1pc3NpbmcpKTsNCgkJCQkkdGhpcy0+YXNzZXJ0KGVtcHR5KCRl +eHRyYSksICJFeHRyYSB0cmFuc2xhdGlvbnMgaW4gJGxhbmc6ICIuIGltcGxvZGUoJywgJywgJGV4 +dHJhKSk7DQoJCQl9DQoJCX0NCgl9DQp9ICANCiANCi8qKg0KICogQ3JlYXRlIGFuZCBydW4gdGVz +dCBpbnN0YW5jZS4NCiAqLw0KIA0KaWYoaXNzZXQoJEhUVFBfR0VUX1ZBUlMpKQ0KICAgICRnbG9i +YWxfdmFycyA9ICRIVFRQX0dFVF9WQVJTOw0KZWxzZQ0KICAgICRnbG9iYWxfdmFycyA9ICRfUkVR +VUVTVDsNCg0KaWYoaXNzZXQoJGdsb2JhbF92YXJzWyJzdWJtaXR0ZWQiXSkpDQp7DQogICAgZWNo +byAiVGVzdCByZXN1bHRzOjxicj4iOw0KICAgICRzdWl0ZSA9IG5ldyBUZXN0U3VpdGUoICJwaHBt +YWlsZXJUZXN0IiApOw0KICAgIA0KICAgICR0ZXN0UnVubmVyID0gbmV3IFRlc3RSdW5uZXI7DQog +ICAgJHRlc3RSdW5uZXItPnJ1bigkc3VpdGUpOw0KICAgIGVjaG8gIjxociBub3NoYWRlLz4iOw0K +fQ0KDQpmdW5jdGlvbiBnZXQoJHNOYW1lKSB7DQogICAgZ2xvYmFsICRnbG9iYWxfdmFyczsNCiAg +ICBpZihpc3NldCgkZ2xvYmFsX3ZhcnNbJHNOYW1lXSkpDQogICAgICAgIHJldHVybiAkZ2xvYmFs +X3ZhcnNbJHNOYW1lXTsNCiAgICBlbHNlDQogICAgICAgIHJldHVybiAiIjsNCn0NCg0KPz4NCg0K +PGh0bWw+DQo8Ym9keT4NCjxoMz5waHBtYWlsZXIgVW5pdCBUZXN0PC9oMz4NCkJ5IGVudGVyaW5n +IGEgU01UUCBob3N0bmFtZSBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgcGVyZm9ybSB0ZXN0cyB3aXRo +IFNNVFAuDQoNCjxmb3JtIG5hbWU9InBocG1haWxlcl91bml0IiBhY3Rpb249InBocG1haWxlcl90 +ZXN0LnBocCIgbWV0aG9kPSJnZXQiPg0KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0ic3VibWl0 +dGVkIiB2YWx1ZT0iMSIvPg0KVG8gQWRkcmVzczogPGlucHV0IHR5cGU9InRleHQiIHNpemU9IjUw +IiBuYW1lPSJtYWlsX3RvIiB2YWx1ZT0iPD9waHAgZWNobyBnZXQoIm1haWxfdG8iKTsgPz4iLz4N +Cjxici8+DQpDYyBBZGRyZXNzOiA8aW5wdXQgdHlwZT0idGV4dCIgc2l6ZT0iNTAiIG5hbWU9Im1h +aWxfY2MiIHZhbHVlPSI8P3BocCBlY2hvIGdldCgibWFpbF9jYyIpOyA/PiIvPg0KPGJyLz4NClNN +VFAgSG9zdG5hbWU6IDxpbnB1dCB0eXBlPSJ0ZXh0IiBzaXplPSI1MCIgbmFtZT0ibWFpbF9ob3N0 +IiB2YWx1ZT0iPD9waHAgZWNobyBnZXQoIm1haWxfaG9zdCIpOyA/PiIvPg0KPHAvPg0KPGlucHV0 +IHR5cGU9InN1Ym1pdCIgdmFsdWU9IlJ1biBUZXN0Ii8+DQoNCjwvZm9ybT4NCjwvYm9keT4NCjwv +aHRtbD4NCg== + + +--b1_220194d0bc6c229cb9c5c2899123dba3-- diff --git a/test/phpmailerTest.php b/test/phpmailerTest.php index 853767dc..aae5d59f 100644 --- a/test/phpmailerTest.php +++ b/test/phpmailerTest.php @@ -315,7 +315,7 @@ class phpmailerTest extends PHPUnit_Framework_TestCase return; } - if(!$this->Mail->AddAttachment("phpmailer_test.php", "test.txt")) + if(!$this->Mail->AddAttachment(__FILE__, "test.txt")) { $this->assertTrue(false, $this->Mail->ErrorInfo); return; @@ -382,7 +382,7 @@ class phpmailerTest extends PHPUnit_Framework_TestCase $this->Mail->Subject .= ": HTML + Attachment"; $this->Mail->IsHTML(true); - if(!$this->Mail->AddAttachment("phpmailer_test.php", "test_attach.txt")) + if(!$this->Mail->AddAttachment(__FILE__, "test_attach.txt")) { $this->assertTrue(false, $this->Mail->ErrorInfo); return; @@ -430,7 +430,7 @@ class phpmailerTest extends PHPUnit_Framework_TestCase return; } - if(!$this->Mail->AddAttachment("phpmailer_test.php", "test.txt")) + if(!$this->Mail->AddAttachment(__FILE__, "test.txt")) { $this->assertTrue(false, $this->Mail->ErrorInfo); return; @@ -467,7 +467,7 @@ class phpmailerTest extends PHPUnit_Framework_TestCase $this->Mail->Subject .= ": AltBody + Attachment"; $this->Mail->IsHTML(true); - if(!$this->Mail->AddAttachment("phpmailer_test.php", "test_attach.txt")) + if(!$this->Mail->AddAttachment(__FILE__, "test_attach.txt")) { $this->assertTrue(false, $this->Mail->ErrorInfo); return; @@ -577,7 +577,7 @@ class phpmailerTest extends PHPUnit_Framework_TestCase

phpmailer Unit Test

By entering a SMTP hostname it will automatically perform tests with SMTP. -
+ From Address: "/>
diff --git a/test/testbootstrap.php b/test/testbootstrap.php new file mode 100644 index 00000000..c31a30cd --- /dev/null +++ b/test/testbootstrap.php @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/x.php b/x.php new file mode 100644 index 00000000..c250f23f --- /dev/null +++ b/x.php @@ -0,0 +1,2003 @@ +' . $this->getMessage() . "
"; + return $errorMsg; + } +} + +class PHPMailer { + + ///////////////////////////////////////////////// + // PROPERTIES, PUBLIC + ///////////////////////////////////////////////// + + /** + * Email priority (1 = High, 3 = Normal, 5 = low). + * @var int + */ + public $Priority = 3; + + /** + * Sets the CharSet of the message. + * @var string + */ + public $CharSet = 'iso-8859-1'; + + /** + * Sets the Content-type of the message. + * @var string + */ + public $ContentType = 'text/plain'; + + /** + * Sets the Encoding of the message. Options for this are "8bit", + * "7bit", "binary", "base64", and "quoted-printable". + * @var string + */ + public $Encoding = '8bit'; + + /** + * Holds the most recent mailer error message. + * @var string + */ + public $ErrorInfo = ''; + + /** + * Sets the From email address for the message. + * @var string + */ + public $From = 'root@localhost'; + + /** + * Sets the From name of the message. + * @var string + */ + 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. + * @var string + */ + public $Sender = ''; + + /** + * Sets the Subject of the message. + * @var string + */ + public $Subject = ''; + + /** + * Sets the Body of the message. This can be either an HTML or text body. + * If HTML then run 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. + * @var string + */ + public $AltBody = ''; + + /** + * Sets word wrapping on the body of the message to a given number of + * characters. + * @var int + */ + public $WordWrap = 0; + + /** + * Method to send mail: ("mail", "sendmail", or "smtp"). + * @var string + */ + public $Mailer = 'mail'; + + /** + * Sets the path of the sendmail program. + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Path to PHPMailer plugins. This is now only useful if the SMTP class + * is in a different directory than the PHP include path. + * @var string + */ + public $PluginDir = ''; + + /** + * Holds PHPMailer version. + * @var string + */ + const VERSION = '5.0.0'; + + /** + * Sets the email address that a reading confirmation will be sent. + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * Sets 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'. + * @var string + */ + public $Hostname = ''; + + /** + * Sets the message ID to be used in the Message-Id header. + * If empty, a unique id will be generated. + * @var string + */ + public $MessageID = ''; + + ///////////////////////////////////////////////// + // PROPERTIES FOR SMTP + ///////////////////////////////////////////////// + + /** + * Sets the SMTP hosts. All hosts must be separated by a + * semicolon. You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * Hosts will be tried in order. + * @var string + */ + public $Host = 'localhost'; + + /** + * Sets the default SMTP server port. + * @var int + */ + public $Port = 25; + + /** + * Sets the SMTP HELO of the message (Default is $Hostname). + * @var string + */ + public $Helo = ''; + + /** + * Sets connection prefix. + * Options are "", "ssl" or "tls" + * @var string + */ + public $SMTPSecure = ""; + + /** + * Sets SMTP authentication. Utilizes the Username and Password variables. + * @var bool + */ + public $SMTPAuth = false; + + /** + * Sets SMTP username. + * @var string + */ + public $Username = ''; + + /** + * Sets SMTP password. + * @var string + */ + public $Password = ''; + + /** + * Sets the SMTP server timeout in seconds. This function will not + * work with the win32 version. + * @var int + */ + public $Timeout = 10; + + /** + * Sets SMTP class debugging on or off. + * @var bool + */ + public $SMTPDebug = false; + + /** + * Prevents the SMTP connection from being closed after each mail + * sending. If this is set to true then to close the connection + * requires an explicit call to SmtpClose(). + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * Provides the ability to have the TO field process individual + * emails, instead of sending to entire TO addresses + * @var bool + */ + public $SingleTo = false; + + /** + * Provides the ability to change the line ending + * @var string + */ + public $LE = "\n"; + + ///////////////////////////////////////////////// + // PROPERTIES, PRIVATE + ///////////////////////////////////////////////// + + private $smtp = NULL; + private $to = array(); + private $cc = array(); + private $bcc = array(); + private $ReplyTo = array(); + private $all_recipients = array(); + private $attachment = array(); + private $CustomHeader = array(); + private $message_type = ''; + private $boundary = array(); + private $language = array(); + private $error_count = 0; + private $sign_cert_file = ''; + private $sign_key_file = ''; + private $sign_key_pass = ''; + private $exceptions = false; + + ///////////////////////////////////////////////// + // METHODS + ///////////////////////////////////////////////// + +/** + * Constructor + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = false) { + $this->exceptions = ($exceptions == true); + } + + /** + * Sets message type to HTML. + * @param bool $bool + * @return void + */ + public function IsHTML($bool) { + if($bool == true) { + $this->ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Sets Mailer to send message using SMTP. + * @return void + */ + public function IsSMTP() { + $this->Mailer = 'smtp'; + } + + /** + * Sets Mailer to send message using PHP mail() function. + * @return void + */ + public function IsMail() { + $this->Mailer = 'mail'; + } + + /** + * Sets Mailer to send message using the $Sendmail program. + * @return void + */ + public function IsSendmail() { + $this->Mailer = 'sendmail'; + } + + /** + * Sets Mailer to send message using the qmail MTA. + * @return void + */ + public function IsQmail() { + $this->Sendmail = '/var/qmail/bin/sendmail'; + $this->Mailer = 'sendmail'; + } + + ///////////////////////////////////////////////// + // METHODS, RECIPIENTS + ///////////////////////////////////////////////// + + /** + * Adds a "To" address. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddAddress($address, $name = '') { + return $this->AddAnAddress('to', $address, $name); + } + + /** + * Adds a "Cc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddCC($address, $name = '') { + return $this->AddAnAddress('cc', $address, $name); + } + + /** + * Adds a "Bcc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddBCC($address, $name = '') { + return $this->AddAnAddress('bcc', $address, $name); + } + + /** + * Adds an address to one of the recipient arrays + * @param string $kind One of 'to', 'cc', 'bcc' + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + * @access private + */ + private function AddAnAddress($kind, $address, $name = '') { + $address = trim($address); + if (!preg_match('/^(to|cc|bcc)$/', $kind)) { + echo 'Invalid recipient array: ' . kind; + return false; + } + if (!self::ValidateAddress($address)) { + if ($this->exceptions) { + throw new phpmailerException('Invalid address: '.$address); + } + echo 'Invalid address: '.$address; + return false; + } + if (!isset($this->all_recipients[strtolower($address)])) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + return false; + } + + /** + * Check that a string looks roughly like an email address should + * Static so it can be used without instantiation + * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator + * Conforms approximately to RFC2822 + * @link http://www.hexillion.com/samples/#Regex Original pattern found here + * @param string $address The email address to check + * @return boolean + * @static + * @access public + */ + public static function ValidateAddress($address) { + if (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); + } + } + + /** + * Adds a "Reply-to" address. + * @param string $address + * @param string $name + * @return void + */ + public function AddReplyTo($address, $name = '') { + if (!self::ValidateAddress($address)) { + //throw exception + echo 'Invalid address: '.$address; + return false; + } + $this->ReplyTo[] = array(trim($address), $name); + } + + ///////////////////////////////////////////////// + // METHODS, MAIL SENDING + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Use the ErrorInfo + * variable to view description of the error. + * @return bool + */ + public function Send() { + try { + if ( (count($this->to) + count($this->cc) + count($this->bcc)) < 1 ) { + throw new phpmailerException($this->Lang('provide_address')); + } + /* Set whether the message is multipart/alternative */ + if ( !empty($this->AltBody) ) { + $this->ContentType = 'multipart/alternative'; + } + $this->error_count = 0; // reset errors + $this->SetMessageType(); + $header = $this->CreateHeader(); + $body = $this->CreateBody(); + if (empty($body)) { + throw new phpmailerException($this->Lang('empty_message')); + } + /* Choose the mailer */ + switch($this->Mailer) { + case 'sendmail': + $result = $this->SendmailSend($header, $body); + break; + case 'smtp': + $result = $this->SmtpSend($header, $body); + break; + case 'mail': + default: + $result = $this->MailSend($header, $body); + break; + } + return $result; + } + catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; //Rethrow to external caller + } + echo $e->errorMessage(); + return false; + } + } + + /** + * Sends mail using the $Sendmail program. + * @param string $header The message headers + * @param string $body The message body + * @access protected + * @return bool + */ + protected function SendmailSend($header, $body) { + if ($this->Sender != '') { + $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } else { + $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + } + + if ( !@$mail = popen($sendmail, 'w') ) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + if (version_compare(phpversion(), '4.2.3') == -1) { + $result = $result >> 8 & 0xFF; + } + if ( $result != 0 ) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail); + } + return true; + } + + /** + * Sends mail using the PHP mail() function. + * @param string $header The message headers + * @param string $body The message body + * @access protected + * @return bool + */ + protected function MailSend($header, $body) { + $error = NULL; + + $toArr = array(); + foreach($this->to as $t) { + $toArr[] = $this->AddrFormat($t); + } + $to = implode(', ', $toArr); + + $params = sprintf("-oi -f %s", $this->Sender); + if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); + } + } + + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if ( !$rt ) { + throw new phpmailerException($this->Lang('instantiate')); + } + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * @param string $header The message headers + * @param string $body The message body + * @uses SMTP + * @access public + * @return bool + */ + protected function SmtpSend($header, $body) { + require_once $this->PluginDir . 'class.smtp.php'; + $bad_rcpt = array(); + + try { + if ( !$this->SmtpConnect() ) { + throw new phpmailerException($this->Lang('connect_host')); + } + $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; + if ( !$this->smtp->Mail($smtp_from) ) { + throw new phpmailerException($this->Lang('from_failed') . $smtp_from); + } + + /* Attempt to send attach all recipients */ + foreach($this->to as $to) { + if (!$this->smtp->Recipient($to[0])) { + $bad_rcpt[] = $to[0]; + } + } + foreach($this->cc as $cc) { + if (!$this->smtp->Recipient($cc[0])) { + $bad_rcpt[] = $cc[0]; + } + } + foreach($this->bcc as $bcc) { + if (!$this->smtp->Recipient($bcc[0])) { + $bad_rcpt[] = $bcc[0]; + } + } + if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses + $badaddresses = implode(', ', $bad_rcpt); + throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); + } + if ( !$this->smtp->Data($header . $body) ) { + throw new phpmailerException($this->Lang('data_not_accepted')); + } + if ( $this->SMTPKeepAlive == true ) { + $this->smtp->Reset(); + } else { + $this->SmtpClose(); + } + } + catch (phpmailerException $e) { + $this->smtp->Reset(); + throw $e; + } + return true; + } + + /** + * Initiates a connection to an SMTP server. + * Returns false if the operation failed. + * @uses SMTP + * @access public + * @return bool + */ + public function SmtpConnect() { + if(is_null($this->smtp)) { + $this->smtp = new SMTP(); + } + + $this->smtp->do_debug = $this->SMTPDebug; + $hosts = explode(';', $this->Host); + $index = 0; + $connection = $this->smtp->Connected(); + + /* 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()); + $this->smtp->Hello($hello); + + if ($tls) { + if (!$this->smtp->StartTLS()) { + throw new phpmailerException($this->Lang('tls')); + } + + //We must resend HELO after tls negotiation + $this->smtp->Hello($hello); + } + + $connection = true; + if ($this->SMTPAuth) { + if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + throw new phpmailerException($this->Lang('authenticate')); + } + } + } + $index++; + if (!$connection) { + throw new phpmailerException($this->Lang('connect_host')); + } + } + } + catch (phpmailerException $e) { + $this->smtp->Reset(); + throw $e; + } + return true; + } + + /** + * Closes the active SMTP session if one exists. + * @return void + */ + public function SmtpClose() { + if(!is_null($this->smtp)) { + if($this->smtp->Connected()) { + $this->smtp->Quit(); + $this->smtp->Close(); + } + } + } + + /** + * Sets the language for all class error messages. Returns false + * if it cannot load the language file. The default language type + * 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 + * @access public + */ + function SetLanguage($langcode = 'en', $lang_path = 'language/') { + //Define full set of translatable strings + $PHPMAILER_LANG = array( + 'provide_address' => 'You must provide at least one recipient email address.', + 'mailer_not_supported' => ' mailer is not supported.', + 'execute' => 'Could not execute: ', + 'instantiate' => 'Could not instantiate mail function.', + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'from_failed' => 'The following From address failed: ', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'data_not_accepted' => 'SMTP Error: Data not accepted.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'encoding' => 'Unknown encoding: ', + 'signing' => 'Signing Error: ', + 'smtp_error' => 'SMTP server error: ', + 'empty_message' => 'Message body empty', + 'invalid_address' => 'Invalid address' + ); + //Overwrite langauge-specific strings. This way we'll never have missing translations - no more "language string failed to load"! + @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; + $this->language = $PHPMAILER_LANG; + } + + /** + * Return the current array of language strings + * @return array + */ + public function GetTranslations() { + return $this->language; + } + + ///////////////////////////////////////////////// + // METHODS, MESSAGE CREATION + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. + * @access public + * @return string + */ + public function AddrAppend($type, $addr) { + $addr_str = $type . ': '; + $addresses = array(); + foreach ($addr as $a) { + $addresses[] = $this->AddrFormat($a); + } + $addr_str .= implode(', ', $addresses); + $addr_str .= $this->LE; + + return $addr_str; + } + + /** + * Formats an address correctly. + * @access public + * @return string + */ + public function AddrFormat($addr) { + if (empty($addr[1])) { + return $this->SecureHeader($addr[0]); + } else { + return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; + } + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode + * @access public + * @return string + */ + 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 + $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + + $message = $this->FixEOL($message); + if (substr($message, -1) == $this->LE) { + $message = substr($message, 0, -1); + } + + $line = explode($this->LE, $message); + $message = ''; + for ($i=0 ;$i < count($line); $i++) { + $line_part = explode(' ', $line[$i]); + $buf = ''; + for ($e = 0; $e $length)) { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->UTF8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + $len = $length; + if ($is_utf8) { + $len = $this->UTF8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf("=%s", $this->LE); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + $buf .= ($e == 0) ? $word : (' ' . $word); + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return $message; + } + + /** + * Finds last character boundary prior to maxLength in a utf-8 + * quoted (printable) encoded string. + * Original written by Colin Brown. + * @access public + * @param string $encodedText utf-8 QP text + * @param int $maxLength find last character boundary prior to this length + * @return int + */ + public function UTF8CharBoundary($encodedText, $maxLength) { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, "="); + if ($encodedCharPos !== false) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + $maxLength = ($encodedCharPos == 0) ? $maxLength : + $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec >= 192) { // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + return $maxLength; + } + + + /** + * Set the body wrapping. + * @access public + * @return void + */ + public function SetWordWrap() { + if ( $this->WordWrap < 1 ) { + return; + } + + switch($this->message_type) { + case 'alt': + case 'alt_attachments': + $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->WrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assembles message header. + * @access public + * @return string The assembled header + */ + public function CreateHeader() { + $result = ''; + + /* Set the boundaries */ + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = 'b1_' . $uniq_id; + $this->boundary[2] = 'b2_' . $uniq_id; + + $result .= $this->HeaderLine('Date', self::RFCDate()); + if($this->Sender == '') { + $result .= $this->HeaderLine('Return-Path', trim($this->From)); + } else { + $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); + } + + /* To be created automatically by mail() */ + if($this->Mailer != 'mail') { + if(count($this->to) > 0) { + $result .= $this->AddrAppend('To', $this->to); + } elseif (count($this->cc) == 0) { + $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); + } + } + + $from = array(); + $from[0][0] = trim($this->From); + $from[0][1] = $this->FromName; + $result .= $this->AddrAppend('From', $from); + + /* sendmail and mail() extract Cc from the header before sending */ + if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (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)) { + $result .= $this->AddrAppend('Bcc', $this->bcc); + } + + if(count($this->ReplyTo) > 0) { + $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); + } + + /* mail() sets the subject itself */ + if($this->Mailer != 'mail') { + $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); + } + + 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); + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer ' . self::VERSION . ' (phpmailer.codeworxtech.com)'); + + if($this->ConfirmReadingTo != '') { + $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); + } + + // Add custom headers + 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) { + $result .= $this->HeaderLine('MIME-Version', '1.0'); + $result .= $this->GetMailMIME(); + } + + return $result; + } + + /** + * Returns the message MIME. + * @access public + * @return string + */ + public function GetMailMIME() { + $result = ''; + switch($this->message_type) { + case 'plain': + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); + $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); + break; + case 'attachments': + /* fall through */ + case 'alt_attachments': + if($this->InlineImageExists()){ + $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); + } else { + $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + } + break; + case 'alt': + $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + } + + if($this->Mailer != 'mail') { + $result .= $this->LE.$this->LE; + } + + return $result; + } + + /** + * Assembles the message body. Returns an empty string on failure. + * @access public + * @return string The assembled message body + */ + public function CreateBody() { + $body = ''; + + if ($this->sign_key_file) { + $body .= $this->GetMailMIME(); + } + + $this->SetWordWrap(); + + switch($this->message_type) { + case 'alt': + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[1]); + break; + case 'plain': + $body .= $this->EncodeString($this->Body, $this->Encoding); + break; + case 'attachments': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE; + $body .= $this->AttachAll(); + break; + case 'alt_attachments': + $body .= sprintf("--%s%s", $this->boundary[1], $this->LE); + $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->AttachAll(); + break; + } + + if ($this->IsError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + $file = tempnam('', '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)) { + @unlink($file); + @unlink($signed); + $body = file_get_contents($signed); + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->Lang("signing").openssl_error_string()); + } + } + + catch (phpmailerException $e) { + $body = ''; + if ($this->exceptions) { + throw $e; + } + } + } + + return $body; + } + + /** + * Returns the start of a message boundary. + * @access private + */ + private function GetBoundary($boundary, $charSet, $contentType, $encoding) { + $result = ''; + if($charSet == '') { + $charSet = $this->CharSet; + } + if($contentType == '') { + $contentType = $this->ContentType; + } + if($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->TextLine('--' . $boundary); + $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); + $result .= $this->LE; + $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); + $result .= $this->LE; + + return $result; + } + + /** + * Returns the end of a message boundary. + * @access private + */ + private function EndBoundary($boundary) { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Sets the message type. + * @access private + * @return void + */ + private function SetMessageType() { + if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { + $this->message_type = 'plain'; + } else { + if(count($this->attachment) > 0) { + $this->message_type = 'attachments'; + } + if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { + $this->message_type = 'alt'; + } + if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { + $this->message_type = 'alt_attachments'; + } + } + } + + /* Returns a formatted header line. + * @access public + * @return string + */ + public function HeaderLine($name, $value) { + return $name . ': ' . $value . $this->LE; + } + + /** + * Returns a formatted mail line. + * @access public + * @return string + */ + public function TextLine($value) { + return $value . $this->LE; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, ATTACHMENTS + ///////////////////////////////////////////////// + + /** + * Adds an attachment from a path on the filesystem. + * Returns false if the file could not be found + * or accessed. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + $error = NULL; + + try { + if ( !@is_file($path) ) { + throw new phpmailerException($this->Lang('file_access') . $path); + } + } + catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->errorMessage(); + return false; + } + + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + + return true; + } + + /** + * Return the current array of attachments + * @return array + */ + public function GetAttachments() { + return $this->attachment; + } + + /** + * Attaches all fs, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access private + * @return string + */ + private function AttachAll() { + /* Return text of body */ + $mime = array(); + + /* Add all attachments */ + foreach ($this->attachment as $attachment) { + /* Check for string attachment */ + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $filename = $attachment[1]; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + + $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); + $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') { + $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); + + /* Encode as string attachment */ + if($bString) { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } else { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } + } + + $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + + return join('', $mime); + } + + /** + * Encodes attachment in requested format. Returns an + * empty string on failure. + * @access private + * @return string + */ + private function EncodeFile($path, $encoding = 'base64') { + if (!is_readable($path)) { + $this->SetError($this->Lang('file_open') . $path); + return ''; + } + if (function_exists('get_magic_quotes')) { + function get_magic_quotes() { + return false; + } +} + if (PHP_VERSION < 6) { + $magic_quotes = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->EncodeString($file_buffer, $encoding); + if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } + return $file_buffer; + } + + /** + * Encodes string to requested format. Returns an + * empty string on failure. + * @access public + * @return string + */ + public function EncodeString ($str, $encoding = 'base64') { + $encoded = ''; + switch(strtolower($encoding)) { + case 'base64': + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->FixEOL($str); + if (substr($encoded, -(strlen($this->LE))) != $this->LE) + $encoded .= $this->LE; + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->EncodeQP($str); + break; + default: + $this->SetError($this->Lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string to best of Q, B, quoted or none. + * @access public + * @return string + */ + public function EncodeHeader ($str, $position = 'text') { + $x = 0; + + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */ + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + case 'comment': + $x = preg_match_all('/[()"]/', $str, $matches); + /* Fall-through */ + case 'text': + default: + $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($x == 0) { + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + /* Try to select the encoding which should produce the shortest output */ + if (strlen($str)/3 < $x) { + $encoding = 'B'; + if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->Base64EncodeWrapMB($str); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + } else { + $encoding = 'Q'; + $encoded = $this->EncodeQ($str, $position); + $encoded = $this->WrapText($encoded, $maxlen, true); + $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Checks if a string contains multibyte characters. + * @access public + * @param string $str multi-byte text to wrap encode + * @return bool + */ + 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; + } + } + + /** + * Correctly encodes and wraps long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php + * @access public + * @param string $str multi-byte text to wrap encode + * @return string + */ + public function Base64EncodeWrapMB($str) { + $start = "=?".$this->CharSet."?B?"; + $end = "?="; + $encoded = ""; + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $offset = $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + $lookBack++; + } + while (strlen($chunk) > $length); + + $encoded .= $chunk . $this->LE; + } + + // Chomp the last linefeed + $encoded = substr($encoded, 0, -strlen($this->LE)); + return $encoded; + } + + /** + * Encode string to quoted-printable. + * @access public + * @param string $string the text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + */ + public function EncodeQP( $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'; + } else if ( $space_conv ) { + $c = '=20'; + } + } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required + $h2 = floor($dec/16); + $h1 = 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 q encoding. + * @access public + * @return string + */ + public function EncodeQ ($str, $position = 'text') { + /* There should not be any EOL in the string */ + $encoded = preg_replace("[\r\n]", '', $str); + + switch (strtolower($position)) { + case 'phrase': + $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + case 'comment': + $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + case 'text': + default: + /* Replace every high ascii, control =, ? and _ characters */ + $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', + "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + } + + /* Replace every spaces to _ (more readable than =20) */ + $encoded = str_replace(' ', '_', $encoded); + + return $encoded; + } + + /** + * Adds a string or binary attachment (non-filesystem) to the list. + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return void + */ + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + /* Append to $attachment array */ + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => $filename, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + } + + /** + * 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". + * @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 $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + + if ( !@is_file($path) ) { + $this->SetError($this->Lang('file_access') . $path); + return false; + } + + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + /* Append to $attachment array */ + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + + return true; + } + + /** + * Returns true if an inline attachment is present. + * @access public + * @return bool + */ + public function InlineImageExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MESSAGE RESET + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @return void + */ + public function ClearAddresses() { + foreach($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @return void + */ + public function ClearCCs() { + foreach($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @return void + */ + public function ClearBCCs() { + foreach($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @return void + */ + public function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @return void + */ + public function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + $this->all_recipients = array(); + } + + /** + * Clears all previously set filesystem, string, and binary + * attachments. Returns void. + * @return void + */ + public function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @return void + */ + public function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MISCELLANEOUS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * @access protected + * @return void + */ + protected function SetError($msg) { + $this->error_count++; + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. + * @access public + * @return string + */ + 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; + } + + /** + * Returns the server hostname or 'localhost.localdomain' if unknown. + * @access private + * @return string + */ + private function ServerHostname() { + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; + } else { + $result = 'localhost.localdomain'; + } + + return $result; + } + + /** + * Returns a message in the appropriate language. + * @access private + * @return string + */ + private function Lang($key) { + if(count($this->language) < 1) { + $this->SetLanguage('en'); // set the default language + } + + if(isset($this->language[$key])) { + return $this->language[$key]; + } else { + return 'Language string failed to load: ' . $key; + } + } + + /** + * Returns true if an error occurred. + * @access public + * @return bool + */ + public function IsError() { + return ($this->error_count > 0); + } + + /** + * Changes every end of line from CR or LF to CRLF. + * @access private + * @return string + */ + private function FixEOL($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", $this->LE, $str); + return $str; + } + + /** + * Adds a custom header. + * @access public + * @return void + */ + public function AddCustomHeader($custom_header) { + $this->CustomHeader[] = explode(':', $custom_header, 2); + } + + /** + * Evaluates the message and returns modifications for inline images and backgrounds + * @access public + * @return $message + */ + public function MsgHTML($message, $basedir = '') { + preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); + if(isset($images[2])) { + foreach($images[2] as $i => $url) { + // do not change urls for absolute images (thanks to corvuscorax) + if (!preg_match('#^[A-z]+://#',$url)) { + $filename = basename($url); + $directory = dirname($url); + ($directory == '.')?$directory='':''; + $cid = 'cid:' . md5($filename); + $ext = pathinfo($filename, PATHINFO_EXTENSION); + $mimeType = $this->_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($filename), $filename, 'base64',$mimeType) ) { + $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + } + } + } + } + $this->IsHTML(true); + $this->Body = $message; + $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message))); + if ( !empty($textMsg) && empty($this->AltBody) ) { + $this->AltBody = html_entity_decode($textMsg); + } + if (empty($this->AltBody) ) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; + } + } + + /** + * Gets the mime type of the embedded or inline image + * @access public + * @return mime type of ext + */ + public function _mime_types($ext = '') { + $mimes = array( + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'doc' => 'application/msword', + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => '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', + 'php' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php3' => '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', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xl' => 'application/excel', + 'eml' => 'message/rfc822' + ); + 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 + */ + public function set ($name, $value = '') { + $error = NULL; + + try { + if ( isset($this->$name) ) { + $this->$name = $value; + } else { + $error = 'Cannot set or reset variable ' . $name; + + throw new phpmailerException($error); + } + } + catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->errorMessage(); + return false; + } + + } + + /** + * Read a file from a supplied filename and return it. + * TODO this function is a pointless wrapper! + * @access public + * @param string $filename Parameter File Name + * @return mixed Either the text of the file or boolean false if it fails to read + */ + public function getFile($filename) { + return @file_get_contents($filename); + } + + /** + * Strips newlines to prevent header injection. + * @access public + * @param string $str String + * @return string + */ + public function SecureHeader($str) { + $str = str_replace("\r", '', $str); + $str = str_replace("\n", '', $str); + return trim($str); + } + + /** + * Set the private key file and password to sign the message. + * + * @access public + * @param string $key_filename Parameter File Name + * @param string $key_pass Password for private key + */ + 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; + } + } +?> \ No newline at end of file