File: 1.02.3a/server/base/phpmailer/class.phpmailer.php (View as Code)

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