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