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