Path: blob/master/externals/phpmailer/class.phpmailer.php
12240 views
<?php1/*~ class.phpmailer.php2.---------------------------------------------------------------------------.3| Software: PHPMailer - PHP email class |4| Version: 5.1 |5| Contact: via sourceforge.net support pages (also www.worxware.com) |6| Info: http://phpmailer.sourceforge.net |7| Support: http://sourceforge.net/projects/phpmailer/ |8| ------------------------------------------------------------------------- |9| Admin: Andy Prevost (project admininistrator) |10| Authors: Andy Prevost (codeworxtech) [email protected] |11| : Marcus Bointon (coolbru) [email protected] |12| Founder: Brent R. Matzelle (original founder) |13| Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. |14| Copyright (c) 2001-2003, Brent R. Matzelle |15| ------------------------------------------------------------------------- |16| License: Distributed under the Lesser General Public License (LGPL) |17| http://www.gnu.org/copyleft/lesser.html |18| This program is distributed in the hope that it will be useful - WITHOUT |19| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |20| FITNESS FOR A PARTICULAR PURPOSE. |21| ------------------------------------------------------------------------- |22| We offer a number of paid services (www.worxware.com): |23| - Web Hosting on highly optimized fast and secure servers |24| - Technology Consulting |25| - Oursourcing (highly qualified programmers and graphic designers) |26'---------------------------------------------------------------------------'27*/2829/**30* PHPMailer - PHP email transport class31* NOTE: Requires PHP version 5 or later32* @package PHPMailer33* @author Andy Prevost34* @author Marcus Bointon35* @copyright 2004 - 2009 Andy Prevost36* @version $Id: class.phpmailer.php 447 2009-05-25 01:36:38Z codeworxtech $37* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License38*/3940if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n");4142class PHPMailer {4344/////////////////////////////////////////////////45// PROPERTIES, PUBLIC46/////////////////////////////////////////////////4748/**49* Email priority (1 = High, 3 = Normal, 5 = low).50* @var int51*/52public $Priority = 3;5354/**55* Sets the CharSet of the message.56* @var string57*/58public $CharSet = 'iso-8859-1';5960/**61* Sets the Content-type of the message.62* @var string63*/64public $ContentType = 'text/plain';6566/**67* Sets the Encoding of the message. Options for this are68* "8bit", "7bit", "binary", "base64", and "quoted-printable".69* @var string70*/71public $Encoding = '8bit';7273/**74* Holds the most recent mailer error message.75* @var string76*/77public $ErrorInfo = '';7879/**80* Sets the From email address for the message.81* @var string82*/83public $From = 'root@localhost';8485/**86* Sets the From name of the message.87* @var string88*/89public $FromName = 'Root User';9091/**92* Sets the Sender email (Return-Path) of the message. If not empty,93* will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.94* @var string95*/96public $Sender = '';9798/**99* Sets the Subject of the message.100* @var string101*/102public $Subject = '';103104/**105* Sets the Body of the message. This can be either an HTML or text body.106* If HTML then run IsHTML(true).107* @var string108*/109public $Body = '';110111/**112* Sets the text-only body of the message. This automatically sets the113* email to multipart/alternative. This body can be read by mail114* clients that do not have HTML email capability such as mutt. Clients115* that can read HTML will view the normal Body.116* @var string117*/118public $AltBody = '';119120/**121* Sets word wrapping on the body of the message to a given number of122* characters.123* @var int124*/125public $WordWrap = 0;126127/**128* Method to send mail: ("mail", "sendmail", or "smtp").129* @var string130*/131public $Mailer = 'mail';132133/**134* Sets the path of the sendmail program.135* @var string136*/137public $Sendmail = '/usr/sbin/sendmail';138139/**140* Path to PHPMailer plugins. Useful if the SMTP class141* is in a different directory than the PHP include path.142* @var string143*/144public $PluginDir = '';145146/**147* Sets the email address that a reading confirmation will be sent.148* @var string149*/150public $ConfirmReadingTo = '';151152/**153* Sets the hostname to use in Message-Id and Received headers154* and as default HELO string. If empty, the value returned155* by SERVER_NAME is used or 'localhost.localdomain'.156* @var string157*/158public $Hostname = '';159160/**161* Sets the message ID to be used in the Message-Id header.162* If empty, a unique id will be generated.163* @var string164*/165public $MessageID = '';166167/////////////////////////////////////////////////168// PROPERTIES FOR SMTP169/////////////////////////////////////////////////170171/**172* Sets the SMTP hosts. All hosts must be separated by a173* semicolon. You can also specify a different port174* for each host by using this format: [hostname:port]175* (e.g. "smtp1.example.com:25;smtp2.example.com").176* Hosts will be tried in order.177* @var string178*/179public $Host = 'localhost';180181/**182* Sets the default SMTP server port.183* @var int184*/185public $Port = 25;186187/**188* Sets the SMTP HELO of the message (Default is $Hostname).189* @var string190*/191public $Helo = '';192193/**194* Sets connection prefix.195* Options are "", "ssl" or "tls"196* @var string197*/198public $SMTPSecure = '';199200/**201* Sets SMTP authentication. Utilizes the Username and Password variables.202* @var bool203*/204public $SMTPAuth = false;205206/**207* Sets SMTP username.208* @var string209*/210public $Username = '';211212/**213* Sets SMTP password.214* @var string215*/216public $Password = '';217218/**219* Sets the SMTP server timeout in seconds.220* This function will not work with the win32 version.221* @var int222*/223public $Timeout = 60;224225/**226* Sets SMTP class debugging on or off.227* @var bool228*/229public $SMTPDebug = false;230231/**232* Prevents the SMTP connection from being closed after each mail233* sending. If this is set to true then to close the connection234* requires an explicit call to SmtpClose().235* @var bool236*/237public $SMTPKeepAlive = false;238239/**240* Provides the ability to have the TO field process individual241* emails, instead of sending to entire TO addresses242* @var bool243*/244public $SingleTo = false;245246/**247* If SingleTo is true, this provides the array to hold the email addresses248* @var bool249*/250public $SingleToArray = array();251252/**253* Provides the ability to change the line ending254* @var string255*/256public $LE = "\n";257258/**259* Used with DKIM DNS Resource Record260* @var string261*/262public $DKIM_selector = 'phpmailer';263264/**265* Used with DKIM DNS Resource Record266* optional, in format of email address '[email protected]'267* @var string268*/269public $DKIM_identity = '';270271/**272* Used with DKIM DNS Resource Record273* optional, in format of email address '[email protected]'274* @var string275*/276public $DKIM_domain = '';277278/**279* Used with DKIM DNS Resource Record280* optional, in format of email address '[email protected]'281* @var string282*/283public $DKIM_private = '';284285/**286* Callback Action function name287* the function that handles the result of the send email action. Parameters:288* bool $result result of the send action289* string $to email address of the recipient290* string $cc cc email addresses291* string $bcc bcc email addresses292* string $subject the subject293* string $body the email body294* @var string295*/296public $action_function = ''; //'callbackAction';297298/**299* Sets the PHPMailer Version number300* @var string301*/302public $Version = '5.1';303304/////////////////////////////////////////////////305// PROPERTIES, PRIVATE AND PROTECTED306/////////////////////////////////////////////////307308private $smtp = NULL;309private $to = array();310private $cc = array();311private $bcc = array();312private $ReplyTo = array();313private $all_recipients = array();314private $attachment = array();315private $CustomHeader = array();316private $message_type = '';317private $boundary = array();318protected $language = array();319private $error_count = 0;320private $sign_cert_file = "";321private $sign_key_file = "";322private $sign_key_pass = "";323private $exceptions = false;324325/////////////////////////////////////////////////326// CONSTANTS327/////////////////////////////////////////////////328329const STOP_MESSAGE = 0; // message only, continue processing330const STOP_CONTINUE = 1; // message?, likely ok to continue processing331const STOP_CRITICAL = 2; // message, plus full stop, critical error reached332333/////////////////////////////////////////////////334// METHODS, VARIABLES335/////////////////////////////////////////////////336337/**338* Constructor339* @param boolean $exceptions Should we throw external exceptions?340*/341public function __construct($exceptions = false) {342$this->exceptions = ($exceptions == true);343}344345/**346* Sets message type to HTML.347* @param bool $ishtml348* @return void349*/350public function IsHTML($ishtml = true) {351if ($ishtml) {352$this->ContentType = 'text/html';353} else {354$this->ContentType = 'text/plain';355}356}357358/**359* Sets Mailer to send message using SMTP.360* @return void361*/362public function IsSMTP() {363$this->Mailer = 'smtp';364}365366/**367* Sets Mailer to send message using PHP mail() function.368* @return void369*/370public function IsMail() {371$this->Mailer = 'mail';372}373374/**375* Sets Mailer to send message using the $Sendmail program.376* @return void377*/378public function IsSendmail() {379if (!stristr(ini_get('sendmail_path'), 'sendmail')) {380$this->Sendmail = '/var/qmail/bin/sendmail';381}382$this->Mailer = 'sendmail';383}384385/**386* Sets Mailer to send message using the qmail MTA.387* @return void388*/389public function IsQmail() {390if (stristr(ini_get('sendmail_path'), 'qmail')) {391$this->Sendmail = '/var/qmail/bin/sendmail';392}393$this->Mailer = 'sendmail';394}395396/////////////////////////////////////////////////397// METHODS, RECIPIENTS398/////////////////////////////////////////////////399400/**401* Adds a "To" address.402* @param string $address403* @param string $name404* @return boolean true on success, false if address already used405*/406public function AddAddress($address, $name = '') {407return $this->AddAnAddress('to', $address, $name);408}409410/**411* Adds a "Cc" address.412* Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.413* @param string $address414* @param string $name415* @return boolean true on success, false if address already used416*/417public function AddCC($address, $name = '') {418return $this->AddAnAddress('cc', $address, $name);419}420421/**422* Adds a "Bcc" address.423* Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.424* @param string $address425* @param string $name426* @return boolean true on success, false if address already used427*/428public function AddBCC($address, $name = '') {429return $this->AddAnAddress('bcc', $address, $name);430}431432/**433* Adds a "Reply-to" address.434* @param string $address435* @param string $name436* @return boolean437*/438public function AddReplyTo($address, $name = '') {439return $this->AddAnAddress('ReplyTo', $address, $name);440}441442/**443* Adds an address to one of the recipient arrays444* Addresses that have been added already return false, but do not throw exceptions445* @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'446* @param string $address The email address to send to447* @param string $name448* @return boolean true on success, false if address already used or invalid in some way449* @access private450*/451private function AddAnAddress($kind, $address, $name = '') {452if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) {453echo 'Invalid recipient array: ' . kind;454return false;455}456$address = trim($address);457$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim458if (!self::ValidateAddress($address)) {459$this->SetError($this->Lang('invalid_address').': '. $address);460if ($this->exceptions) {461throw new phpmailerException($this->Lang('invalid_address').': '.$address);462}463echo $this->Lang('invalid_address').': '.$address;464return false;465}466if ($kind != 'ReplyTo') {467if (!isset($this->all_recipients[strtolower($address)])) {468array_push($this->$kind, array($address, $name));469$this->all_recipients[strtolower($address)] = true;470return true;471}472} else {473if (!array_key_exists(strtolower($address), $this->ReplyTo)) {474$this->ReplyTo[strtolower($address)] = array($address, $name);475return true;476}477}478return false;479}480481/**482* Set the From and FromName properties483* @param string $address484* @param string $name485* @return boolean486*/487public function SetFrom($address, $name = '',$auto=1) {488$address = trim($address);489$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim490if (!self::ValidateAddress($address)) {491$this->SetError($this->Lang('invalid_address').': '. $address);492if ($this->exceptions) {493throw new phpmailerException($this->Lang('invalid_address').': '.$address);494}495echo $this->Lang('invalid_address').': '.$address;496return false;497}498$this->From = $address;499$this->FromName = $name;500if ($auto) {501if (empty($this->ReplyTo)) {502$this->AddAnAddress('ReplyTo', $address, $name);503}504if (empty($this->Sender)) {505$this->Sender = $address;506}507}508return true;509}510511/**512* Check that a string looks roughly like an email address should513* Static so it can be used without instantiation514* Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator515* Conforms approximately to RFC2822516* @link http://www.hexillion.com/samples/#Regex Original pattern found here517* @param string $address The email address to check518* @return boolean519* @static520* @access public521*/522public static function ValidateAddress($address) {523if (function_exists('filter_var')) { //Introduced in PHP 5.2524if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {525return false;526} else {527return true;528}529} else {530return 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);531}532}533534/////////////////////////////////////////////////535// METHODS, MAIL SENDING536/////////////////////////////////////////////////537538/**539* Creates message and assigns Mailer. If the message is540* not sent successfully then it returns false. Use the ErrorInfo541* variable to view description of the error.542* @return bool543*/544public function Send() {545try {546if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {547throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);548}549550// Set whether the message is multipart/alternative551if(!empty($this->AltBody)) {552$this->ContentType = 'multipart/alternative';553}554555$this->error_count = 0; // reset errors556$this->SetMessageType();557$header = $this->CreateHeader();558$body = $this->CreateBody();559560if (empty($this->Body)) {561throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);562}563564// digitally sign with DKIM if enabled565if ($this->DKIM_domain && $this->DKIM_private) {566$header_dkim = $this->DKIM_Add($header,$this->Subject,$body);567$header = str_replace("\r\n","\n",$header_dkim) . $header;568}569570// Choose the mailer and send through it571switch($this->Mailer) {572case 'sendmail':573return $this->SendmailSend($header, $body);574case 'smtp':575return $this->SmtpSend($header, $body);576default:577return $this->MailSend($header, $body);578}579580} catch (phpmailerException $e) {581$this->SetError($e->getMessage());582if ($this->exceptions) {583throw $e;584}585echo $e->getMessage()."\n";586return false;587}588}589590/**591* Sends mail using the $Sendmail program.592* @param string $header The message headers593* @param string $body The message body594* @access protected595* @return bool596*/597protected function SendmailSend($header, $body) {598if ($this->Sender != '') {599$sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));600} else {601$sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));602}603604if ($this->SingleTo === true) {605foreach ($this->SingleToArray as $key => $val) {606$mail = new ExecFuture('%C', $sendmail);607$mail->write("To: {$val}\n", true);608$mail->write($header.$body);609$mail->resolvex();610}611} else {612$mail = new ExecFuture('%C', $sendmail);613$mail->write($header.$body);614$mail->resolvex();615}616617return true;618}619620/**621* Sends mail using the PHP mail() function.622* @param string $header The message headers623* @param string $body The message body624* @access protected625* @return bool626*/627protected function MailSend($header, $body) {628$toArr = array();629foreach($this->to as $t) {630$toArr[] = $this->AddrFormat($t);631}632$to = implode(', ', $toArr);633634$params = sprintf("-oi -f %s", $this->Sender);635if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {636$old_from = ini_get('sendmail_from');637ini_set('sendmail_from', $this->Sender);638if ($this->SingleTo === true && count($toArr) > 1) {639foreach ($toArr as $key => $val) {640$rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);641// implement call back function if it exists642$isSent = ($rt == 1) ? 1 : 0;643$this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);644}645} else {646$rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);647// implement call back function if it exists648$isSent = ($rt == 1) ? 1 : 0;649$this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);650}651} else {652if ($this->SingleTo === true && count($toArr) > 1) {653foreach ($toArr as $key => $val) {654$rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);655// implement call back function if it exists656$isSent = ($rt == 1) ? 1 : 0;657$this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);658}659} else {660$rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);661// implement call back function if it exists662$isSent = ($rt == 1) ? 1 : 0;663$this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);664}665}666if (isset($old_from)) {667ini_set('sendmail_from', $old_from);668}669if(!$rt) {670throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);671}672return true;673}674675/**676* Sends mail via SMTP using PhpSMTP677* Returns false if there is a bad MAIL FROM, RCPT, or DATA input.678* @param string $header The message headers679* @param string $body The message body680* @uses SMTP681* @access protected682* @return bool683*/684protected function SmtpSend($header, $body) {685require_once $this->PluginDir . 'class.smtp.php';686$bad_rcpt = array();687688if(!$this->SmtpConnect()) {689throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);690}691$smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;692if(!$this->smtp->Mail($smtp_from)) {693throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL);694}695696// Attempt to send attach all recipients697foreach($this->to as $to) {698if (!$this->smtp->Recipient($to[0])) {699$bad_rcpt[] = $to[0];700// implement call back function if it exists701$isSent = 0;702$this->doCallback($isSent,$to[0],'','',$this->Subject,$body);703} else {704// implement call back function if it exists705$isSent = 1;706$this->doCallback($isSent,$to[0],'','',$this->Subject,$body);707}708}709foreach($this->cc as $cc) {710if (!$this->smtp->Recipient($cc[0])) {711$bad_rcpt[] = $cc[0];712// implement call back function if it exists713$isSent = 0;714$this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);715} else {716// implement call back function if it exists717$isSent = 1;718$this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);719}720}721foreach($this->bcc as $bcc) {722if (!$this->smtp->Recipient($bcc[0])) {723$bad_rcpt[] = $bcc[0];724// implement call back function if it exists725$isSent = 0;726$this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);727} else {728// implement call back function if it exists729$isSent = 1;730$this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);731}732}733734735if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses736$badaddresses = implode(', ', $bad_rcpt);737throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);738}739if(!$this->smtp->Data($header . $body)) {740throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);741}742if($this->SMTPKeepAlive == true) {743$this->smtp->Reset();744}745return true;746}747748/**749* Initiates a connection to an SMTP server.750* Returns false if the operation failed.751* @uses SMTP752* @access public753* @return bool754*/755public function SmtpConnect() {756if(is_null($this->smtp)) {757$this->smtp = new SMTP();758}759760$this->smtp->do_debug = $this->SMTPDebug;761$hosts = explode(';', $this->Host);762$index = 0;763$connection = $this->smtp->Connected();764765// Retry while there is no connection766try {767while($index < count($hosts) && !$connection) {768$hostinfo = array();769if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {770$host = $hostinfo[1];771$port = $hostinfo[2];772} else {773$host = $hosts[$index];774$port = $this->Port;775}776777$tls = ($this->SMTPSecure == 'tls');778$ssl = ($this->SMTPSecure == 'ssl');779780if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {781782$hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());783$this->smtp->Hello($hello);784785if ($tls) {786if (!$this->smtp->StartTLS()) {787throw new phpmailerException($this->Lang('tls'));788}789790//We must resend HELO after tls negotiation791$this->smtp->Hello($hello);792}793794$connection = true;795if ($this->SMTPAuth) {796if (!$this->smtp->Authenticate($this->Username, $this->Password)) {797throw new phpmailerException($this->Lang('authenticate'));798}799}800}801$index++;802if (!$connection) {803throw new phpmailerException($this->Lang('connect_host'));804}805}806} catch (phpmailerException $e) {807$this->smtp->Reset();808throw $e;809}810return true;811}812813/**814* Closes the active SMTP session if one exists.815* @return void816*/817public function SmtpClose() {818if(!is_null($this->smtp)) {819if($this->smtp->Connected()) {820$this->smtp->Quit();821$this->smtp->Close();822}823}824}825826/**827* Sets the language for all class error messages.828* Returns false if it cannot load the language file. The default language is English.829* @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")830* @param string $lang_path Path to the language file directory831* @access public832*/833function SetLanguage($langcode = 'en', $lang_path = 'language/') {834//Define full set of translatable strings835$PHPMAILER_LANG = array(836'provide_address' => 'You must provide at least one recipient email address.',837'mailer_not_supported' => ' mailer is not supported.',838'execute' => 'Could not execute: ',839'instantiate' => 'Could not instantiate mail function.',840'authenticate' => 'SMTP Error: Could not authenticate.',841'from_failed' => 'The following From address failed: ',842'recipients_failed' => 'SMTP Error: The following recipients failed: ',843'data_not_accepted' => 'SMTP Error: Data not accepted.',844'connect_host' => 'SMTP Error: Could not connect to SMTP host.',845'file_access' => 'Could not access file: ',846'file_open' => 'File Error: Could not open file: ',847'encoding' => 'Unknown encoding: ',848'signing' => 'Signing Error: ',849'smtp_error' => 'SMTP server error: ',850'empty_message' => 'Message body empty',851'invalid_address' => 'Invalid address',852'variable_set' => 'Cannot set or reset variable: '853);854//Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!855$l = true;856if ($langcode != 'en') { //There is no English translation file857$l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';858}859$this->language = $PHPMAILER_LANG;860return ($l == true); //Returns false if language not found861}862863/**864* Return the current array of language strings865* @return array866*/867public function GetTranslations() {868return $this->language;869}870871/////////////////////////////////////////////////872// METHODS, MESSAGE CREATION873/////////////////////////////////////////////////874875/**876* Creates recipient headers.877* @access public878* @return string879*/880public function AddrAppend($type, $addr) {881$addr_str = $type . ': ';882$addresses = array();883foreach ($addr as $a) {884$addresses[] = $this->AddrFormat($a);885}886$addr_str .= implode(', ', $addresses);887$addr_str .= $this->LE;888889return $addr_str;890}891892/**893* Formats an address correctly.894* @access public895* @return string896*/897public function AddrFormat($addr) {898if (empty($addr[1])) {899return $this->SecureHeader($addr[0]);900} else {901return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";902}903}904905/**906* Wraps message for use with mailers that do not907* automatically perform wrapping and for quoted-printable.908* Original written by philippe.909* @param string $message The message to wrap910* @param integer $length The line length to wrap to911* @param boolean $qp_mode Whether to run in Quoted-Printable mode912* @access public913* @return string914*/915public function WrapText($message, $length, $qp_mode = false) {916$soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;917// If utf-8 encoding is used, we will need to make sure we don't918// split multibyte characters when we wrap919$is_utf8 = (strtolower($this->CharSet) == "utf-8");920921$message = $this->FixEOL($message);922if (substr($message, -1) == $this->LE) {923$message = substr($message, 0, -1);924}925926$line = explode($this->LE, $message);927$message = '';928for ($i=0 ;$i < count($line); $i++) {929$line_part = explode(' ', $line[$i]);930$buf = '';931for ($e = 0; $e<count($line_part); $e++) {932$word = $line_part[$e];933if ($qp_mode and (strlen($word) > $length)) {934$space_left = $length - strlen($buf) - 1;935if ($e != 0) {936if ($space_left > 20) {937$len = $space_left;938if ($is_utf8) {939$len = $this->UTF8CharBoundary($word, $len);940} elseif (substr($word, $len - 1, 1) == "=") {941$len--;942} elseif (substr($word, $len - 2, 1) == "=") {943$len -= 2;944}945$part = substr($word, 0, $len);946$word = substr($word, $len);947$buf .= ' ' . $part;948$message .= $buf . sprintf("=%s", $this->LE);949} else {950$message .= $buf . $soft_break;951}952$buf = '';953}954while (strlen($word) > 0) {955$len = $length;956if ($is_utf8) {957$len = $this->UTF8CharBoundary($word, $len);958} elseif (substr($word, $len - 1, 1) == "=") {959$len--;960} elseif (substr($word, $len - 2, 1) == "=") {961$len -= 2;962}963$part = substr($word, 0, $len);964$word = substr($word, $len);965966if (strlen($word) > 0) {967$message .= $part . sprintf("=%s", $this->LE);968} else {969$buf = $part;970}971}972} else {973$buf_o = $buf;974$buf .= ($e == 0) ? $word : (' ' . $word);975976if (strlen($buf) > $length and $buf_o != '') {977$message .= $buf_o . $soft_break;978$buf = $word;979}980}981}982$message .= $buf . $this->LE;983}984985return $message;986}987988/**989* Finds last character boundary prior to maxLength in a utf-8990* quoted (printable) encoded string.991* Original written by Colin Brown.992* @access public993* @param string $encodedText utf-8 QP text994* @param int $maxLength find last character boundary prior to this length995* @return int996*/997public function UTF8CharBoundary($encodedText, $maxLength) {998$foundSplitPos = false;999$lookBack = 3;1000while (!$foundSplitPos) {1001$lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);1002$encodedCharPos = strpos($lastChunk, "=");1003if ($encodedCharPos !== false) {1004// Found start of encoded character byte within $lookBack block.1005// Check the encoded byte value (the 2 chars after the '=')1006$hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);1007$dec = hexdec($hex);1008if ($dec < 128) { // Single byte character.1009// If the encoded char was found at pos 0, it will fit1010// otherwise reduce maxLength to start of the encoded char1011$maxLength = ($encodedCharPos == 0) ? $maxLength :1012$maxLength - ($lookBack - $encodedCharPos);1013$foundSplitPos = true;1014} elseif ($dec >= 192) { // First byte of a multi byte character1015// Reduce maxLength to split at start of character1016$maxLength = $maxLength - ($lookBack - $encodedCharPos);1017$foundSplitPos = true;1018} elseif ($dec < 192) { // Middle byte of a multi byte character, look further back1019$lookBack += 3;1020}1021} else {1022// No encoded character found1023$foundSplitPos = true;1024}1025}1026return $maxLength;1027}102810291030/**1031* Set the body wrapping.1032* @access public1033* @return void1034*/1035public function SetWordWrap() {1036if($this->WordWrap < 1) {1037return;1038}10391040switch($this->message_type) {1041case 'alt':1042case 'alt_attachments':1043$this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);1044break;1045default:1046$this->Body = $this->WrapText($this->Body, $this->WordWrap);1047break;1048}1049}10501051/**1052* Assembles message header.1053* @access public1054* @return string The assembled header1055*/1056public function CreateHeader() {1057$result = '';10581059// Set the boundaries1060$uniq_id = md5(uniqid(time()));1061$this->boundary[1] = 'b1_' . $uniq_id;1062$this->boundary[2] = 'b2_' . $uniq_id;10631064$result .= $this->HeaderLine('Date', self::RFCDate());1065if($this->Sender == '') {1066$result .= $this->HeaderLine('Return-Path', trim($this->From));1067} else {1068$result .= $this->HeaderLine('Return-Path', trim($this->Sender));1069}10701071// To be created automatically by mail()1072if($this->Mailer != 'mail') {1073if ($this->SingleTo === true) {1074foreach($this->to as $t) {1075$this->SingleToArray[] = $this->AddrFormat($t);1076}1077} else {1078if(count($this->to) > 0) {1079$result .= $this->AddrAppend('To', $this->to);1080} elseif (count($this->cc) == 0) {1081$result .= $this->HeaderLine('To', 'undisclosed-recipients:;');1082}1083}1084}10851086$from = array();1087$from[0][0] = trim($this->From);1088$from[0][1] = $this->FromName;1089$result .= $this->AddrAppend('From', $from);10901091// sendmail and mail() extract Cc from the header before sending1092if(count($this->cc) > 0) {1093$result .= $this->AddrAppend('Cc', $this->cc);1094}10951096// sendmail and mail() extract Bcc from the header before sending1097if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {1098$result .= $this->AddrAppend('Bcc', $this->bcc);1099}11001101if(count($this->ReplyTo) > 0) {1102$result .= $this->AddrAppend('Reply-to', $this->ReplyTo);1103}11041105// mail() sets the subject itself1106if($this->Mailer != 'mail') {1107$result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));1108}11091110if($this->MessageID != '') {1111$result .= $this->HeaderLine('Message-ID',$this->MessageID);1112}1113$result .= $this->HeaderLine('X-Priority', $this->Priority);1114$result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.sourceforge.net)');11151116if($this->ConfirmReadingTo != '') {1117$result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');1118}11191120// Add custom headers1121for($index = 0; $index < count($this->CustomHeader); $index++) {1122$result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));1123}1124if (!$this->sign_key_file) {1125$result .= $this->HeaderLine('MIME-Version', '1.0');1126$result .= $this->GetMailMIME();1127}11281129return $result;1130}11311132/**1133* Returns the message MIME.1134* @access public1135* @return string1136*/1137public function GetMailMIME() {1138$result = '';1139switch($this->message_type) {1140case 'plain':1141$result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);1142$result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);1143break;1144case 'attachments':1145case 'alt_attachments':1146if($this->InlineImageExists()){1147$result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);1148} else {1149$result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');1150$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1151}1152break;1153case 'alt':1154$result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');1155$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1156break;1157}11581159if($this->Mailer != 'mail') {1160$result .= $this->LE.$this->LE;1161}11621163return $result;1164}11651166/**1167* Assembles the message body. Returns an empty string on failure.1168* @access public1169* @return string The assembled message body1170*/1171public function CreateBody() {1172$body = '';11731174if ($this->sign_key_file) {1175$body .= $this->GetMailMIME();1176}11771178$this->SetWordWrap();11791180switch($this->message_type) {1181case 'alt':1182$body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');1183$body .= $this->EncodeString($this->AltBody, $this->Encoding);1184$body .= $this->LE.$this->LE;1185$body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');1186$body .= $this->EncodeString($this->Body, $this->Encoding);1187$body .= $this->LE.$this->LE;1188$body .= $this->EndBoundary($this->boundary[1]);1189break;1190case 'plain':1191$body .= $this->EncodeString($this->Body, $this->Encoding);1192break;1193case 'attachments':1194$body .= $this->GetBoundary($this->boundary[1], '', '', '');1195$body .= $this->EncodeString($this->Body, $this->Encoding);1196$body .= $this->LE;1197$body .= $this->AttachAll();1198break;1199case 'alt_attachments':1200$body .= sprintf("--%s%s", $this->boundary[1], $this->LE);1201$body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);1202$body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body1203$body .= $this->EncodeString($this->AltBody, $this->Encoding);1204$body .= $this->LE.$this->LE;1205$body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body1206$body .= $this->EncodeString($this->Body, $this->Encoding);1207$body .= $this->LE.$this->LE;1208$body .= $this->EndBoundary($this->boundary[2]);1209$body .= $this->AttachAll();1210break;1211}12121213if ($this->IsError()) {1214$body = '';1215} elseif ($this->sign_key_file) {1216try {1217$file = tempnam('', 'mail');1218file_put_contents($file, $body); //TODO check this worked1219$signed = tempnam("", "signed");1220if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {1221@unlink($file);1222@unlink($signed);1223$body = file_get_contents($signed);1224} else {1225@unlink($file);1226@unlink($signed);1227throw new phpmailerException($this->Lang("signing").openssl_error_string());1228}1229} catch (phpmailerException $e) {1230$body = '';1231if ($this->exceptions) {1232throw $e;1233}1234}1235}12361237return $body;1238}12391240/**1241* Returns the start of a message boundary.1242* @access private1243*/1244private function GetBoundary($boundary, $charSet, $contentType, $encoding) {1245$result = '';1246if($charSet == '') {1247$charSet = $this->CharSet;1248}1249if($contentType == '') {1250$contentType = $this->ContentType;1251}1252if($encoding == '') {1253$encoding = $this->Encoding;1254}1255$result .= $this->TextLine('--' . $boundary);1256$result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);1257$result .= $this->LE;1258$result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);1259$result .= $this->LE;12601261return $result;1262}12631264/**1265* Returns the end of a message boundary.1266* @access private1267*/1268private function EndBoundary($boundary) {1269return $this->LE . '--' . $boundary . '--' . $this->LE;1270}12711272/**1273* Sets the message type.1274* @access private1275* @return void1276*/1277private function SetMessageType() {1278if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {1279$this->message_type = 'plain';1280} else {1281if(count($this->attachment) > 0) {1282$this->message_type = 'attachments';1283}1284if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {1285$this->message_type = 'alt';1286}1287if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {1288$this->message_type = 'alt_attachments';1289}1290}1291}12921293/**1294* Returns a formatted header line.1295* @access public1296* @return string1297*/1298public function HeaderLine($name, $value) {1299return $name . ': ' . $value . $this->LE;1300}13011302/**1303* Returns a formatted mail line.1304* @access public1305* @return string1306*/1307public function TextLine($value) {1308return $value . $this->LE;1309}13101311/////////////////////////////////////////////////1312// CLASS METHODS, ATTACHMENTS1313/////////////////////////////////////////////////13141315/**1316* Adds an attachment from a path on the filesystem.1317* Returns false if the file could not be found1318* or accessed.1319* @param string $path Path to the attachment.1320* @param string $name Overrides the attachment name.1321* @param string $encoding File encoding (see $Encoding).1322* @param string $type File extension (MIME) type.1323* @return bool1324*/1325public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {1326try {1327if ( !@is_file($path) ) {1328throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);1329}1330$filename = basename($path);1331if ( $name == '' ) {1332$name = $filename;1333}13341335$this->attachment[] = array(13360 => $path,13371 => $filename,13382 => $name,13393 => $encoding,13404 => $type,13415 => false, // isStringAttachment13426 => 'attachment',13437 => 01344);13451346} catch (phpmailerException $e) {1347$this->SetError($e->getMessage());1348if ($this->exceptions) {1349throw $e;1350}1351echo $e->getMessage()."\n";1352if ( $e->getCode() == self::STOP_CRITICAL ) {1353return false;1354}1355}1356return true;1357}13581359/**1360* Return the current array of attachments1361* @return array1362*/1363public function GetAttachments() {1364return $this->attachment;1365}13661367/**1368* Attaches all fs, string, and binary attachments to the message.1369* Returns an empty string on failure.1370* @access private1371* @return string1372*/1373private function AttachAll() {1374// Return text of body1375$mime = array();1376$cidUniq = array();1377$incl = array();13781379// Add all attachments1380foreach ($this->attachment as $attachment) {1381// Check for string attachment1382$bString = $attachment[5];1383if ($bString) {1384$string = $attachment[0];1385} else {1386$path = $attachment[0];1387}13881389if (in_array($attachment[0], $incl)) { continue; }1390$filename = $attachment[1];1391$name = $attachment[2];1392$encoding = $attachment[3];1393$type = $attachment[4];1394$disposition = $attachment[6];1395$cid = $attachment[7];1396$incl[] = $attachment[0];1397if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }1398$cidUniq[$cid] = true;13991400$mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);1401$mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);1402$mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);14031404if($disposition == 'inline') {1405$mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);1406}14071408$mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);14091410// Encode as string attachment1411if($bString) {1412$mime[] = $this->EncodeString($string, $encoding);1413if($this->IsError()) {1414return '';1415}1416$mime[] = $this->LE.$this->LE;1417} else {1418$mime[] = $this->EncodeFile($path, $encoding);1419if($this->IsError()) {1420return '';1421}1422$mime[] = $this->LE.$this->LE;1423}1424}14251426$mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);14271428return join('', $mime);1429}14301431/**1432* Encodes attachment in requested format.1433* Returns an empty string on failure.1434* @param string $path The full path to the file1435* @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1436* @see EncodeFile()1437* @access private1438* @return string1439*/1440private function EncodeFile($path, $encoding = 'base64') {1441try {1442if (!is_readable($path)) {1443throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);1444}1445if (function_exists('get_magic_quotes')) {1446function get_magic_quotes() {1447return false;1448}1449}1450if (PHP_VERSION < 6) {1451$magic_quotes = get_magic_quotes_runtime();1452set_magic_quotes_runtime(0);1453}1454$file_buffer = file_get_contents($path);1455$file_buffer = $this->EncodeString($file_buffer, $encoding);1456if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }1457return $file_buffer;1458} catch (Exception $e) {1459$this->SetError($e->getMessage());1460return '';1461}1462}14631464/**1465* Encodes string to requested format.1466* Returns an empty string on failure.1467* @param string $str The text to encode1468* @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1469* @access public1470* @return string1471*/1472public function EncodeString ($str, $encoding = 'base64') {1473$encoded = '';1474switch(strtolower($encoding)) {1475case 'base64':1476$encoded = chunk_split(base64_encode($str), 76, $this->LE);1477break;1478case '7bit':1479case '8bit':1480$encoded = $this->FixEOL($str);1481//Make sure it ends with a line break1482if (substr($encoded, -(strlen($this->LE))) != $this->LE)1483$encoded .= $this->LE;1484break;1485case 'binary':1486$encoded = $str;1487break;1488case 'quoted-printable':1489$encoded = $this->EncodeQP($str);1490break;1491default:1492$this->SetError($this->Lang('encoding') . $encoding);1493break;1494}1495return $encoded;1496}14971498/**1499* Encode a header string to best (shortest) of Q, B, quoted or none.1500* @access public1501* @return string1502*/1503public function EncodeHeader($str, $position = 'text') {1504$x = 0;15051506switch (strtolower($position)) {1507case 'phrase':1508if (!preg_match('/[\200-\377]/', $str)) {1509// Can't use addslashes as we don't know what value has magic_quotes_sybase1510$encoded = addcslashes($str, "\0..\37\177\\\"");1511if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {1512return ($encoded);1513} else {1514return ("\"$encoded\"");1515}1516}1517$x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);1518break;1519case 'comment':1520$x = preg_match_all('/[()"]/', $str, $matches);1521// Fall-through1522case 'text':1523default:1524$x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);1525break;1526}15271528if ($x == 0) {1529return ($str);1530}15311532$maxlen = 75 - 7 - strlen($this->CharSet);1533// Try to select the encoding which should produce the shortest output1534if (strlen($str)/3 < $x) {1535$encoding = 'B';1536if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {1537// Use a custom function which correctly encodes and wraps long1538// multibyte strings without breaking lines within a character1539$encoded = $this->Base64EncodeWrapMB($str);1540} else {1541$encoded = base64_encode($str);1542$maxlen -= $maxlen % 4;1543$encoded = trim(chunk_split($encoded, $maxlen, "\n"));1544}1545} else {1546$encoding = 'Q';1547$encoded = $this->EncodeQ($str, $position);1548$encoded = $this->WrapText($encoded, $maxlen, true);1549$encoded = str_replace('='.$this->LE, "\n", trim($encoded));1550}15511552$encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);1553$encoded = trim(str_replace("\n", $this->LE, $encoded));15541555return $encoded;1556}15571558/**1559* Checks if a string contains multibyte characters.1560* @access public1561* @param string $str multi-byte text to wrap encode1562* @return bool1563*/1564public function HasMultiBytes($str) {1565if (function_exists('mb_strlen')) {1566return (strlen($str) > mb_strlen($str, $this->CharSet));1567} else { // Assume no multibytes (we can't handle without mbstring functions anyway)1568return false;1569}1570}15711572/**1573* Correctly encodes and wraps long multibyte strings for mail headers1574* without breaking lines within a character.1575* Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php1576* @access public1577* @param string $str multi-byte text to wrap encode1578* @return string1579*/1580public function Base64EncodeWrapMB($str) {1581$start = "=?".$this->CharSet."?B?";1582$end = "?=";1583$encoded = "";15841585$mb_length = mb_strlen($str, $this->CharSet);1586// Each line must have length <= 75, including $start and $end1587$length = 75 - strlen($start) - strlen($end);1588// Average multi-byte ratio1589$ratio = $mb_length / strlen($str);1590// Base64 has a 4:3 ratio1591$offset = $avgLength = floor($length * $ratio * .75);15921593for ($i = 0; $i < $mb_length; $i += $offset) {1594$lookBack = 0;15951596do {1597$offset = $avgLength - $lookBack;1598$chunk = mb_substr($str, $i, $offset, $this->CharSet);1599$chunk = base64_encode($chunk);1600$lookBack++;1601}1602while (strlen($chunk) > $length);16031604$encoded .= $chunk . $this->LE;1605}16061607// Chomp the last linefeed1608$encoded = substr($encoded, 0, -strlen($this->LE));1609return $encoded;1610}16111612/**1613* Encode string to quoted-printable.1614* Only uses standard PHP, slow, but will always work1615* @access public1616* @param string $string the text to encode1617* @param integer $line_max Number of chars allowed on a line before wrapping1618* @return string1619*/1620public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {1621$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');1622$lines = preg_split('/(?:\r\n|\r|\n)/', $input);1623$eol = "\r\n";1624$escape = '=';1625$output = '';1626while( list(, $line) = each($lines) ) {1627$linlen = strlen($line);1628$newline = '';1629for($i = 0; $i < $linlen; $i++) {1630$c = substr( $line, $i, 1 );1631$dec = ord( $c );1632if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E1633$c = '=2E';1634}1635if ( $dec == 32 ) {1636if ( $i == ( $linlen - 1 ) ) { // convert space at eol only1637$c = '=20';1638} else if ( $space_conv ) {1639$c = '=20';1640}1641} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required1642$h2 = floor($dec/16);1643$h1 = floor($dec%16);1644$c = $escape.$hex[$h2].$hex[$h1];1645}1646if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted1647$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay1648$newline = '';1649// check if newline first character will be point or not1650if ( $dec == 46 ) {1651$c = '=2E';1652}1653}1654$newline .= $c;1655} // end of for1656$output .= $newline.$eol;1657} // end of while1658return $output;1659}16601661/**1662* Encode string to RFC2045 (6.7) quoted-printable format1663* Uses a PHP5 stream filter to do the encoding about 64x faster than the old version1664* Also results in same content as you started with after decoding1665* @see EncodeQPphp()1666* @access public1667* @param string $string the text to encode1668* @param integer $line_max Number of chars allowed on a line before wrapping1669* @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function1670* @return string1671* @author Marcus Bointon1672*/1673public function EncodeQP($string, $line_max = 76, $space_conv = false) {1674if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)1675return quoted_printable_encode($string);1676}1677$filters = stream_get_filters();1678if (!in_array('convert.*', $filters)) { //Got convert stream filter?1679return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation1680}1681$fp = fopen('php://temp/', 'r+');1682$string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks1683$params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);1684$s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);1685fputs($fp, $string);1686rewind($fp);1687$out = stream_get_contents($fp);1688stream_filter_remove($s);1689$out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange1690fclose($fp);1691return $out;1692}16931694/**1695* NOTE: Phabricator patch to remove use of "/e". See D2147.1696*/1697private function encodeQCallback(array $matches) {1698return '='.sprintf('%02X', ord($matches[1]));1699}17001701/**1702* Encode string to q encoding.1703* @link http://tools.ietf.org/html/rfc20471704* @param string $str the text to encode1705* @param string $position Where the text is going to be used, see the RFC for what that means1706* @access public1707* @return string1708*/1709public function EncodeQ ($str, $position = 'text') {17101711// NOTE: Phabricator patch to remove use of "/e". See D2147.17121713// There should not be any EOL in the string1714$encoded = preg_replace('/[\r\n]*/', '', $str);17151716switch (strtolower($position)) {1717case 'phrase':1718$encoded = preg_replace_callback(1719"/([^A-Za-z0-9!*+\/ -])/",1720array($this, 'encodeQCallback'),1721$encoded);1722break;1723case 'comment':1724$encoded = preg_replace_callback(1725"/([\(\)\"])/",1726array($this, 'encodeQCallback'),1727$encoded);1728break;1729case 'text':1730default:1731// Replace every high ascii, control =, ? and _ characters1732$encoded = preg_replace_callback(1733'/([\000-\011\013\014\016-\037\075\077\137\177-\377])/',1734array($this, 'encodeQCallback'),1735$encoded);1736break;1737}17381739// Replace every spaces to _ (more readable than =20)1740$encoded = str_replace(' ', '_', $encoded);17411742return $encoded;1743}17441745/**1746* Adds a string or binary attachment (non-filesystem) to the list.1747* This method can be used to attach ascii or binary data,1748* such as a BLOB record from a database.1749* @param string $string String attachment data.1750* @param string $filename Name of the attachment.1751* @param string $encoding File encoding (see $Encoding).1752* @param string $type File extension (MIME) type.1753* @return void1754*/1755public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {1756// Append to $attachment array1757$this->attachment[] = array(17580 => $string,17591 => $filename,17602 => basename($filename),17613 => $encoding,17624 => $type,17635 => true, // isStringAttachment17646 => 'attachment',17657 => 01766);1767}17681769/**1770* Adds an embedded attachment. This can include images, sounds, and1771* just about any other document. Make sure to set the $type to an1772* image type. For JPEG images use "image/jpeg" and for GIF images1773* use "image/gif".1774* @param string $path Path to the attachment.1775* @param string $cid Content ID of the attachment. Use this to identify1776* the Id for accessing the image in an HTML form.1777* @param string $name Overrides the attachment name.1778* @param string $encoding File encoding (see $Encoding).1779* @param string $type File extension (MIME) type.1780* @return bool1781*/1782public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {17831784if ( !@is_file($path) ) {1785$this->SetError($this->Lang('file_access') . $path);1786return false;1787}17881789$filename = basename($path);1790if ( $name == '' ) {1791$name = $filename;1792}17931794// Append to $attachment array1795$this->attachment[] = array(17960 => $path,17971 => $filename,17982 => $name,17993 => $encoding,18004 => $type,18015 => false, // isStringAttachment18026 => 'inline',18037 => $cid1804);18051806return true;1807}18081809/**1810* Returns true if an inline attachment is present.1811* @access public1812* @return bool1813*/1814public function InlineImageExists() {1815foreach($this->attachment as $attachment) {1816if ($attachment[6] == 'inline') {1817return true;1818}1819}1820return false;1821}18221823/////////////////////////////////////////////////1824// CLASS METHODS, MESSAGE RESET1825/////////////////////////////////////////////////18261827/**1828* Clears all recipients assigned in the TO array. Returns void.1829* @return void1830*/1831public function ClearAddresses() {1832foreach($this->to as $to) {1833unset($this->all_recipients[strtolower($to[0])]);1834}1835$this->to = array();1836}18371838/**1839* Clears all recipients assigned in the CC array. Returns void.1840* @return void1841*/1842public function ClearCCs() {1843foreach($this->cc as $cc) {1844unset($this->all_recipients[strtolower($cc[0])]);1845}1846$this->cc = array();1847}18481849/**1850* Clears all recipients assigned in the BCC array. Returns void.1851* @return void1852*/1853public function ClearBCCs() {1854foreach($this->bcc as $bcc) {1855unset($this->all_recipients[strtolower($bcc[0])]);1856}1857$this->bcc = array();1858}18591860/**1861* Clears all recipients assigned in the ReplyTo array. Returns void.1862* @return void1863*/1864public function ClearReplyTos() {1865$this->ReplyTo = array();1866}18671868/**1869* Clears all recipients assigned in the TO, CC and BCC1870* array. Returns void.1871* @return void1872*/1873public function ClearAllRecipients() {1874$this->to = array();1875$this->cc = array();1876$this->bcc = array();1877$this->all_recipients = array();1878}18791880/**1881* Clears all previously set filesystem, string, and binary1882* attachments. Returns void.1883* @return void1884*/1885public function ClearAttachments() {1886$this->attachment = array();1887}18881889/**1890* Clears all custom headers. Returns void.1891* @return void1892*/1893public function ClearCustomHeaders() {1894$this->CustomHeader = array();1895}18961897/////////////////////////////////////////////////1898// CLASS METHODS, MISCELLANEOUS1899/////////////////////////////////////////////////19001901/**1902* Adds the error message to the error container.1903* @access protected1904* @return void1905*/1906protected function SetError($msg) {1907$this->error_count++;1908if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {1909$lasterror = $this->smtp->getError();1910if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {1911$msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";1912}1913}1914$this->ErrorInfo = $msg;1915}19161917/**1918* Returns the proper RFC 822 formatted date.1919* @access public1920* @return string1921* @static1922*/1923public static function RFCDate() {1924$tz = date('Z');1925$tzs = ($tz < 0) ? '-' : '+';1926$tz = abs($tz);1927$tz = (int)($tz/3600)*100 + ($tz%3600)/60;1928$result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);19291930return $result;1931}19321933/**1934* Returns the server hostname or 'localhost.localdomain' if unknown.1935* @access private1936* @return string1937*/1938private function ServerHostname() {1939if (!empty($this->Hostname)) {1940$result = $this->Hostname;1941} elseif (isset($_SERVER['SERVER_NAME'])) {1942$result = $_SERVER['SERVER_NAME'];1943} else {1944$result = 'localhost.localdomain';1945}19461947return $result;1948}19491950/**1951* Returns a message in the appropriate language.1952* @access private1953* @return string1954*/1955private function Lang($key) {1956if(count($this->language) < 1) {1957$this->SetLanguage('en'); // set the default language1958}19591960if(isset($this->language[$key])) {1961return $this->language[$key];1962} else {1963return 'Language string failed to load: ' . $key;1964}1965}19661967/**1968* Returns true if an error occurred.1969* @access public1970* @return bool1971*/1972public function IsError() {1973return ($this->error_count > 0);1974}19751976/**1977* Changes every end of line from CR or LF to CRLF.1978* @access private1979* @return string1980*/1981private function FixEOL($str) {1982$str = str_replace("\r\n", "\n", $str);1983$str = str_replace("\r", "\n", $str);1984$str = str_replace("\n", $this->LE, $str);1985return $str;1986}19871988/**1989* Adds a custom header.1990* @access public1991* @return void1992*/1993public function AddCustomHeader($custom_header) {1994$this->CustomHeader[] = explode(':', $custom_header, 2);1995}19961997/**1998* Evaluates the message and returns modifications for inline images and backgrounds1999* @access public2000* @return $message2001*/2002public function MsgHTML($message, $basedir = '') {2003preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);2004if(isset($images[2])) {2005foreach($images[2] as $i => $url) {2006// do not change urls for absolute images (thanks to corvuscorax)2007if (!preg_match('#^[A-z]+://#',$url)) {2008$filename = basename($url);2009$directory = dirname($url);2010($directory == '.')?$directory='':'';2011$cid = 'cid:' . md5($filename);2012$ext = pathinfo($filename, PATHINFO_EXTENSION);2013$mimeType = self::_mime_types($ext);2014if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }2015if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }2016if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {2017$message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);2018}2019}2020}2021}2022$this->IsHTML(true);2023$this->Body = $message;2024$textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));2025if (!empty($textMsg) && empty($this->AltBody)) {2026$this->AltBody = html_entity_decode($textMsg);2027}2028if (empty($this->AltBody)) {2029$this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";2030}2031}20322033/**2034* Gets the MIME type of the embedded or inline image2035* @param string File extension2036* @access public2037* @return string MIME type of ext2038* @static2039*/2040public static function _mime_types($ext = '') {2041$mimes = array(2042'hqx' => 'application/mac-binhex40',2043'cpt' => 'application/mac-compactpro',2044'doc' => 'application/msword',2045'bin' => 'application/macbinary',2046'dms' => 'application/octet-stream',2047'lha' => 'application/octet-stream',2048'lzh' => 'application/octet-stream',2049'exe' => 'application/octet-stream',2050'class' => 'application/octet-stream',2051'psd' => 'application/octet-stream',2052'so' => 'application/octet-stream',2053'sea' => 'application/octet-stream',2054'dll' => 'application/octet-stream',2055'oda' => 'application/oda',2056'pdf' => 'application/pdf',2057'ai' => 'application/postscript',2058'eps' => 'application/postscript',2059'ps' => 'application/postscript',2060'smi' => 'application/smil',2061'smil' => 'application/smil',2062'mif' => 'application/vnd.mif',2063'xls' => 'application/vnd.ms-excel',2064'ppt' => 'application/vnd.ms-powerpoint',2065'wbxml' => 'application/vnd.wap.wbxml',2066'wmlc' => 'application/vnd.wap.wmlc',2067'dcr' => 'application/x-director',2068'dir' => 'application/x-director',2069'dxr' => 'application/x-director',2070'dvi' => 'application/x-dvi',2071'gtar' => 'application/x-gtar',2072'php' => 'application/x-httpd-php',2073'php4' => 'application/x-httpd-php',2074'php3' => 'application/x-httpd-php',2075'phtml' => 'application/x-httpd-php',2076'phps' => 'application/x-httpd-php-source',2077'js' => 'application/x-javascript',2078'swf' => 'application/x-shockwave-flash',2079'sit' => 'application/x-stuffit',2080'tar' => 'application/x-tar',2081'tgz' => 'application/x-tar',2082'xhtml' => 'application/xhtml+xml',2083'xht' => 'application/xhtml+xml',2084'zip' => 'application/zip',2085'mid' => 'audio/midi',2086'midi' => 'audio/midi',2087'mpga' => 'audio/mpeg',2088'mp2' => 'audio/mpeg',2089'mp3' => 'audio/mpeg',2090'aif' => 'audio/x-aiff',2091'aiff' => 'audio/x-aiff',2092'aifc' => 'audio/x-aiff',2093'ram' => 'audio/x-pn-realaudio',2094'rm' => 'audio/x-pn-realaudio',2095'rpm' => 'audio/x-pn-realaudio-plugin',2096'ra' => 'audio/x-realaudio',2097'rv' => 'video/vnd.rn-realvideo',2098'wav' => 'audio/x-wav',2099'bmp' => 'image/bmp',2100'gif' => 'image/gif',2101'jpeg' => 'image/jpeg',2102'jpg' => 'image/jpeg',2103'jpe' => 'image/jpeg',2104'png' => 'image/png',2105'tiff' => 'image/tiff',2106'tif' => 'image/tiff',2107'css' => 'text/css',2108'html' => 'text/html',2109'htm' => 'text/html',2110'shtml' => 'text/html',2111'txt' => 'text/plain',2112'text' => 'text/plain',2113'log' => 'text/plain',2114'rtx' => 'text/richtext',2115'rtf' => 'text/rtf',2116'xml' => 'text/xml',2117'xsl' => 'text/xml',2118'mpeg' => 'video/mpeg',2119'mpg' => 'video/mpeg',2120'mpe' => 'video/mpeg',2121'qt' => 'video/quicktime',2122'mov' => 'video/quicktime',2123'avi' => 'video/x-msvideo',2124'movie' => 'video/x-sgi-movie',2125'doc' => 'application/msword',2126'word' => 'application/msword',2127'xl' => 'application/excel',2128'eml' => 'message/rfc822'2129);2130return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];2131}21322133/**2134* Set (or reset) Class Objects (variables)2135*2136* Usage Example:2137* $page->set('X-Priority', '3');2138*2139* @access public2140* @param string $name Parameter Name2141* @param mixed $value Parameter Value2142* NOTE: will not work with arrays, there are no arrays to set/reset2143* @todo Should this not be using __set() magic function?2144*/2145public function set($name, $value = '') {2146try {2147if (isset($this->$name) ) {2148$this->$name = $value;2149} else {2150throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);2151}2152} catch (Exception $e) {2153$this->SetError($e->getMessage());2154if ($e->getCode() == self::STOP_CRITICAL) {2155return false;2156}2157}2158return true;2159}21602161/**2162* Strips newlines to prevent header injection.2163* @access public2164* @param string $str String2165* @return string2166*/2167public function SecureHeader($str) {2168$str = str_replace("\r", '', $str);2169$str = str_replace("\n", '', $str);2170return trim($str);2171}21722173/**2174* Set the private key file and password to sign the message.2175*2176* @access public2177* @param string $key_filename Parameter File Name2178* @param string $key_pass Password for private key2179*/2180public function Sign($cert_filename, $key_filename, $key_pass) {2181$this->sign_cert_file = $cert_filename;2182$this->sign_key_file = $key_filename;2183$this->sign_key_pass = $key_pass;2184}21852186/**2187* Set the private key file and password to sign the message.2188*2189* @access public2190* @param string $key_filename Parameter File Name2191* @param string $key_pass Password for private key2192*/2193public function DKIM_QP($txt) {2194$tmp="";2195$line="";2196for ($i=0;$i<strlen($txt);$i++) {2197$ord=ord($txt[$i]);2198if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {2199$line.=$txt[$i];2200} else {2201$line.="=".sprintf("%02X",$ord);2202}2203}2204return $line;2205}22062207/**2208* Generate DKIM signature2209*2210* @access public2211* @param string $s Header2212*/2213public function DKIM_Sign($s) {2214$privKeyStr = file_get_contents($this->DKIM_private);2215if ($this->DKIM_passphrase!='') {2216$privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase);2217} else {2218$privKey = $privKeyStr;2219}2220if (openssl_sign($s, $signature, $privKey)) {2221return base64_encode($signature);2222}2223}22242225/**2226* Generate DKIM Canonicalization Header2227*2228* @access public2229* @param string $s Header2230*/2231public function DKIM_HeaderC($s) {2232$s=preg_replace("/\r\n\s+/"," ",$s);2233$lines=explode("\r\n",$s);2234foreach ($lines as $key=>$line) {2235list($heading,$value)=explode(":",$line,2);2236$heading=strtolower($heading);2237$value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces2238$lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value2239}2240$s=implode("\r\n",$lines);2241return $s;2242}22432244/**2245* Generate DKIM Canonicalization Body2246*2247* @access public2248* @param string $body Message Body2249*/2250public function DKIM_BodyC($body) {2251if ($body == '') return "\r\n";2252// stabilize line endings2253$body=str_replace("\r\n","\n",$body);2254$body=str_replace("\n","\r\n",$body);2255// END stabilize line endings2256while (substr($body,strlen($body)-4,4) == "\r\n\r\n") {2257$body=substr($body,0,strlen($body)-2);2258}2259return $body;2260}22612262/**2263* Create the DKIM header, body, as new header2264*2265* @access public2266* @param string $headers_line Header lines2267* @param string $subject Subject2268* @param string $body Body2269*/2270public function DKIM_Add($headers_line,$subject,$body) {2271$DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms2272$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body2273$DKIMquery = 'dns/txt'; // Query method2274$DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)2275$subject_header = "Subject: $subject";2276$headers = explode("\r\n",$headers_line);2277foreach($headers as $header) {2278if (strpos($header,'From:') === 0) {2279$from_header=$header;2280} elseif (strpos($header,'To:') === 0) {2281$to_header=$header;2282}2283}2284$from = str_replace('|','=7C',$this->DKIM_QP($from_header));2285$to = str_replace('|','=7C',$this->DKIM_QP($to_header));2286$subject = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable2287$body = $this->DKIM_BodyC($body);2288$DKIMlen = strlen($body) ; // Length of body2289$DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body2290$ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";2291$dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".2292"\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".2293"\th=From:To:Subject;\r\n".2294"\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".2295"\tz=$from\r\n".2296"\t|$to\r\n".2297"\t|$subject;\r\n".2298"\tbh=" . $DKIMb64 . ";\r\n".2299"\tb=";2300$toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);2301$signed = $this->DKIM_Sign($toSign);2302return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n";2303}23042305protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) {2306if (!empty($this->action_function) && function_exists($this->action_function)) {2307$params = array($isSent,$to,$cc,$bcc,$subject,$body);2308call_user_func_array($this->action_function,$params);2309}2310}2311}23122313class phpmailerException extends Exception {2314public function errorMessage() {2315$errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";2316return $errorMsg;2317}2318}2319?>232023212322