1<?php 2//////////////////////////////////////////////////// 3// PHPMailer - PHP email class 4// 5// Class for sending email using either 6// sendmail, PHP mail(), or SMTP. Methods are 7// based upon the standard AspEmail(tm) classes. 8// 9// Copyright (C) 2001 - 2003 Brent R. Matzelle 10// 11// License: LGPL, see LICENSE 12//////////////////////////////////////////////////// 13 14/** 15 * PHPMailer - PHP email transport class 16 * @package PHPMailer 17 * @author Brent R. Matzelle 18 * @copyright 2001 - 2003 Brent R. Matzelle 19 */ 20class PHPMailer 21{ 22 ///////////////////////////////////////////////// 23 // PUBLIC VARIABLES 24 ///////////////////////////////////////////////// 25 26 /** 27 * Email priority (1 = High, 3 = Normal, 5 = low). 28 * @var int 29 */ 30 var $Priority = 3; 31 32 /** 33 * Sets the CharSet of the message. 34 * @var string 35 */ 36 var $CharSet = "iso-8859-1"; 37 38 /** 39 * Sets the Content-type of the message. 40 * @var string 41 */ 42 var $ContentType = "text/plain"; 43 44 /** 45 * Sets the Encoding of the message. Options for this are "8bit", 46 * "7bit", "binary", "base64", and "quoted-printable". 47 * @var string 48 */ 49 var $Encoding = "8bit"; 50 51 /** 52 * Holds the most recent mailer error message. 53 * @var string 54 */ 55 var $ErrorInfo = ""; 56 57 /** 58 * Sets the From email address for the message. 59 * @var string 60 */ 61 var $From = "root@localhost"; 62 63 /** 64 * Sets the From name of the message. 65 * @var string 66 */ 67 var $FromName = "Root User"; 68 69 /** 70 * Sets the Sender email (Return-Path) of the message. If not empty, 71 * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 72 * @var string 73 */ 74 var $Sender = ""; 75 76 /** 77 * Sets the Subject of the message. 78 * @var string 79 */ 80 var $Subject = ""; 81 82 /** 83 * Sets the Body of the message. This can be either an HTML or text body. 84 * If HTML then run IsHTML(true). 85 * @var string 86 */ 87 var $Body = ""; 88 89 /** 90 * Sets the text-only body of the message. This automatically sets the 91 * email to multipart/alternative. This body can be read by mail 92 * clients that do not have HTML email capability such as mutt. Clients 93 * that can read HTML will view the normal Body. 94 * @var string 95 */ 96 var $AltBody = ""; 97 98 /** 99 * Sets word wrapping on the body of the message to a given number of 100 * characters. 101 * @var int 102 */ 103 var $WordWrap = 0; 104 105 /** 106 * Method to send mail: ("mail", "sendmail", or "smtp"). 107 * @var string 108 */ 109 var $Mailer = "mail"; 110 111 /** 112 * Sets the path of the sendmail program. 113 * @var string 114 */ 115 var $Sendmail = "/usr/sbin/sendmail"; 116 117 /** 118 * Path to PHPMailer plugins. This is now only useful if the SMTP class 119 * is in a different directory than the PHP include path. 120 * @var string 121 */ 122 var $PluginDir = ""; 123 124 /** 125 * Holds PHPMailer version. 126 * @var string 127 */ 128 var $Version = "1.72"; 129 130 /** 131 * Sets the email address that a reading confirmation will be sent. 132 * @var string 133 */ 134 var $ConfirmReadingTo = ""; 135 136 /** 137 * Sets the hostname to use in Message-Id and Received headers 138 * and as default HELO string. If empty, the value returned 139 * by SERVER_NAME is used or 'localhost.localdomain'. 140 * @var string 141 */ 142 var $Hostname = ""; 143 144 ///////////////////////////////////////////////// 145 // SMTP VARIABLES 146 ///////////////////////////////////////////////// 147 148 /** 149 * Sets the SMTP hosts. All hosts must be separated by a 150 * semicolon. You can also specify a different port 151 * for each host by using this format: [hostname:port] 152 * (e.g. "smtp1.example.com:25;smtp2.example.com"). 153 * Hosts will be tried in order. 154 * @var string 155 */ 156 var $Host = "localhost"; 157 158 /** 159 * Sets the default SMTP server port. 160 * @var int 161 */ 162 var $Port = 25; 163 164 /** 165 * Sets the SMTP HELO of the message (Default is $Hostname). 166 * @var string 167 */ 168 var $Helo = ""; 169 170 /** 171 * Sets SMTP authentication. Utilizes the Username and Password variables. 172 * @var bool 173 */ 174 var $SMTPAuth = false; 175 176 /** 177 * Sets SMTP username. 178 * @var string 179 */ 180 var $Username = ""; 181 182 /** 183 * Sets SMTP password. 184 * @var string 185 */ 186 var $Password = ""; 187 188 /** 189 * Sets the SMTP server timeout in seconds. This function will not 190 * work with the win32 version. 191 * @var int 192 */ 193 var $Timeout = 10; 194 195 /** 196 * Sets SMTP class debugging on or off. 197 * @var bool 198 */ 199 var $SMTPDebug = false; 200 201 /** 202 * Prevents the SMTP connection from being closed after each mail 203 * sending. If this is set to true then to close the connection 204 * requires an explicit call to SmtpClose(). 205 * @var bool 206 */ 207 var $SMTPKeepAlive = false; 208 209 /**#@+ 210 * @access private 211 */ 212 var $smtp = NULL; 213 var $to = array(); 214 var $cc = array(); 215 var $bcc = array(); 216 var $ReplyTo = array(); 217 var $attachment = array(); 218 var $CustomHeader = array(); 219 var $message_type = ""; 220 var $boundary = array(); 221 var $language = array(); 222 var $error_count = 0; 223 var $LE = "\n"; 224 /**#@-*/ 225 226 ///////////////////////////////////////////////// 227 // CONSTRUCTOR 228 ///////////////////////////////////////////////// 229 /** 230 * Constructor, just set up language 231 * @param none 232 * @return void 233 */ 234 function PHPMailer() { 235 global $conf; 236 global $charset; 237 238 $this->CharSet = $charset; 239 240 $this->SetLanguage(); 241 242 $this->Mailer = $conf['app']['emailType']; 243 244 if ($this->Mailer == 'smtp') { // Set smtp variables 245 $this->Host = $conf['app']['smtpHost']; 246 $this->Port = $conf['app']['smtpPort']; 247 } 248 249 if ($this->Mailer == 'sendmail') // Set sendmail variables 250 $this->Sendmail = $conf['app']['sendmailPath']; 251 252 if ($this->Mailer == 'qmail') // Set qmail variables 253 $this->Sendmail = $conf['app']['qmailPath']; 254 } 255 256 ///////////////////////////////////////////////// 257 // VARIABLE METHODS 258 ///////////////////////////////////////////////// 259 260 /** 261 * Sets message type to HTML. 262 * @param bool $bool 263 * @return void 264 */ 265 function IsHTML($bool) { 266 if($bool == true) 267 $this->ContentType = "text/html"; 268 else 269 $this->ContentType = "text/plain"; 270 } 271 272 /** 273 * Sets Mailer to send message using SMTP. 274 * @return void 275 */ 276 function IsSMTP() { 277 $this->Mailer = "smtp"; 278 } 279 280 /** 281 * Sets Mailer to send message using PHP mail() function. 282 * @return void 283 */ 284 function IsMail() { 285 $this->Mailer = "mail"; 286 } 287 288 /** 289 * Sets Mailer to send message using the $Sendmail program. 290 * @return void 291 */ 292 function IsSendmail() { 293 $this->Mailer = "sendmail"; 294 } 295 296 /** 297 * Sets Mailer to send message using the qmail MTA. 298 * @return void 299 */ 300 function IsQmail() { 301 $this->Sendmail = "/var/qmail/bin/sendmail"; 302 $this->Mailer = "sendmail"; 303 } 304 305 306 ///////////////////////////////////////////////// 307 // RECIPIENT METHODS 308 ///////////////////////////////////////////////// 309 310 /** 311 * Adds a "To" address. 312 * @param string $address 313 * @param string $name 314 * @return void 315 */ 316 function AddAddress($address, $name = "") { 317 $cur = count($this->to); 318 $this->to[$cur][0] = trim($address); 319 $this->to[$cur][1] = $name; 320 } 321 322 /** 323 * Adds a "Cc" address. Note: this function works 324 * with the SMTP mailer on win32, not with the "mail" 325 * mailer. 326 * @param string $address 327 * @param string $name 328 * @return void 329 */ 330 function AddCC($address, $name = "") { 331 $cur = count($this->cc); 332 $this->cc[$cur][0] = trim($address); 333 $this->cc[$cur][1] = $name; 334 } 335 336 /** 337 * Adds a "Bcc" address. Note: this function works 338 * with the SMTP mailer on win32, not with the "mail" 339 * mailer. 340 * @param string $address 341 * @param string $name 342 * @return void 343 */ 344 function AddBCC($address, $name = "") { 345 $cur = count($this->bcc); 346 $this->bcc[$cur][0] = trim($address); 347 $this->bcc[$cur][1] = $name; 348 } 349 350 /** 351 * Adds a "Reply-to" address. 352 * @param string $address 353 * @param string $name 354 * @return void 355 */ 356 function AddReplyTo($address, $name = "") { 357 $cur = count($this->ReplyTo); 358 $this->ReplyTo[$cur][0] = trim($address); 359 $this->ReplyTo[$cur][1] = $name; 360 } 361 362 363 ///////////////////////////////////////////////// 364 // MAIL SENDING METHODS 365 ///////////////////////////////////////////////// 366 367 /** 368 * Creates message and assigns Mailer. If the message is 369 * not sent successfully then it returns false. Use the ErrorInfo 370 * variable to view description of the error. 371 * @return bool 372 */ 373 function Send() { 374 $header = ""; 375 $body = ""; 376 $result = true; 377 378 if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) 379 { 380 $this->SetError($this->Lang("provide_address")); 381 return false; 382 } 383 384 // Set whether the message is multipart/alternative 385 if(!empty($this->AltBody)) 386 $this->ContentType = "multipart/alternative"; 387 388 $this->error_count = 0; // reset errors 389 $this->SetMessageType(); 390 $header .= $this->CreateHeader(); 391 $body = $this->CreateBody(); 392 393 // Nick Korbel - 08-21-2005 394 if (version_compare('4.3.0',phpversion(), '<=') == 1) { 395 $this->Subject = html_entity_decode($this->Subject, ENT_COMPAT, $this->CharSet); 396 if ($this->ContentType != "text/html") 397 $body = html_entity_decode($body, ENT_COMPAT, $this->CharSet); 398 } 399 400 if($body == "") { return false; } 401 402 // Choose the mailer 403 switch($this->Mailer) 404 { 405 case "sendmail": 406 $result = $this->SendmailSend($header, $body); 407 break; 408 case "mail": 409 $result = $this->MailSend($header, $body); 410 break; 411 case "smtp": 412 $result = $this->SmtpSend($header, $body); 413 break; 414 default: 415 $this->SetError($this->Mailer . $this->Lang("mailer_not_supported")); 416 $result = false; 417 break; 418 } 419 420 return $result; 421 } 422 423 /** 424 * Sends mail using the $Sendmail program. 425 * @access private 426 * @return bool 427 */ 428 function SendmailSend($header, $body) { 429 if ($this->Sender != "") 430 $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender); 431 else 432 $sendmail = sprintf("%s -oi -t", $this->Sendmail); 433 434 if(!@$mail = popen($sendmail, "w")) 435 { 436 $this->SetError($this->Lang("execute") . $this->Sendmail); 437 return false; 438 } 439 440 fputs($mail, $header); 441 fputs($mail, $body); 442 443 $result = pclose($mail) >> 8 & 0xFF; 444 if($result != 0) 445 { 446 $this->SetError($this->Lang("execute") . $this->Sendmail); 447 return false; 448 } 449 450 return true; 451 } 452 453 /** 454 * Sends mail using the PHP mail() function. 455 * @access private 456 * @return bool 457 */ 458 function MailSend($header, $body) { 459 $to = ""; 460 for($i = 0; $i < count($this->to); $i++) 461 { 462 if($i != 0) { $to .= ", "; } 463 $to .= $this->to[$i][0]; 464 } 465 466 if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1) 467 { 468 $old_from = ini_get("sendmail_from"); 469 ini_set("sendmail_from", $this->Sender); 470 $params = sprintf("-oi -f %s", $this->Sender); 471 $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, 472 $header, $params); 473 } 474 else 475 $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header); 476 477 if (isset($old_from)) 478 ini_set("sendmail_from", $old_from); 479 480 if(!$rt) 481 { 482 $this->SetError($this->Lang("instantiate")); 483 return false; 484 } 485 486 return true; 487 } 488 489 /** 490 * Sends mail via SMTP using PhpSMTP (Author: 491 * Chris Ryan). Returns bool. Returns false if there is a 492 * bad MAIL FROM, RCPT, or DATA input. 493 * @access private 494 * @return bool 495 */ 496 function SmtpSend($header, $body) { 497 include_once($this->PluginDir . "Smtp.class.php"); 498 $error = ""; 499 $bad_rcpt = array(); 500 501 if(!$this->SmtpConnect()) 502 return false; 503 504 $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender; 505 if(!$this->smtp->Mail($smtp_from)) 506 { 507 $error = $this->Lang("from_failed") . $smtp_from; 508 $this->SetError($error); 509 $this->smtp->Reset(); 510 return false; 511 } 512 513 // Attempt to send attach all recipients 514 for($i = 0; $i < count($this->to); $i++) 515 { 516 if(!$this->smtp->Recipient($this->to[$i][0])) 517 $bad_rcpt[] = $this->to[$i][0]; 518 } 519 for($i = 0; $i < count($this->cc); $i++) 520 { 521 if(!$this->smtp->Recipient($this->cc[$i][0])) 522 $bad_rcpt[] = $this->cc[$i][0]; 523 } 524 for($i = 0; $i < count($this->bcc); $i++) 525 { 526 if(!$this->smtp->Recipient($this->bcc[$i][0])) 527 $bad_rcpt[] = $this->bcc[$i][0]; 528 } 529 530 if(count($bad_rcpt) > 0) // Create error message 531 { 532 for($i = 0; $i < count($bad_rcpt); $i++) 533 { 534 if($i != 0) { $error .= ", "; } 535 $error .= $bad_rcpt[$i]; 536 } 537 $error = $this->Lang("recipients_failed") . $error; 538 $this->SetError($error); 539 $this->smtp->Reset(); 540 return false; 541 } 542 543 if(!$this->smtp->Data($header . $body)) 544 { 545 $this->SetError($this->Lang("data_not_accepted")); 546 $this->smtp->Reset(); 547 return false; 548 } 549 if($this->SMTPKeepAlive == true) 550 $this->smtp->Reset(); 551 else 552 $this->SmtpClose(); 553 554 return true; 555 } 556 557 /** 558 * Initiates a connection to an SMTP server. Returns false if the 559 * operation failed. 560 * @access private 561 * @return bool 562 */ 563 function SmtpConnect() { 564 if($this->smtp == NULL) { $this->smtp = new SMTP(); } 565 566 $this->smtp->do_debug = $this->SMTPDebug; 567 $hosts = explode(";", $this->Host); 568 $index = 0; 569 $connection = ($this->smtp->Connected()); 570 571 // Retry while there is no connection 572 while($index < count($hosts) && $connection == false) 573 { 574 if(strstr($hosts[$index], ":")) 575 list($host, $port) = explode(":", $hosts[$index]); 576 else 577 { 578 $host = $hosts[$index]; 579 $port = $this->Port; 580 } 581 582 if($this->smtp->Connect($host, $port, $this->Timeout)) 583 { 584 if ($this->Helo != '') 585 $this->smtp->Hello($this->Helo); 586 else 587 $this->smtp->Hello($this->ServerHostname()); 588 589 if($this->SMTPAuth) 590 { 591 if(!$this->smtp->Authenticate($this->Username, 592 $this->Password)) 593 { 594 $this->SetError($this->Lang("authenticate")); 595 $this->smtp->Reset(); 596 $connection = false; 597 } 598 } 599 $connection = true; 600 } 601 $index++; 602 } 603 if(!$connection) 604 $this->SetError($this->Lang("connect_host")); 605 606 return $connection; 607 } 608 609 /** 610 * Closes the active SMTP session if one exists. 611 * @return void 612 */ 613 function SmtpClose() { 614 if($this->smtp != NULL) 615 { 616 if($this->smtp->Connected()) 617 { 618 $this->smtp->Quit(); 619 $this->smtp->Close(); 620 } 621 } 622 } 623 624 /** 625 * Sets the language for all class error messages. Always in English. 626 * @param none 627 * @access public 628 * @return bool 629 */ 630 function SetLanguage() { 631 /** 632 * Only printing errors in english 633 */ 634 $PHPMAILER_LANG = array(); 635 636 $PHPMAILER_LANG["provide_address"] = 'You must provide at least one ' . 637 'recipient email address.'; 638 $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.'; 639 $PHPMAILER_LANG["execute"] = 'Could not execute: '; 640 $PHPMAILER_LANG["instantiate"] = 'Could not instantiate mail function.'; 641 $PHPMAILER_LANG["authenticate"] = 'SMTP Error: Could not authenticate.'; 642 $PHPMAILER_LANG["from_failed"] = 'The following From address failed: '; 643 $PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: The following ' . 644 'recipients failed: '; 645 $PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data not accepted.'; 646 $PHPMAILER_LANG["connect_host"] = 'SMTP Error: Could not connect to SMTP host.'; 647 $PHPMAILER_LANG["file_access"] = 'Could not access file: '; 648 $PHPMAILER_LANG["file_open"] = 'File Error: Could not open file: '; 649 $PHPMAILER_LANG["encoding"] = 'Unknown encoding: '; 650 651 $this->language = $PHPMAILER_LANG; 652 653 return true; 654 } 655 656 ///////////////////////////////////////////////// 657 // MESSAGE CREATION METHODS 658 ///////////////////////////////////////////////// 659 660 /** 661 * Creates recipient headers. 662 * @access private 663 * @return string 664 */ 665 function AddrAppend($type, $addr) { 666 $addr_str = $type . ": "; 667 $addr_str .= $this->AddrFormat($addr[0]); 668 if(count($addr) > 1) 669 { 670 for($i = 1; $i < count($addr); $i++) 671 $addr_str .= ", " . $this->AddrFormat($addr[$i]); 672 } 673 $addr_str .= $this->LE; 674 675 return $addr_str; 676 } 677 678 /** 679 * Formats an address correctly. 680 * @access private 681 * @return string 682 */ 683 function AddrFormat($addr) { 684 if(empty($addr[1])) 685 $formatted = $addr[0]; 686 else 687 { 688 $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" . 689 $addr[0] . ">"; 690 } 691 692 return $formatted; 693 } 694 695 /** 696 * Wraps message for use with mailers that do not 697 * automatically perform wrapping and for quoted-printable. 698 * Original written by philippe. 699 * @access private 700 * @return string 701 */ 702 function WrapText($message, $length, $qp_mode = false) { 703 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 704 705 $message = $this->FixEOL($message); 706 if (substr($message, -1) == $this->LE) 707 $message = substr($message, 0, -1); 708 709 $line = explode($this->LE, $message); 710 $message = ""; 711 for ($i=0 ;$i < count($line); $i++) 712 { 713 $line_part = explode(" ", $line[$i]); 714 $buf = ""; 715 for ($e = 0; $e<count($line_part); $e++) 716 { 717 $word = $line_part[$e]; 718 if ($qp_mode and (strlen($word) > $length)) 719 { 720 $space_left = $length - strlen($buf) - 1; 721 if ($e != 0) 722 { 723 if ($space_left > 20) 724 { 725 $len = $space_left; 726 if (substr($word, $len - 1, 1) == "=") 727 $len--; 728 elseif (substr($word, $len - 2, 1) == "=") 729 $len -= 2; 730 $part = substr($word, 0, $len); 731 $word = substr($word, $len); 732 $buf .= " " . $part; 733 $message .= $buf . sprintf("=%s", $this->LE); 734 } 735 else 736 { 737 $message .= $buf . $soft_break; 738 } 739 $buf = ""; 740 } 741 while (strlen($word) > 0) 742 { 743 $len = $length; 744 if (substr($word, $len - 1, 1) == "=") 745 $len--; 746 elseif (substr($word, $len - 2, 1) == "=") 747 $len -= 2; 748 $part = substr($word, 0, $len); 749 $word = substr($word, $len); 750 751 if (strlen($word) > 0) 752 $message .= $part . sprintf("=%s", $this->LE); 753 else 754 $buf = $part; 755 } 756 } 757 else 758 { 759 $buf_o = $buf; 760 $buf .= ($e == 0) ? $word : (" " . $word); 761 762 if (strlen($buf) > $length and $buf_o != "") 763 { 764 $message .= $buf_o . $soft_break; 765 $buf = $word; 766 } 767 } 768 } 769 $message .= $buf . $this->LE; 770 } 771 772 return $message; 773 } 774 775 /** 776 * Set the body wrapping. 777 * @access private 778 * @return void 779 */ 780 function SetWordWrap() { 781 if($this->WordWrap < 1) 782 return; 783 784 switch($this->message_type) 785 { 786 case "alt": 787 // fall through 788 case "alt_attachment": 789 $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); 790 break; 791 default: 792 $this->Body = $this->WrapText($this->Body, $this->WordWrap); 793 break; 794 } 795 } 796 797 /** 798 * Assembles message header. 799 * @access private 800 * @return string 801 */ 802 function CreateHeader() { 803 $result = ""; 804 805 // Set the boundaries 806 $uniq_id = md5(uniqid(time())); 807 $this->boundary[1] = "b1_" . $uniq_id; 808 $this->boundary[2] = "b2_" . $uniq_id; 809 810 $result .= $this->HeaderLine("Date", $this->RFCDate()); 811 if($this->Sender == "") 812 $result .= $this->HeaderLine("Return-Path", trim($this->From)); 813 else 814 $result .= $this->HeaderLine("Return-Path", trim($this->Sender)); 815 816 // To be created automatically by mail() 817 if($this->Mailer != "mail") 818 { 819 if(count($this->to) > 0) 820 $result .= $this->AddrAppend("To", $this->to); 821 else if (count($this->cc) == 0) 822 $result .= $this->HeaderLine("To", "undisclosed-recipients:;"); 823 if(count($this->cc) > 0) 824 $result .= $this->AddrAppend("Cc", $this->cc); 825 } 826 827 $from = array(); 828 $from[0][0] = trim($this->From); 829 $from[0][1] = $this->FromName; 830 $result .= $this->AddrAppend("From", $from); 831 832 // sendmail and mail() extract Bcc from the header before sending 833 if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0)) 834 $result .= $this->AddrAppend("Bcc", $this->bcc); 835 836 if(count($this->ReplyTo) > 0) 837 $result .= $this->AddrAppend("Reply-to", $this->ReplyTo); 838 839 // mail() sets the subject itself 840 if($this->Mailer != "mail") 841 $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject))); 842 843 $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); 844 $result .= $this->HeaderLine("X-Priority", $this->Priority); 845 $result .= $this->HeaderLine("X-Mailer", "PHPMailer [version " . $this->Version . "]"); 846 847 if($this->ConfirmReadingTo != "") 848 { 849 $result .= $this->HeaderLine("Disposition-Notification-To", 850 "<" . trim($this->ConfirmReadingTo) . ">"); 851 } 852 853 // Add custom headers 854 for($index = 0; $index < count($this->CustomHeader); $index++) 855 { 856 $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), 857 $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); 858 } 859 $result .= $this->HeaderLine("MIME-Version", "1.0"); 860 861 switch($this->message_type) 862 { 863 case "plain": 864 $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding); 865 $result .= sprintf("Content-Type: %s; charset=\"%s\"", 866 $this->ContentType, $this->CharSet); 867 break; 868 case "attachments": 869 // fall through 870 case "alt_attachments": 871 if($this->InlineImageExists()) 872 { 873 $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 874 "multipart/related", $this->LE, $this->LE, 875 $this->boundary[1], $this->LE); 876 } 877 else 878 { 879 $result .= $this->HeaderLine("Content-Type", "multipart/mixed;"); 880 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 881 } 882 break; 883 case "alt": 884 $result .= $this->HeaderLine("Content-Type", "multipart/alternative;"); 885 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 886 break; 887 } 888 889 if($this->Mailer != "mail") 890 $result .= $this->LE.$this->LE; 891 892 return $result; 893 } 894 895 /** 896 * Assembles the message body. Returns an empty string on failure. 897 * @access private 898 * @return string 899 */ 900 function CreateBody() { 901 $result = ""; 902 903 $this->SetWordWrap(); 904 905 switch($this->message_type) 906 { 907 case "alt": 908 $result .= $this->GetBoundary($this->boundary[1], "", 909 "text/plain", ""); 910 $result .= $this->EncodeString($this->AltBody, $this->Encoding); 911 $result .= $this->LE.$this->LE; 912 $result .= $this->GetBoundary($this->boundary[1], "", 913 "text/html", ""); 914 915 $result .= $this->EncodeString($this->Body, $this->Encoding); 916 $result .= $this->LE.$this->LE; 917 918 $result .= $this->EndBoundary($this->boundary[1]); 919 break; 920 case "plain": 921 $result .= $this->EncodeString($this->Body, $this->Encoding); 922 break; 923 case "attachments": 924 $result .= $this->GetBoundary($this->boundary[1], "", "", ""); 925 $result .= $this->EncodeString($this->Body, $this->Encoding); 926 $result .= $this->LE; 927 928 $result .= $this->AttachAll(); 929 break; 930 case "alt_attachments": 931 $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); 932 $result .= sprintf("Content-Type: %s;%s" . 933 "\tboundary=\"%s\"%s", 934 "multipart/alternative", $this->LE, 935 $this->boundary[2], $this->LE.$this->LE); 936 937 // Create text body 938 $result .= $this->GetBoundary($this->boundary[2], "", 939 "text/plain", "") . $this->LE; 940 941 $result .= $this->EncodeString($this->AltBody, $this->Encoding); 942 $result .= $this->LE.$this->LE; 943 944 // Create the HTML body 945 $result .= $this->GetBoundary($this->boundary[2], "", 946 "text/html", "") . $this->LE; 947 948 $result .= $this->EncodeString($this->Body, $this->Encoding); 949 $result .= $this->LE.$this->LE; 950 951 $result .= $this->EndBoundary($this->boundary[2]); 952 953 $result .= $this->AttachAll(); 954 break; 955 } 956 if($this->IsError()) 957 $result = ""; 958 959 return $result; 960 } 961 962 /** 963 * Returns the start of a message boundary. 964 * @access private 965 */ 966 function GetBoundary($boundary, $charSet, $contentType, $encoding) { 967 $result = ""; 968 if($charSet == "") { $charSet = $this->CharSet; } 969 if($contentType == "") { $contentType = $this->ContentType; } 970 if($encoding == "") { $encoding = $this->Encoding; } 971 972 $result .= $this->TextLine("--" . $boundary); 973 $result .= sprintf("Content-Type: %s; charset = \"%s\"", 974 $contentType, $charSet); 975 $result .= $this->LE; 976 $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding); 977 $result .= $this->LE; 978 979 return $result; 980 } 981 982 /** 983 * Returns the end of a message boundary. 984 * @access private 985 */ 986 function EndBoundary($boundary) { 987 return $this->LE . "--" . $boundary . "--" . $this->LE; 988 } 989 990 /** 991 * Sets the message type. 992 * @access private 993 * @return void 994 */ 995 function SetMessageType() { 996 if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) 997 $this->message_type = "plain"; 998 else 999 { 1000 if(count($this->attachment) > 0) 1001 $this->message_type = "attachments"; 1002 if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) 1003 $this->message_type = "alt"; 1004 if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) 1005 $this->message_type = "alt_attachments"; 1006 } 1007 } 1008 1009 /** 1010 * Returns a formatted header line. 1011 * @access private 1012 * @return string 1013 */ 1014 function HeaderLine($name, $value) { 1015 return $name . ": " . $value . $this->LE; 1016 } 1017 1018 /** 1019 * Returns a formatted mail line. 1020 * @access private 1021 * @return string 1022 */ 1023 function TextLine($value) { 1024 return $value . $this->LE; 1025 } 1026 1027 ///////////////////////////////////////////////// 1028 // ATTACHMENT METHODS 1029 ///////////////////////////////////////////////// 1030 1031 /** 1032 * Adds an attachment from a path on the filesystem. 1033 * Returns false if the file could not be found 1034 * or accessed. 1035 * @param string $path Path to the attachment. 1036 * @param string $name Overrides the attachment name. 1037 * @param string $encoding File encoding (see $Encoding). 1038 * @param string $type File extension (MIME) type. 1039 * @return bool 1040 */ 1041 function AddAttachment($path, $name = "", $encoding = "base64", 1042 $type = "application/octet-stream") { 1043 if(!@is_file($path)) 1044 { 1045 $this->SetError($this->Lang("file_access") . $path); 1046 return false; 1047 } 1048 1049 $filename = basename($path); 1050 if($name == "") 1051 $name = $filename; 1052 1053 $cur = count($this->attachment); 1054 $this->attachment[$cur][0] = $path; 1055 $this->attachment[$cur][1] = $filename; 1056 $this->attachment[$cur][2] = $name; 1057 $this->attachment[$cur][3] = $encoding; 1058 $this->attachment[$cur][4] = $type; 1059 $this->attachment[$cur][5] = false; // isStringAttachment 1060 $this->attachment[$cur][6] = "attachment"; 1061 $this->attachment[$cur][7] = 0; 1062 1063 return true; 1064 } 1065 1066 /** 1067 * Attaches all fs, string, and binary attachments to the message. 1068 * Returns an empty string on failure. 1069 * @access private 1070 * @return string 1071 */ 1072 function AttachAll() { 1073 // Return text of body 1074 $mime = array(); 1075 1076 // Add all attachments 1077 for($i = 0; $i < count($this->attachment); $i++) 1078 { 1079 // Check for string attachment 1080 $bString = $this->attachment[$i][5]; 1081 if ($bString) 1082 $string = $this->attachment[$i][0]; 1083 else 1084 $path = $this->attachment[$i][0]; 1085 1086 $filename = $this->attachment[$i][1]; 1087 $name = $this->attachment[$i][2]; 1088 $encoding = $this->attachment[$i][3]; 1089 $type = $this->attachment[$i][4]; 1090 $disposition = $this->attachment[$i][6]; 1091 $cid = $this->attachment[$i][7]; 1092 1093 $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); 1094 $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); 1095 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 1096 1097 if($disposition == "inline") 1098 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); 1099 1100 $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", 1101 $disposition, $name, $this->LE.$this->LE); 1102 1103 // Encode as string attachment 1104 if($bString) 1105 { 1106 $mime[] = $this->EncodeString($string, $encoding); 1107 if($this->IsError()) { return ""; } 1108 $mime[] = $this->LE.$this->LE; 1109 } 1110 else 1111 { 1112 $mime[] = $this->EncodeFile($path, $encoding); 1113 if($this->IsError()) { return ""; } 1114 $mime[] = $this->LE.$this->LE; 1115 } 1116 } 1117 1118 $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); 1119 1120 return join("", $mime); 1121 } 1122 1123 /** 1124 * Encodes attachment in requested format. Returns an 1125 * empty string on failure. 1126 * @access private 1127 * @return string 1128 */ 1129 function EncodeFile ($path, $encoding = "base64") { 1130 if(!@$fd = fopen($path, "rb")) 1131 { 1132 $this->SetError($this->Lang("file_open") . $path); 1133 return ""; 1134 } 1135 $file_buffer = fread($fd, filesize($path)); 1136 $file_buffer = $this->EncodeString($file_buffer, $encoding); 1137 fclose($fd); 1138 1139 return $file_buffer; 1140 } 1141 1142 /** 1143 * Encodes string to requested format. Returns an 1144 * empty string on failure. 1145 * @access private 1146 * @return string 1147 */ 1148 function EncodeString ($str, $encoding = "base64") { 1149 $encoded = ""; 1150 switch(strtolower($encoding)) { 1151 case "base64": 1152 // chunk_split is found in PHP >= 3.0.6 1153 $encoded = chunk_split(base64_encode($str), 76, $this->LE); 1154 break; 1155 case "7bit": 1156 case "8bit": 1157 $encoded = $this->FixEOL($str); 1158 if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1159 $encoded .= $this->LE; 1160 break; 1161 case "binary": 1162 $encoded = $str; 1163 break; 1164 case "quoted-printable": 1165 $encoded = $this->EncodeQP($str); 1166 break; 1167 default: 1168 $this->SetError($this->Lang("encoding") . $encoding); 1169 break; 1170 } 1171 return $encoded; 1172 } 1173 1174 /** 1175 * Encode a header string to best of Q, B, quoted or none. 1176 * @access private 1177 * @return string 1178 */ 1179 function EncodeHeader ($str, $position = 'text') { 1180 $x = 0; 1181 1182 switch (strtolower($position)) { 1183 case 'phrase': 1184 if (!preg_match('/[\200-\377]/', $str)) { 1185 // Can't use addslashes as we don't know what value has magic_quotes_sybase. 1186 $encoded = addcslashes($str, "\0..\37\177\\\""); 1187 1188 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) 1189 return ($encoded); 1190 else 1191 return ("\"$encoded\""); 1192 } 1193 $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 1194 break; 1195 case 'comment': 1196 $x = preg_match_all('/[()"]/', $str, $matches); 1197 // Fall-through 1198 case 'text': 1199 default: 1200 $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 1201 break; 1202 } 1203 1204 if ($x == 0) 1205 return ($str); 1206 1207 $maxlen = 75 - 7 - strlen($this->CharSet); 1208 // Try to select the encoding which should produce the shortest output 1209 if (strlen($str)/3 < $x) { 1210 $encoding = 'B'; 1211 $encoded = base64_encode($str); 1212 $maxlen -= $maxlen % 4; 1213 $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 1214 } else { 1215 $encoding = 'Q'; 1216 $encoded = $this->EncodeQ($str, $position); 1217 $encoded = $this->WrapText($encoded, $maxlen, true); 1218 $encoded = str_replace("=".$this->LE, "\n", trim($encoded)); 1219 } 1220 1221 $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); 1222 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 1223 1224 return $encoded; 1225 } 1226 1227 /** 1228 * Encode string to quoted-printable. 1229 * @access private 1230 * @return string 1231 */ 1232 function EncodeQP ($str) { 1233 $encoded = $this->FixEOL($str); 1234 if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1235 $encoded .= $this->LE; 1236 1237 // Replace every high ascii, control and = characters 1238 $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e', 1239 "'='.sprintf('%02X', ord('\\1'))", $encoded); 1240 // Replace every spaces and tabs when it's the last character on a line 1241 $encoded = preg_replace("/([\011\040])".$this->LE."/e", 1242 "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); 1243 1244 // Maximum line length of 76 characters before CRLF (74 + space + '=') 1245 $encoded = $this->WrapText($encoded, 74, true); 1246 1247 return $encoded; 1248 } 1249 1250 /** 1251 * Encode string to q encoding. 1252 * @access private 1253 * @return string 1254 */ 1255 function EncodeQ ($str, $position = "text") { 1256 // There should not be any EOL in the string 1257 $encoded = preg_replace("[\r\n]", "", $str); 1258 1259 switch (strtolower($position)) { 1260 case "phrase": 1261 $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); 1262 break; 1263 case "comment": 1264 $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); 1265 case "text": 1266 default: 1267 // Replace every high ascii, control =, ? and _ characters 1268 $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', 1269 "'='.sprintf('%02X', ord('\\1'))", $encoded); 1270 break; 1271 } 1272 1273 // Replace every spaces to _ (more readable than =20) 1274 $encoded = str_replace(" ", "_", $encoded); 1275 1276 return $encoded; 1277 } 1278 1279 /** 1280 * Adds a string or binary attachment (non-filesystem) to the list. 1281 * This method can be used to attach ascii or binary data, 1282 * such as a BLOB record from a database. 1283 * @param string $string String attachment data. 1284 * @param string $filename Name of the attachment. 1285 * @param string $encoding File encoding (see $Encoding). 1286 * @param string $type File extension (MIME) type. 1287 * @return void 1288 */ 1289 function AddStringAttachment($string, $filename, $encoding = "base64", 1290 $type = "application/octet-stream") { 1291 // Append to $attachment array 1292 $cur = count($this->attachment); 1293 $this->attachment[$cur][0] = $string; 1294 $this->attachment[$cur][1] = $filename; 1295 $this->attachment[$cur][2] = $filename; 1296 $this->attachment[$cur][3] = $encoding; 1297 $this->attachment[$cur][4] = $type; 1298 $this->attachment[$cur][5] = true; // isString 1299 $this->attachment[$cur][6] = "attachment"; 1300 $this->attachment[$cur][7] = 0; 1301 } 1302 1303 /** 1304 * Adds an embedded attachment. This can include images, sounds, and 1305 * just about any other document. Make sure to set the $type to an 1306 * image type. For JPEG images use "image/jpeg" and for GIF images 1307 * use "image/gif". 1308 * @param string $path Path to the attachment. 1309 * @param string $cid Content ID of the attachment. Use this to identify 1310 * the Id for accessing the image in an HTML form. 1311 * @param string $name Overrides the attachment name. 1312 * @param string $encoding File encoding (see $Encoding). 1313 * @param string $type File extension (MIME) type. 1314 * @return bool 1315 */ 1316 function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64", 1317 $type = "application/octet-stream") { 1318 1319 if(!@is_file($path)) 1320 { 1321 $this->SetError($this->Lang("file_access") . $path); 1322 return false; 1323 } 1324 1325 $filename = basename($path); 1326 if($name == "") 1327 $name = $filename; 1328 1329 // Append to $attachment array 1330 $cur = count($this->attachment); 1331 $this->attachment[$cur][0] = $path; 1332 $this->attachment[$cur][1] = $filename; 1333 $this->attachment[$cur][2] = $name; 1334 $this->attachment[$cur][3] = $encoding; 1335 $this->attachment[$cur][4] = $type; 1336 $this->attachment[$cur][5] = false; // isStringAttachment 1337 $this->attachment[$cur][6] = "inline"; 1338 $this->attachment[$cur][7] = $cid; 1339 1340 return true; 1341 } 1342 1343 /** 1344 * Returns true if an inline attachment is present. 1345 * @access private 1346 * @return bool 1347 */ 1348 function InlineImageExists() { 1349 $result = false; 1350 for($i = 0; $i < count($this->attachment); $i++) 1351 { 1352 if($this->attachment[$i][6] == "inline") 1353 { 1354 $result = true; 1355 break; 1356 } 1357 } 1358 1359 return $result; 1360 } 1361 1362 ///////////////////////////////////////////////// 1363 // MESSAGE RESET METHODS 1364 ///////////////////////////////////////////////// 1365 1366 /** 1367 * Clears all recipients assigned in the TO array. Returns void. 1368 * @return void 1369 */ 1370 function ClearAddresses() { 1371 $this->to = array(); 1372 } 1373 1374 /** 1375 * Clears all recipients assigned in the CC array. Returns void. 1376 * @return void 1377 */ 1378 function ClearCCs() { 1379 $this->cc = array(); 1380 } 1381 1382 /** 1383 * Clears all recipients assigned in the BCC array. Returns void. 1384 * @return void 1385 */ 1386 function ClearBCCs() { 1387 $this->bcc = array(); 1388 } 1389 1390 /** 1391 * Clears all recipients assigned in the ReplyTo array. Returns void. 1392 * @return void 1393 */ 1394 function ClearReplyTos() { 1395 $this->ReplyTo = array(); 1396 } 1397 1398 /** 1399 * Clears all recipients assigned in the TO, CC and BCC 1400 * array. Returns void. 1401 * @return void 1402 */ 1403 function ClearAllRecipients() { 1404 $this->to = array(); 1405 $this->cc = array(); 1406 $this->bcc = array(); 1407 } 1408 1409 /** 1410 * Clears all previously set filesystem, string, and binary 1411 * attachments. Returns void. 1412 * @return void 1413 */ 1414 function ClearAttachments() { 1415 $this->attachment = array(); 1416 } 1417 1418 /** 1419 * Clears all custom headers. Returns void. 1420 * @return void 1421 */ 1422 function ClearCustomHeaders() { 1423 $this->CustomHeader = array(); 1424 } 1425 1426 1427 ///////////////////////////////////////////////// 1428 // MISCELLANEOUS METHODS 1429 ///////////////////////////////////////////////// 1430 1431 /** 1432 * Adds the error message to the error container. 1433 * Returns void. 1434 * @access private 1435 * @return void 1436 */ 1437 function SetError($msg) { 1438 $this->error_count++; 1439 $this->ErrorInfo = $msg; 1440 } 1441 1442 /** 1443 * Returns the proper RFC 822 formatted date. 1444 * @access private 1445 * @return string 1446 */ 1447 function RFCDate() { 1448 $tz = date("Z"); 1449 $tzs = ($tz < 0) ? "-" : "+"; 1450 $tz = abs($tz); 1451 $tz = ($tz/3600)*100 + ($tz%3600)/60; 1452 $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz); 1453 1454 return $result; 1455 } 1456 1457 /** 1458 * Returns the appropriate server variable. Should work with both 1459 * PHP 4.1.0+ as well as older versions. Returns an empty string 1460 * if nothing is found. 1461 * @access private 1462 * @return mixed 1463 */ 1464 function ServerVar($varName) { 1465 global $HTTP_SERVER_VARS; 1466 global $HTTP_ENV_VARS; 1467 1468 if(!isset($_SERVER)) 1469 { 1470 $_SERVER = $HTTP_SERVER_VARS; 1471 if(!isset($_SERVER["REMOTE_ADDR"])) 1472 $_SERVER = $HTTP_ENV_VARS; // must be Apache 1473 } 1474 1475 if(isset($_SERVER[$varName])) 1476 return $_SERVER[$varName]; 1477 else 1478 return ""; 1479 } 1480 1481 /** 1482 * Returns the server hostname or 'localhost.localdomain' if unknown. 1483 * @access private 1484 * @return string 1485 */ 1486 function ServerHostname() { 1487 if ($this->Hostname != "") 1488 $result = $this->Hostname; 1489 elseif ($this->ServerVar('SERVER_NAME') != "") 1490 $result = $this->ServerVar('SERVER_NAME'); 1491 else 1492 $result = "localhost.localdomain"; 1493 1494 return $result; 1495 } 1496 1497 /** 1498 * Returns a message in the appropriate language. 1499 * @access private 1500 * @return string 1501 */ 1502 function Lang($key) { 1503 if(count($this->language) < 1) 1504 $this->SetLanguage("en"); // set the default language 1505 1506 if(isset($this->language[$key])) 1507 return $this->language[$key]; 1508 else 1509 return "Language string failed to load: " . $key; 1510 } 1511 1512 /** 1513 * Returns true if an error occurred. 1514 * @return bool 1515 */ 1516 function IsError() { 1517 return ($this->error_count > 0); 1518 } 1519 1520 /** 1521 * Changes every end of line from CR or LF to CRLF. 1522 * @access private 1523 * @return string 1524 */ 1525 function FixEOL($str) { 1526 $str = str_replace("\r\n", "\n", $str); 1527 $str = str_replace("\r", "\n", $str); 1528 $str = str_replace("\n", $this->LE, $str); 1529 return $str; 1530 } 1531 1532 /** 1533 * Adds a custom header. 1534 * @return void 1535 */ 1536 function AddCustomHeader($custom_header) { 1537 $this->CustomHeader[] = explode(":", $custom_header, 2); 1538 } 1539} 1540 1541?> 1542