File: 1.13.3b/server/base/phpmailer/class.phpmailer.php (View as Code)

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

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

\n";
1787: } 1788: } 1789: $this->ErrorInfo = $msg; 1790: } 1791: 1792: /** 1793: * Returns the proper RFC 822 formatted date. 1794: * @access public 1795: * @return string 1796: * @static 1797: */ 1798: public static function RFCDate() { 1799: $tz = date('Z'); 1800: $tzs = ($tz < 0) ? '-' : '+'; 1801: $tz = abs($tz); 1802: $tz = (int)($tz/3600)*100 + ($tz%3600)/60; 1803: $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); 1804: 1805: return $result; 1806: } 1807: 1808: /** 1809: * Returns the server hostname or 'localhost.localdomain' if unknown. 1810: * @access private 1811: * @return string 1812: */ 1813: private function ServerHostname() { 1814: if (!empty($this->Hostname)) { 1815: $result = $this->Hostname; 1816: } elseif (isset($_SERVER['SERVER_NAME'])) { 1817: $result = $_SERVER['SERVER_NAME']; 1818: } else { 1819: $result = 'localhost.localdomain'; 1820: } 1821: 1822: return $result; 1823: } 1824: 1825: /** 1826: * Returns a message in the appropriate language. 1827: * @access private 1828: * @return string 1829: */ 1830: private function Lang($key) { 1831: if(count($this->language) < 1) { 1832: $this->SetLanguage('en'); // set the default language 1833: } 1834: 1835: if(isset($this->language[$key])) { 1836: return $this->language[$key]; 1837: } else { 1838: return 'Language string failed to load: ' . $key; 1839: } 1840: } 1841: 1842: /** 1843: * Returns true if an error occurred. 1844: * @access public 1845: * @return bool 1846: */ 1847: public function IsError() { 1848: return ($this->error_count > 0); 1849: } 1850: 1851: /** 1852: * Changes every end of line from CR or LF to CRLF. 1853: * @access private 1854: * @return string 1855: */ 1856: private function FixEOL($str) { 1857: $str = str_replace("\r\n", "\n", $str); 1858: $str = str_replace("\r", "\n", $str); 1859: $str = str_replace("\n", $this->LE, $str); 1860: return $str; 1861: } 1862: 1863: /** 1864: * Adds a custom header. 1865: * @access public 1866: * @return void 1867: */ 1868: public function AddCustomHeader($custom_header) { 1869: $this->CustomHeader[] = explode(':', $custom_header, 2); 1870: } 1871: 1872: /** 1873: * Evaluates the message and returns modifications for inline images and backgrounds 1874: * @access public 1875: * @return $message 1876: */ 1877: public function MsgHTML($message, $basedir = '') { 1878: preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); 1879: if(isset($images[2])) { 1880: foreach($images[2] as $i => $url) { 1881: // do not change urls for absolute images (thanks to corvuscorax) 1882: if (!preg_match('#^[A-z]+://#',$url)) { 1883: $filename = basename($url); 1884: $directory = dirname($url); 1885: ($directory == '.')?$directory='':''; 1886: $cid = 'cid:' . md5($filename); 1887: $ext = pathinfo($filename, PATHINFO_EXTENSION); 1888: $mimeType = self::_mime_types($ext); 1889: if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; } 1890: if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } 1891: if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) { 1892: $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); 1893: } 1894: } 1895: } 1896: } 1897: $this->IsHTML(true); 1898: $this->Body = $message; 1899: $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message))); 1900: if (!empty($textMsg) && empty($this->AltBody)) { 1901: $this->AltBody = html_entity_decode($textMsg); 1902: } 1903: if (empty($this->AltBody)) { 1904: $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 1905: } 1906: } 1907: 1908: /** 1909: * Gets the MIME type of the embedded or inline image 1910: * @param string File extension 1911: * @access public 1912: * @return string MIME type of ext 1913: * @static 1914: */ 1915: public static function _mime_types($ext = '') { 1916: $mimes = array( 1917: 'hqx' => 'application/mac-binhex40', 1918: 'cpt' => 'application/mac-compactpro', 1919: 'doc' => 'application/msword', 1920: 'bin' => 'application/macbinary', 1921: 'dms' => 'application/octet-stream', 1922: 'lha' => 'application/octet-stream', 1923: 'lzh' => 'application/octet-stream', 1924: 'exe' => 'application/octet-stream', 1925: 'class' => 'application/octet-stream', 1926: 'psd' => 'application/octet-stream', 1927: 'so' => 'application/octet-stream', 1928: 'sea' => 'application/octet-stream', 1929: 'dll' => 'application/octet-stream', 1930: 'oda' => 'application/oda', 1931: 'pdf' => 'application/pdf', 1932: 'ai' => 'application/postscript', 1933: 'eps' => 'application/postscript', 1934: 'ps' => 'application/postscript', 1935: 'smi' => 'application/smil', 1936: 'smil' => 'application/smil', 1937: 'mif' => 'application/vnd.mif', 1938: 'xls' => 'application/vnd.ms-excel', 1939: 'ppt' => 'application/vnd.ms-powerpoint', 1940: 'wbxml' => 'application/vnd.wap.wbxml', 1941: 'wmlc' => 'application/vnd.wap.wmlc', 1942: 'dcr' => 'application/x-director', 1943: 'dir' => 'application/x-director', 1944: 'dxr' => 'application/x-director', 1945: 'dvi' => 'application/x-dvi', 1946: 'gtar' => 'application/x-gtar', 1947: 'php' => 'application/x-httpd-php', 1948: 'php4' => 'application/x-httpd-php', 1949: 'php3' => 'application/x-httpd-php', 1950: 'phtml' => 'application/x-httpd-php', 1951: 'phps' => 'application/x-httpd-php-source', 1952: 'js' => 'application/x-javascript', 1953: 'swf' => 'application/x-shockwave-flash', 1954: 'sit' => 'application/x-stuffit', 1955: 'tar' => 'application/x-tar', 1956: 'tgz' => 'application/x-tar', 1957: 'xhtml' => 'application/xhtml+xml', 1958: 'xht' => 'application/xhtml+xml', 1959: 'zip' => 'application/zip', 1960: 'mid' => 'audio/midi', 1961: 'midi' => 'audio/midi', 1962: 'mpga' => 'audio/mpeg', 1963: 'mp2' => 'audio/mpeg', 1964: 'mp3' => 'audio/mpeg', 1965: 'aif' => 'audio/x-aiff', 1966: 'aiff' => 'audio/x-aiff', 1967: 'aifc' => 'audio/x-aiff', 1968: 'ram' => 'audio/x-pn-realaudio', 1969: 'rm' => 'audio/x-pn-realaudio', 1970: 'rpm' => 'audio/x-pn-realaudio-plugin', 1971: 'ra' => 'audio/x-realaudio', 1972: 'rv' => 'video/vnd.rn-realvideo', 1973: 'wav' => 'audio/x-wav', 1974: 'bmp' => 'image/bmp', 1975: 'gif' => 'image/gif', 1976: 'jpeg' => 'image/jpeg', 1977: 'jpg' => 'image/jpeg', 1978: 'jpe' => 'image/jpeg', 1979: 'png' => 'image/png', 1980: 'tiff' => 'image/tiff', 1981: 'tif' => 'image/tiff', 1982: 'css' => 'text/css', 1983: 'html' => 'text/html', 1984: 'htm' => 'text/html', 1985: 'shtml' => 'text/html', 1986: 'txt' => 'text/plain', 1987: 'text' => 'text/plain', 1988: 'log' => 'text/plain', 1989: 'rtx' => 'text/richtext', 1990: 'rtf' => 'text/rtf', 1991: 'xml' => 'text/xml', 1992: 'xsl' => 'text/xml', 1993: 'mpeg' => 'video/mpeg', 1994: 'mpg' => 'video/mpeg', 1995: 'mpe' => 'video/mpeg', 1996: 'qt' => 'video/quicktime', 1997: 'mov' => 'video/quicktime', 1998: 'avi' => 'video/x-msvideo', 1999: 'movie' => 'video/x-sgi-movie', 2000: 'doc' => 'application/msword', 2001: 'word' => 'application/msword', 2002: 'xl' => 'application/excel', 2003: 'eml' => 'message/rfc822' 2004: ); 2005: return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; 2006: } 2007: 2008: /** 2009: * Set (or reset) Class Objects (variables) 2010: * 2011: * Usage Example: 2012: * $page->set('X-Priority', '3'); 2013: * 2014: * @access public 2015: * @param string $name Parameter Name 2016: * @param mixed $value Parameter Value 2017: * NOTE: will not work with arrays, there are no arrays to set/reset 2018: * @todo Should this not be using __set() magic function? 2019: */ 2020: public function set($name, $value = '') { 2021: try { 2022: if (isset($this->$name) ) { 2023: $this->$name = $value; 2024: } else { 2025: throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); 2026: } 2027: } catch (Exception $e) { 2028: $this->SetError($e->getMessage()); 2029: if ($e->getCode() == self::STOP_CRITICAL) { 2030: return false; 2031: } 2032: } 2033: return true; 2034: } 2035: 2036: /** 2037: * Strips newlines to prevent header injection. 2038: * @access public 2039: * @param string $str String 2040: * @return string 2041: */ 2042: public function SecureHeader($str) { 2043: $str = str_replace("\r", '', $str); 2044: $str = str_replace("\n", '', $str); 2045: return trim($str); 2046: } 2047: 2048: /** 2049: * Set the private key file and password to sign the message. 2050: * 2051: * @access public 2052: * @param string $key_filename Parameter File Name 2053: * @param string $key_pass Password for private key 2054: */ 2055: public function Sign($cert_filename, $key_filename, $key_pass) { 2056: $this->sign_cert_file = $cert_filename; 2057: $this->sign_key_file = $key_filename; 2058: $this->sign_key_pass = $key_pass; 2059: } 2060: } 2061: 2062: class phpmailerException extends Exception { 2063: public function errorMessage() { 2064: $errorMsg = '' . $this->getMessage() . "
\n";
2065: return $errorMsg; 2066: } 2067: } 2068: ?>