Path: blob/master/externals/phpmailer/class.phpmailer-lite.php
12241 views
<?php1/*~ class.phpmailer-lite.php2.---------------------------------------------------------------------------.3| Software: PHPMailer Lite - PHP email class |4| Version: 5.1 |5| Contact: via sourceforge.net support pages (also www.codeworxtech.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.codeworxtech.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 Lite - PHP email transport class31* NOTE: Requires PHP version 5 or later32* @package PHPMailer Lite33* @author Andy Prevost34* @author Marcus Bointon35* @copyright 2004 - 2009 Andy Prevost36* @version $Id: class.phpmailer-lite.php 447 2009-09-12 13:21: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 PHPMailerLite {4344public static function newFromMessage(45PhabricatorMailExternalMessage $message) {4647$mailer = new self($use_exceptions = true);4849// By default, PHPMailerLite sends one mail per recipient. We handle50// combining or separating To and Cc higher in the stack, so tell it to51// send mail exactly like we ask.52$mailer->SingleTo = false;5354$mailer->CharSet = 'utf-8';55$mailer->Encoding = 'base64';5657$subject = $message->getSubject();58if ($subject !== null) {59$mailer->Subject = $subject;60}6162$from_address = $message->getFromAddress();63if ($from_address) {64$mailer->SetFrom(65$from_address->getAddress(),66(string)$from_address->getDisplayName(),67$crazy_side_effects = false);68}6970$reply_address = $message->getReplyToAddress();71if ($reply_address) {72$mailer->AddReplyTo(73$reply_address->getAddress(),74(string)$reply_address->getDisplayName());75}7677$to_addresses = $message->getToAddresses();78if ($to_addresses) {79foreach ($to_addresses as $address) {80$mailer->AddAddress(81$address->getAddress(),82(string)$address->getDisplayName());83}84}8586$cc_addresses = $message->getCCAddresses();87if ($cc_addresses) {88foreach ($cc_addresses as $address) {89$mailer->AddCC(90$address->getAddress(),91(string)$address->getDisplayName());92}93}9495$headers = $message->getHeaders();96if ($headers) {97foreach ($headers as $header) {98$name = $header->getName();99$value = $header->getValue();100101if (phutil_utf8_strtolower($name) === 'message-id') {102$mailer->MessageID = $value;103} else {104$mailer->AddCustomHeader("{$name}: {$value}");105}106}107}108109$attachments = $message->getAttachments();110if ($attachments) {111foreach ($attachments as $attachment) {112$mailer->AddStringAttachment(113$attachment->getData(),114$attachment->getFilename(),115'base64',116$attachment->getMimeType());117}118}119120$text_body = $message->getTextBody();121if ($text_body !== null) {122$mailer->Body = $text_body;123}124125$html_body = $message->getHTMLBody();126if ($html_body !== null) {127$mailer->IsHTML(true);128$mailer->Body = $html_body;129if ($text_body !== null) {130$mailer->AltBody = $text_body;131}132}133134return $mailer;135}136137138139140/////////////////////////////////////////////////141// PROPERTIES, PUBLIC142/////////////////////////////////////////////////143144/**145* Email priority (1 = High, 3 = Normal, 5 = low).146* @var int147*/148public $Priority = 3;149150/**151* Sets the CharSet of the message.152* @var string153*/154public $CharSet = 'iso-8859-1';155156/**157* Sets the Content-type of the message.158* @var string159*/160public $ContentType = 'text/plain';161162/**163* Sets the Encoding of the message. Options for this are164* "8bit", "7bit", "binary", "base64", and "quoted-printable".165* @var string166*/167public $Encoding = '8bit';168169/**170* Holds the most recent mailer error message.171* @var string172*/173public $ErrorInfo = '';174175/**176* Sets the From email address for the message.177* @var string178*/179public $From = 'root@localhost';180181/**182* Sets the From name of the message.183* @var string184*/185public $FromName = 'Root User';186187/**188* Sets the Sender email (Return-Path) of the message. If not empty,189* will be sent via -f to sendmail190* @var string191*/192public $Sender = '';193194/**195* Sets the Subject of the message.196* @var string197*/198public $Subject = '';199200/**201* Sets the Body of the message. This can be either an HTML or text body.202* If HTML then run IsHTML(true).203* @var string204*/205public $Body = '';206207/**208* Sets the text-only body of the message. This automatically sets the209* email to multipart/alternative. This body can be read by mail210* clients that do not have HTML email capability such as mutt. Clients211* that can read HTML will view the normal Body.212* @var string213*/214public $AltBody = '';215216/**217* Sets word wrapping on the body of the message to a given number of218* characters.219* @var int220*/221public $WordWrap = 0;222223/**224* Method to send mail: ("mail", or "sendmail").225* @var string226*/227public $Mailer = 'sendmail';228229/**230* Sets the path of the sendmail program.231* @var string232*/233public $Sendmail = '/usr/sbin/sendmail';234235/**236* Sets the email address that a reading confirmation will be sent.237* @var string238*/239public $ConfirmReadingTo = '';240241/**242* Sets the hostname to use in Message-Id and Received headers243* and as default HELO string. If empty, the value returned244* by SERVER_NAME is used or 'localhost.localdomain'.245* @var string246*/247public $Hostname = '';248249/**250* Sets the message ID to be used in the Message-Id header.251* If empty, a unique id will be generated.252* @var string253*/254public $MessageID = '';255256/**257* Provides the ability to have the TO field process individual258* emails, instead of sending to entire TO addresses259* @var bool260*/261public $SingleTo = true;262263/**264* If SingleTo is true, this provides the array to hold the email addresses265* @var bool266*/267public $SingleToArray = array();268269/**270* Provides the ability to change the line ending271* @var string272*/273public $LE = "\n";274275/**276* Used with DKIM DNS Resource Record277* @var string278*/279public $DKIM_selector = 'phpmailer';280281/**282* Used with DKIM DNS Resource Record283* optional, in format of email address '[email protected]'284* @var string285*/286public $DKIM_identity = '';287288/**289* Used with DKIM DNS Resource Record290* required, in format of base domain 'yourdomain.com'291* @var string292*/293public $DKIM_domain = '';294295/**296* Used with DKIM Digital Signing process297* optional298* @var string299*/300public $DKIM_passphrase = '';301302/**303* Used with DKIM DNS Resource Record304* required, private key (read from /.htprivkey)305* @var string306*/307public $DKIM_private = '';308309/**310* Callback Action function name311* the function that handles the result of the send email action. Parameters:312* bool $result result of the send action313* string $to email address of the recipient314* string $cc cc email addresses315* string $bcc bcc email addresses316* string $subject the subject317* string $body the email body318* @var string319*/320public $action_function = ''; //'callbackAction';321322/**323* Sets the PHPMailer Version number324* @var string325*/326public $Version = 'Lite 5.1';327328/////////////////////////////////////////////////329// PROPERTIES, PRIVATE AND PROTECTED330/////////////////////////////////////////////////331332private $to = array();333private $cc = array();334private $bcc = array();335private $ReplyTo = array();336private $all_recipients = array();337private $attachment = array();338private $CustomHeader = array();339private $message_type = '';340private $boundary = array();341protected $language = array();342private $error_count = 0;343private $sign_cert_file = "";344private $sign_key_file = "";345private $sign_key_pass = "";346private $exceptions = false;347348/////////////////////////////////////////////////349// CONSTANTS350/////////////////////////////////////////////////351352const STOP_MESSAGE = 0; // message only, continue processing353const STOP_CONTINUE = 1; // message?, likely ok to continue processing354const STOP_CRITICAL = 2; // message, plus full stop, critical error reached355356/////////////////////////////////////////////////357// METHODS, VARIABLES358/////////////////////////////////////////////////359360/**361* Constructor362* @param boolean $exceptions Should we throw external exceptions?363*/364public function __construct($exceptions = false) {365$this->exceptions = ($exceptions == true);366}367368/**369* Sets message type to HTML.370* @param bool $ishtml371* @return void372*/373public function IsHTML($ishtml = true) {374if ($ishtml) {375$this->ContentType = 'text/html';376} else {377$this->ContentType = 'text/plain';378}379}380381/**382* Sets Mailer to send message using PHP mail() function.383* @return void384*/385public function IsMail() {386$this->Mailer = 'mail';387}388389/**390* Sets Mailer to send message using the $Sendmail program.391* @return void392*/393public function IsSendmail() {394if (!stristr(ini_get('sendmail_path'), 'sendmail')) {395$this->Sendmail = '/var/qmail/bin/sendmail';396}397$this->Mailer = 'sendmail';398}399400/**401* Sets Mailer to send message using the qmail MTA.402* @return void403*/404public function IsQmail() {405if (stristr(ini_get('sendmail_path'), 'qmail')) {406$this->Sendmail = '/var/qmail/bin/sendmail';407}408$this->Mailer = 'sendmail';409}410411/////////////////////////////////////////////////412// METHODS, RECIPIENTS413/////////////////////////////////////////////////414415/**416* Adds a "To" address.417* @param string $address418* @param string $name419* @return boolean true on success, false if address already used420*/421public function AddAddress($address, $name = '') {422return $this->AddAnAddress('to', $address, $name);423}424425/**426* Adds a "Cc" address.427* Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.428* @param string $address429* @param string $name430* @return boolean true on success, false if address already used431*/432public function AddCC($address, $name = '') {433return $this->AddAnAddress('cc', $address, $name);434}435436/**437* Adds a "Bcc" address.438* Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.439* @param string $address440* @param string $name441* @return boolean true on success, false if address already used442*/443public function AddBCC($address, $name = '') {444return $this->AddAnAddress('bcc', $address, $name);445}446447/**448* Adds a "Reply-to" address.449* @param string $address450* @param string $name451* @return boolean452*/453public function AddReplyTo($address, $name = '') {454return $this->AddAnAddress('ReplyTo', $address, $name);455}456457/**458* Adds an address to one of the recipient arrays459* Addresses that have been added already return false, but do not throw exceptions460* @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'461* @param string $address The email address to send to462* @param string $name463* @return boolean true on success, false if address already used or invalid in some way464* @access private465*/466private function AddAnAddress($kind, $address, $name = '') {467if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) {468echo 'Invalid recipient array: ' . $kind;469return false;470}471$address = trim($address);472$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim473if (!self::ValidateAddress($address)) {474$this->SetError($this->Lang('invalid_address').': '. $address);475if ($this->exceptions) {476throw new phpmailerException($this->Lang('invalid_address').': '.$address);477}478echo $this->Lang('invalid_address').': '.$address;479return false;480}481if ($kind != 'ReplyTo') {482if (!isset($this->all_recipients[strtolower($address)])) {483array_push($this->$kind, array($address, $name));484$this->all_recipients[strtolower($address)] = true;485return true;486}487} else {488if (!array_key_exists(strtolower($address), $this->ReplyTo)) {489$this->ReplyTo[strtolower($address)] = array($address, $name);490return true;491}492}493return false;494}495496/**497* Set the From and FromName properties498* @param string $address499* @param string $name500* @return boolean501*/502public function SetFrom($address, $name = '',$auto=1) {503$address = trim($address);504$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim505if (!self::ValidateAddress($address)) {506$this->SetError($this->Lang('invalid_address').': '. $address);507if ($this->exceptions) {508throw new phpmailerException($this->Lang('invalid_address').': '.$address);509}510echo $this->Lang('invalid_address').': '.$address;511return false;512}513$this->From = $address;514$this->FromName = $name;515if ($auto) {516if (empty($this->ReplyTo)) {517$this->AddAnAddress('ReplyTo', $address, $name);518}519if (empty($this->Sender)) {520$this->Sender = $address;521}522}523return true;524}525526/**527* Check that a string looks roughly like an email address should528* Static so it can be used without instantiation529* Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator530* Conforms approximately to RFC2822531* @link http://www.hexillion.com/samples/#Regex Original pattern found here532* @param string $address The email address to check533* @return boolean534* @static535* @access public536*/537public static function ValidateAddress($address) {538if (function_exists('filter_var')) { //Introduced in PHP 5.2539if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {540return false;541} else {542return true;543}544} else {545return 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);546}547}548549/////////////////////////////////////////////////550// METHODS, MAIL SENDING551/////////////////////////////////////////////////552553/**554* Creates message and assigns Mailer. If the message is555* not sent successfully then it returns false. Use the ErrorInfo556* variable to view description of the error.557* @return bool558*/559public function Send() {560try {561if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {562throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);563}564565// Set whether the message is multipart/alternative566if(!empty($this->AltBody)) {567$this->ContentType = 'multipart/alternative';568}569570$this->error_count = 0; // reset errors571$this->SetMessageType();572$header = $this->CreateHeader();573$body = $this->CreateBody();574575if (empty($this->Body)) {576throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);577}578579// digitally sign with DKIM if enabled580if ($this->DKIM_domain && $this->DKIM_private) {581$header_dkim = $this->DKIM_Add($header,$this->Subject,$body);582$header = str_replace("\r\n","\n",$header_dkim) . $header;583}584585// Choose the mailer and send through it586switch($this->Mailer) {587588case 'amazon-ses':589return $this->customMailer->executeSend(590$header.591$body);592593case 'sendmail':594$sendAction = $this->SendmailSend($header, $body);595return $sendAction;596default:597$sendAction = $this->MailSend($header, $body);598return $sendAction;599}600601} catch (phpmailerException $e) {602$this->SetError($e->getMessage());603if ($this->exceptions) {604throw $e;605}606echo $e->getMessage()."\n";607return false;608}609}610611/**612* Sends mail using the $Sendmail program.613* @param string $header The message headers614* @param string $body The message body615* @access protected616* @return bool617*/618protected function SendmailSend($header, $body) {619if ($this->Sender != '') {620$sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));621} else {622$sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));623}624625if ($this->SingleTo === true) {626foreach ($this->SingleToArray as $key => $val) {627$mail = new ExecFuture('%C', $sendmail);628$mail->write("To: {$val}\n", true);629$mail->write($header.$body);630$mail->resolvex();631}632} else {633$mail = new ExecFuture('%C', $sendmail);634$mail->write($header.$body);635$mail->resolvex();636}637638return true;639}640641/**642* Sends mail using the PHP mail() function.643* @param string $header The message headers644* @param string $body The message body645* @access protected646* @return bool647*/648protected function MailSend($header, $body) {649$toArr = array();650foreach($this->to as $t) {651$toArr[] = $this->AddrFormat($t);652}653$to = implode(', ', $toArr);654655$params = sprintf("-oi -f %s", $this->Sender);656if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {657$old_from = ini_get('sendmail_from');658ini_set('sendmail_from', $this->Sender);659if ($this->SingleTo === true && count($toArr) > 1) {660foreach ($toArr as $key => $val) {661$rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);662// implement call back function if it exists663$isSent = ($rt == 1) ? 1 : 0;664$this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);665}666} else {667$rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);668// implement call back function if it exists669$isSent = ($rt == 1) ? 1 : 0;670$this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);671}672} else {673if ($this->SingleTo === true && count($toArr) > 1) {674foreach ($toArr as $key => $val) {675$rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);676// implement call back function if it exists677$isSent = ($rt == 1) ? 1 : 0;678$this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);679}680} else {681$rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);682// implement call back function if it exists683$isSent = ($rt == 1) ? 1 : 0;684$this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);685}686}687if (isset($old_from)) {688ini_set('sendmail_from', $old_from);689}690if(!$rt) {691throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);692}693return true;694}695696/**697* Sets the language for all class error messages.698* Returns false if it cannot load the language file. The default language is English.699* @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")700* @param string $lang_path Path to the language file directory701* @access public702*/703function SetLanguage($langcode = 'en', $lang_path = 'language/') {704//Define full set of translatable strings705$PHPMAILER_LANG = array(706'provide_address' => 'You must provide at least one recipient email address.',707'mailer_not_supported' => ' mailer is not supported.',708'execute' => 'Could not execute: ',709'instantiate' => 'Could not instantiate mail function.',710'from_failed' => 'The following From address failed: ',711'file_access' => 'Could not access file: ',712'file_open' => 'File Error: Could not open file: ',713'encoding' => 'Unknown encoding: ',714'signing' => 'Signing Error: ',715'empty_message' => 'Message body empty',716'invalid_address' => 'Invalid address',717'variable_set' => 'Cannot set or reset variable: '718);719//Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!720$l = true;721if ($langcode != 'en') { //There is no English translation file722$l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';723}724$this->language = $PHPMAILER_LANG;725return ($l == true); //Returns false if language not found726}727728/**729* Return the current array of language strings730* @return array731*/732public function GetTranslations() {733return $this->language;734}735736/////////////////////////////////////////////////737// METHODS, MESSAGE CREATION738/////////////////////////////////////////////////739740/**741* Creates recipient headers.742* @access public743* @return string744*/745public function AddrAppend($type, $addr) {746$addr_str = $type . ': ';747$addresses = array();748foreach ($addr as $a) {749$addresses[] = $this->AddrFormat($a);750}751$addr_str .= implode(', ', $addresses);752$addr_str .= $this->LE;753754// NOTE: This is a narrow hack to fix an issue with 1000+ characters of755// recipients, described in T12372.756$addr_str = wordwrap($addr_str, 75, "\n ");757758return $addr_str;759}760761/**762* Formats an address correctly.763* @access public764* @return string765*/766public function AddrFormat($addr) {767if (empty($addr[1])) {768return $this->SecureHeader($addr[0]);769} else {770return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";771}772}773774/**775* Wraps message for use with mailers that do not776* automatically perform wrapping and for quoted-printable.777* Original written by philippe.778* @param string $message The message to wrap779* @param integer $length The line length to wrap to780* @param boolean $qp_mode Whether to run in Quoted-Printable mode781* @access public782* @return string783*/784public function WrapText($message, $length, $qp_mode = false) {785$soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;786// If utf-8 encoding is used, we will need to make sure we don't787// split multibyte characters when we wrap788$is_utf8 = (strtolower($this->CharSet) == "utf-8");789790$message = $this->FixEOL($message);791if (substr($message, -1) == $this->LE) {792$message = substr($message, 0, -1);793}794795$line = explode($this->LE, $message);796$message = '';797for ($i=0 ;$i < count($line); $i++) {798$line_part = explode(' ', $line[$i]);799$buf = '';800for ($e = 0; $e<count($line_part); $e++) {801$word = $line_part[$e];802if ($qp_mode and (strlen($word) > $length)) {803$space_left = $length - strlen($buf) - 1;804if ($e != 0) {805if ($space_left > 20) {806$len = $space_left;807if ($is_utf8) {808$len = $this->UTF8CharBoundary($word, $len);809} elseif (substr($word, $len - 1, 1) == "=") {810$len--;811} elseif (substr($word, $len - 2, 1) == "=") {812$len -= 2;813}814$part = substr($word, 0, $len);815$word = substr($word, $len);816$buf .= ' ' . $part;817$message .= $buf . sprintf("=%s", $this->LE);818} else {819$message .= $buf . $soft_break;820}821$buf = '';822}823while (strlen($word) > 0) {824$len = $length;825if ($is_utf8) {826$len = $this->UTF8CharBoundary($word, $len);827} elseif (substr($word, $len - 1, 1) == "=") {828$len--;829} elseif (substr($word, $len - 2, 1) == "=") {830$len -= 2;831}832$part = substr($word, 0, $len);833$word = substr($word, $len);834835if (strlen($word) > 0) {836$message .= $part . sprintf("=%s", $this->LE);837} else {838$buf = $part;839}840}841} else {842$buf_o = $buf;843$buf .= ($e == 0) ? $word : (' ' . $word);844845if (strlen($buf) > $length and $buf_o != '') {846$message .= $buf_o . $soft_break;847$buf = $word;848}849}850}851$message .= $buf . $this->LE;852}853854return $message;855}856857/**858* Finds last character boundary prior to maxLength in a utf-8859* quoted (printable) encoded string.860* Original written by Colin Brown.861* @access public862* @param string $encodedText utf-8 QP text863* @param int $maxLength find last character boundary prior to this length864* @return int865*/866public function UTF8CharBoundary($encodedText, $maxLength) {867$foundSplitPos = false;868$lookBack = 3;869while (!$foundSplitPos) {870$lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);871$encodedCharPos = strpos($lastChunk, "=");872if ($encodedCharPos !== false) {873// Found start of encoded character byte within $lookBack block.874// Check the encoded byte value (the 2 chars after the '=')875$hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);876$dec = hexdec($hex);877if ($dec < 128) { // Single byte character.878// If the encoded char was found at pos 0, it will fit879// otherwise reduce maxLength to start of the encoded char880$maxLength = ($encodedCharPos == 0) ? $maxLength :881$maxLength - ($lookBack - $encodedCharPos);882$foundSplitPos = true;883} elseif ($dec >= 192) { // First byte of a multi byte character884// Reduce maxLength to split at start of character885$maxLength = $maxLength - ($lookBack - $encodedCharPos);886$foundSplitPos = true;887} elseif ($dec < 192) { // Middle byte of a multi byte character, look further back888$lookBack += 3;889}890} else {891// No encoded character found892$foundSplitPos = true;893}894}895return $maxLength;896}897898/**899* Set the body wrapping.900* @access public901* @return void902*/903public function SetWordWrap() {904if($this->WordWrap < 1) {905return;906}907switch($this->message_type) {908case 'alt':909case 'alt_attachments':910$this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);911break;912default:913$this->Body = $this->WrapText($this->Body, $this->WordWrap);914break;915}916}917918/**919* Assembles message header.920* @access public921* @return string The assembled header922*/923public function CreateHeader() {924$result = '';925926// Set the boundaries927$uniq_id = md5(uniqid(time()));928$this->boundary[1] = 'b1_' . $uniq_id;929$this->boundary[2] = 'b2_' . $uniq_id;930931$result .= $this->HeaderLine('Date', self::RFCDate());932if($this->Sender == '') {933$result .= $this->HeaderLine('Return-Path', trim($this->From));934} else {935$result .= $this->HeaderLine('Return-Path', trim($this->Sender));936}937938// To be created automatically by mail()939if($this->Mailer != 'mail') {940if ($this->SingleTo === true) {941foreach($this->to as $t) {942$this->SingleToArray[] = $this->AddrFormat($t);943}944} else {945if(count($this->to) > 0) {946$result .= $this->AddrAppend('To', $this->to);947} elseif (count($this->cc) == 0) {948$result .= $this->HeaderLine('To', 'undisclosed-recipients:;');949}950}951}952953$from = array();954$from[0][0] = trim($this->From);955$from[0][1] = $this->FromName;956$result .= $this->AddrAppend('From', $from);957958// sendmail and mail() extract Cc from the header before sending959if(count($this->cc) > 0) {960$result .= $this->AddrAppend('Cc', $this->cc);961}962963// sendmail and mail() extract Bcc from the header before sending964if(count($this->bcc) > 0) {965$result .= $this->AddrAppend('Bcc', $this->bcc);966}967968if(count($this->ReplyTo) > 0) {969$result .= $this->AddrAppend('Reply-to', $this->ReplyTo);970}971972// mail() sets the subject itself973if($this->Mailer != 'mail') {974$result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));975}976977if($this->MessageID != '') {978$result .= $this->HeaderLine('Message-ID',$this->MessageID);979} else {980$result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);981}982$result .= $this->HeaderLine('X-Priority', $this->Priority);983984if($this->ConfirmReadingTo != '') {985$result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');986}987988// Add custom headers989for($index = 0; $index < count($this->CustomHeader); $index++) {990$result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));991}992if (!$this->sign_key_file) {993$result .= $this->HeaderLine('MIME-Version', '1.0');994$result .= $this->GetMailMIME();995}996997return $result;998}9991000/**1001* Returns the message MIME.1002* @access public1003* @return string1004*/1005public function GetMailMIME() {1006$result = '';1007switch($this->message_type) {1008case 'plain':1009$result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);1010$result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);1011break;1012case 'attachments':1013case 'alt_attachments':1014if($this->InlineImageExists()){1015$result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);1016} else {1017$result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');1018$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1019}1020break;1021case 'alt':1022$result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');1023$result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1024break;1025}10261027if($this->Mailer != 'mail') {1028$result .= $this->LE.$this->LE;1029}10301031return $result;1032}10331034/**1035* Assembles the message body. Returns an empty string on failure.1036* @access public1037* @return string The assembled message body1038*/1039public function CreateBody() {1040$body = '';10411042if ($this->sign_key_file) {1043$body .= $this->GetMailMIME();1044}10451046$this->SetWordWrap();10471048switch($this->message_type) {1049case 'alt':1050$body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');1051$body .= $this->EncodeString($this->AltBody, $this->Encoding);1052$body .= $this->LE.$this->LE;1053$body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');1054$body .= $this->EncodeString($this->Body, $this->Encoding);1055$body .= $this->LE.$this->LE;1056$body .= $this->EndBoundary($this->boundary[1]);1057break;1058case 'plain':1059$body .= $this->EncodeString($this->Body, $this->Encoding);1060break;1061case 'attachments':1062$body .= $this->GetBoundary($this->boundary[1], '', '', '');1063$body .= $this->EncodeString($this->Body, $this->Encoding);1064$body .= $this->LE;1065$body .= $this->AttachAll();1066break;1067case 'alt_attachments':1068$body .= sprintf("--%s%s", $this->boundary[1], $this->LE);1069$body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);1070$body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body1071$body .= $this->EncodeString($this->AltBody, $this->Encoding);1072$body .= $this->LE.$this->LE;1073$body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body1074$body .= $this->EncodeString($this->Body, $this->Encoding);1075$body .= $this->LE.$this->LE;1076$body .= $this->EndBoundary($this->boundary[2]);1077$body .= $this->AttachAll();1078break;1079}10801081if ($this->IsError()) {1082$body = '';1083} elseif ($this->sign_key_file) {1084try {1085$file = tempnam('', 'mail');1086file_put_contents($file, $body); //TODO check this worked1087$signed = tempnam("", "signed");1088if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {1089@unlink($file);1090@unlink($signed);1091$body = file_get_contents($signed);1092} else {1093@unlink($file);1094@unlink($signed);1095throw new phpmailerException($this->Lang("signing").openssl_error_string());1096}1097} catch (phpmailerException $e) {1098$body = '';1099if ($this->exceptions) {1100throw $e;1101}1102}1103}11041105return $body;1106}11071108/**1109* Returns the start of a message boundary.1110* @access private1111*/1112private function GetBoundary($boundary, $charSet, $contentType, $encoding) {1113$result = '';1114if($charSet == '') {1115$charSet = $this->CharSet;1116}1117if($contentType == '') {1118$contentType = $this->ContentType;1119}1120if($encoding == '') {1121$encoding = $this->Encoding;1122}1123$result .= $this->TextLine('--' . $boundary);1124$result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);1125$result .= $this->LE;1126$result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);1127$result .= $this->LE;11281129return $result;1130}11311132/**1133* Returns the end of a message boundary.1134* @access private1135*/1136private function EndBoundary($boundary) {1137return $this->LE . '--' . $boundary . '--' . $this->LE;1138}11391140/**1141* Sets the message type.1142* @access private1143* @return void1144*/1145private function SetMessageType() {1146if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {1147$this->message_type = 'plain';1148} else {1149if(count($this->attachment) > 0) {1150$this->message_type = 'attachments';1151}1152if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {1153$this->message_type = 'alt';1154}1155if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {1156$this->message_type = 'alt_attachments';1157}1158}1159}11601161/**1162* Returns a formatted header line.1163* @access public1164* @return string1165*/1166public function HeaderLine($name, $value) {1167return $name . ': ' . $value . $this->LE;1168}11691170/**1171* Returns a formatted mail line.1172* @access public1173* @return string1174*/1175public function TextLine($value) {1176return $value . $this->LE;1177}11781179/////////////////////////////////////////////////1180// CLASS METHODS, ATTACHMENTS1181/////////////////////////////////////////////////11821183/**1184* Adds an attachment from a path on the filesystem.1185* Returns false if the file could not be found1186* or accessed.1187* @param string $path Path to the attachment.1188* @param string $name Overrides the attachment name.1189* @param string $encoding File encoding (see $Encoding).1190* @param string $type File extension (MIME) type.1191* @return bool1192*/1193public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {1194try {1195if ( !@is_file($path) ) {1196throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);1197}1198$filename = basename($path);1199if ( $name == '' ) {1200$name = $filename;1201}12021203$this->attachment[] = array(12040 => $path,12051 => $filename,12062 => $name,12073 => $encoding,12084 => $type,12095 => false, // isStringAttachment12106 => 'attachment',12117 => 01212);12131214} catch (phpmailerException $e) {1215$this->SetError($e->getMessage());1216if ($this->exceptions) {1217throw $e;1218}1219echo $e->getMessage()."\n";1220if ( $e->getCode() == self::STOP_CRITICAL ) {1221return false;1222}1223}1224return true;1225}12261227/**1228* Return the current array of attachments1229* @return array1230*/1231public function GetAttachments() {1232return $this->attachment;1233}12341235/**1236* Attaches all fs, string, and binary attachments to the message.1237* Returns an empty string on failure.1238* @access private1239* @return string1240*/1241private function AttachAll() {1242// Return text of body1243$mime = array();1244$cidUniq = array();1245$incl = array();12461247// Add all attachments1248foreach ($this->attachment as $attachment) {1249// Check for string attachment1250$bString = $attachment[5];1251if ($bString) {1252$string = $attachment[0];1253} else {1254$path = $attachment[0];1255}12561257if (in_array($attachment[0], $incl)) { continue; }1258$filename = $attachment[1];1259$name = $attachment[2];1260$encoding = $attachment[3];1261$type = $attachment[4];1262$disposition = $attachment[6];1263$cid = $attachment[7];1264$incl[] = $attachment[0];1265if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }1266$cidUniq[$cid] = true;12671268$mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);1269$mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);1270$mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);12711272if($disposition == 'inline') {1273$mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);1274}12751276$mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);12771278// Encode as string attachment1279if($bString) {1280$mime[] = $this->EncodeString($string, $encoding);1281if($this->IsError()) {1282return '';1283}1284$mime[] = $this->LE.$this->LE;1285} else {1286$mime[] = $this->EncodeFile($path, $encoding);1287if($this->IsError()) {1288return '';1289}1290$mime[] = $this->LE.$this->LE;1291}1292}12931294$mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);12951296return join('', $mime);1297}12981299/**1300* Encodes attachment in requested format.1301* Returns an empty string on failure.1302* @param string $path The full path to the file1303* @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1304* @see EncodeFile()1305* @access private1306* @return string1307*/1308private function EncodeFile($path, $encoding = 'base64') {1309try {1310if (!is_readable($path)) {1311throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);1312}1313if (function_exists('get_magic_quotes')) {1314function get_magic_quotes() {1315return false;1316}1317}1318if (PHP_VERSION < 6) {1319$magic_quotes = get_magic_quotes_runtime();1320set_magic_quotes_runtime(0);1321}1322$file_buffer = file_get_contents($path);1323$file_buffer = $this->EncodeString($file_buffer, $encoding);1324if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }1325return $file_buffer;1326} catch (Exception $e) {1327$this->SetError($e->getMessage());1328return '';1329}1330}13311332/**1333* Encodes string to requested format.1334* Returns an empty string on failure.1335* @param string $str The text to encode1336* @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1337* @access public1338* @return string1339*/1340public function EncodeString ($str, $encoding = 'base64') {1341$encoded = '';1342switch(strtolower($encoding)) {1343case 'base64':1344$encoded = chunk_split(base64_encode($str), 76, $this->LE);1345break;1346case '7bit':1347case '8bit':1348$encoded = $this->FixEOL($str);1349//Make sure it ends with a line break1350if (substr($encoded, -(strlen($this->LE))) != $this->LE)1351$encoded .= $this->LE;1352break;1353case 'binary':1354$encoded = $str;1355break;1356case 'quoted-printable':1357$encoded = $this->EncodeQP($str);1358break;1359default:1360$this->SetError($this->Lang('encoding') . $encoding);1361break;1362}1363return $encoded;1364}13651366/**1367* Encode a header string to best (shortest) of Q, B, quoted or none.1368* @access public1369* @return string1370*/1371public function EncodeHeader($str, $position = 'text') {1372$x = 0;13731374switch (strtolower($position)) {1375case 'phrase':1376if (!preg_match('/[\200-\377]/', $str)) {1377// Can't use addslashes as we don't know what value has magic_quotes_sybase1378$encoded = addcslashes($str, "\0..\37\177\\\"");1379if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {1380return ($encoded);1381} else {1382return ("\"$encoded\"");1383}1384}1385$x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);1386break;1387case 'comment':1388$x = preg_match_all('/[()"]/', $str, $matches);1389// Fall-through1390case 'text':1391default:1392$x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);1393break;1394}13951396if ($x == 0) {1397return ($str);1398}13991400$maxlen = 75 - 7 - strlen($this->CharSet);1401// Try to select the encoding which should produce the shortest output1402if (strlen($str)/3 < $x) {1403$encoding = 'B';1404if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {1405// Use a custom function which correctly encodes and wraps long1406// multibyte strings without breaking lines within a character1407$encoded = $this->Base64EncodeWrapMB($str);1408} else {1409$encoded = base64_encode($str);1410$maxlen -= $maxlen % 4;1411$encoded = trim(chunk_split($encoded, $maxlen, "\n"));1412}1413} else {1414$encoding = 'Q';1415$encoded = $this->EncodeQ($str, $position);1416$encoded = $this->WrapText($encoded, $maxlen, true);1417$encoded = str_replace('='.$this->LE, "\n", trim($encoded));1418}14191420$encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);1421$encoded = trim(str_replace("\n", $this->LE, $encoded));14221423return $encoded;1424}14251426/**1427* Checks if a string contains multibyte characters.1428* @access public1429* @param string $str multi-byte text to wrap encode1430* @return bool1431*/1432public function HasMultiBytes($str) {1433if (function_exists('mb_strlen')) {1434return (strlen($str) > mb_strlen($str, $this->CharSet));1435} else { // Assume no multibytes (we can't handle without mbstring functions anyway)1436return false;1437}1438}14391440/**1441* Correctly encodes and wraps long multibyte strings for mail headers1442* without breaking lines within a character.1443* Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php1444* @access public1445* @param string $str multi-byte text to wrap encode1446* @return string1447*/1448public function Base64EncodeWrapMB($str) {1449$start = "=?".$this->CharSet."?B?";1450$end = "?=";1451$encoded = "";14521453$mb_length = mb_strlen($str, $this->CharSet);1454// Each line must have length <= 75, including $start and $end1455$length = 75 - strlen($start) - strlen($end);1456// Average multi-byte ratio1457$ratio = $mb_length / strlen($str);1458// Base64 has a 4:3 ratio1459$offset = $avgLength = floor($length * $ratio * .75);14601461for ($i = 0; $i < $mb_length; $i += $offset) {1462$lookBack = 0;14631464do {1465$offset = $avgLength - $lookBack;1466$chunk = mb_substr($str, $i, $offset, $this->CharSet);1467$chunk = base64_encode($chunk);1468$lookBack++;1469}1470while (strlen($chunk) > $length);14711472$encoded .= $chunk . $this->LE;1473}14741475// Chomp the last linefeed1476$encoded = substr($encoded, 0, -strlen($this->LE));1477return $encoded;1478}14791480/**1481* Encode string to quoted-printable.1482* Only uses standard PHP, slow, but will always work1483* @access public1484* @param string $string the text to encode1485* @param integer $line_max Number of chars allowed on a line before wrapping1486* @return string1487*/1488public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {1489$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');1490$lines = preg_split('/(?:\r\n|\r|\n)/', $input);1491$eol = "\r\n";1492$escape = '=';1493$output = '';1494while( list(, $line) = each($lines) ) {1495$linlen = strlen($line);1496$newline = '';1497for($i = 0; $i < $linlen; $i++) {1498$c = substr( $line, $i, 1 );1499$dec = ord( $c );1500if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E1501$c = '=2E';1502}1503if ( $dec == 32 ) {1504if ( $i == ( $linlen - 1 ) ) { // convert space at eol only1505$c = '=20';1506} else if ( $space_conv ) {1507$c = '=20';1508}1509} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required1510$h2 = floor($dec/16);1511$h1 = floor($dec%16);1512$c = $escape.$hex[$h2].$hex[$h1];1513}1514if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted1515$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay1516$newline = '';1517// check if newline first character will be point or not1518if ( $dec == 46 ) {1519$c = '=2E';1520}1521}1522$newline .= $c;1523} // end of for1524$output .= $newline.$eol;1525} // end of while1526return $output;1527}15281529/**1530* Encode string to RFC2045 (6.7) quoted-printable format1531* Uses a PHP5 stream filter to do the encoding about 64x faster than the old version1532* Also results in same content as you started with after decoding1533* @see EncodeQPphp()1534* @access public1535* @param string $string the text to encode1536* @param integer $line_max Number of chars allowed on a line before wrapping1537* @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function1538* @return string1539* @author Marcus Bointon1540*/1541public function EncodeQP($string, $line_max = 76, $space_conv = false) {1542if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)1543return quoted_printable_encode($string);1544}1545$filters = stream_get_filters();1546if (!in_array('convert.*', $filters)) { //Got convert stream filter?1547return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation1548}1549$fp = fopen('php://temp/', 'r+');1550$string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks1551$params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);1552$s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);1553fputs($fp, $string);1554rewind($fp);1555$out = stream_get_contents($fp);1556stream_filter_remove($s);1557$out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange1558fclose($fp);1559return $out;1560}15611562/**1563* NOTE: Phabricator patch to remove use of "/e". See D2147.1564*/1565private function encodeQCallback(array $matches) {1566return '='.sprintf('%02X', ord($matches[1]));1567}15681569/**1570* Encode string to q encoding.1571* @link http://tools.ietf.org/html/rfc20471572* @param string $str the text to encode1573* @param string $position Where the text is going to be used, see the RFC for what that means1574* @access public1575* @return string1576*/1577public function EncodeQ ($str, $position = 'text') {15781579// NOTE: Phabricator patch to remove use of "/e". See D2147.15801581// There should not be any EOL in the string1582$encoded = preg_replace('/[\r\n]*/', '', $str);15831584switch (strtolower($position)) {1585case 'phrase':1586$encoded = preg_replace_callback(1587"/([^A-Za-z0-9!*+\/ -])/",1588array($this, 'encodeQCallback'),1589$encoded);1590break;1591case 'comment':1592$encoded = preg_replace_callback(1593"/([\(\)\"])/",1594array($this, 'encodeQCallback'),1595$encoded);1596break;1597case 'text':1598default:1599// Replace every high ascii, control =, ? and _ characters1600$encoded = preg_replace_callback(1601'/([\000-\011\013\014\016-\037\075\077\137\177-\377])/',1602array($this, 'encodeQCallback'),1603$encoded);1604break;1605}16061607// Replace every spaces to _ (more readable than =20)1608$encoded = str_replace(' ', '_', $encoded);16091610return $encoded;1611}16121613/**1614* Adds a string or binary attachment (non-filesystem) to the list.1615* This method can be used to attach ascii or binary data,1616* such as a BLOB record from a database.1617* @param string $string String attachment data.1618* @param string $filename Name of the attachment.1619* @param string $encoding File encoding (see $Encoding).1620* @param string $type File extension (MIME) type.1621* @return void1622*/1623public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {1624// Append to $attachment array1625$this->attachment[] = array(16260 => $string,16271 => $filename,16282 => basename($filename),16293 => $encoding,16304 => $type,16315 => true, // isStringAttachment16326 => 'attachment',16337 => 01634);1635}16361637/**1638* Adds an embedded attachment. This can include images, sounds, and1639* just about any other document. Make sure to set the $type to an1640* image type. For JPEG images use "image/jpeg" and for GIF images1641* use "image/gif".1642* @param string $path Path to the attachment.1643* @param string $cid Content ID of the attachment. Use this to identify1644* the Id for accessing the image in an HTML form.1645* @param string $name Overrides the attachment name.1646* @param string $encoding File encoding (see $Encoding).1647* @param string $type File extension (MIME) type.1648* @return bool1649*/1650public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {16511652if ( !@is_file($path) ) {1653$this->SetError($this->Lang('file_access') . $path);1654return false;1655}16561657$filename = basename($path);1658if ( $name == '' ) {1659$name = $filename;1660}16611662// Append to $attachment array1663$this->attachment[] = array(16640 => $path,16651 => $filename,16662 => $name,16673 => $encoding,16684 => $type,16695 => false, // isStringAttachment16706 => 'inline',16717 => $cid1672);16731674return true;1675}16761677/**1678* Returns true if an inline attachment is present.1679* @access public1680* @return bool1681*/1682public function InlineImageExists() {1683foreach($this->attachment as $attachment) {1684if ($attachment[6] == 'inline') {1685return true;1686}1687}1688return false;1689}16901691/////////////////////////////////////////////////1692// CLASS METHODS, MESSAGE RESET1693/////////////////////////////////////////////////16941695/**1696* Clears all recipients assigned in the TO array. Returns void.1697* @return void1698*/1699public function ClearAddresses() {1700foreach($this->to as $to) {1701unset($this->all_recipients[strtolower($to[0])]);1702}1703$this->to = array();1704}17051706/**1707* Clears all recipients assigned in the CC array. Returns void.1708* @return void1709*/1710public function ClearCCs() {1711foreach($this->cc as $cc) {1712unset($this->all_recipients[strtolower($cc[0])]);1713}1714$this->cc = array();1715}17161717/**1718* Clears all recipients assigned in the BCC array. Returns void.1719* @return void1720*/1721public function ClearBCCs() {1722foreach($this->bcc as $bcc) {1723unset($this->all_recipients[strtolower($bcc[0])]);1724}1725$this->bcc = array();1726}17271728/**1729* Clears all recipients assigned in the ReplyTo array. Returns void.1730* @return void1731*/1732public function ClearReplyTos() {1733$this->ReplyTo = array();1734}17351736/**1737* Clears all recipients assigned in the TO, CC and BCC1738* array. Returns void.1739* @return void1740*/1741public function ClearAllRecipients() {1742$this->to = array();1743$this->cc = array();1744$this->bcc = array();1745$this->all_recipients = array();1746}17471748/**1749* Clears all previously set filesystem, string, and binary1750* attachments. Returns void.1751* @return void1752*/1753public function ClearAttachments() {1754$this->attachment = array();1755}17561757/**1758* Clears all custom headers. Returns void.1759* @return void1760*/1761public function ClearCustomHeaders() {1762$this->CustomHeader = array();1763}17641765/////////////////////////////////////////////////1766// CLASS METHODS, MISCELLANEOUS1767/////////////////////////////////////////////////17681769/**1770* Adds the error message to the error container.1771* @access protected1772* @return void1773*/1774protected function SetError($msg) {1775$this->error_count++;1776$this->ErrorInfo = $msg;1777}17781779/**1780* Returns the proper RFC 822 formatted date.1781* @access public1782* @return string1783* @static1784*/1785public static function RFCDate() {1786$tz = date('Z');1787$tzs = ($tz < 0) ? '-' : '+';1788$tz = abs($tz);1789$tz = (int)($tz/3600)*100 + ($tz%3600)/60;1790$result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);17911792return $result;1793}17941795/**1796* Returns the server hostname or 'localhost.localdomain' if unknown.1797* @access private1798* @return string1799*/1800private function ServerHostname() {1801if (!empty($this->Hostname)) {1802$result = $this->Hostname;1803} elseif (isset($_SERVER['SERVER_NAME'])) {1804$result = $_SERVER['SERVER_NAME'];1805} else {1806$result = 'localhost.localdomain';1807}18081809return $result;1810}18111812/**1813* Returns a message in the appropriate language.1814* @access private1815* @return string1816*/1817private function Lang($key) {1818if(count($this->language) < 1) {1819$this->SetLanguage('en'); // set the default language1820}18211822if(isset($this->language[$key])) {1823return $this->language[$key];1824} else {1825return 'Language string failed to load: ' . $key;1826}1827}18281829/**1830* Returns true if an error occurred.1831* @access public1832* @return bool1833*/1834public function IsError() {1835return ($this->error_count > 0);1836}18371838/**1839* Changes every end of line from CR or LF to CRLF.1840* @access private1841* @return string1842*/1843private function FixEOL($str) {1844$str = str_replace("\r\n", "\n", $str);1845$str = str_replace("\r", "\n", $str);1846$str = str_replace("\n", $this->LE, $str);1847return $str;1848}18491850/**1851* Adds a custom header.1852* @access public1853* @return void1854*/1855public function AddCustomHeader($custom_header) {1856$this->CustomHeader[] = explode(':', $custom_header, 2);1857}18581859/**1860* Evaluates the message and returns modifications for inline images and backgrounds1861* @access public1862* @return $message1863*/1864public function MsgHTML($message, $basedir = '') {1865preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);1866if(isset($images[2])) {1867foreach($images[2] as $i => $url) {1868// do not change urls for absolute images (thanks to corvuscorax)1869if (!preg_match('#^[A-z]+://#',$url)) {1870$filename = basename($url);1871$directory = dirname($url);1872($directory == '.')?$directory='':'';1873$cid = 'cid:' . md5($filename);1874$ext = pathinfo($filename, PATHINFO_EXTENSION);1875$mimeType = self::_mime_types($ext);1876if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }1877if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }1878if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {1879$message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);1880}1881}1882}1883}1884$this->IsHTML(true);1885$this->Body = $message;1886$textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));1887if (!empty($textMsg) && empty($this->AltBody)) {1888$this->AltBody = html_entity_decode($textMsg);1889}1890if (empty($this->AltBody)) {1891$this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";1892}1893}18941895/**1896* Gets the MIME type of the embedded or inline image1897* @param string File extension1898* @access public1899* @return string MIME type of ext1900* @static1901*/1902public static function _mime_types($ext = '') {1903$mimes = array(1904'hqx' => 'application/mac-binhex40',1905'cpt' => 'application/mac-compactpro',1906'doc' => 'application/msword',1907'bin' => 'application/macbinary',1908'dms' => 'application/octet-stream',1909'lha' => 'application/octet-stream',1910'lzh' => 'application/octet-stream',1911'exe' => 'application/octet-stream',1912'class' => 'application/octet-stream',1913'psd' => 'application/octet-stream',1914'so' => 'application/octet-stream',1915'sea' => 'application/octet-stream',1916'dll' => 'application/octet-stream',1917'oda' => 'application/oda',1918'pdf' => 'application/pdf',1919'ai' => 'application/postscript',1920'eps' => 'application/postscript',1921'ps' => 'application/postscript',1922'smi' => 'application/smil',1923'smil' => 'application/smil',1924'mif' => 'application/vnd.mif',1925'xls' => 'application/vnd.ms-excel',1926'ppt' => 'application/vnd.ms-powerpoint',1927'wbxml' => 'application/vnd.wap.wbxml',1928'wmlc' => 'application/vnd.wap.wmlc',1929'dcr' => 'application/x-director',1930'dir' => 'application/x-director',1931'dxr' => 'application/x-director',1932'dvi' => 'application/x-dvi',1933'gtar' => 'application/x-gtar',1934'php' => 'application/x-httpd-php',1935'php4' => 'application/x-httpd-php',1936'php3' => 'application/x-httpd-php',1937'phtml' => 'application/x-httpd-php',1938'phps' => 'application/x-httpd-php-source',1939'js' => 'application/x-javascript',1940'swf' => 'application/x-shockwave-flash',1941'sit' => 'application/x-stuffit',1942'tar' => 'application/x-tar',1943'tgz' => 'application/x-tar',1944'xhtml' => 'application/xhtml+xml',1945'xht' => 'application/xhtml+xml',1946'zip' => 'application/zip',1947'mid' => 'audio/midi',1948'midi' => 'audio/midi',1949'mpga' => 'audio/mpeg',1950'mp2' => 'audio/mpeg',1951'mp3' => 'audio/mpeg',1952'aif' => 'audio/x-aiff',1953'aiff' => 'audio/x-aiff',1954'aifc' => 'audio/x-aiff',1955'ram' => 'audio/x-pn-realaudio',1956'rm' => 'audio/x-pn-realaudio',1957'rpm' => 'audio/x-pn-realaudio-plugin',1958'ra' => 'audio/x-realaudio',1959'rv' => 'video/vnd.rn-realvideo',1960'wav' => 'audio/x-wav',1961'bmp' => 'image/bmp',1962'gif' => 'image/gif',1963'jpeg' => 'image/jpeg',1964'jpg' => 'image/jpeg',1965'jpe' => 'image/jpeg',1966'png' => 'image/png',1967'tiff' => 'image/tiff',1968'tif' => 'image/tiff',1969'css' => 'text/css',1970'html' => 'text/html',1971'htm' => 'text/html',1972'shtml' => 'text/html',1973'txt' => 'text/plain',1974'text' => 'text/plain',1975'log' => 'text/plain',1976'rtx' => 'text/richtext',1977'rtf' => 'text/rtf',1978'xml' => 'text/xml',1979'xsl' => 'text/xml',1980'mpeg' => 'video/mpeg',1981'mpg' => 'video/mpeg',1982'mpe' => 'video/mpeg',1983'qt' => 'video/quicktime',1984'mov' => 'video/quicktime',1985'avi' => 'video/x-msvideo',1986'movie' => 'video/x-sgi-movie',1987'doc' => 'application/msword',1988'word' => 'application/msword',1989'xl' => 'application/excel',1990'eml' => 'message/rfc822'1991);1992return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];1993}19941995/**1996* Set (or reset) Class Objects (variables)1997*1998* Usage Example:1999* $page->set('X-Priority', '3');2000*2001* @access public2002* @param string $name Parameter Name2003* @param mixed $value Parameter Value2004* NOTE: will not work with arrays, there are no arrays to set/reset2005* @todo Should this not be using __set() magic function?2006*/2007public function set($name, $value = '') {2008try {2009if (isset($this->$name) ) {2010$this->$name = $value;2011} else {2012throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);2013}2014} catch (Exception $e) {2015$this->SetError($e->getMessage());2016if ($e->getCode() == self::STOP_CRITICAL) {2017return false;2018}2019}2020return true;2021}20222023/**2024* Strips newlines to prevent header injection.2025* @access public2026* @param string $str String2027* @return string2028*/2029public function SecureHeader($str) {2030$str = str_replace("\r", '', $str);2031$str = str_replace("\n", '', $str);2032return trim($str);2033}20342035/**2036* Set the private key file and password to sign the message.2037*2038* @access public2039* @param string $key_filename Parameter File Name2040* @param string $key_pass Password for private key2041*/2042public function Sign($cert_filename, $key_filename, $key_pass) {2043$this->sign_cert_file = $cert_filename;2044$this->sign_key_file = $key_filename;2045$this->sign_key_pass = $key_pass;2046}20472048/**2049* Set the private key file and password to sign the message.2050*2051* @access public2052* @param string $key_filename Parameter File Name2053* @param string $key_pass Password for private key2054*/2055public function DKIM_QP($txt) {2056$tmp="";2057$line="";2058for ($i=0;$i<strlen($txt);$i++) {2059$ord=ord($txt[$i]);2060if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {2061$line.=$txt[$i];2062} else {2063$line.="=".sprintf("%02X",$ord);2064}2065}2066return $line;2067}20682069/**2070* Generate DKIM signature2071*2072* @access public2073* @param string $s Header2074*/2075public function DKIM_Sign($s) {2076$privKeyStr = file_get_contents($this->DKIM_private);2077if ($this->DKIM_passphrase!='') {2078$privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase);2079} else {2080$privKey = $privKeyStr;2081}2082if (openssl_sign($s, $signature, $privKey)) {2083return base64_encode($signature);2084}2085}20862087/**2088* Generate DKIM Canonicalization Header2089*2090* @access public2091* @param string $s Header2092*/2093public function DKIM_HeaderC($s) {2094$s=preg_replace("/\r\n\s+/"," ",$s);2095$lines=explode("\r\n",$s);2096foreach ($lines as $key=>$line) {2097list($heading,$value)=explode(":",$line,2);2098$heading=strtolower($heading);2099$value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces2100$lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value2101}2102$s=implode("\r\n",$lines);2103return $s;2104}21052106/**2107* Generate DKIM Canonicalization Body2108*2109* @access public2110* @param string $body Message Body2111*/2112public function DKIM_BodyC($body) {2113if ($body == '') return "\r\n";2114// stabilize line endings2115$body=str_replace("\r\n","\n",$body);2116$body=str_replace("\n","\r\n",$body);2117// END stabilize line endings2118while (substr($body,strlen($body)-4,4) == "\r\n\r\n") {2119$body=substr($body,0,strlen($body)-2);2120}2121return $body;2122}21232124/**2125* Create the DKIM header, body, as new header2126*2127* @access public2128* @param string $headers_line Header lines2129* @param string $subject Subject2130* @param string $body Body2131*/2132public function DKIM_Add($headers_line,$subject,$body) {2133$DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms2134$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body2135$DKIMquery = 'dns/txt'; // Query method2136$DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)2137$subject_header = "Subject: $subject";2138$headers = explode("\r\n",$headers_line);2139foreach($headers as $header) {2140if (strpos($header,'From:') === 0) {2141$from_header=$header;2142} elseif (strpos($header,'To:') === 0) {2143$to_header=$header;2144}2145}2146$from = str_replace('|','=7C',$this->DKIM_QP($from_header));2147$to = str_replace('|','=7C',$this->DKIM_QP($to_header));2148$subject = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable2149$body = $this->DKIM_BodyC($body);2150$DKIMlen = strlen($body) ; // Length of body2151$DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body2152$ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";2153$dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".2154"\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".2155"\th=From:To:Subject;\r\n".2156"\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".2157"\tz=$from\r\n".2158"\t|$to\r\n".2159"\t|$subject;\r\n".2160"\tbh=" . $DKIMb64 . ";\r\n".2161"\tb=";2162$toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);2163$signed = $this->DKIM_Sign($toSign);2164return "X-PHPMAILER-DKIM: phpmailer.sourceforge.net\r\n".$dkimhdrs.$signed."\r\n";2165}21662167protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) {2168if (!empty($this->action_function) && function_exists($this->action_function)) {2169$params = array($isSent,$to,$cc,$bcc,$subject,$body);2170call_user_func_array($this->action_function,$params);2171}2172}2173}21742175class phpmailerException extends Exception {2176public function errorMessage() {2177$errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";2178return $errorMsg;2179}2180}2181?>218221832184