File: 0.00.1a/core/phpmailer/class.phpmailer.php (View as Code)

1: 2: /*~ class.phpmailer.php 3: .---------------------------------------------------------------------------. 4: | Software: PHPMailer - PHP email class | 5: | Version: 5.2.1 | 6: | Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | 7: | ------------------------------------------------------------------------- | 8: | Admin: Jim Jagielski (project admininistrator) | 9: | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | 10: | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | 11: | : Jim Jagielski (jimjag) jimjag@gmail.com | 12: | Founder: Brent R. Matzelle (original founder) | 13: | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | 14: | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | 15: | Copyright (c) 2001-2003, Brent R. Matzelle | 16: | ------------------------------------------------------------------------- | 17: | License: Distributed under the Lesser General Public License (LGPL) | 18: | http://www.gnu.org/copyleft/lesser.html | 19: | This program is distributed in the hope that it will be useful - WITHOUT | 20: | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 21: | FITNESS FOR A PARTICULAR PURPOSE. | 22: '---------------------------------------------------------------------------' 23: */ 24: 25: /** 26: * PHPMailer - PHP email transport class 27: * NOTE: Requires PHP version 5 or later 28: * @package PHPMailer 29: * @author Andy Prevost 30: * @author Marcus Bointon 31: * @author Jim Jagielski 32: * @copyright 2010 - 2012 Jim Jagielski 33: * @copyright 2004 - 2009 Andy Prevost 34: * @version $Id: class.phpmailer.php 450 2010-06-23 16:46:33Z coolbru $ 35: * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 36: */ 37: 38: if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); 39: 40: class PHPMailer { 41: 42: ///////////////////////////////////////////////// 43: // PROPERTIES, PUBLIC 44: ///////////////////////////////////////////////// 45: 46: /** 47: * Email priority (1 = High, 3 = Normal, 5 = low). 48: * @var int 49: */ 50: public $Priority = 3; 51: 52: /** 53: * Sets the CharSet of the message. 54: * @var string 55: */ 56: public $CharSet = 'iso-8859-1'; 57: 58: /** 59: * Sets the Content-type of the message. 60: * @var string 61: */ 62: public $ContentType = 'text/plain'; 63: 64: /** 65: * Sets the Encoding of the message. Options for this are 66: * "8bit", "7bit", "binary", "base64", and "quoted-printable". 67: * @var string 68: */ 69: public $Encoding = '8bit'; 70: 71: /** 72: * Holds the most recent mailer error message. 73: * @var string 74: */ 75: public $ErrorInfo = ''; 76: 77: /** 78: * Sets the From email address for the message. 79: * @var string 80: */ 81: public $From = 'root@localhost'; 82: 83: /** 84: * Sets the From name of the message. 85: * @var string 86: */ 87: public $FromName = 'Root User'; 88: 89: /** 90: * Sets the Sender email (Return-Path) of the message. If not empty, 91: * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 92: * @var string 93: */ 94: public $Sender = ''; 95: 96: /** 97: * Sets the Subject of the message. 98: * @var string 99: */ 100: public $Subject = ''; 101: 102: /** 103: * Sets the Body of the message. This can be either an HTML or text body. 104: * If HTML then run IsHTML(true). 105: * @var string 106: */ 107: public $Body = ''; 108: 109: /** 110: * Sets the text-only body of the message. This automatically sets the 111: * email to multipart/alternative. This body can be read by mail 112: * clients that do not have HTML email capability such as mutt. Clients 113: * that can read HTML will view the normal Body. 114: * @var string 115: */ 116: public $AltBody = ''; 117: 118: /** 119: * Stores the complete compiled MIME message body. 120: * @var string 121: * @access protected 122: */ 123: protected $MIMEBody = ''; 124: 125: /** 126: * Stores the complete compiled MIME message headers. 127: * @var string 128: * @access protected 129: */ 130: protected $MIMEHeader = ''; 131: 132: /** 133: * Stores the complete sent MIME message (Body and Headers) 134: * @var string 135: * @access protected 136: */ 137: protected $SentMIMEMessage = ''; 138: 139: /** 140: * Sets word wrapping on the body of the message to a given number of 141: * characters. 142: * @var int 143: */ 144: public $WordWrap = 0; 145: 146: /** 147: * Method to send mail: ("mail", "sendmail", or "smtp"). 148: * @var string 149: */ 150: public $Mailer = 'mail'; 151: 152: /** 153: * Sets the path of the sendmail program. 154: * @var string 155: */ 156: public $Sendmail = '/usr/sbin/sendmail'; 157: 158: /** 159: * Path to PHPMailer plugins. Useful if the SMTP class 160: * is in a different directory than the PHP include path. 161: * @var string 162: */ 163: public $PluginDir = ''; 164: 165: /** 166: * Sets the email address that a reading confirmation will be sent. 167: * @var string 168: */ 169: public $ConfirmReadingTo = ''; 170: 171: /** 172: * Sets the hostname to use in Message-Id and Received headers 173: * and as default HELO string. If empty, the value returned 174: * by SERVER_NAME is used or 'localhost.localdomain'. 175: * @var string 176: */ 177: public $Hostname = ''; 178: 179: /** 180: * Sets the message ID to be used in the Message-Id header. 181: * If empty, a unique id will be generated. 182: * @var string 183: */ 184: public $MessageID = ''; 185: 186: ///////////////////////////////////////////////// 187: // PROPERTIES FOR SMTP 188: ///////////////////////////////////////////////// 189: 190: /** 191: * Sets the SMTP hosts. All hosts must be separated by a 192: * semicolon. You can also specify a different port 193: * for each host by using this format: [hostname:port] 194: * (e.g. "smtp1.example.com:25;smtp2.example.com"). 195: * Hosts will be tried in order. 196: * @var string 197: */ 198: public $Host = 'localhost'; 199: 200: /** 201: * Sets the default SMTP server port. 202: * @var int 203: */ 204: public $Port = 25; 205: 206: /** 207: * Sets the SMTP HELO of the message (Default is $Hostname). 208: * @var string 209: */ 210: public $Helo = ''; 211: 212: /** 213: * Sets connection prefix. 214: * Options are "", "ssl" or "tls" 215: * @var string 216: */ 217: public $SMTPSecure = ''; 218: 219: /** 220: * Sets SMTP authentication. Utilizes the Username and Password variables. 221: * @var bool 222: */ 223: public $SMTPAuth = false; 224: 225: /** 226: * Sets SMTP username. 227: * @var string 228: */ 229: public $Username = ''; 230: 231: /** 232: * Sets SMTP password. 233: * @var string 234: */ 235: public $Password = ''; 236: 237: /** 238: * Sets the SMTP server timeout in seconds. 239: * This function will not work with the win32 version. 240: * @var int 241: */ 242: public $Timeout = 10; 243: 244: /** 245: * Sets SMTP class debugging on or off. 246: * @var bool 247: */ 248: public $SMTPDebug = false; 249: 250: /** 251: * Prevents the SMTP connection from being closed after each mail 252: * sending. If this is set to true then to close the connection 253: * requires an explicit call to SmtpClose(). 254: * @var bool 255: */ 256: public $SMTPKeepAlive = false; 257: 258: /** 259: * Provides the ability to have the TO field process individual 260: * emails, instead of sending to entire TO addresses 261: * @var bool 262: */ 263: public $SingleTo = false; 264: 265: /** 266: * If SingleTo is true, this provides the array to hold the email addresses 267: * @var bool 268: */ 269: public $SingleToArray = array(); 270: 271: /** 272: * Provides the ability to change the line ending 273: * @var string 274: */ 275: public $LE = "\n"; 276: 277: /** 278: * Used with DKIM DNS Resource Record 279: * @var string 280: */ 281: public $DKIM_selector = 'phpmailer'; 282: 283: /** 284: * Used with DKIM DNS Resource Record 285: * optional, in format of email address 'you@yourdomain.com' 286: * @var string 287: */ 288: public $DKIM_identity = ''; 289: 290: /** 291: * Used with DKIM DNS Resource Record 292: * @var string 293: */ 294: public $DKIM_passphrase = ''; 295: 296: /** 297: * Used with DKIM DNS Resource Record 298: * optional, in format of email address 'you@yourdomain.com' 299: * @var string 300: */ 301: public $DKIM_domain = ''; 302: 303: /** 304: * Used with DKIM DNS Resource Record 305: * optional, in format of email address 'you@yourdomain.com' 306: * @var string 307: */ 308: public $DKIM_private = ''; 309: 310: /** 311: * Callback Action function name 312: * the function that handles the result of the send email action. Parameters: 313: * bool $result result of the send action 314: * string $to email address of the recipient 315: * string $cc cc email addresses 316: * string $bcc bcc email addresses 317: * string $subject the subject 318: * string $body the email body 319: * @var string 320: */ 321: public $action_function = ''; //'callbackAction'; 322: 323: /** 324: * Sets the PHPMailer Version number 325: * @var string 326: */ 327: public $Version = '5.2.1'; 328: 329: /** 330: * What to use in the X-Mailer header 331: * @var string 332: */ 333: public $XMailer = ''; 334: 335: ///////////////////////////////////////////////// 336: // PROPERTIES, PRIVATE AND PROTECTED 337: ///////////////////////////////////////////////// 338: 339: protected $smtp = NULL; 340: protected $to = array(); 341: protected $cc = array(); 342: protected $bcc = array(); 343: protected $ReplyTo = array(); 344: protected $all_recipients = array(); 345: protected $attachment = array(); 346: protected $CustomHeader = array(); 347: protected $message_type = ''; 348: protected $boundary = array(); 349: protected $language = array(); 350: protected $error_count = 0; 351: protected $sign_cert_file = ''; 352: protected $sign_key_file = ''; 353: protected $sign_key_pass = ''; 354: protected $exceptions = false; 355: 356: ///////////////////////////////////////////////// 357: // CONSTANTS 358: ///////////////////////////////////////////////// 359: 360: const STOP_MESSAGE = 0; // message only, continue processing 361: const STOP_CONTINUE = 1; // message?, likely ok to continue processing 362: const STOP_CRITICAL = 2; // message, plus full stop, critical error reached 363: 364: ///////////////////////////////////////////////// 365: // METHODS, VARIABLES 366: ///////////////////////////////////////////////// 367: 368: /** 369: * Constructor 370: * @param boolean $exceptions Should we throw external exceptions? 371: */ 372: public function __construct($exceptions = false) { 373: $this->exceptions = ($exceptions == true); 374: } 375: 376: /** 377: * Sets message type to HTML. 378: * @param bool $ishtml 379: * @return void 380: */ 381: public function IsHTML($ishtml = true) { 382: if ($ishtml) { 383: $this->ContentType = 'text/html'; 384: } else { 385: $this->ContentType = 'text/plain'; 386: } 387: } 388: 389: /** 390: * Sets Mailer to send message using SMTP. 391: * @return void 392: */ 393: public function IsSMTP() { 394: $this->Mailer = 'smtp'; 395: } 396: 397: /** 398: * Sets Mailer to send message using PHP mail() function. 399: * @return void 400: */ 401: public function IsMail() { 402: $this->Mailer = 'mail'; 403: } 404: 405: /** 406: * Sets Mailer to send message using the $Sendmail program. 407: * @return void 408: */ 409: public function IsSendmail() { 410: if (!stristr(ini_get('sendmail_path'), 'sendmail')) { 411: $this->Sendmail = '/var/qmail/bin/sendmail'; 412: } 413: $this->Mailer = 'sendmail'; 414: } 415: 416: /** 417: * Sets Mailer to send message using the qmail MTA. 418: * @return void 419: */ 420: public function IsQmail() { 421: if (stristr(ini_get('sendmail_path'), 'qmail')) { 422: $this->Sendmail = '/var/qmail/bin/sendmail'; 423: } 424: $this->Mailer = 'sendmail'; 425: } 426: 427: ///////////////////////////////////////////////// 428: // METHODS, RECIPIENTS 429: ///////////////////////////////////////////////// 430: 431: /** 432: * Adds a "To" address. 433: * @param string $address 434: * @param string $name 435: * @return boolean true on success, false if address already used 436: */ 437: public function AddAddress($address, $name = '') { 438: return $this->AddAnAddress('to', $address, $name); 439: } 440: 441: /** 442: * Adds a "Cc" address. 443: * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 444: * @param string $address 445: * @param string $name 446: * @return boolean true on success, false if address already used 447: */ 448: public function AddCC($address, $name = '') { 449: return $this->AddAnAddress('cc', $address, $name); 450: } 451: 452: /** 453: * Adds a "Bcc" address. 454: * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 455: * @param string $address 456: * @param string $name 457: * @return boolean true on success, false if address already used 458: */ 459: public function AddBCC($address, $name = '') { 460: return $this->AddAnAddress('bcc', $address, $name); 461: } 462: 463: /** 464: * Adds a "Reply-to" address. 465: * @param string $address 466: * @param string $name 467: * @return boolean 468: */ 469: public function AddReplyTo($address, $name = '') { 470: return $this->AddAnAddress('Reply-To', $address, $name); 471: } 472: 473: /** 474: * Adds an address to one of the recipient arrays 475: * Addresses that have been added already return false, but do not throw exceptions 476: * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' 477: * @param string $address The email address to send to 478: * @param string $name 479: * @return boolean true on success, false if address already used or invalid in some way 480: * @access protected 481: */ 482: protected function AddAnAddress($kind, $address, $name = '') { 483: if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { 484: $this->SetError($this->Lang('Invalid recipient array').': '.$kind); 485: if ($this->exceptions) { 486: throw new phpmailerException('Invalid recipient array: ' . $kind); 487: } 488: if ($this->SMTPDebug) { 489: echo $this->Lang('Invalid recipient array').': '.$kind; 490: } 491: return false; 492: } 493: $address = trim($address); 494: $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 495: if (!self::ValidateAddress($address)) { 496: $this->SetError($this->Lang('invalid_address').': '. $address); 497: if ($this->exceptions) { 498: throw new phpmailerException($this->Lang('invalid_address').': '.$address); 499: } 500: if ($this->SMTPDebug) { 501: echo $this->Lang('invalid_address').': '.$address; 502: } 503: return false; 504: } 505: if ($kind != 'Reply-To') { 506: if (!isset($this->all_recipients[strtolower($address)])) { 507: array_push($this->$kind, array($address, $name)); 508: $this->all_recipients[strtolower($address)] = true; 509: return true; 510: } 511: } else { 512: if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 513: $this->ReplyTo[strtolower($address)] = array($address, $name); 514: return true; 515: } 516: } 517: return false; 518: } 519: 520: /** 521: * Set the From and FromName properties 522: * @param string $address 523: * @param string $name 524: * @return boolean 525: */ 526: public function SetFrom($address, $name = '', $auto = 1) { 527: $address = trim($address); 528: $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 529: if (!self::ValidateAddress($address)) { 530: $this->SetError($this->Lang('invalid_address').': '. $address); 531: if ($this->exceptions) { 532: throw new phpmailerException($this->Lang('invalid_address').': '.$address); 533: } 534: if ($this->SMTPDebug) { 535: echo $this->Lang('invalid_address').': '.$address; 536: } 537: return false; 538: } 539: $this->From = $address; 540: $this->FromName = $name; 541: if ($auto) { 542: if (empty($this->ReplyTo)) { 543: $this->AddAnAddress('Reply-To', $address, $name); 544: } 545: if (empty($this->Sender)) { 546: $this->Sender = $address; 547: } 548: } 549: return true; 550: } 551: 552: /** 553: * Check that a string looks roughly like an email address should 554: * Static so it can be used without instantiation 555: * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator 556: * Conforms approximately to RFC2822 557: * @link http://www.hexillion.com/samples/#Regex Original pattern found here 558: * @param string $address The email address to check 559: * @return boolean 560: * @static 561: * @access public 562: */ 563: public static function ValidateAddress($address) { 564: if (function_exists('filter_var')) { //Introduced in PHP 5.2 565: if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { 566: return false; 567: } else { 568: return true; 569: } 570: } else { 571: return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); 572: } 573: } 574: 575: ///////////////////////////////////////////////// 576: // METHODS, MAIL SENDING 577: ///////////////////////////////////////////////// 578: 579: /** 580: * Creates message and assigns Mailer. If the message is 581: * not sent successfully then it returns false. Use the ErrorInfo 582: * variable to view description of the error. 583: * @return bool 584: */ 585: public function Send() { 586: try { 587: if(!$this->PreSend()) return false; 588: return $this->PostSend(); 589: } catch (phpmailerException $e) { 590: $this->SentMIMEMessage = ''; 591: $this->SetError($e->getMessage()); 592: if ($this->exceptions) { 593: throw $e; 594: } 595: return false; 596: } 597: } 598: 599: protected function PreSend() { 600: try { 601: $mailHeader = ""; 602: if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { 603: throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); 604: } 605: 606: // Set whether the message is multipart/alternative 607: if(!empty($this->AltBody)) { 608: $this->ContentType = 'multipart/alternative'; 609: } 610: 611: $this->error_count = 0; // reset errors 612: $this->SetMessageType(); 613: //Refuse to send an empty message 614: if (empty($this->Body)) { 615: throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); 616: } 617: 618: $this->MIMEHeader = $this->CreateHeader(); 619: $this->MIMEBody = $this->CreateBody(); 620: 621: // To capture the complete message when using mail(), create 622: // an extra header list which CreateHeader() doesn't fold in 623: if ($this->Mailer == 'mail') { 624: if (count($this->to) > 0) { 625: $mailHeader .= $this->AddrAppend("To", $this->to); 626: } else { 627: $mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); 628: } 629: $mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); 630: // if(count($this->cc) > 0) { 631: // $mailHeader .= $this->AddrAppend("Cc", $this->cc); 632: // } 633: } 634: 635: // digitally sign with DKIM if enabled 636: if ($this->DKIM_domain && $this->DKIM_private) { 637: $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); 638: $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; 639: } 640: 641: $this->SentMIMEMessage = sprintf("%s%s\r\n\r\n%s",$this->MIMEHeader,$mailHeader,$this->MIMEBody); 642: return true; 643: 644: } catch (phpmailerException $e) { 645: $this->SetError($e->getMessage()); 646: if ($this->exceptions) { 647: throw $e; 648: } 649: return false; 650: } 651: } 652: 653: protected function PostSend() { 654: try { 655: // Choose the mailer and send through it 656: switch($this->Mailer) { 657: case 'sendmail': 658: return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); 659: case 'smtp': 660: return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); 661: case 'mail': 662: return $this->MailSend($this->MIMEHeader, $this->MIMEBody); 663: default: 664: return $this->MailSend($this->MIMEHeader, $this->MIMEBody); 665: } 666: 667: } catch (phpmailerException $e) { 668: $this->SetError($e->getMessage()); 669: if ($this->exceptions) { 670: throw $e; 671: } 672: if ($this->SMTPDebug) { 673: echo $e->getMessage()."\n"; 674: } 675: return false; 676: } 677: } 678: 679: /** 680: * Sends mail using the $Sendmail program. 681: * @param string $header The message headers 682: * @param string $body The message body 683: * @access protected 684: * @return bool 685: */ 686: protected function SendmailSend($header, $body) { 687: if ($this->Sender != '') { 688: $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 689: } else { 690: $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 691: } 692: if ($this->SingleTo === true) { 693: foreach ($this->SingleToArray as $key => $val) { 694: if(!@$mail = popen($sendmail, 'w')) { 695: throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 696: } 697: fputs($mail, "To: " . $val . "\n"); 698: fputs($mail, $header); 699: fputs($mail, $body); 700: $result = pclose($mail); 701: // implement call back function if it exists 702: $isSent = ($result == 0) ? 1 : 0; 703: $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); 704: if($result != 0) { 705: throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 706: } 707: } 708: } else { 709: if(!@$mail = popen($sendmail, 'w')) { 710: throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 711: } 712: fputs($mail, $header); 713: fputs($mail, $body); 714: $result = pclose($mail); 715: // implement call back function if it exists 716: $isSent = ($result == 0) ? 1 : 0; 717: $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); 718: if($result != 0) { 719: throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 720: } 721: } 722: return true; 723: } 724: 725: /** 726: * Sends mail using the PHP mail() function. 727: * @param string $header The message headers 728: * @param string $body The message body 729: * @access protected 730: * @return bool 731: */ 732: protected function MailSend($header, $body) { 733: $toArr = array(); 734: foreach($this->to as $t) { 735: $toArr[] = $this->AddrFormat($t); 736: } 737: $to = implode(', ', $toArr); 738: 739: if (empty($this->Sender)) { 740: $params = "-oi "; 741: } else { 742: $params = sprintf("-oi -f %s", $this->Sender); 743: } 744: if ($this->Sender != '' and !ini_get('safe_mode')) { 745: $old_from = ini_get('sendmail_from'); 746: ini_set('sendmail_from', $this->Sender); 747: if ($this->SingleTo === true && count($toArr) > 1) { 748: foreach ($toArr as $key => $val) { 749: $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 750: // implement call back function if it exists 751: $isSent = ($rt == 1) ? 1 : 0; 752: $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); 753: } 754: } else { 755: $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 756: // implement call back function if it exists 757: $isSent = ($rt == 1) ? 1 : 0; 758: $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); 759: } 760: } else { 761: if ($this->SingleTo === true && count($toArr) > 1) { 762: foreach ($toArr as $key => $val) { 763: $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 764: // implement call back function if it exists 765: $isSent = ($rt == 1) ? 1 : 0; 766: $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); 767: } 768: } else { 769: $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 770: // implement call back function if it exists 771: $isSent = ($rt == 1) ? 1 : 0; 772: $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); 773: } 774: } 775: if (isset($old_from)) { 776: ini_set('sendmail_from', $old_from); 777: } 778: if(!$rt) { 779: throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); 780: } 781: return true; 782: } 783: 784: /** 785: * Sends mail via SMTP using PhpSMTP 786: * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. 787: * @param string $header The message headers 788: * @param string $body The message body 789: * @uses SMTP 790: * @access protected 791: * @return bool 792: */ 793: protected function SmtpSend($header, $body) { 794: require_once $this->PluginDir . 'class.smtp.php'; 795: $bad_rcpt = array(); 796: 797: if(!$this->SmtpConnect()) { 798: throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); 799: } 800: $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; 801: if(!$this->smtp->Mail($smtp_from)) { 802: throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); 803: } 804: 805: // Attempt to send attach all recipients 806: foreach($this->to as $to) { 807: if (!$this->smtp->Recipient($to[0])) { 808: $bad_rcpt[] = $to[0]; 809: // implement call back function if it exists 810: $isSent = 0; 811: $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); 812: } else { 813: // implement call back function if it exists 814: $isSent = 1; 815: $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); 816: } 817: } 818: foreach($this->cc as $cc) { 819: if (!$this->smtp->Recipient($cc[0])) { 820: $bad_rcpt[] = $cc[0]; 821: // implement call back function if it exists 822: $isSent = 0; 823: $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); 824: } else { 825: // implement call back function if it exists 826: $isSent = 1; 827: $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); 828: } 829: } 830: foreach($this->bcc as $bcc) { 831: if (!$this->smtp->Recipient($bcc[0])) { 832: $bad_rcpt[] = $bcc[0]; 833: // implement call back function if it exists 834: $isSent = 0; 835: $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); 836: } else { 837: // implement call back function if it exists 838: $isSent = 1; 839: $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); 840: } 841: } 842: 843: 844: if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses 845: $badaddresses = implode(', ', $bad_rcpt); 846: throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); 847: } 848: if(!$this->smtp->Data($header . $body)) { 849: throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); 850: } 851: if($this->SMTPKeepAlive == true) { 852: $this->smtp->Reset(); 853: } 854: return true; 855: } 856: 857: /** 858: * Initiates a connection to an SMTP server. 859: * Returns false if the operation failed. 860: * @uses SMTP 861: * @access public 862: * @return bool 863: */ 864: public function SmtpConnect() { 865: if(is_null($this->smtp)) { 866: $this->smtp = new SMTP(); 867: } 868: 869: $this->smtp->do_debug = $this->SMTPDebug; 870: $hosts = explode(';', $this->Host); 871: $index = 0; 872: $connection = $this->smtp->Connected(); 873: 874: // Retry while there is no connection 875: try { 876: while($index < count($hosts) && !$connection) { 877: $hostinfo = array(); 878: if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { 879: $host = $hostinfo[1]; 880: $port = $hostinfo[2]; 881: } else { 882: $host = $hosts[$index]; 883: $port = $this->Port; 884: } 885: 886: $tls = ($this->SMTPSecure == 'tls'); 887: $ssl = ($this->SMTPSecure == 'ssl'); 888: 889: if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { 890: 891: $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); 892: $this->smtp->Hello($hello); 893: 894: if ($tls) { 895: if (!$this->smtp->StartTLS()) { 896: throw new phpmailerException($this->Lang('tls')); 897: } 898: 899: //We must resend HELO after tls negotiation 900: $this->smtp->Hello($hello); 901: } 902: 903: $connection = true; 904: if ($this->SMTPAuth) { 905: if (!$this->smtp->Authenticate($this->Username, $this->Password)) { 906: throw new phpmailerException($this->Lang('authenticate')); 907: } 908: } 909: } 910: $index++; 911: if (!$connection) { 912: throw new phpmailerException($this->Lang('connect_host')); 913: } 914: } 915: } catch (phpmailerException $e) { 916: $this->smtp->Reset(); 917: if ($this->exceptions) { 918: throw $e; 919: } 920: } 921: return true; 922: } 923: 924: /** 925: * Closes the active SMTP session if one exists. 926: * @return void 927: */ 928: public function SmtpClose() { 929: if(!is_null($this->smtp)) { 930: if($this->smtp->Connected()) { 931: $this->smtp->Quit(); 932: $this->smtp->Close(); 933: } 934: } 935: } 936: 937: /** 938: * Sets the language for all class error messages. 939: * Returns false if it cannot load the language file. The default language is English. 940: * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") 941: * @param string $lang_path Path to the language file directory 942: * @access public 943: */ 944: function SetLanguage($langcode = 'en', $lang_path = 'language/') { 945: //Define full set of translatable strings 946: $PHPMAILER_LANG = array( 947: 'provide_address' => 'You must provide at least one recipient email address.', 948: 'mailer_not_supported' => ' mailer is not supported.', 949: 'execute' => 'Could not execute: ', 950: 'instantiate' => 'Could not instantiate mail function.', 951: 'authenticate' => 'SMTP Error: Could not authenticate.', 952: 'from_failed' => 'The following From address failed: ', 953: 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 954: 'data_not_accepted' => 'SMTP Error: Data not accepted.', 955: 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 956: 'file_access' => 'Could not access file: ', 957: 'file_open' => 'File Error: Could not open file: ', 958: 'encoding' => 'Unknown encoding: ', 959: 'signing' => 'Signing Error: ', 960: 'smtp_error' => 'SMTP server error: ', 961: 'empty_message' => 'Message body empty', 962: 'invalid_address' => 'Invalid address', 963: 'variable_set' => 'Cannot set or reset variable: ' 964: ); 965: //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! 966: $l = true; 967: if ($langcode != 'en') { //There is no English translation file 968: $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; 969: } 970: $this->language = $PHPMAILER_LANG; 971: return ($l == true); //Returns false if language not found 972: } 973: 974: /** 975: * Return the current array of language strings 976: * @return array 977: */ 978: public function GetTranslations() { 979: return $this->language; 980: } 981: 982: ///////////////////////////////////////////////// 983: // METHODS, MESSAGE CREATION 984: ///////////////////////////////////////////////// 985: 986: /** 987: * Creates recipient headers. 988: * @access public 989: * @return string 990: */ 991: public function AddrAppend($type, $addr) { 992: $addr_str = $type . ': '; 993: $addresses = array(); 994: foreach ($addr as $a) { 995: $addresses[] = $this->AddrFormat($a); 996: } 997: $addr_str .= implode(', ', $addresses); 998: $addr_str .= $this->LE; 999: 1000: return $addr_str; 1001: } 1002: 1003: /** 1004: * Formats an address correctly. 1005: * @access public 1006: * @return string 1007: */ 1008: public function AddrFormat($addr) { 1009: if (empty($addr[1])) { 1010: return $this->SecureHeader($addr[0]); 1011: } else { 1012: return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; 1013: } 1014: } 1015: 1016: /** 1017: * Wraps message for use with mailers that do not 1018: * automatically perform wrapping and for quoted-printable. 1019: * Original written by philippe. 1020: * @param string $message The message to wrap 1021: * @param integer $length The line length to wrap to 1022: * @param boolean $qp_mode Whether to run in Quoted-Printable mode 1023: * @access public 1024: * @return string 1025: */ 1026: public function WrapText($message, $length, $qp_mode = false) { 1027: $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 1028: // If utf-8 encoding is used, we will need to make sure we don't 1029: // split multibyte characters when we wrap 1030: $is_utf8 = (strtolower($this->CharSet) == "utf-8"); 1031: 1032: $message = $this->FixEOL($message); 1033: if (substr($message, -1) == $this->LE) { 1034: $message = substr($message, 0, -1); 1035: } 1036: 1037: $line = explode($this->LE, $message); 1038: $message = ''; 1039: for ($i = 0 ;$i < count($line); $i++) { 1040: $line_part = explode(' ', $line[$i]); 1041: $buf = ''; 1042: for ($e = 0; $e1043: $word = $line_part[$e]; 1044: if ($qp_mode and (strlen($word) > $length)) { 1045: $space_left = $length - strlen($buf) - 1; 1046: if ($e != 0) { 1047: if ($space_left > 20) { 1048: $len = $space_left; 1049: if ($is_utf8) { 1050: $len = $this->UTF8CharBoundary($word, $len); 1051: } elseif (substr($word, $len - 1, 1) == "=") { 1052: $len--; 1053: } elseif (substr($word, $len - 2, 1) == "=") { 1054: $len -= 2; 1055: } 1056: $part = substr($word, 0, $len); 1057: $word = substr($word, $len); 1058: $buf .= ' ' . $part; 1059: $message .= $buf . sprintf("=%s", $this->LE); 1060: } else { 1061: $message .= $buf . $soft_break; 1062: } 1063: $buf = ''; 1064: } 1065: while (strlen($word) > 0) { 1066: $len = $length; 1067: if ($is_utf8) { 1068: $len = $this->UTF8CharBoundary($word, $len); 1069: } elseif (substr($word, $len - 1, 1) == "=") { 1070: $len--; 1071: } elseif (substr($word, $len - 2, 1) == "=") { 1072: $len -= 2; 1073: } 1074: $part = substr($word, 0, $len); 1075: $word = substr($word, $len); 1076: 1077: if (strlen($word) > 0) { 1078: $message .= $part . sprintf("=%s", $this->LE); 1079: } else { 1080: $buf = $part; 1081: } 1082: } 1083: } else { 1084: $buf_o = $buf; 1085: $buf .= ($e == 0) ? $word : (' ' . $word); 1086: 1087: if (strlen($buf) > $length and $buf_o != '') { 1088: $message .= $buf_o . $soft_break; 1089: $buf = $word; 1090: } 1091: } 1092: } 1093: $message .= $buf . $this->LE; 1094: } 1095: 1096: return $message; 1097: } 1098: 1099: /** 1100: * Finds last character boundary prior to maxLength in a utf-8 1101: * quoted (printable) encoded string. 1102: * Original written by Colin Brown. 1103: * @access public 1104: * @param string $encodedText utf-8 QP text 1105: * @param int $maxLength find last character boundary prior to this length 1106: * @return int 1107: */ 1108: public function UTF8CharBoundary($encodedText, $maxLength) { 1109: $foundSplitPos = false; 1110: $lookBack = 3; 1111: while (!$foundSplitPos) { 1112: $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 1113: $encodedCharPos = strpos($lastChunk, "="); 1114: if ($encodedCharPos !== false) { 1115: // Found start of encoded character byte within $lookBack block. 1116: // Check the encoded byte value (the 2 chars after the '=') 1117: $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 1118: $dec = hexdec($hex); 1119: if ($dec < 128) { // Single byte character. 1120: // If the encoded char was found at pos 0, it will fit 1121: // otherwise reduce maxLength to start of the encoded char 1122: $maxLength = ($encodedCharPos == 0) ? $maxLength : 1123: $maxLength - ($lookBack - $encodedCharPos); 1124: $foundSplitPos = true; 1125: } elseif ($dec >= 192) { // First byte of a multi byte character 1126: // Reduce maxLength to split at start of character 1127: $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1128: $foundSplitPos = true; 1129: } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back 1130: $lookBack += 3; 1131: } 1132: } else { 1133: // No encoded character found 1134: $foundSplitPos = true; 1135: } 1136: } 1137: return $maxLength; 1138: } 1139: 1140: 1141: /** 1142: * Set the body wrapping. 1143: * @access public 1144: * @return void 1145: */ 1146: public function SetWordWrap() { 1147: if($this->WordWrap < 1) { 1148: return; 1149: } 1150: 1151: switch($this->message_type) { 1152: case 'alt': 1153: case 'alt_inline': 1154: case 'alt_attach': 1155: case 'alt_inline_attach': 1156: $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); 1157: break; 1158: default: 1159: $this->Body = $this->WrapText($this->Body, $this->WordWrap); 1160: break; 1161: } 1162: } 1163: 1164: /** 1165: * Assembles message header. 1166: * @access public 1167: * @return string The assembled header 1168: */ 1169: public function CreateHeader() { 1170: $result = ''; 1171: 1172: // Set the boundaries 1173: $uniq_id = md5(uniqid(time())); 1174: $this->boundary[1] = 'b1_' . $uniq_id; 1175: $this->boundary[2] = 'b2_' . $uniq_id; 1176: $this->boundary[3] = 'b3_' . $uniq_id; 1177: 1178: $result .= $this->HeaderLine('Date', self::RFCDate()); 1179: if($this->Sender == '') { 1180: $result .= $this->HeaderLine('Return-Path', trim($this->From)); 1181: } else { 1182: $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); 1183: } 1184: 1185: // To be created automatically by mail() 1186: if($this->Mailer != 'mail') { 1187: if ($this->SingleTo === true) { 1188: foreach($this->to as $t) { 1189: $this->SingleToArray[] = $this->AddrFormat($t); 1190: } 1191: } else { 1192: if(count($this->to) > 0) { 1193: $result .= $this->AddrAppend('To', $this->to); 1194: } elseif (count($this->cc) == 0) { 1195: $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); 1196: } 1197: } 1198: } 1199: 1200: $from = array(); 1201: $from[0][0] = trim($this->From); 1202: $from[0][1] = $this->FromName; 1203: $result .= $this->AddrAppend('From', $from); 1204: 1205: // sendmail and mail() extract Cc from the header before sending 1206: if(count($this->cc) > 0) { 1207: $result .= $this->AddrAppend('Cc', $this->cc); 1208: } 1209: 1210: // sendmail and mail() extract Bcc from the header before sending 1211: if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { 1212: $result .= $this->AddrAppend('Bcc', $this->bcc); 1213: } 1214: 1215: if(count($this->ReplyTo) > 0) { 1216: $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); 1217: } 1218: 1219: // mail() sets the subject itself 1220: if($this->Mailer != 'mail') { 1221: $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); 1222: } 1223: 1224: if($this->MessageID != '') { 1225: $result .= $this->HeaderLine('Message-ID', $this->MessageID); 1226: } else { 1227: $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); 1228: } 1229: $result .= $this->HeaderLine('X-Priority', $this->Priority); 1230: if($this->XMailer) { 1231: $result .= $this->HeaderLine('X-Mailer', $this->XMailer); 1232: } else { 1233: $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); 1234: } 1235: 1236: if($this->ConfirmReadingTo != '') { 1237: $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); 1238: } 1239: 1240: // Add custom headers 1241: for($index = 0; $index < count($this->CustomHeader); $index++) { 1242: $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); 1243: } 1244: if (!$this->sign_key_file) { 1245: $result .= $this->HeaderLine('MIME-Version', '1.0'); 1246: $result .= $this->GetMailMIME(); 1247: } 1248: 1249: return $result; 1250: } 1251: 1252: /** 1253: * Returns the message MIME. 1254: * @access public 1255: * @return string 1256: */ 1257: public function GetMailMIME() { 1258: $result = ''; 1259: switch($this->message_type) { 1260: case 'plain': 1261: $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); 1262: $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset="'.$this->CharSet.'"'); 1263: break; 1264: case 'inline': 1265: $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); 1266: $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 1267: break; 1268: case 'attach': 1269: case 'inline_attach': 1270: case 'alt_attach': 1271: case 'alt_inline_attach': 1272: $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); 1273: $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 1274: break; 1275: case 'alt': 1276: case 'alt_inline': 1277: $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); 1278: $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 1279: break; 1280: } 1281: 1282: if($this->Mailer != 'mail') { 1283: $result .= $this->LE.$this->LE; 1284: } 1285: 1286: return $result; 1287: } 1288: 1289: /** 1290: * Returns the MIME message (headers and body). Only really valid post PreSend(). 1291: * @access public 1292: * @return string 1293: */ 1294: public function GetSentMIMEMessage() { 1295: return $this->SentMIMEMessage; 1296: } 1297: 1298: 1299: /** 1300: * Assembles the message body. Returns an empty string on failure. 1301: * @access public 1302: * @return string The assembled message body 1303: */ 1304: public function CreateBody() { 1305: $body = ''; 1306: 1307: if ($this->sign_key_file) { 1308: $body .= $this->GetMailMIME(); 1309: } 1310: 1311: $this->SetWordWrap(); 1312: 1313: switch($this->message_type) { 1314: case 'plain': 1315: $body .= $this->EncodeString($this->Body, $this->Encoding); 1316: break; 1317: case 'inline': 1318: $body .= $this->GetBoundary($this->boundary[1], '', '', ''); 1319: $body .= $this->EncodeString($this->Body, $this->Encoding); 1320: $body .= $this->LE.$this->LE; 1321: $body .= $this->AttachAll("inline", $this->boundary[1]); 1322: break; 1323: case 'attach': 1324: $body .= $this->GetBoundary($this->boundary[1], '', '', ''); 1325: $body .= $this->EncodeString($this->Body, $this->Encoding); 1326: $body .= $this->LE.$this->LE; 1327: $body .= $this->AttachAll("attachment", $this->boundary[1]); 1328: break; 1329: case 'inline_attach': 1330: $body .= $this->TextLine("--" . $this->boundary[1]); 1331: $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); 1332: $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); 1333: $body .= $this->LE; 1334: $body .= $this->GetBoundary($this->boundary[2], '', '', ''); 1335: $body .= $this->EncodeString($this->Body, $this->Encoding); 1336: $body .= $this->LE.$this->LE; 1337: $body .= $this->AttachAll("inline", $this->boundary[2]); 1338: $body .= $this->LE; 1339: $body .= $this->AttachAll("attachment", $this->boundary[1]); 1340: break; 1341: case 'alt': 1342: $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); 1343: $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1344: $body .= $this->LE.$this->LE; 1345: $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); 1346: $body .= $this->EncodeString($this->Body, $this->Encoding); 1347: $body .= $this->LE.$this->LE; 1348: $body .= $this->EndBoundary($this->boundary[1]); 1349: break; 1350: case 'alt_inline': 1351: $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); 1352: $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1353: $body .= $this->LE.$this->LE; 1354: $body .= $this->TextLine("--" . $this->boundary[1]); 1355: $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); 1356: $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); 1357: $body .= $this->LE; 1358: $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); 1359: $body .= $this->EncodeString($this->Body, $this->Encoding); 1360: $body .= $this->LE.$this->LE; 1361: $body .= $this->AttachAll("inline", $this->boundary[2]); 1362: $body .= $this->LE; 1363: $body .= $this->EndBoundary($this->boundary[1]); 1364: break; 1365: case 'alt_attach': 1366: $body .= $this->TextLine("--" . $this->boundary[1]); 1367: $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); 1368: $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); 1369: $body .= $this->LE; 1370: $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); 1371: $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1372: $body .= $this->LE.$this->LE; 1373: $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); 1374: $body .= $this->EncodeString($this->Body, $this->Encoding); 1375: $body .= $this->LE.$this->LE; 1376: $body .= $this->EndBoundary($this->boundary[2]); 1377: $body .= $this->LE; 1378: $body .= $this->AttachAll("attachment", $this->boundary[1]); 1379: break; 1380: case 'alt_inline_attach': 1381: $body .= $this->TextLine("--" . $this->boundary[1]); 1382: $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); 1383: $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); 1384: $body .= $this->LE; 1385: $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); 1386: $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1387: $body .= $this->LE.$this->LE; 1388: $body .= $this->TextLine("--" . $this->boundary[2]); 1389: $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); 1390: $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); 1391: $body .= $this->LE; 1392: $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); 1393: $body .= $this->EncodeString($this->Body, $this->Encoding); 1394: $body .= $this->LE.$this->LE; 1395: $body .= $this->AttachAll("inline", $this->boundary[3]); 1396: $body .= $this->LE; 1397: $body .= $this->EndBoundary($this->boundary[2]); 1398: $body .= $this->LE; 1399: $body .= $this->AttachAll("attachment", $this->boundary[1]); 1400: break; 1401: } 1402: 1403: if ($this->IsError()) { 1404: $body = ''; 1405: } elseif ($this->sign_key_file) { 1406: try { 1407: $file = tempnam('', 'mail'); 1408: file_put_contents($file, $body); //TODO check this worked 1409: $signed = tempnam("", "signed"); 1410: if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { 1411: @unlink($file); 1412: $body = file_get_contents($signed); 1413: @unlink($signed); 1414: } else { 1415: @unlink($file); 1416: @unlink($signed); 1417: throw new phpmailerException($this->Lang("signing").openssl_error_string()); 1418: } 1419: } catch (phpmailerException $e) { 1420: $body = ''; 1421: if ($this->exceptions) { 1422: throw $e; 1423: } 1424: } 1425: } 1426: 1427: return $body; 1428: } 1429: 1430: /** 1431: * Returns the start of a message boundary. 1432: * @access protected 1433: * @return string 1434: */ 1435: protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { 1436: $result = ''; 1437: if($charSet == '') { 1438: $charSet = $this->CharSet; 1439: } 1440: if($contentType == '') { 1441: $contentType = $this->ContentType; 1442: } 1443: if($encoding == '') { 1444: $encoding = $this->Encoding; 1445: } 1446: $result .= $this->TextLine('--' . $boundary); 1447: $result .= sprintf("Content-Type: %s; charset=\"%s\"", $contentType, $charSet); 1448: $result .= $this->LE; 1449: $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); 1450: $result .= $this->LE; 1451: 1452: return $result; 1453: } 1454: 1455: /** 1456: * Returns the end of a message boundary. 1457: * @access protected 1458: * @return string 1459: */ 1460: protected function EndBoundary($boundary) { 1461: return $this->LE . '--' . $boundary . '--' . $this->LE; 1462: } 1463: 1464: /** 1465: * Sets the message type. 1466: * @access protected 1467: * @return void 1468: */ 1469: protected function SetMessageType() { 1470: $this->message_type = array(); 1471: if($this->AlternativeExists()) $this->message_type[] = "alt"; 1472: if($this->InlineImageExists()) $this->message_type[] = "inline"; 1473: if($this->AttachmentExists()) $this->message_type[] = "attach"; 1474: $this->message_type = implode("_", $this->message_type); 1475: if($this->message_type == "") $this->message_type = "plain"; 1476: } 1477: 1478: /** 1479: * Returns a formatted header line. 1480: * @access public 1481: * @return string 1482: */ 1483: public function HeaderLine($name, $value) { 1484: return $name . ': ' . $value . $this->LE; 1485: } 1486: 1487: /** 1488: * Returns a formatted mail line. 1489: * @access public 1490: * @return string 1491: */ 1492: public function TextLine($value) { 1493: return $value . $this->LE; 1494: } 1495: 1496: ///////////////////////////////////////////////// 1497: // CLASS METHODS, ATTACHMENTS 1498: ///////////////////////////////////////////////// 1499: 1500: /** 1501: * Adds an attachment from a path on the filesystem. 1502: * Returns false if the file could not be found 1503: * or accessed. 1504: * @param string $path Path to the attachment. 1505: * @param string $name Overrides the attachment name. 1506: * @param string $encoding File encoding (see $Encoding). 1507: * @param string $type File extension (MIME) type. 1508: * @return bool 1509: */ 1510: public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { 1511: try { 1512: if ( !@is_file($path) ) { 1513: throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); 1514: } 1515: $filename = basename($path); 1516: if ( $name == '' ) { 1517: $name = $filename; 1518: } 1519: 1520: $this->attachment[] = array( 1521: 0 => $path, 1522: 1 => $filename, 1523: 2 => $name, 1524: 3 => $encoding, 1525: 4 => $type, 1526: 5 => false, // isStringAttachment 1527: 6 => 'attachment', 1528: 7 => 0 1529: ); 1530: 1531: } catch (phpmailerException $e) { 1532: $this->SetError($e->getMessage()); 1533: if ($this->exceptions) { 1534: throw $e; 1535: } 1536: if ($this->SMTPDebug) { 1537: echo $e->getMessage()."\n"; 1538: } 1539: if ( $e->getCode() == self::STOP_CRITICAL ) { 1540: return false; 1541: } 1542: } 1543: return true; 1544: } 1545: 1546: /** 1547: * Return the current array of attachments 1548: * @return array 1549: */ 1550: public function GetAttachments() { 1551: return $this->attachment; 1552: } 1553: 1554: /** 1555: * Attaches all fs, string, and binary attachments to the message. 1556: * Returns an empty string on failure. 1557: * @access protected 1558: * @return string 1559: */ 1560: protected function AttachAll($disposition_type, $boundary) { 1561: // Return text of body 1562: $mime = array(); 1563: $cidUniq = array(); 1564: $incl = array(); 1565: 1566: // Add all attachments 1567: foreach ($this->attachment as $attachment) { 1568: // CHECK IF IT IS A VALID DISPOSITION_FILTER 1569: if($attachment[6] == $disposition_type) { 1570: // Check for string attachment 1571: $bString = $attachment[5]; 1572: if ($bString) { 1573: $string = $attachment[0]; 1574: } else { 1575: $path = $attachment[0]; 1576: } 1577: 1578: $inclhash = md5(serialize($attachment)); 1579: if (in_array($inclhash, $incl)) { continue; } 1580: $incl[] = $inclhash; 1581: $filename = $attachment[1]; 1582: $name = $attachment[2]; 1583: $encoding = $attachment[3]; 1584: $type = $attachment[4]; 1585: $disposition = $attachment[6]; 1586: $cid = $attachment[7]; 1587: if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } 1588: $cidUniq[$cid] = true; 1589: 1590: $mime[] = sprintf("--%s%s", $boundary, $this->LE); 1591: $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); 1592: $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 1593: 1594: if($disposition == 'inline') { 1595: $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); 1596: } 1597: 1598: $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); 1599: 1600: // Encode as string attachment 1601: if($bString) { 1602: $mime[] = $this->EncodeString($string, $encoding); 1603: if($this->IsError()) { 1604: return ''; 1605: } 1606: $mime[] = $this->LE.$this->LE; 1607: } else { 1608: $mime[] = $this->EncodeFile($path, $encoding); 1609: if($this->IsError()) { 1610: return ''; 1611: } 1612: $mime[] = $this->LE.$this->LE; 1613: } 1614: } 1615: } 1616: 1617: $mime[] = sprintf("--%s--%s", $boundary, $this->LE); 1618: 1619: return implode("", $mime); 1620: } 1621: 1622: /** 1623: * Encodes attachment in requested format. 1624: * Returns an empty string on failure. 1625: * @param string $path The full path to the file 1626: * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 1627: * @see EncodeFile() 1628: * @access protected 1629: * @return string 1630: */ 1631: protected function EncodeFile($path, $encoding = 'base64') { 1632: try { 1633: if (!is_readable($path)) { 1634: throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); 1635: } 1636: if (function_exists('get_magic_quotes')) { 1637: function get_magic_quotes() { 1638: return false; 1639: } 1640: } 1641: $magic_quotes = get_magic_quotes_runtime(); 1642: if ($magic_quotes) { 1643: if (version_compare(PHP_VERSION, '5.3.0', '<')) { 1644: set_magic_quotes_runtime(0); 1645: } else { 1646: ini_set('magic_quotes_runtime', 0); 1647: } 1648: } 1649: $file_buffer = file_get_contents($path); 1650: $file_buffer = $this->EncodeString($file_buffer, $encoding); 1651: if ($magic_quotes) { 1652: if (version_compare(PHP_VERSION, '5.3.0', '<')) { 1653: set_magic_quotes_runtime($magic_quotes); 1654: } else { 1655: ini_set('magic_quotes_runtime', $magic_quotes); 1656: } 1657: } 1658: return $file_buffer; 1659: } catch (Exception $e) { 1660: $this->SetError($e->getMessage()); 1661: return ''; 1662: } 1663: } 1664: 1665: /** 1666: * Encodes string to requested format. 1667: * Returns an empty string on failure. 1668: * @param string $str The text to encode 1669: * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 1670: * @access public 1671: * @return string 1672: */ 1673: public function EncodeString($str, $encoding = 'base64') { 1674: $encoded = ''; 1675: switch(strtolower($encoding)) { 1676: case 'base64': 1677: $encoded = chunk_split(base64_encode($str), 76, $this->LE); 1678: break; 1679: case '7bit': 1680: case '8bit': 1681: $encoded = $this->FixEOL($str); 1682: //Make sure it ends with a line break 1683: if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1684: $encoded .= $this->LE; 1685: break; 1686: case 'binary': 1687: $encoded = $str; 1688: break; 1689: case 'quoted-printable': 1690: $encoded = $this->EncodeQP($str); 1691: break; 1692: default: 1693: $this->SetError($this->Lang('encoding') . $encoding); 1694: break; 1695: } 1696: return $encoded; 1697: } 1698: 1699: /** 1700: * Encode a header string to best (shortest) of Q, B, quoted or none. 1701: * @access public 1702: * @return string 1703: */ 1704: public function EncodeHeader($str, $position = 'text') { 1705: $x = 0; 1706: 1707: switch (strtolower($position)) { 1708: case 'phrase': 1709: if (!preg_match('/[\200-\377]/', $str)) { 1710: // Can't use addslashes as we don't know what value has magic_quotes_sybase 1711: $encoded = addcslashes($str, "\0..\37\177\\\""); 1712: if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 1713: return ($encoded); 1714: } else { 1715: return ("\"$encoded\""); 1716: } 1717: } 1718: $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 1719: break; 1720: case 'comment': 1721: $x = preg_match_all('/[()"]/', $str, $matches); 1722: // Fall-through 1723: case 'text': 1724: default: 1725: $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 1726: break; 1727: } 1728: 1729: if ($x == 0) { 1730: return ($str); 1731: } 1732: 1733: $maxlen = 75 - 7 - strlen($this->CharSet); 1734: // Try to select the encoding which should produce the shortest output 1735: if (strlen($str)/3 < $x) { 1736: $encoding = 'B'; 1737: if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { 1738: // Use a custom function which correctly encodes and wraps long 1739: // multibyte strings without breaking lines within a character 1740: $encoded = $this->Base64EncodeWrapMB($str); 1741: } else { 1742: $encoded = base64_encode($str); 1743: $maxlen -= $maxlen % 4; 1744: $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 1745: } 1746: } else { 1747: $encoding = 'Q'; 1748: $encoded = $this->EncodeQ($str, $position); 1749: $encoded = $this->WrapText($encoded, $maxlen, true); 1750: $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); 1751: } 1752: 1753: $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); 1754: $encoded = trim(str_replace("\n", $this->LE, $encoded)); 1755: 1756: return $encoded; 1757: } 1758: 1759: /** 1760: * Checks if a string contains multibyte characters. 1761: * @access public 1762: * @param string $str multi-byte text to wrap encode 1763: * @return bool 1764: */ 1765: public function HasMultiBytes($str) { 1766: if (function_exists('mb_strlen')) { 1767: return (strlen($str) > mb_strlen($str, $this->CharSet)); 1768: } else { // Assume no multibytes (we can't handle without mbstring functions anyway) 1769: return false; 1770: } 1771: } 1772: 1773: /** 1774: * Correctly encodes and wraps long multibyte strings for mail headers 1775: * without breaking lines within a character. 1776: * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php 1777: * @access public 1778: * @param string $str multi-byte text to wrap encode 1779: * @return string 1780: */ 1781: public function Base64EncodeWrapMB($str) { 1782: $start = "=?".$this->CharSet."?B?"; 1783: $end = "?="; 1784: $encoded = ""; 1785: 1786: $mb_length = mb_strlen($str, $this->CharSet); 1787: // Each line must have length <= 75, including $start and $end 1788: $length = 75 - strlen($start) - strlen($end); 1789: // Average multi-byte ratio 1790: $ratio = $mb_length / strlen($str); 1791: // Base64 has a 4:3 ratio 1792: $offset = $avgLength = floor($length * $ratio * .75); 1793: 1794: for ($i = 0; $i < $mb_length; $i += $offset) { 1795: $lookBack = 0; 1796: 1797: do { 1798: $offset = $avgLength - $lookBack; 1799: $chunk = mb_substr($str, $i, $offset, $this->CharSet); 1800: $chunk = base64_encode($chunk); 1801: $lookBack++; 1802: } 1803: while (strlen($chunk) > $length); 1804: 1805: $encoded .= $chunk . $this->LE; 1806: } 1807: 1808: // Chomp the last linefeed 1809: $encoded = substr($encoded, 0, -strlen($this->LE)); 1810: return $encoded; 1811: } 1812: 1813: /** 1814: * Encode string to quoted-printable. 1815: * Only uses standard PHP, slow, but will always work 1816: * @access public 1817: * @param string $string the text to encode 1818: * @param integer $line_max Number of chars allowed on a line before wrapping 1819: * @return string 1820: */ 1821: public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { 1822: $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); 1823: $lines = preg_split('/(?:\r\n|\r|\n)/', $input); 1824: $eol = "\r\n"; 1825: $escape = '='; 1826: $output = ''; 1827: while( list(, $line) = each($lines) ) { 1828: $linlen = strlen($line); 1829: $newline = ''; 1830: for($i = 0; $i < $linlen; $i++) { 1831: $c = substr( $line, $i, 1 ); 1832: $dec = ord( $c ); 1833: if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E 1834: $c = '=2E'; 1835: } 1836: if ( $dec == 32 ) { 1837: if ( $i == ( $linlen - 1 ) ) { // convert space at eol only 1838: $c = '=20'; 1839: } else if ( $space_conv ) { 1840: $c = '=20'; 1841: } 1842: } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required 1843: $h2 = floor($dec/16); 1844: $h1 = floor($dec%16); 1845: $c = $escape.$hex[$h2].$hex[$h1]; 1846: } 1847: if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted 1848: $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay 1849: $newline = ''; 1850: // check if newline first character will be point or not 1851: if ( $dec == 46 ) { 1852: $c = '=2E'; 1853: } 1854: } 1855: $newline .= $c; 1856: } // end of for 1857: $output .= $newline.$eol; 1858: } // end of while 1859: return $output; 1860: } 1861: 1862: /** 1863: * Encode string to RFC2045 (6.7) quoted-printable format 1864: * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version 1865: * Also results in same content as you started with after decoding 1866: * @see EncodeQPphp() 1867: * @access public 1868: * @param string $string the text to encode 1869: * @param integer $line_max Number of chars allowed on a line before wrapping 1870: * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function 1871: * @return string 1872: * @author Marcus Bointon 1873: */ 1874: public function EncodeQP($string, $line_max = 76, $space_conv = false) { 1875: if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) 1876: return quoted_printable_encode($string); 1877: } 1878: $filters = stream_get_filters(); 1879: if (!in_array('convert.*', $filters)) { //Got convert stream filter? 1880: return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation 1881: } 1882: $fp = fopen('php://temp/', 'r+'); 1883: $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks 1884: $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); 1885: $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); 1886: fputs($fp, $string); 1887: rewind($fp); 1888: $out = stream_get_contents($fp); 1889: stream_filter_remove($s); 1890: $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange 1891: fclose($fp); 1892: return $out; 1893: } 1894: 1895: /** 1896: * Encode string to q encoding. 1897: * @link http://tools.ietf.org/html/rfc2047 1898: * @param string $str the text to encode 1899: * @param string $position Where the text is going to be used, see the RFC for what that means 1900: * @access public 1901: * @return string 1902: */ 1903: public function EncodeQ($str, $position = 'text') { 1904: // There should not be any EOL in the string 1905: $encoded = preg_replace('/[\r\n]*/', '', $str); 1906: 1907: switch (strtolower($position)) { 1908: case 'phrase': 1909: $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); 1910: break; 1911: case 'comment': 1912: $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); 1913: case 'text': 1914: default: 1915: // Replace every high ascii, control =, ? and _ characters 1916: //TODO using /e (equivalent to eval()) is probably not a good idea 1917: $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', 1918: "'='.sprintf('%02X', ord(stripslashes('\\1')))", $encoded); 1919: break; 1920: } 1921: 1922: // Replace every spaces to _ (more readable than =20) 1923: $encoded = str_replace(' ', '_', $encoded); 1924: 1925: return $encoded; 1926: } 1927: 1928: /** 1929: * Adds a string or binary attachment (non-filesystem) to the list. 1930: * This method can be used to attach ascii or binary data, 1931: * such as a BLOB record from a database. 1932: * @param string $string String attachment data. 1933: * @param string $filename Name of the attachment. 1934: * @param string $encoding File encoding (see $Encoding). 1935: * @param string $type File extension (MIME) type. 1936: * @return void 1937: */ 1938: public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { 1939: // Append to $attachment array 1940: $this->attachment[] = array( 1941: 0 => $string, 1942: 1 => $filename, 1943: 2 => basename($filename), 1944: 3 => $encoding, 1945: 4 => $type, 1946: 5 => true, // isStringAttachment 1947: 6 => 'attachment', 1948: 7 => 0 1949: ); 1950: } 1951: 1952: /** 1953: * Adds an embedded attachment. This can include images, sounds, and 1954: * just about any other document. Make sure to set the $type to an 1955: * image type. For JPEG images use "image/jpeg" and for GIF images 1956: * use "image/gif". 1957: * @param string $path Path to the attachment. 1958: * @param string $cid Content ID of the attachment. Use this to identify 1959: * the Id for accessing the image in an HTML form. 1960: * @param string $name Overrides the attachment name. 1961: * @param string $encoding File encoding (see $Encoding). 1962: * @param string $type File extension (MIME) type. 1963: * @return bool 1964: */ 1965: public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { 1966: 1967: if ( !@is_file($path) ) { 1968: $this->SetError($this->Lang('file_access') . $path); 1969: return false; 1970: } 1971: 1972: $filename = basename($path); 1973: if ( $name == '' ) { 1974: $name = $filename; 1975: } 1976: 1977: // Append to $attachment array 1978: $this->attachment[] = array( 1979: 0 => $path, 1980: 1 => $filename, 1981: 2 => $name, 1982: 3 => $encoding, 1983: 4 => $type, 1984: 5 => false, // isStringAttachment 1985: 6 => 'inline', 1986: 7 => $cid 1987: ); 1988: 1989: return true; 1990: } 1991: 1992: public function AddStringEmbeddedImage($string, $cid, $filename = '', $encoding = 'base64', $type = 'application/octet-stream') { 1993: // Append to $attachment array 1994: $this->attachment[] = array( 1995: 0 => $string, 1996: 1 => $filename, 1997: 2 => basename($filename), 1998: 3 => $encoding, 1999: 4 => $type, 2000: 5 => true, // isStringAttachment 2001: 6 => 'inline', 2002: 7 => $cid 2003: ); 2004: } 2005: 2006: /** 2007: * Returns true if an inline attachment is present. 2008: * @access public 2009: * @return bool 2010: */ 2011: public function InlineImageExists() { 2012: foreach($this->attachment as $attachment) { 2013: if ($attachment[6] == 'inline') { 2014: return true; 2015: } 2016: } 2017: return false; 2018: } 2019: 2020: public function AttachmentExists() { 2021: foreach($this->attachment as $attachment) { 2022: if ($attachment[6] == 'attachment') { 2023: return true; 2024: } 2025: } 2026: return false; 2027: } 2028: 2029: public function AlternativeExists() { 2030: return strlen($this->AltBody)>0; 2031: } 2032: 2033: ///////////////////////////////////////////////// 2034: // CLASS METHODS, MESSAGE RESET 2035: ///////////////////////////////////////////////// 2036: 2037: /** 2038: * Clears all recipients assigned in the TO array. Returns void. 2039: * @return void 2040: */ 2041: public function ClearAddresses() { 2042: foreach($this->to as $to) { 2043: unset($this->all_recipients[strtolower($to[0])]); 2044: } 2045: $this->to = array(); 2046: } 2047: 2048: /** 2049: * Clears all recipients assigned in the CC array. Returns void. 2050: * @return void 2051: */ 2052: public function ClearCCs() { 2053: foreach($this->cc as $cc) { 2054: unset($this->all_recipients[strtolower($cc[0])]); 2055: } 2056: $this->cc = array(); 2057: } 2058: 2059: /** 2060: * Clears all recipients assigned in the BCC array. Returns void. 2061: * @return void 2062: */ 2063: public function ClearBCCs() { 2064: foreach($this->bcc as $bcc) { 2065: unset($this->all_recipients[strtolower($bcc[0])]); 2066: } 2067: $this->bcc = array(); 2068: } 2069: 2070: /** 2071: * Clears all recipients assigned in the ReplyTo array. Returns void. 2072: * @return void 2073: */ 2074: public function ClearReplyTos() { 2075: $this->ReplyTo = array(); 2076: } 2077: 2078: /** 2079: * Clears all recipients assigned in the TO, CC and BCC 2080: * array. Returns void. 2081: * @return void 2082: */ 2083: public function ClearAllRecipients() { 2084: $this->to = array(); 2085: $this->cc = array(); 2086: $this->bcc = array(); 2087: $this->all_recipients = array(); 2088: } 2089: 2090: /** 2091: * Clears all previously set filesystem, string, and binary 2092: * attachments. Returns void. 2093: * @return void 2094: */ 2095: public function ClearAttachments() { 2096: $this->attachment = array(); 2097: } 2098: 2099: /** 2100: * Clears all custom headers. Returns void. 2101: * @return void 2102: */ 2103: public function ClearCustomHeaders() { 2104: $this->CustomHeader = array(); 2105: } 2106: 2107: ///////////////////////////////////////////////// 2108: // CLASS METHODS, MISCELLANEOUS 2109: ///////////////////////////////////////////////// 2110: 2111: /** 2112: * Adds the error message to the error container. 2113: * @access protected 2114: * @return void 2115: */ 2116: protected function SetError($msg) { 2117: $this->error_count++; 2118: if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { 2119: $lasterror = $this->smtp->getError(); 2120: if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { 2121: $msg .= '

' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "

\n";
2122: } 2123: } 2124: $this->ErrorInfo = $msg; 2125: } 2126: 2127: /** 2128: * Returns the proper RFC 822 formatted date. 2129: * @access public 2130: * @return string 2131: * @static 2132: */ 2133: public static function RFCDate() { 2134: $tz = date('Z'); 2135: $tzs = ($tz < 0) ? '-' : '+'; 2136: $tz = abs($tz); 2137: $tz = (int)($tz/3600)*100 + ($tz%3600)/60; 2138: $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); 2139: 2140: return $result; 2141: } 2142: 2143: /** 2144: * Returns the server hostname or 'localhost.localdomain' if unknown. 2145: * @access protected 2146: * @return string 2147: */ 2148: protected function ServerHostname() { 2149: if (!empty($this->Hostname)) { 2150: $result = $this->Hostname; 2151: } elseif (isset($_SERVER['SERVER_NAME'])) { 2152: $result = $_SERVER['SERVER_NAME']; 2153: } else { 2154: $result = 'localhost.localdomain'; 2155: } 2156: 2157: return $result; 2158: } 2159: 2160: /** 2161: * Returns a message in the appropriate language. 2162: * @access protected 2163: * @return string 2164: */ 2165: protected function Lang($key) { 2166: if(count($this->language) < 1) { 2167: $this->SetLanguage('en'); // set the default language 2168: } 2169: 2170: if(isset($this->language[$key])) { 2171: return $this->language[$key]; 2172: } else { 2173: return 'Language string failed to load: ' . $key; 2174: } 2175: } 2176: 2177: /** 2178: * Returns true if an error occurred. 2179: * @access public 2180: * @return bool 2181: */ 2182: public function IsError() { 2183: return ($this->error_count > 0); 2184: } 2185: 2186: /** 2187: * Changes every end of line from CR or LF to CRLF. 2188: * @access public 2189: * @return string 2190: */ 2191: public function FixEOL($str) { 2192: $str = str_replace("\r\n", "\n", $str); 2193: $str = str_replace("\r", "\n", $str); 2194: $str = str_replace("\n", $this->LE, $str); 2195: return $str; 2196: } 2197: 2198: /** 2199: * Adds a custom header. 2200: * @access public 2201: * @return void 2202: */ 2203: public function AddCustomHeader($custom_header) { 2204: $this->CustomHeader[] = explode(':', $custom_header, 2); 2205: } 2206: 2207: /** 2208: * Evaluates the message and returns modifications for inline images and backgrounds 2209: * @access public 2210: * @return $message 2211: */ 2212: public function MsgHTML($message, $basedir = '') { 2213: preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); 2214: if(isset($images[2])) { 2215: foreach($images[2] as $i => $url) { 2216: // do not change urls for absolute images (thanks to corvuscorax) 2217: if (!preg_match('#^[A-z]+://#', $url)) { 2218: $filename = basename($url); 2219: $directory = dirname($url); 2220: ($directory == '.') ? $directory='': ''; 2221: $cid = 'cid:' . md5($filename); 2222: $ext = pathinfo($filename, PATHINFO_EXTENSION); 2223: $mimeType = self::_mime_types($ext); 2224: if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } 2225: if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } 2226: if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64', $mimeType) ) { 2227: $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message); 2228: } 2229: } 2230: } 2231: } 2232: $this->IsHTML(true); 2233: $this->Body = $message; 2234: if (empty($this->AltBody)) { 2235: $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); 2236: if (!empty($textMsg)) { 2237: $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet); 2238: } 2239: } 2240: if (empty($this->AltBody)) { 2241: $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 2242: } 2243: return $message; 2244: } 2245: 2246: /** 2247: * Gets the MIME type of the embedded or inline image 2248: * @param string File extension 2249: * @access public 2250: * @return string MIME type of ext 2251: * @static 2252: */ 2253: public static function _mime_types($ext = '') { 2254: $mimes = array( 2255: 'hqx' => 'application/mac-binhex40', 2256: 'cpt' => 'application/mac-compactpro', 2257: 'doc' => 'application/msword', 2258: 'bin' => 'application/macbinary', 2259: 'dms' => 'application/octet-stream', 2260: 'lha' => 'application/octet-stream', 2261: 'lzh' => 'application/octet-stream', 2262: 'exe' => 'application/octet-stream', 2263: 'class' => 'application/octet-stream', 2264: 'psd' => 'application/octet-stream', 2265: 'so' => 'application/octet-stream', 2266: 'sea' => 'application/octet-stream', 2267: 'dll' => 'application/octet-stream', 2268: 'oda' => 'application/oda', 2269: 'pdf' => 'application/pdf', 2270: 'ai' => 'application/postscript', 2271: 'eps' => 'application/postscript', 2272: 'ps' => 'application/postscript', 2273: 'smi' => 'application/smil', 2274: 'smil' => 'application/smil', 2275: 'mif' => 'application/vnd.mif', 2276: 'xls' => 'application/vnd.ms-excel', 2277: 'ppt' => 'application/vnd.ms-powerpoint', 2278: 'wbxml' => 'application/vnd.wap.wbxml', 2279: 'wmlc' => 'application/vnd.wap.wmlc', 2280: 'dcr' => 'application/x-director', 2281: 'dir' => 'application/x-director', 2282: 'dxr' => 'application/x-director', 2283: 'dvi' => 'application/x-dvi', 2284: 'gtar' => 'application/x-gtar', 2285: 'php' => 'application/x-httpd-php', 2286: 'php4' => 'application/x-httpd-php', 2287: 'php3' => 'application/x-httpd-php', 2288: 'phtml' => 'application/x-httpd-php', 2289: 'phps' => 'application/x-httpd-php-source', 2290: 'js' => 'application/x-javascript', 2291: 'swf' => 'application/x-shockwave-flash', 2292: 'sit' => 'application/x-stuffit', 2293: 'tar' => 'application/x-tar', 2294: 'tgz' => 'application/x-tar', 2295: 'xhtml' => 'application/xhtml+xml', 2296: 'xht' => 'application/xhtml+xml', 2297: 'zip' => 'application/zip', 2298: 'mid' => 'audio/midi', 2299: 'midi' => 'audio/midi', 2300: 'mpga' => 'audio/mpeg', 2301: 'mp2' => 'audio/mpeg', 2302: 'mp3' => 'audio/mpeg', 2303: 'aif' => 'audio/x-aiff', 2304: 'aiff' => 'audio/x-aiff', 2305: 'aifc' => 'audio/x-aiff', 2306: 'ram' => 'audio/x-pn-realaudio', 2307: 'rm' => 'audio/x-pn-realaudio', 2308: 'rpm' => 'audio/x-pn-realaudio-plugin', 2309: 'ra' => 'audio/x-realaudio', 2310: 'rv' => 'video/vnd.rn-realvideo', 2311: 'wav' => 'audio/x-wav', 2312: 'bmp' => 'image/bmp', 2313: 'gif' => 'image/gif', 2314: 'jpeg' => 'image/jpeg', 2315: 'jpg' => 'image/jpeg', 2316: 'jpe' => 'image/jpeg', 2317: 'png' => 'image/png', 2318: 'tiff' => 'image/tiff', 2319: 'tif' => 'image/tiff', 2320: 'css' => 'text/css', 2321: 'html' => 'text/html', 2322: 'htm' => 'text/html', 2323: 'shtml' => 'text/html', 2324: 'txt' => 'text/plain', 2325: 'text' => 'text/plain', 2326: 'log' => 'text/plain', 2327: 'rtx' => 'text/richtext', 2328: 'rtf' => 'text/rtf', 2329: 'xml' => 'text/xml', 2330: 'xsl' => 'text/xml', 2331: 'mpeg' => 'video/mpeg', 2332: 'mpg' => 'video/mpeg', 2333: 'mpe' => 'video/mpeg', 2334: 'qt' => 'video/quicktime', 2335: 'mov' => 'video/quicktime', 2336: 'avi' => 'video/x-msvideo', 2337: 'movie' => 'video/x-sgi-movie', 2338: 'doc' => 'application/msword', 2339: 'word' => 'application/msword', 2340: 'xl' => 'application/excel', 2341: 'eml' => 'message/rfc822' 2342: ); 2343: return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; 2344: } 2345: 2346: /** 2347: * Set (or reset) Class Objects (variables) 2348: * 2349: * Usage Example: 2350: * $page->set('X-Priority', '3'); 2351: * 2352: * @access public 2353: * @param string $name Parameter Name 2354: * @param mixed $value Parameter Value 2355: * NOTE: will not work with arrays, there are no arrays to set/reset 2356: * @todo Should this not be using __set() magic function? 2357: */ 2358: public function set($name, $value = '') { 2359: try { 2360: if (isset($this->$name) ) { 2361: $this->$name = $value; 2362: } else { 2363: throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); 2364: } 2365: } catch (Exception $e) { 2366: $this->SetError($e->getMessage()); 2367: if ($e->getCode() == self::STOP_CRITICAL) { 2368: return false; 2369: } 2370: } 2371: return true; 2372: } 2373: 2374: /** 2375: * Strips newlines to prevent header injection. 2376: * @access public 2377: * @param string $str String 2378: * @return string 2379: */ 2380: public function SecureHeader($str) { 2381: $str = str_replace("\r", '', $str); 2382: $str = str_replace("\n", '', $str); 2383: return trim($str); 2384: } 2385: 2386: /** 2387: * Set the private key file and password to sign the message. 2388: * 2389: * @access public 2390: * @param string $key_filename Parameter File Name 2391: * @param string $key_pass Password for private key 2392: */ 2393: public function Sign($cert_filename, $key_filename, $key_pass) { 2394: $this->sign_cert_file = $cert_filename; 2395: $this->sign_key_file = $key_filename; 2396: $this->sign_key_pass = $key_pass; 2397: } 2398: 2399: /** 2400: * Set the private key file and password to sign the message. 2401: * 2402: * @access public 2403: * @param string $key_filename Parameter File Name 2404: * @param string $key_pass Password for private key 2405: */ 2406: public function DKIM_QP($txt) { 2407: $tmp = ''; 2408: $line = ''; 2409: for ($i = 0; $i < strlen($txt); $i++) { 2410: $ord = ord($txt[$i]); 2411: if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { 2412: $line .= $txt[$i]; 2413: } else { 2414: $line .= "=".sprintf("%02X", $ord); 2415: } 2416: } 2417: return $line; 2418: } 2419: 2420: /** 2421: * Generate DKIM signature 2422: * 2423: * @access public 2424: * @param string $s Header 2425: */ 2426: public function DKIM_Sign($s) { 2427: $privKeyStr = file_get_contents($this->DKIM_private); 2428: if ($this->DKIM_passphrase != '') { 2429: $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 2430: } else { 2431: $privKey = $privKeyStr; 2432: } 2433: if (openssl_sign($s, $signature, $privKey)) { 2434: return base64_encode($signature); 2435: } 2436: } 2437: 2438: /** 2439: * Generate DKIM Canonicalization Header 2440: * 2441: * @access public 2442: * @param string $s Header 2443: */ 2444: public function DKIM_HeaderC($s) { 2445: $s = preg_replace("/\r\n\s+/", " ", $s); 2446: $lines = explode("\r\n", $s); 2447: foreach ($lines as $key => $line) { 2448: list($heading, $value) = explode(":", $line, 2); 2449: $heading = strtolower($heading); 2450: $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces 2451: $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value 2452: } 2453: $s = implode("\r\n", $lines); 2454: return $s; 2455: } 2456: 2457: /** 2458: * Generate DKIM Canonicalization Body 2459: * 2460: * @access public 2461: * @param string $body Message Body 2462: */ 2463: public function DKIM_BodyC($body) { 2464: if ($body == '') return "\r\n"; 2465: // stabilize line endings 2466: $body = str_replace("\r\n", "\n", $body); 2467: $body = str_replace("\n", "\r\n", $body); 2468: // END stabilize line endings 2469: while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { 2470: $body = substr($body, 0, strlen($body) - 2); 2471: } 2472: return $body; 2473: } 2474: 2475: /** 2476: * Create the DKIM header, body, as new header 2477: * 2478: * @access public 2479: * @param string $headers_line Header lines 2480: * @param string $subject Subject 2481: * @param string $body Body 2482: */ 2483: public function DKIM_Add($headers_line, $subject, $body) { 2484: $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 2485: $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 2486: $DKIMquery = 'dns/txt'; // Query method 2487: $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 2488: $subject_header = "Subject: $subject"; 2489: $headers = explode($this->LE, $headers_line); 2490: foreach($headers as $header) { 2491: if (strpos($header, 'From:') === 0) { 2492: $from_header = $header; 2493: } elseif (strpos($header, 'To:') === 0) { 2494: $to_header = $header; 2495: } 2496: } 2497: $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 2498: $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 2499: $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable 2500: $body = $this->DKIM_BodyC($body); 2501: $DKIMlen = strlen($body) ; // Length of body 2502: $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body 2503: $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; 2504: $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". 2505: "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". 2506: "\th=From:To:Subject;\r\n". 2507: "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". 2508: "\tz=$from\r\n". 2509: "\t|$to\r\n". 2510: "\t|$subject;\r\n". 2511: "\tbh=" . $DKIMb64 . ";\r\n". 2512: "\tb="; 2513: $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); 2514: $signed = $this->DKIM_Sign($toSign); 2515: return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n"; 2516: } 2517: 2518: protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body) { 2519: if (!empty($this->action_function) && function_exists($this->action_function)) { 2520: $params = array($isSent, $to, $cc, $bcc, $subject, $body); 2521: call_user_func_array($this->action_function, $params); 2522: } 2523: } 2524: } 2525: 2526: class phpmailerException extends Exception { 2527: public function errorMessage() { 2528: $errorMsg = '' . $this->getMessage() . "
\n";
2529: return $errorMsg; 2530: } 2531: } 2532: ?> 2533: