Changeset 27385
- Timestamp:
- 03/03/2014 08:24:31 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-phpmailer.php
r25682 r27385 1 1 <?php 2 /*~ class.phpmailer.php3 .---------------------------------------------------------------------------.4 | Software: PHPMailer - PHP email class |5 | Version: 5.2.4 |6 | Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ |7 | ------------------------------------------------------------------------- |8 | Admin: Jim Jagielski (project admininistrator) |9 | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |10 | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net |11 | : Jim Jagielski (jimjag) jimjag@gmail.com |12 | Founder: Brent R. Matzelle (original founder) |13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. |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 */24 25 2 /** 26 * PHPMailer - PHP email creation and transport class 27 * NOTE: Requires PHP version 5 or later 3 * PHPMailer - PHP email creation and transport class. 4 * PHP Version 5.0.0 5 * Version 5.2.7 28 6 * @package PHPMailer 29 * @author Andy Prevost 30 * @author Marcus Bointon 31 * @author Jim Jagielski 7 * @link https://github.com/PHPMailer/PHPMailer/ 8 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> 9 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 10 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 11 * @author Brent R. Matzelle (original founder) 12 * @copyright 2013 Marcus Bointon 32 13 * @copyright 2010 - 2012 Jim Jagielski 33 14 * @copyright 2004 - 2009 Andy Prevost 34 15 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 16 * @note This program is distributed in the hope that it will be useful - WITHOUT 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 * FITNESS FOR A PARTICULAR PURPOSE. 35 19 */ 36 20 37 if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); 21 if (version_compare(PHP_VERSION, '5.0.0', '<')) { 22 exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n"); 23 } 38 24 39 25 /** 40 * PHP email creation and transport class 26 * PHPMailer - PHP email creation and transport class. 27 * PHP Version 5.0.0 28 * @package PHPMailer 29 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> 30 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 31 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 32 * @author Brent R. Matzelle (original founder) 33 * @copyright 2013 Marcus Bointon 34 * @copyright 2010 - 2012 Jim Jagielski 35 * @copyright 2004 - 2009 Andy Prevost 36 */ 37 class PHPMailer 38 { 39 /** 40 * The PHPMailer Version number. 41 * @type string 42 */ 43 public $Version = '5.2.7'; 44 45 /** 46 * Email priority. 47 * Options: 1 = High, 3 = Normal, 5 = low. 48 * @type int 49 */ 50 public $Priority = 3; 51 52 /** 53 * The character set of the message. 54 * @type string 55 */ 56 public $CharSet = 'iso-8859-1'; 57 58 /** 59 * The MIME Content-type of the message. 60 * @type string 61 */ 62 public $ContentType = 'text/plain'; 63 64 /** 65 * The message encoding. 66 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". 67 * @type string 68 */ 69 public $Encoding = '8bit'; 70 71 /** 72 * Holds the most recent mailer error message. 73 * @type string 74 */ 75 public $ErrorInfo = ''; 76 77 /** 78 * The From email address for the message. 79 * @type string 80 */ 81 public $From = 'root@localhost'; 82 83 /** 84 * The From name of the message. 85 * @type string 86 */ 87 public $FromName = 'Root User'; 88 89 /** 90 * The Sender email (Return-Path) of the message. 91 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 92 * @type string 93 */ 94 public $Sender = ''; 95 96 /** 97 * The Return-Path of the message. 98 * If empty, it will be set to either From or Sender. 99 * @type string 100 */ 101 public $ReturnPath = ''; 102 103 /** 104 * The Subject of the message. 105 * @type string 106 */ 107 public $Subject = ''; 108 109 /** 110 * An HTML or plain text message body. 111 * If HTML then call isHTML(true). 112 * @type string 113 */ 114 public $Body = ''; 115 116 /** 117 * The plain-text message body. 118 * This body can be read by mail clients that do not have HTML email 119 * capability such as mutt & Eudora. 120 * Clients that can read HTML will view the normal Body. 121 * @type string 122 */ 123 public $AltBody = ''; 124 125 /** 126 * An iCal message part body. 127 * Only supported in simple alt or alt_inline message types 128 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator 129 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ 130 * @link http://kigkonsult.se/iCalcreator/ 131 * @type string 132 */ 133 public $Ical = ''; 134 135 /** 136 * The complete compiled MIME message body. 137 * @access protected 138 * @type string 139 */ 140 protected $MIMEBody = ''; 141 142 /** 143 * The complete compiled MIME message headers. 144 * @type string 145 * @access protected 146 */ 147 protected $MIMEHeader = ''; 148 149 /** 150 * Extra headers that createHeader() doesn't fold in. 151 * @type string 152 * @access protected 153 */ 154 protected $mailHeader = ''; 155 156 /** 157 * Word-wrap the message body to this number of chars. 158 * @type int 159 */ 160 public $WordWrap = 0; 161 162 /** 163 * Which method to use to send mail. 164 * Options: "mail", "sendmail", or "smtp". 165 * @type string 166 */ 167 public $Mailer = 'mail'; 168 169 /** 170 * The path to the sendmail program. 171 * @type string 172 */ 173 public $Sendmail = '/usr/sbin/sendmail'; 174 175 /** 176 * Whether mail() uses a fully sendmail-compatible MTA. 177 * One which supports sendmail's "-oi -f" options. 178 * @type bool 179 */ 180 public $UseSendmailOptions = true; 181 182 /** 183 * Path to PHPMailer plugins. 184 * Useful if the SMTP class is not in the PHP include path. 185 * @type string 186 * @deprecated Should not be needed now there is an autoloader. 187 */ 188 public $PluginDir = ''; 189 190 /** 191 * The email address that a reading confirmation should be sent to. 192 * @type string 193 */ 194 public $ConfirmReadingTo = ''; 195 196 /** 197 * The hostname to use in Message-Id and Received headers 198 * and as default HELO string. 199 * If empty, the value returned 200 * by SERVER_NAME is used or 'localhost.localdomain'. 201 * @type string 202 */ 203 public $Hostname = ''; 204 205 /** 206 * An ID to be used in the Message-Id header. 207 * If empty, a unique id will be generated. 208 * @type string 209 */ 210 public $MessageID = ''; 211 212 /** 213 * The message Date to be used in the Date header. 214 * If empty, the current date will be added. 215 * @type string 216 */ 217 public $MessageDate = ''; 218 219 /** 220 * SMTP hosts. 221 * Either a single hostname or multiple semicolon-delimited hostnames. 222 * You can also specify a different port 223 * for each host by using this format: [hostname:port] 224 * (e.g. "smtp1.example.com:25;smtp2.example.com"). 225 * Hosts will be tried in order. 226 * @type string 227 */ 228 public $Host = 'localhost'; 229 230 /** 231 * The default SMTP server port. 232 * @type int 233 * @Todo Why is this needed when the SMTP class takes care of it? 234 */ 235 public $Port = 25; 236 237 /** 238 * The SMTP HELO of the message. 239 * Default is $Hostname. 240 * @type string 241 * @see PHPMailer::$Hostname 242 */ 243 public $Helo = ''; 244 245 /** 246 * The secure connection prefix. 247 * Options: "", "ssl" or "tls" 248 * @type string 249 */ 250 public $SMTPSecure = ''; 251 252 /** 253 * Whether to use SMTP authentication. 254 * Uses the Username and Password properties. 255 * @type bool 256 * @see PHPMailer::$Username 257 * @see PHPMailer::$Password 258 */ 259 public $SMTPAuth = false; 260 261 /** 262 * SMTP username. 263 * @type string 264 */ 265 public $Username = ''; 266 267 /** 268 * SMTP password. 269 * @type string 270 */ 271 public $Password = ''; 272 273 /** 274 * SMTP auth type. 275 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 276 * @type string 277 */ 278 public $AuthType = ''; 279 280 /** 281 * SMTP realm. 282 * Used for NTLM auth 283 * @type string 284 */ 285 public $Realm = ''; 286 287 /** 288 * SMTP workstation. 289 * Used for NTLM auth 290 * @type string 291 */ 292 public $Workstation = ''; 293 294 /** 295 * The SMTP server timeout in seconds. 296 * @type int 297 */ 298 public $Timeout = 10; 299 300 /** 301 * SMTP class debug output mode. 302 * Options: 0 = off, 1 = commands, 2 = commands and data 303 * @type int 304 * @see SMTP::$do_debug 305 */ 306 public $SMTPDebug = 0; 307 308 /** 309 * The function/method to use for debugging output. 310 * Options: "echo" or "error_log" 311 * @type string 312 * @see SMTP::$Debugoutput 313 */ 314 public $Debugoutput = "echo"; 315 316 /** 317 * Whether to keep SMTP connection open after each message. 318 * If this is set to true then to close the connection 319 * requires an explicit call to smtpClose(). 320 * @type bool 321 */ 322 public $SMTPKeepAlive = false; 323 324 /** 325 * Whether to split multiple to addresses into multiple messages 326 * or send them all in one message. 327 * @type bool 328 */ 329 public $SingleTo = false; 330 331 /** 332 * Storage for addresses when SingleTo is enabled. 333 * @type array 334 * @todo This should really not be public 335 */ 336 public $SingleToArray = array(); 337 338 /** 339 * Whether to generate VERP addresses on send. 340 * Only applicable when sending via SMTP. 341 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 342 * @type bool 343 */ 344 public $do_verp = false; 345 346 /** 347 * Whether to allow sending messages with an empty body. 348 * @type bool 349 */ 350 public $AllowEmpty = false; 351 352 /** 353 * The default line ending. 354 * @note The default remains "\n". We force CRLF where we know 355 * it must be used via self::CRLF. 356 * @type string 357 */ 358 public $LE = "\n"; 359 360 /** 361 * DKIM selector. 362 * @type string 363 */ 364 public $DKIM_selector = ''; 365 366 /** 367 * DKIM Identity. 368 * Usually the email address used as the source of the email 369 * @type string 370 */ 371 public $DKIM_identity = ''; 372 373 /** 374 * DKIM passphrase. 375 * Used if your key is encrypted. 376 * @type string 377 */ 378 public $DKIM_passphrase = ''; 379 380 /** 381 * DKIM signing domain name. 382 * @example 'example.com' 383 * @type string 384 */ 385 public $DKIM_domain = ''; 386 387 /** 388 * DKIM private key file path. 389 * @type string 390 */ 391 public $DKIM_private = ''; 392 393 /** 394 * Callback Action function name. 395 * 396 * The function that handles the result of the send email action. 397 * It is called out by send() for each email sent. 398 * 399 * Value can be: 400 * - 'function_name' for function names 401 * - 'Class::Method' for static method calls 402 * - array($object, 'Method') for calling methods on $object 403 * See http://php.net/is_callable manual page for more details. 404 * 405 * Parameters: 406 * bool $result result of the send action 407 * string $to email address of the recipient 408 * string $cc cc email addresses 409 * string $bcc bcc email addresses 410 * string $subject the subject 411 * string $body the email body 412 * string $from email address of sender 413 * 414 * @type string 415 */ 416 public $action_function = ''; 417 418 /** 419 * What to use in the X-Mailer header. 420 * Options: null for default, whitespace for none, or a string to use 421 * @type string 422 */ 423 public $XMailer = ''; 424 425 /** 426 * An instance of the SMTP sender class. 427 * @type SMTP 428 * @access protected 429 */ 430 protected $smtp = null; 431 432 /** 433 * The array of 'to' addresses. 434 * @type array 435 * @access protected 436 */ 437 protected $to = array(); 438 439 /** 440 * The array of 'cc' addresses. 441 * @type array 442 * @access protected 443 */ 444 protected $cc = array(); 445 446 /** 447 * The array of 'bcc' addresses. 448 * @type array 449 * @access protected 450 */ 451 protected $bcc = array(); 452 453 /** 454 * The array of reply-to names and addresses. 455 * @type array 456 * @access protected 457 */ 458 protected $ReplyTo = array(); 459 460 /** 461 * An array of all kinds of addresses. 462 * Includes all of $to, $cc, $bcc, $replyto 463 * @type array 464 * @access protected 465 */ 466 protected $all_recipients = array(); 467 468 /** 469 * The array of attachments. 470 * @type array 471 * @access protected 472 */ 473 protected $attachment = array(); 474 475 /** 476 * The array of custom headers. 477 * @type array 478 * @access protected 479 */ 480 protected $CustomHeader = array(); 481 482 /** 483 * The most recent Message-ID (including angular brackets). 484 * @type string 485 * @access protected 486 */ 487 protected $lastMessageID = ''; 488 489 /** 490 * The message's MIME type. 491 * @type string 492 * @access protected 493 */ 494 protected $message_type = ''; 495 496 /** 497 * The array of MIME boundary strings. 498 * @type array 499 * @access protected 500 */ 501 protected $boundary = array(); 502 503 /** 504 * The array of available languages. 505 * @type array 506 * @access protected 507 */ 508 protected $language = array(); 509 510 /** 511 * The number of errors encountered. 512 * @type integer 513 * @access protected 514 */ 515 protected $error_count = 0; 516 517 /** 518 * The S/MIME certificate file path. 519 * @type string 520 * @access protected 521 */ 522 protected $sign_cert_file = ''; 523 524 /** 525 * The S/MIME key file path. 526 * @type string 527 * @access protected 528 */ 529 protected $sign_key_file = ''; 530 531 /** 532 * The S/MIME password for the key. 533 * Used only if the key is encrypted. 534 * @type string 535 * @access protected 536 */ 537 protected $sign_key_pass = ''; 538 539 /** 540 * Whether to throw exceptions for errors. 541 * @type bool 542 * @access protected 543 */ 544 protected $exceptions = false; 545 546 /** 547 * Error severity: message only, continue processing 548 */ 549 const STOP_MESSAGE = 0; 550 551 /** 552 * Error severity: message, likely ok to continue processing 553 */ 554 const STOP_CONTINUE = 1; 555 556 /** 557 * Error severity: message, plus full stop, critical error reached 558 */ 559 const STOP_CRITICAL = 2; 560 561 /** 562 * SMTP RFC standard line ending 563 */ 564 const CRLF = "\r\n"; 565 566 /** 567 * Constructor 568 * @param bool $exceptions Should we throw external exceptions? 569 */ 570 public function __construct($exceptions = false) 571 { 572 $this->exceptions = ($exceptions == true); 573 } 574 575 /** 576 * Destructor. 577 */ 578 public function __destruct() 579 { 580 if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely 581 $this->smtpClose(); 582 } 583 } 584 585 /** 586 * Call mail() in a safe_mode-aware fashion. 587 * Also, unless sendmail_path points to sendmail (or something that 588 * claims to be sendmail), don't pass params (not a perfect fix, 589 * but it will do) 590 * @param string $to To 591 * @param string $subject Subject 592 * @param string $body Message Body 593 * @param string $header Additional Header(s) 594 * @param string $params Params 595 * @access private 596 * @return bool 597 */ 598 private function mailPassthru($to, $subject, $body, $header, $params) 599 { 600 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { 601 $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header); 602 } else { 603 $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params); 604 } 605 return $rt; 606 } 607 608 /** 609 * Output debugging info via user-defined method. 610 * Only if debug output is enabled. 611 * @see PHPMailer::$Debugoutput 612 * @see PHPMailer::$SMTPDebug 613 * @param string $str 614 */ 615 protected function edebug($str) 616 { 617 if (!$this->SMTPDebug) { 618 return; 619 } 620 switch ($this->Debugoutput) { 621 case 'error_log': 622 error_log($str); 623 break; 624 case 'html': 625 //Cleans up output a bit for a better looking display that's HTML-safe 626 echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n"; 627 break; 628 case 'echo': 629 default: 630 //Just echoes exactly what was received 631 echo $str; 632 } 633 } 634 635 /** 636 * Sets message type to HTML or plain. 637 * @param bool $ishtml True for HTML mode. 638 * @return void 639 */ 640 public function isHTML($ishtml = true) 641 { 642 if ($ishtml) { 643 $this->ContentType = 'text/html'; 644 } else { 645 $this->ContentType = 'text/plain'; 646 } 647 } 648 649 /** 650 * Send messages using SMTP. 651 * @return void 652 */ 653 public function isSMTP() 654 { 655 $this->Mailer = 'smtp'; 656 } 657 658 /** 659 * Send messages using PHP's mail() function. 660 * @return void 661 */ 662 public function isMail() 663 { 664 $this->Mailer = 'mail'; 665 } 666 667 /** 668 * Send messages using $Sendmail. 669 * @return void 670 */ 671 public function isSendmail() 672 { 673 if (!stristr(ini_get('sendmail_path'), 'sendmail')) { 674 $this->Sendmail = '/var/qmail/bin/sendmail'; 675 } 676 $this->Mailer = 'sendmail'; 677 } 678 679 /** 680 * Send messages using qmail. 681 * @return void 682 */ 683 public function isQmail() 684 { 685 if (stristr(ini_get('sendmail_path'), 'qmail')) { 686 $this->Sendmail = '/var/qmail/bin/sendmail'; 687 } 688 $this->Mailer = 'sendmail'; 689 } 690 691 /** 692 * Add a "To" address. 693 * @param string $address 694 * @param string $name 695 * @return bool true on success, false if address already used 696 */ 697 public function addAddress($address, $name = '') 698 { 699 return $this->addAnAddress('to', $address, $name); 700 } 701 702 /** 703 * Add a "CC" address. 704 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 705 * @param string $address 706 * @param string $name 707 * @return bool true on success, false if address already used 708 */ 709 public function addCC($address, $name = '') 710 { 711 return $this->addAnAddress('cc', $address, $name); 712 } 713 714 /** 715 * Add a "BCC" address. 716 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 717 * @param string $address 718 * @param string $name 719 * @return bool true on success, false if address already used 720 */ 721 public function addBCC($address, $name = '') 722 { 723 return $this->addAnAddress('bcc', $address, $name); 724 } 725 726 /** 727 * Add a "Reply-to" address. 728 * @param string $address 729 * @param string $name 730 * @return bool 731 */ 732 public function addReplyTo($address, $name = '') 733 { 734 return $this->addAnAddress('Reply-To', $address, $name); 735 } 736 737 /** 738 * Add an address to one of the recipient arrays. 739 * Addresses that have been added already return false, but do not throw exceptions 740 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' 741 * @param string $address The email address to send to 742 * @param string $name 743 * @throws phpmailerException 744 * @return bool true on success, false if address already used or invalid in some way 745 * @access protected 746 */ 747 protected function addAnAddress($kind, $address, $name = '') 748 { 749 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { 750 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind); 751 if ($this->exceptions) { 752 throw new phpmailerException('Invalid recipient array: ' . $kind); 753 } 754 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind); 755 return false; 756 } 757 $address = trim($address); 758 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 759 if (!$this->validateAddress($address)) { 760 $this->setError($this->lang('invalid_address') . ': ' . $address); 761 if ($this->exceptions) { 762 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); 763 } 764 $this->edebug($this->lang('invalid_address') . ': ' . $address); 765 return false; 766 } 767 if ($kind != 'Reply-To') { 768 if (!isset($this->all_recipients[strtolower($address)])) { 769 array_push($this->$kind, array($address, $name)); 770 $this->all_recipients[strtolower($address)] = true; 771 return true; 772 } 773 } else { 774 if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 775 $this->ReplyTo[strtolower($address)] = array($address, $name); 776 return true; 777 } 778 } 779 return false; 780 } 781 782 /** 783 * Set the From and FromName properties. 784 * @param string $address 785 * @param string $name 786 * @param bool $auto Whether to also set the Sender address, defaults to true 787 * @throws phpmailerException 788 * @return bool 789 */ 790 public function setFrom($address, $name = '', $auto = true) 791 { 792 $address = trim($address); 793 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 794 if (!$this->validateAddress($address)) { 795 $this->setError($this->lang('invalid_address') . ': ' . $address); 796 if ($this->exceptions) { 797 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); 798 } 799 $this->edebug($this->lang('invalid_address') . ': ' . $address); 800 return false; 801 } 802 $this->From = $address; 803 $this->FromName = $name; 804 if ($auto) { 805 if (empty($this->Sender)) { 806 $this->Sender = $address; 807 } 808 } 809 return true; 810 } 811 812 /** 813 * Return the Message-ID header of the last email. 814 * Technically this is the value from the last time the headers were created, 815 * but it's also the message ID of the last sent message except in 816 * pathological cases. 817 * @return string 818 */ 819 public function getLastMessageID() 820 { 821 return $this->lastMessageID; 822 } 823 824 /** 825 * Check that a string looks like an email address. 826 * @param string $address The email address to check 827 * @param string $patternselect A selector for the validation pattern to use : 828 * 'auto' - pick best one automatically; 829 * 'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; 830 * 'pcre' - use old PCRE implementation; 831 * 'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough; 832 * 'noregex' - super fast, really dumb. 833 * @return bool 834 * @static 835 * @access public 836 */ 837 public static function validateAddress($address, $patternselect = 'auto') 838 { 839 if ($patternselect == 'auto') { 840 if (defined( 841 'PCRE_VERSION' 842 ) 843 ) { //Check this instead of extension_loaded so it works when that function is disabled 844 if (version_compare(PCRE_VERSION, '8.0') >= 0) { 845 $patternselect = 'pcre8'; 846 } else { 847 $patternselect = 'pcre'; 848 } 849 } else { 850 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension 851 if (version_compare(PHP_VERSION, '5.2.0') >= 0) { 852 $patternselect = 'php'; 853 } else { 854 $patternselect = 'noregex'; 855 } 856 } 857 } 858 switch ($patternselect) { 859 case 'pcre8': 860 /** 861 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is 862 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to 863 * not allow a@b type valid addresses :( 864 * @link http://squiloople.com/2009/12/20/email-address-validation/ 865 * @copyright 2009-2010 Michael Rushton 866 * Feel free to use and redistribute this code. But please keep this copyright notice. 867 */ 868 return (bool)preg_match( 869 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . 870 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . 871 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . 872 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . 873 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . 874 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . 875 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . 876 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . 877 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', 878 $address 879 ); 880 break; 881 case 'pcre': 882 //An older regex that doesn't need a recent PCRE 883 return (bool)preg_match( 884 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . 885 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . 886 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . 887 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . 888 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . 889 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . 890 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . 891 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . 892 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . 893 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', 894 $address 895 ); 896 break; 897 case 'php': 898 default: 899 return (bool)filter_var($address, FILTER_VALIDATE_EMAIL); 900 break; 901 case 'noregex': 902 //No PCRE! Do something _very_ approximate! 903 //Check the address is 3 chars or longer and contains an @ that's not the first or last char 904 return (strlen($address) >= 3 905 and strpos($address, '@') >= 1 906 and strpos($address, '@') != strlen($address) - 1); 907 break; 908 } 909 } 910 911 /** 912 * Create a message and send it. 913 * Uses the sending method specified by $Mailer. 914 * Returns false on error - Use the ErrorInfo variable to view description of the error. 915 * @throws phpmailerException 916 * @return bool 917 */ 918 public function send() 919 { 920 try { 921 if (!$this->preSend()) { 922 return false; 923 } 924 return $this->postSend(); 925 } catch (phpmailerException $e) { 926 $this->mailHeader = ''; 927 $this->setError($e->getMessage()); 928 if ($this->exceptions) { 929 throw $e; 930 } 931 return false; 932 } 933 } 934 935 /** 936 * Prepare a message for sending. 937 * @throws phpmailerException 938 * @return bool 939 */ 940 public function preSend() 941 { 942 try { 943 $this->mailHeader = ""; 944 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { 945 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); 946 } 947 948 // Set whether the message is multipart/alternative 949 if (!empty($this->AltBody)) { 950 $this->ContentType = 'multipart/alternative'; 951 } 952 953 $this->error_count = 0; // reset errors 954 $this->setMessageType(); 955 // Refuse to send an empty message unless we are specifically allowing it 956 if (!$this->AllowEmpty and empty($this->Body)) { 957 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); 958 } 959 960 $this->MIMEHeader = $this->createHeader(); 961 $this->MIMEBody = $this->createBody(); 962 963 // To capture the complete message when using mail(), create 964 // an extra header list which createHeader() doesn't fold in 965 if ($this->Mailer == 'mail') { 966 if (count($this->to) > 0) { 967 $this->mailHeader .= $this->addrAppend("To", $this->to); 968 } else { 969 $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;"); 970 } 971 $this->mailHeader .= $this->headerLine( 972 'Subject', 973 $this->encodeHeader($this->secureHeader(trim($this->Subject))) 974 ); 975 } 976 977 // Sign with DKIM if enabled 978 if (!empty($this->DKIM_domain) 979 && !empty($this->DKIM_private) 980 && !empty($this->DKIM_selector) 981 && !empty($this->DKIM_domain) 982 && file_exists($this->DKIM_private)) { 983 $header_dkim = $this->DKIM_Add( 984 $this->MIMEHeader . $this->mailHeader, 985 $this->encodeHeader($this->secureHeader($this->Subject)), 986 $this->MIMEBody 987 ); 988 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . 989 str_replace("\r\n", "\n", $header_dkim) . self::CRLF; 990 } 991 return true; 992 993 } catch (phpmailerException $e) { 994 $this->setError($e->getMessage()); 995 if ($this->exceptions) { 996 throw $e; 997 } 998 return false; 999 } 1000 } 1001 1002 /** 1003 * Actually send a message. 1004 * Send the email via the selected mechanism 1005 * @throws phpmailerException 1006 * @return bool 1007 */ 1008 public function postSend() 1009 { 1010 try { 1011 // Choose the mailer and send through it 1012 switch ($this->Mailer) { 1013 case 'sendmail': 1014 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); 1015 case 'smtp': 1016 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); 1017 case 'mail': 1018 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1019 default: 1020 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1021 } 1022 } catch (phpmailerException $e) { 1023 $this->setError($e->getMessage()); 1024 if ($this->exceptions) { 1025 throw $e; 1026 } 1027 $this->edebug($e->getMessage() . "\n"); 1028 } 1029 return false; 1030 } 1031 1032 /** 1033 * Send mail using the $Sendmail program. 1034 * @param string $header The message headers 1035 * @param string $body The message body 1036 * @see PHPMailer::$Sendmail 1037 * @throws phpmailerException 1038 * @access protected 1039 * @return bool 1040 */ 1041 protected function sendmailSend($header, $body) 1042 { 1043 if ($this->Sender != '') { 1044 $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1045 } else { 1046 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 1047 } 1048 if ($this->SingleTo === true) { 1049 foreach ($this->SingleToArray as $val) { 1050 if (!@$mail = popen($sendmail, 'w')) { 1051 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1052 } 1053 fputs($mail, "To: " . $val . "\n"); 1054 fputs($mail, $header); 1055 fputs($mail, $body); 1056 $result = pclose($mail); 1057 // implement call back function if it exists 1058 $isSent = ($result == 0) ? 1 : 0; 1059 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1060 if ($result != 0) { 1061 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1062 } 1063 } 1064 } else { 1065 if (!@$mail = popen($sendmail, 'w')) { 1066 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1067 } 1068 fputs($mail, $header); 1069 fputs($mail, $body); 1070 $result = pclose($mail); 1071 // implement call back function if it exists 1072 $isSent = ($result == 0) ? 1 : 0; 1073 $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1074 if ($result != 0) { 1075 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1076 } 1077 } 1078 return true; 1079 } 1080 1081 /** 1082 * Send mail using the PHP mail() function. 1083 * @param string $header The message headers 1084 * @param string $body The message body 1085 * @link http://www.php.net/manual/en/book.mail.php 1086 * @throws phpmailerException 1087 * @access protected 1088 * @return bool 1089 */ 1090 protected function mailSend($header, $body) 1091 { 1092 $toArr = array(); 1093 foreach ($this->to as $t) { 1094 $toArr[] = $this->addrFormat($t); 1095 } 1096 $to = implode(', ', $toArr); 1097 1098 if (empty($this->Sender)) { 1099 $params = " "; 1100 } else { 1101 $params = sprintf("-f%s", $this->Sender); 1102 } 1103 if ($this->Sender != '' and !ini_get('safe_mode')) { 1104 $old_from = ini_get('sendmail_from'); 1105 ini_set('sendmail_from', $this->Sender); 1106 } 1107 $rt = false; 1108 if ($this->SingleTo === true && count($toArr) > 1) { 1109 foreach ($toArr as $val) { 1110 $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params); 1111 // implement call back function if it exists 1112 $isSent = ($rt == 1) ? 1 : 0; 1113 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1114 } 1115 } else { 1116 $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params); 1117 // implement call back function if it exists 1118 $isSent = ($rt == 1) ? 1 : 0; 1119 $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1120 } 1121 if (isset($old_from)) { 1122 ini_set('sendmail_from', $old_from); 1123 } 1124 if (!$rt) { 1125 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); 1126 } 1127 return true; 1128 } 1129 1130 /** 1131 * Get an instance to use for SMTP operations. 1132 * Override this function to load your own SMTP implementation 1133 * @return SMTP 1134 */ 1135 public function getSMTPInstance() 1136 { 1137 if (!is_object($this->smtp)) { 1138 require_once 'class-smtp.php'; 1139 $this->smtp = new SMTP; 1140 } 1141 return $this->smtp; 1142 } 1143 1144 /** 1145 * Send mail via SMTP. 1146 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. 1147 * Uses the PHPMailerSMTP class by default. 1148 * @see PHPMailer::getSMTPInstance() to use a different class. 1149 * @param string $header The message headers 1150 * @param string $body The message body 1151 * @throws phpmailerException 1152 * @uses SMTP 1153 * @access protected 1154 * @return bool 1155 */ 1156 protected function smtpSend($header, $body) 1157 { 1158 $bad_rcpt = array(); 1159 1160 if (!$this->smtpConnect()) { 1161 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1162 } 1163 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; 1164 if (!$this->smtp->mail($smtp_from)) { 1165 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); 1166 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); 1167 } 1168 1169 // Attempt to send attach all recipients 1170 foreach ($this->to as $to) { 1171 if (!$this->smtp->recipient($to[0])) { 1172 $bad_rcpt[] = $to[0]; 1173 $isSent = 0; 1174 } else { 1175 $isSent = 1; 1176 } 1177 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From); 1178 } 1179 foreach ($this->cc as $cc) { 1180 if (!$this->smtp->recipient($cc[0])) { 1181 $bad_rcpt[] = $cc[0]; 1182 $isSent = 0; 1183 } else { 1184 $isSent = 1; 1185 } 1186 $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From); 1187 } 1188 foreach ($this->bcc as $bcc) { 1189 if (!$this->smtp->recipient($bcc[0])) { 1190 $bad_rcpt[] = $bcc[0]; 1191 $isSent = 0; 1192 } else { 1193 $isSent = 1; 1194 } 1195 $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From); 1196 } 1197 1198 if (count($bad_rcpt) > 0) { //Create error message for any bad addresses 1199 throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt)); 1200 } 1201 if (!$this->smtp->data($header . $body)) { 1202 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); 1203 } 1204 if ($this->SMTPKeepAlive == true) { 1205 $this->smtp->reset(); 1206 } else { 1207 $this->smtp->quit(); 1208 $this->smtp->close(); 1209 } 1210 return true; 1211 } 1212 1213 /** 1214 * Initiate a connection to an SMTP server. 1215 * Returns false if the operation failed. 1216 * @param array $options An array of options compatible with stream_context_create() 1217 * @uses SMTP 1218 * @access public 1219 * @throws phpmailerException 1220 * @return bool 1221 */ 1222 public function smtpConnect($options = array()) 1223 { 1224 if (is_null($this->smtp)) { 1225 $this->smtp = $this->getSMTPInstance(); 1226 } 1227 1228 //Already connected? 1229 if ($this->smtp->connected()) { 1230 return true; 1231 } 1232 1233 $this->smtp->setTimeout($this->Timeout); 1234 $this->smtp->setDebugLevel($this->SMTPDebug); 1235 $this->smtp->setDebugOutput($this->Debugoutput); 1236 $this->smtp->setVerp($this->do_verp); 1237 $tls = ($this->SMTPSecure == 'tls'); 1238 $ssl = ($this->SMTPSecure == 'ssl'); 1239 $hosts = explode(';', $this->Host); 1240 $lastexception = null; 1241 1242 foreach ($hosts as $hostentry) { 1243 $hostinfo = array(); 1244 $host = $hostentry; 1245 $port = $this->Port; 1246 if (preg_match( 1247 '/^(.+):([0-9]+)$/', 1248 $hostentry, 1249 $hostinfo 1250 ) 1251 ) { //If $hostentry contains 'address:port', override default 1252 $host = $hostinfo[1]; 1253 $port = $hostinfo[2]; 1254 } 1255 if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) { 1256 try { 1257 if ($this->Helo) { 1258 $hello = $this->Helo; 1259 } else { 1260 $hello = $this->serverHostname(); 1261 } 1262 $this->smtp->hello($hello); 1263 1264 if ($tls) { 1265 if (!$this->smtp->startTLS()) { 1266 throw new phpmailerException($this->lang('connect_host')); 1267 } 1268 //We must resend HELO after tls negotiation 1269 $this->smtp->hello($hello); 1270 } 1271 if ($this->SMTPAuth) { 1272 if (!$this->smtp->authenticate( 1273 $this->Username, 1274 $this->Password, 1275 $this->AuthType, 1276 $this->Realm, 1277 $this->Workstation 1278 ) 1279 ) { 1280 throw new phpmailerException($this->lang('authenticate')); 1281 } 1282 } 1283 return true; 1284 } catch (phpmailerException $e) { 1285 $lastexception = $e; 1286 //We must have connected, but then failed TLS or Auth, so close connection nicely 1287 $this->smtp->quit(); 1288 } 1289 } 1290 } 1291 //If we get here, all connection attempts have failed, so close connection hard 1292 $this->smtp->close(); 1293 //As we've caught all exceptions, just report whatever the last one was 1294 if ($this->exceptions and !is_null($lastexception)) { 1295 throw $lastexception; 1296 } 1297 return false; 1298 } 1299 1300 /** 1301 * Close the active SMTP session if one exists. 1302 * @return void 1303 */ 1304 public function smtpClose() 1305 { 1306 if ($this->smtp !== null) { 1307 if ($this->smtp->connected()) { 1308 $this->smtp->quit(); 1309 $this->smtp->close(); 1310 } 1311 } 1312 } 1313 1314 /** 1315 * Set the language for error messages. 1316 * Returns false if it cannot load the language file. 1317 * The default language is English. 1318 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 1319 * @param string $lang_path Path to the language file directory, with trailing separator (slash) 1320 * @return bool 1321 * @access public 1322 */ 1323 public function setLanguage($langcode = 'en', $lang_path = 'language/') 1324 { 1325 //Define full set of translatable strings 1326 $PHPMAILER_LANG = array( 1327 'authenticate' => 'SMTP Error: Could not authenticate.', 1328 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 1329 'data_not_accepted' => 'SMTP Error: data not accepted.', 1330 'empty_message' => 'Message body empty', 1331 'encoding' => 'Unknown encoding: ', 1332 'execute' => 'Could not execute: ', 1333 'file_access' => 'Could not access file: ', 1334 'file_open' => 'File Error: Could not open file: ', 1335 'from_failed' => 'The following From address failed: ', 1336 'instantiate' => 'Could not instantiate mail function.', 1337 'invalid_address' => 'Invalid address', 1338 'mailer_not_supported' => ' mailer is not supported.', 1339 'provide_address' => 'You must provide at least one recipient email address.', 1340 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 1341 'signing' => 'Signing Error: ', 1342 'smtp_connect_failed' => 'SMTP connect() failed.', 1343 'smtp_error' => 'SMTP server error: ', 1344 'variable_set' => 'Cannot set or reset variable: ' 1345 ); 1346 //Overwrite language-specific strings. 1347 //This way we'll never have missing translations - no more "language string failed to load"! 1348 $l = true; 1349 if ($langcode != 'en') { //There is no English translation file 1350 $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1351 } 1352 $this->language = $PHPMAILER_LANG; 1353 return ($l == true); //Returns false if language not found 1354 } 1355 1356 /** 1357 * Get the array of strings for the current language. 1358 * @return array 1359 */ 1360 public function getTranslations() 1361 { 1362 return $this->language; 1363 } 1364 1365 /** 1366 * Create recipient headers. 1367 * @access public 1368 * @param string $type 1369 * @param array $addr An array of recipient, 1370 * where each recipient is a 2-element indexed array with element 0 containing an address 1371 * and element 1 containing a name, like: 1372 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) 1373 * @return string 1374 */ 1375 public function addrAppend($type, $addr) 1376 { 1377 $addresses = array(); 1378 foreach ($addr as $a) { 1379 $addresses[] = $this->addrFormat($a); 1380 } 1381 return $type . ': ' . implode(', ', $addresses) . $this->LE; 1382 } 1383 1384 /** 1385 * Format an address for use in a message header. 1386 * @access public 1387 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name 1388 * like array('joe@example.com', 'Joe User') 1389 * @return string 1390 */ 1391 public function addrFormat($addr) 1392 { 1393 if (empty($addr[1])) { // No name provided 1394 return $this->secureHeader($addr[0]); 1395 } else { 1396 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader( 1397 $addr[0] 1398 ) . ">"; 1399 } 1400 } 1401 1402 /** 1403 * Word-wrap message. 1404 * For use with mailers that do not automatically perform wrapping 1405 * and for quoted-printable encoded messages. 1406 * Original written by philippe. 1407 * @param string $message The message to wrap 1408 * @param integer $length The line length to wrap to 1409 * @param bool $qp_mode Whether to run in Quoted-Printable mode 1410 * @access public 1411 * @return string 1412 */ 1413 public function wrapText($message, $length, $qp_mode = false) 1414 { 1415 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 1416 // If utf-8 encoding is used, we will need to make sure we don't 1417 // split multibyte characters when we wrap 1418 $is_utf8 = (strtolower($this->CharSet) == "utf-8"); 1419 $lelen = strlen($this->LE); 1420 $crlflen = strlen(self::CRLF); 1421 1422 $message = $this->fixEOL($message); 1423 if (substr($message, -$lelen) == $this->LE) { 1424 $message = substr($message, 0, -$lelen); 1425 } 1426 1427 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE 1428 $message = ''; 1429 for ($i = 0; $i < count($line); $i++) { 1430 $line_part = explode(' ', $line[$i]); 1431 $buf = ''; 1432 for ($e = 0; $e < count($line_part); $e++) { 1433 $word = $line_part[$e]; 1434 if ($qp_mode and (strlen($word) > $length)) { 1435 $space_left = $length - strlen($buf) - $crlflen; 1436 if ($e != 0) { 1437 if ($space_left > 20) { 1438 $len = $space_left; 1439 if ($is_utf8) { 1440 $len = $this->utf8CharBoundary($word, $len); 1441 } elseif (substr($word, $len - 1, 1) == "=") { 1442 $len--; 1443 } elseif (substr($word, $len - 2, 1) == "=") { 1444 $len -= 2; 1445 } 1446 $part = substr($word, 0, $len); 1447 $word = substr($word, $len); 1448 $buf .= ' ' . $part; 1449 $message .= $buf . sprintf("=%s", self::CRLF); 1450 } else { 1451 $message .= $buf . $soft_break; 1452 } 1453 $buf = ''; 1454 } 1455 while (strlen($word) > 0) { 1456 if ($length <= 0) { 1457 break; 1458 } 1459 $len = $length; 1460 if ($is_utf8) { 1461 $len = $this->utf8CharBoundary($word, $len); 1462 } elseif (substr($word, $len - 1, 1) == "=") { 1463 $len--; 1464 } elseif (substr($word, $len - 2, 1) == "=") { 1465 $len -= 2; 1466 } 1467 $part = substr($word, 0, $len); 1468 $word = substr($word, $len); 1469 1470 if (strlen($word) > 0) { 1471 $message .= $part . sprintf("=%s", self::CRLF); 1472 } else { 1473 $buf = $part; 1474 } 1475 } 1476 } else { 1477 $buf_o = $buf; 1478 $buf .= ($e == 0) ? $word : (' ' . $word); 1479 1480 if (strlen($buf) > $length and $buf_o != '') { 1481 $message .= $buf_o . $soft_break; 1482 $buf = $word; 1483 } 1484 } 1485 } 1486 $message .= $buf . self::CRLF; 1487 } 1488 1489 return $message; 1490 } 1491 1492 /** 1493 * Find the last character boundary prior to $maxLength in a utf-8 1494 * quoted (printable) encoded string. 1495 * Original written by Colin Brown. 1496 * @access public 1497 * @param string $encodedText utf-8 QP text 1498 * @param int $maxLength find last character boundary prior to this length 1499 * @return int 1500 */ 1501 public function utf8CharBoundary($encodedText, $maxLength) 1502 { 1503 $foundSplitPos = false; 1504 $lookBack = 3; 1505 while (!$foundSplitPos) { 1506 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 1507 $encodedCharPos = strpos($lastChunk, "="); 1508 if ($encodedCharPos !== false) { 1509 // Found start of encoded character byte within $lookBack block. 1510 // Check the encoded byte value (the 2 chars after the '=') 1511 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 1512 $dec = hexdec($hex); 1513 if ($dec < 128) { // Single byte character. 1514 // If the encoded char was found at pos 0, it will fit 1515 // otherwise reduce maxLength to start of the encoded char 1516 $maxLength = ($encodedCharPos == 0) ? $maxLength : 1517 $maxLength - ($lookBack - $encodedCharPos); 1518 $foundSplitPos = true; 1519 } elseif ($dec >= 192) { // First byte of a multi byte character 1520 // Reduce maxLength to split at start of character 1521 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1522 $foundSplitPos = true; 1523 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back 1524 $lookBack += 3; 1525 } 1526 } else { 1527 // No encoded character found 1528 $foundSplitPos = true; 1529 } 1530 } 1531 return $maxLength; 1532 } 1533 1534 1535 /** 1536 * Set the body wrapping. 1537 * @access public 1538 * @return void 1539 */ 1540 public function setWordWrap() 1541 { 1542 if ($this->WordWrap < 1) { 1543 return; 1544 } 1545 1546 switch ($this->message_type) { 1547 case 'alt': 1548 case 'alt_inline': 1549 case 'alt_attach': 1550 case 'alt_inline_attach': 1551 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); 1552 break; 1553 default: 1554 $this->Body = $this->wrapText($this->Body, $this->WordWrap); 1555 break; 1556 } 1557 } 1558 1559 /** 1560 * Assemble message headers. 1561 * @access public 1562 * @return string The assembled headers 1563 */ 1564 public function createHeader() 1565 { 1566 $result = ''; 1567 1568 // Set the boundaries 1569 $uniq_id = md5(uniqid(time())); 1570 $this->boundary[1] = 'b1_' . $uniq_id; 1571 $this->boundary[2] = 'b2_' . $uniq_id; 1572 $this->boundary[3] = 'b3_' . $uniq_id; 1573 1574 if ($this->MessageDate == '') { 1575 $result .= $this->headerLine('Date', self::rfcDate()); 1576 } else { 1577 $result .= $this->headerLine('Date', $this->MessageDate); 1578 } 1579 1580 if ($this->ReturnPath) { 1581 $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>'); 1582 } elseif ($this->Sender == '') { 1583 $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>'); 1584 } else { 1585 $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>'); 1586 } 1587 1588 // To be created automatically by mail() 1589 if ($this->Mailer != 'mail') { 1590 if ($this->SingleTo === true) { 1591 foreach ($this->to as $t) { 1592 $this->SingleToArray[] = $this->addrFormat($t); 1593 } 1594 } else { 1595 if (count($this->to) > 0) { 1596 $result .= $this->addrAppend('To', $this->to); 1597 } elseif (count($this->cc) == 0) { 1598 $result .= $this->headerLine('To', 'undisclosed-recipients:;'); 1599 } 1600 } 1601 } 1602 1603 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); 1604 1605 // sendmail and mail() extract Cc from the header before sending 1606 if (count($this->cc) > 0) { 1607 $result .= $this->addrAppend('Cc', $this->cc); 1608 } 1609 1610 // sendmail and mail() extract Bcc from the header before sending 1611 if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { 1612 $result .= $this->addrAppend('Bcc', $this->bcc); 1613 } 1614 1615 if (count($this->ReplyTo) > 0) { 1616 $result .= $this->addrAppend('Reply-To', $this->ReplyTo); 1617 } 1618 1619 // mail() sets the subject itself 1620 if ($this->Mailer != 'mail') { 1621 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 1622 } 1623 1624 if ($this->MessageID != '') { 1625 $this->lastMessageID = $this->MessageID; 1626 } else { 1627 $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname()); 1628 } 1629 $result .= $this->HeaderLine('Message-ID', $this->lastMessageID); 1630 $result .= $this->headerLine('X-Priority', $this->Priority); 1631 if ($this->XMailer == '') { 1632 $result .= $this->headerLine( 1633 'X-Mailer', 1634 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)' 1635 ); 1636 } else { 1637 $myXmailer = trim($this->XMailer); 1638 if ($myXmailer) { 1639 $result .= $this->headerLine('X-Mailer', $myXmailer); 1640 } 1641 } 1642 1643 if ($this->ConfirmReadingTo != '') { 1644 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); 1645 } 1646 1647 // Add custom headers 1648 for ($index = 0; $index < count($this->CustomHeader); $index++) { 1649 $result .= $this->headerLine( 1650 trim($this->CustomHeader[$index][0]), 1651 $this->encodeHeader(trim($this->CustomHeader[$index][1])) 1652 ); 1653 } 1654 if (!$this->sign_key_file) { 1655 $result .= $this->headerLine('MIME-Version', '1.0'); 1656 $result .= $this->getMailMIME(); 1657 } 1658 1659 return $result; 1660 } 1661 1662 /** 1663 * Get the message MIME type headers. 1664 * @access public 1665 * @return string 1666 */ 1667 public function getMailMIME() 1668 { 1669 $result = ''; 1670 switch ($this->message_type) { 1671 case 'inline': 1672 $result .= $this->headerLine('Content-Type', 'multipart/related;'); 1673 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 1674 break; 1675 case 'attach': 1676 case 'inline_attach': 1677 case 'alt_attach': 1678 case 'alt_inline_attach': 1679 $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); 1680 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 1681 break; 1682 case 'alt': 1683 case 'alt_inline': 1684 $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1685 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 1686 break; 1687 default: 1688 // Catches case 'plain': and case '': 1689 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); 1690 break; 1691 } 1692 //RFC1341 part 5 says 7bit is assumed if not specified 1693 if ($this->Encoding != '7bit') { 1694 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 1695 } 1696 1697 if ($this->Mailer != 'mail') { 1698 $result .= $this->LE; 1699 } 1700 1701 return $result; 1702 } 1703 1704 /** 1705 * Returns the whole MIME message. 1706 * Includes complete headers and body. 1707 * Only valid post PreSend(). 1708 * @see PHPMailer::PreSend() 1709 * @access public 1710 * @return string 1711 */ 1712 public function getSentMIMEMessage() 1713 { 1714 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 1715 } 1716 1717 1718 /** 1719 * Assemble the message body. 1720 * Returns an empty string on failure. 1721 * @access public 1722 * @throws phpmailerException 1723 * @return string The assembled message body 1724 */ 1725 public function createBody() 1726 { 1727 $body = ''; 1728 1729 if ($this->sign_key_file) { 1730 $body .= $this->getMailMIME() . $this->LE; 1731 } 1732 1733 $this->setWordWrap(); 1734 1735 switch ($this->message_type) { 1736 case 'inline': 1737 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1738 $body .= $this->encodeString($this->Body, $this->Encoding); 1739 $body .= $this->LE . $this->LE; 1740 $body .= $this->attachAll('inline', $this->boundary[1]); 1741 break; 1742 case 'attach': 1743 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1744 $body .= $this->encodeString($this->Body, $this->Encoding); 1745 $body .= $this->LE . $this->LE; 1746 $body .= $this->attachAll('attachment', $this->boundary[1]); 1747 break; 1748 case 'inline_attach': 1749 $body .= $this->textLine('--' . $this->boundary[1]); 1750 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1751 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1752 $body .= $this->LE; 1753 $body .= $this->getBoundary($this->boundary[2], '', '', ''); 1754 $body .= $this->encodeString($this->Body, $this->Encoding); 1755 $body .= $this->LE . $this->LE; 1756 $body .= $this->attachAll('inline', $this->boundary[2]); 1757 $body .= $this->LE; 1758 $body .= $this->attachAll('attachment', $this->boundary[1]); 1759 break; 1760 case 'alt': 1761 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1762 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1763 $body .= $this->LE . $this->LE; 1764 $body .= $this->getBoundary($this->boundary[1], '', 'text/html', ''); 1765 $body .= $this->encodeString($this->Body, $this->Encoding); 1766 $body .= $this->LE . $this->LE; 1767 if (!empty($this->Ical)) { 1768 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); 1769 $body .= $this->encodeString($this->Ical, $this->Encoding); 1770 $body .= $this->LE . $this->LE; 1771 } 1772 $body .= $this->endBoundary($this->boundary[1]); 1773 break; 1774 case 'alt_inline': 1775 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1776 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1777 $body .= $this->LE . $this->LE; 1778 $body .= $this->textLine('--' . $this->boundary[1]); 1779 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1780 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1781 $body .= $this->LE; 1782 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); 1783 $body .= $this->encodeString($this->Body, $this->Encoding); 1784 $body .= $this->LE . $this->LE; 1785 $body .= $this->attachAll('inline', $this->boundary[2]); 1786 $body .= $this->LE; 1787 $body .= $this->endBoundary($this->boundary[1]); 1788 break; 1789 case 'alt_attach': 1790 $body .= $this->textLine('--' . $this->boundary[1]); 1791 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1792 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1793 $body .= $this->LE; 1794 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); 1795 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1796 $body .= $this->LE . $this->LE; 1797 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); 1798 $body .= $this->encodeString($this->Body, $this->Encoding); 1799 $body .= $this->LE . $this->LE; 1800 $body .= $this->endBoundary($this->boundary[2]); 1801 $body .= $this->LE; 1802 $body .= $this->attachAll('attachment', $this->boundary[1]); 1803 break; 1804 case 'alt_inline_attach': 1805 $body .= $this->textLine('--' . $this->boundary[1]); 1806 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1807 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1808 $body .= $this->LE; 1809 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); 1810 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1811 $body .= $this->LE . $this->LE; 1812 $body .= $this->textLine('--' . $this->boundary[2]); 1813 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1814 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); 1815 $body .= $this->LE; 1816 $body .= $this->getBoundary($this->boundary[3], '', 'text/html', ''); 1817 $body .= $this->encodeString($this->Body, $this->Encoding); 1818 $body .= $this->LE . $this->LE; 1819 $body .= $this->attachAll('inline', $this->boundary[3]); 1820 $body .= $this->LE; 1821 $body .= $this->endBoundary($this->boundary[2]); 1822 $body .= $this->LE; 1823 $body .= $this->attachAll('attachment', $this->boundary[1]); 1824 break; 1825 default: 1826 // catch case 'plain' and case '' 1827 $body .= $this->encodeString($this->Body, $this->Encoding); 1828 break; 1829 } 1830 1831 if ($this->isError()) { 1832 $body = ''; 1833 } elseif ($this->sign_key_file) { 1834 try { 1835 if (!defined('PKCS7_TEXT')) { 1836 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.'); 1837 } 1838 $file = tempnam(sys_get_temp_dir(), 'mail'); 1839 file_put_contents($file, $body); //TODO check this worked 1840 $signed = tempnam(sys_get_temp_dir(), 'signed'); 1841 if (@openssl_pkcs7_sign( 1842 $file, 1843 $signed, 1844 'file://' . realpath($this->sign_cert_file), 1845 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 1846 null 1847 ) 1848 ) { 1849 @unlink($file); 1850 $body = file_get_contents($signed); 1851 @unlink($signed); 1852 } else { 1853 @unlink($file); 1854 @unlink($signed); 1855 throw new phpmailerException($this->lang('signing') . openssl_error_string()); 1856 } 1857 } catch (phpmailerException $e) { 1858 $body = ''; 1859 if ($this->exceptions) { 1860 throw $e; 1861 } 1862 } 1863 } 1864 return $body; 1865 } 1866 1867 /** 1868 * Return the start of a message boundary. 1869 * @access protected 1870 * @param string $boundary 1871 * @param string $charSet 1872 * @param string $contentType 1873 * @param string $encoding 1874 * @return string 1875 */ 1876 protected function getBoundary($boundary, $charSet, $contentType, $encoding) 1877 { 1878 $result = ''; 1879 if ($charSet == '') { 1880 $charSet = $this->CharSet; 1881 } 1882 if ($contentType == '') { 1883 $contentType = $this->ContentType; 1884 } 1885 if ($encoding == '') { 1886 $encoding = $this->Encoding; 1887 } 1888 $result .= $this->textLine('--' . $boundary); 1889 $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); 1890 $result .= $this->LE; 1891 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 1892 $result .= $this->LE; 1893 1894 return $result; 1895 } 1896 1897 /** 1898 * Return the end of a message boundary. 1899 * @access protected 1900 * @param string $boundary 1901 * @return string 1902 */ 1903 protected function endBoundary($boundary) 1904 { 1905 return $this->LE . '--' . $boundary . '--' . $this->LE; 1906 } 1907 1908 /** 1909 * Set the message type. 1910 * PHPMailer only supports some preset message types, 1911 * not arbitrary MIME structures. 1912 * @access protected 1913 * @return void 1914 */ 1915 protected function setMessageType() 1916 { 1917 $this->message_type = array(); 1918 if ($this->alternativeExists()) { 1919 $this->message_type[] = "alt"; 1920 } 1921 if ($this->inlineImageExists()) { 1922 $this->message_type[] = "inline"; 1923 } 1924 if ($this->attachmentExists()) { 1925 $this->message_type[] = "attach"; 1926 } 1927 $this->message_type = implode("_", $this->message_type); 1928 if ($this->message_type == "") { 1929 $this->message_type = "plain"; 1930 } 1931 } 1932 1933 /** 1934 * Format a header line. 1935 * @access public 1936 * @param string $name 1937 * @param string $value 1938 * @return string 1939 */ 1940 public function headerLine($name, $value) 1941 { 1942 return $name . ': ' . $value . $this->LE; 1943 } 1944 1945 /** 1946 * Return a formatted mail line. 1947 * @access public 1948 * @param string $value 1949 * @return string 1950 */ 1951 public function textLine($value) 1952 { 1953 return $value . $this->LE; 1954 } 1955 1956 /** 1957 * Add an attachment from a path on the filesystem. 1958 * Returns false if the file could not be found or read. 1959 * @param string $path Path to the attachment. 1960 * @param string $name Overrides the attachment name. 1961 * @param string $encoding File encoding (see $Encoding). 1962 * @param string $type File extension (MIME) type. 1963 * @param string $disposition Disposition to use 1964 * @throws phpmailerException 1965 * @return bool 1966 */ 1967 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') 1968 { 1969 try { 1970 if (!@is_file($path)) { 1971 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); 1972 } 1973 1974 //If a MIME type is not specified, try to work it out from the file name 1975 if ($type == '') { 1976 $type = self::filenameToType($path); 1977 } 1978 1979 $filename = basename($path); 1980 if ($name == '') { 1981 $name = $filename; 1982 } 1983 1984 $this->attachment[] = array( 1985 0 => $path, 1986 1 => $filename, 1987 2 => $name, 1988 3 => $encoding, 1989 4 => $type, 1990 5 => false, // isStringAttachment 1991 6 => $disposition, 1992 7 => 0 1993 ); 1994 1995 } catch (phpmailerException $e) { 1996 $this->setError($e->getMessage()); 1997 if ($this->exceptions) { 1998 throw $e; 1999 } 2000 $this->edebug($e->getMessage() . "\n"); 2001 return false; 2002 } 2003 return true; 2004 } 2005 2006 /** 2007 * Return the array of attachments. 2008 * @return array 2009 */ 2010 public function getAttachments() 2011 { 2012 return $this->attachment; 2013 } 2014 2015 /** 2016 * Attach all file, string, and binary attachments to the message. 2017 * Returns an empty string on failure. 2018 * @access protected 2019 * @param string $disposition_type 2020 * @param string $boundary 2021 * @return string 2022 */ 2023 protected function attachAll($disposition_type, $boundary) 2024 { 2025 // Return text of body 2026 $mime = array(); 2027 $cidUniq = array(); 2028 $incl = array(); 2029 2030 // Add all attachments 2031 foreach ($this->attachment as $attachment) { 2032 // Check if it is a valid disposition_filter 2033 if ($attachment[6] == $disposition_type) { 2034 // Check for string attachment 2035 $string = ''; 2036 $path = ''; 2037 $bString = $attachment[5]; 2038 if ($bString) { 2039 $string = $attachment[0]; 2040 } else { 2041 $path = $attachment[0]; 2042 } 2043 2044 $inclhash = md5(serialize($attachment)); 2045 if (in_array($inclhash, $incl)) { 2046 continue; 2047 } 2048 $incl[] = $inclhash; 2049 $name = $attachment[2]; 2050 $encoding = $attachment[3]; 2051 $type = $attachment[4]; 2052 $disposition = $attachment[6]; 2053 $cid = $attachment[7]; 2054 if ($disposition == 'inline' && isset($cidUniq[$cid])) { 2055 continue; 2056 } 2057 $cidUniq[$cid] = true; 2058 2059 $mime[] = sprintf("--%s%s", $boundary, $this->LE); 2060 $mime[] = sprintf( 2061 "Content-Type: %s; name=\"%s\"%s", 2062 $type, 2063 $this->encodeHeader($this->secureHeader($name)), 2064 $this->LE 2065 ); 2066 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 2067 2068 if ($disposition == 'inline') { 2069 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); 2070 } 2071 2072 // If a filename contains any of these chars, it should be quoted, 2073 // but not otherwise: RFC2183 & RFC2045 5.1 2074 // Fixes a warning in IETF's msglint MIME checker 2075 // Allow for bypassing the Content-Disposition header totally 2076 if (!(empty($disposition))) { 2077 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { 2078 $mime[] = sprintf( 2079 "Content-Disposition: %s; filename=\"%s\"%s", 2080 $disposition, 2081 $this->encodeHeader($this->secureHeader($name)), 2082 $this->LE . $this->LE 2083 ); 2084 } else { 2085 $mime[] = sprintf( 2086 "Content-Disposition: %s; filename=%s%s", 2087 $disposition, 2088 $this->encodeHeader($this->secureHeader($name)), 2089 $this->LE . $this->LE 2090 ); 2091 } 2092 } else { 2093 $mime[] = $this->LE; 2094 } 2095 2096 // Encode as string attachment 2097 if ($bString) { 2098 $mime[] = $this->encodeString($string, $encoding); 2099 if ($this->isError()) { 2100 return ''; 2101 } 2102 $mime[] = $this->LE . $this->LE; 2103 } else { 2104 $mime[] = $this->encodeFile($path, $encoding); 2105 if ($this->isError()) { 2106 return ''; 2107 } 2108 $mime[] = $this->LE . $this->LE; 2109 } 2110 } 2111 } 2112 2113 $mime[] = sprintf("--%s--%s", $boundary, $this->LE); 2114 2115 return implode("", $mime); 2116 } 2117 2118 /** 2119 * Encode a file attachment in requested format. 2120 * Returns an empty string on failure. 2121 * @param string $path The full path to the file 2122 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2123 * @throws phpmailerException 2124 * @see EncodeFile(encodeFile 2125 * @access protected 2126 * @return string 2127 */ 2128 protected function encodeFile($path, $encoding = 'base64') 2129 { 2130 try { 2131 if (!is_readable($path)) { 2132 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); 2133 } 2134 $magic_quotes = get_magic_quotes_runtime(); 2135 if ($magic_quotes) { 2136 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2137 set_magic_quotes_runtime(0); 2138 } else { 2139 ini_set('magic_quotes_runtime', 0); 2140 } 2141 } 2142 $file_buffer = file_get_contents($path); 2143 $file_buffer = $this->encodeString($file_buffer, $encoding); 2144 if ($magic_quotes) { 2145 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2146 set_magic_quotes_runtime($magic_quotes); 2147 } else { 2148 ini_set('magic_quotes_runtime', $magic_quotes); 2149 } 2150 } 2151 return $file_buffer; 2152 } catch (Exception $e) { 2153 $this->setError($e->getMessage()); 2154 return ''; 2155 } 2156 } 2157 2158 /** 2159 * Encode a string in requested format. 2160 * Returns an empty string on failure. 2161 * @param string $str The text to encode 2162 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2163 * @access public 2164 * @return string 2165 */ 2166 public function encodeString($str, $encoding = 'base64') 2167 { 2168 $encoded = ''; 2169 switch (strtolower($encoding)) { 2170 case 'base64': 2171 $encoded = chunk_split(base64_encode($str), 76, $this->LE); 2172 break; 2173 case '7bit': 2174 case '8bit': 2175 $encoded = $this->fixEOL($str); 2176 //Make sure it ends with a line break 2177 if (substr($encoded, -(strlen($this->LE))) != $this->LE) { 2178 $encoded .= $this->LE; 2179 } 2180 break; 2181 case 'binary': 2182 $encoded = $str; 2183 break; 2184 case 'quoted-printable': 2185 $encoded = $this->encodeQP($str); 2186 break; 2187 default: 2188 $this->setError($this->lang('encoding') . $encoding); 2189 break; 2190 } 2191 return $encoded; 2192 } 2193 2194 /** 2195 * Encode a header string optimally. 2196 * Picks shortest of Q, B, quoted-printable or none. 2197 * @access public 2198 * @param string $str 2199 * @param string $position 2200 * @return string 2201 */ 2202 public function encodeHeader($str, $position = 'text') 2203 { 2204 $x = 0; 2205 switch (strtolower($position)) { 2206 case 'phrase': 2207 if (!preg_match('/[\200-\377]/', $str)) { 2208 // Can't use addslashes as we don't know what value has magic_quotes_sybase 2209 $encoded = addcslashes($str, "\0..\37\177\\\""); 2210 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 2211 return ($encoded); 2212 } else { 2213 return ("\"$encoded\""); 2214 } 2215 } 2216 $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 2217 break; 2218 /** @noinspection PhpMissingBreakStatementInspection */ 2219 case 'comment': 2220 $x = preg_match_all('/[()"]/', $str, $matches); 2221 // Intentional fall-through 2222 case 'text': 2223 default: 2224 $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 2225 break; 2226 } 2227 2228 if ($x == 0) { //There are no chars that need encoding 2229 return ($str); 2230 } 2231 2232 $maxlen = 75 - 7 - strlen($this->CharSet); 2233 // Try to select the encoding which should produce the shortest output 2234 if ($x > strlen($str) / 3) { 2235 //More than a third of the content will need encoding, so B encoding will be most efficient 2236 $encoding = 'B'; 2237 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { 2238 // Use a custom function which correctly encodes and wraps long 2239 // multibyte strings without breaking lines within a character 2240 $encoded = $this->base64EncodeWrapMB($str, "\n"); 2241 } else { 2242 $encoded = base64_encode($str); 2243 $maxlen -= $maxlen % 4; 2244 $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 2245 } 2246 } else { 2247 $encoding = 'Q'; 2248 $encoded = $this->encodeQ($str, $position); 2249 $encoded = $this->wrapText($encoded, $maxlen, true); 2250 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); 2251 } 2252 2253 $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded); 2254 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 2255 2256 return $encoded; 2257 } 2258 2259 /** 2260 * Check if a string contains multi-byte characters. 2261 * @access public 2262 * @param string $str multi-byte text to wrap encode 2263 * @return bool 2264 */ 2265 public function hasMultiBytes($str) 2266 { 2267 if (function_exists('mb_strlen')) { 2268 return (strlen($str) > mb_strlen($str, $this->CharSet)); 2269 } else { // Assume no multibytes (we can't handle without mbstring functions anyway) 2270 return false; 2271 } 2272 } 2273 2274 /** 2275 * Encode and wrap long multibyte strings for mail headers 2276 * without breaking lines within a character. 2277 * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php 2278 * @access public 2279 * @param string $str multi-byte text to wrap encode 2280 * @param string $lf string to use as linefeed/end-of-line 2281 * @return string 2282 */ 2283 public function base64EncodeWrapMB($str, $lf = null) 2284 { 2285 $start = "=?" . $this->CharSet . "?B?"; 2286 $end = "?="; 2287 $encoded = ""; 2288 if ($lf === null) { 2289 $lf = $this->LE; 2290 } 2291 2292 $mb_length = mb_strlen($str, $this->CharSet); 2293 // Each line must have length <= 75, including $start and $end 2294 $length = 75 - strlen($start) - strlen($end); 2295 // Average multi-byte ratio 2296 $ratio = $mb_length / strlen($str); 2297 // Base64 has a 4:3 ratio 2298 $avgLength = floor($length * $ratio * .75); 2299 2300 for ($i = 0; $i < $mb_length; $i += $offset) { 2301 $lookBack = 0; 2302 do { 2303 $offset = $avgLength - $lookBack; 2304 $chunk = mb_substr($str, $i, $offset, $this->CharSet); 2305 $chunk = base64_encode($chunk); 2306 $lookBack++; 2307 } while (strlen($chunk) > $length); 2308 $encoded .= $chunk . $lf; 2309 } 2310 2311 // Chomp the last linefeed 2312 $encoded = substr($encoded, 0, -strlen($lf)); 2313 return $encoded; 2314 } 2315 2316 /** 2317 * Encode a string in quoted-printable format. 2318 * According to RFC2045 section 6.7. 2319 * @access public 2320 * @param string $string The text to encode 2321 * @param integer $line_max Number of chars allowed on a line before wrapping 2322 * @return string 2323 * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 2324 */ 2325 public function encodeQP($string, $line_max = 76) 2326 { 2327 if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) 2328 return quoted_printable_encode($string); 2329 } 2330 //Fall back to a pure PHP implementation 2331 $string = str_replace( 2332 array('%20', '%0D%0A.', '%0D%0A', '%'), 2333 array(' ', "\r\n=2E", "\r\n", '='), 2334 rawurlencode($string) 2335 ); 2336 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); 2337 return $string; 2338 } 2339 2340 /** 2341 * Backward compatibility wrapper for an old QP encoding function that was removed. 2342 * @see PHPMailer::encodeQP() 2343 * @access public 2344 * @param string $string 2345 * @param integer $line_max 2346 * @param bool $space_conv 2347 * @return string 2348 * @deprecated Use encodeQP instead. 2349 */ 2350 public function encodeQPphp( 2351 $string, 2352 $line_max = 76, 2353 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false 2354 ) { 2355 return $this->encodeQP($string, $line_max); 2356 } 2357 2358 /** 2359 * Encode a string using Q encoding. 2360 * @link http://tools.ietf.org/html/rfc2047 2361 * @param string $str the text to encode 2362 * @param string $position Where the text is going to be used, see the RFC for what that means 2363 * @access public 2364 * @return string 2365 */ 2366 public function encodeQ($str, $position = 'text') 2367 { 2368 //There should not be any EOL in the string 2369 $pattern = ''; 2370 $encoded = str_replace(array("\r", "\n"), '', $str); 2371 switch (strtolower($position)) { 2372 case 'phrase': 2373 //RFC 2047 section 5.3 2374 $pattern = '^A-Za-z0-9!*+\/ -'; 2375 break; 2376 /** @noinspection PhpMissingBreakStatementInspection */ 2377 case 'comment': 2378 //RFC 2047 section 5.2 2379 $pattern = '\(\)"'; 2380 //intentional fall-through 2381 //for this reason we build the $pattern without including delimiters and [] 2382 case 'text': 2383 default: 2384 //RFC 2047 section 5.1 2385 //Replace every high ascii, control, =, ? and _ characters 2386 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; 2387 break; 2388 } 2389 $matches = array(); 2390 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { 2391 //If the string contains an '=', make sure it's the first thing we replace 2392 //so as to avoid double-encoding 2393 $s = array_search('=', $matches[0]); 2394 if ($s !== false) { 2395 unset($matches[0][$s]); 2396 array_unshift($matches[0], '='); 2397 } 2398 foreach (array_unique($matches[0]) as $char) { 2399 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); 2400 } 2401 } 2402 //Replace every spaces to _ (more readable than =20) 2403 return str_replace(' ', '_', $encoded); 2404 } 2405 2406 2407 /** 2408 * Add a string or binary attachment (non-filesystem). 2409 * This method can be used to attach ascii or binary data, 2410 * such as a BLOB record from a database. 2411 * @param string $string String attachment data. 2412 * @param string $filename Name of the attachment. 2413 * @param string $encoding File encoding (see $Encoding). 2414 * @param string $type File extension (MIME) type. 2415 * @param string $disposition Disposition to use 2416 * @return void 2417 */ 2418 public function addStringAttachment( 2419 $string, 2420 $filename, 2421 $encoding = 'base64', 2422 $type = '', 2423 $disposition = 'attachment' 2424 ) { 2425 //If a MIME type is not specified, try to work it out from the file name 2426 if ($type == '') { 2427 $type = self::filenameToType($filename); 2428 } 2429 // Append to $attachment array 2430 $this->attachment[] = array( 2431 0 => $string, 2432 1 => $filename, 2433 2 => basename($filename), 2434 3 => $encoding, 2435 4 => $type, 2436 5 => true, // isStringAttachment 2437 6 => $disposition, 2438 7 => 0 2439 ); 2440 } 2441 2442 /** 2443 * Add an embedded (inline) attachment from a file. 2444 * This can include images, sounds, and just about any other document type. 2445 * These differ from 'regular' attachmants in that they are intended to be 2446 * displayed inline with the message, not just attached for download. 2447 * This is used in HTML messages that embed the images 2448 * the HTML refers to using the $cid value. 2449 * @param string $path Path to the attachment. 2450 * @param string $cid Content ID of the attachment; Use this to reference 2451 * the content when using an embedded image in HTML. 2452 * @param string $name Overrides the attachment name. 2453 * @param string $encoding File encoding (see $Encoding). 2454 * @param string $type File MIME type. 2455 * @param string $disposition Disposition to use 2456 * @return bool True on successfully adding an attachment 2457 */ 2458 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') 2459 { 2460 if (!@is_file($path)) { 2461 $this->setError($this->lang('file_access') . $path); 2462 return false; 2463 } 2464 2465 //If a MIME type is not specified, try to work it out from the file name 2466 if ($type == '') { 2467 $type = self::filenameToType($path); 2468 } 2469 2470 $filename = basename($path); 2471 if ($name == '') { 2472 $name = $filename; 2473 } 2474 2475 // Append to $attachment array 2476 $this->attachment[] = array( 2477 0 => $path, 2478 1 => $filename, 2479 2 => $name, 2480 3 => $encoding, 2481 4 => $type, 2482 5 => false, // isStringAttachment 2483 6 => $disposition, 2484 7 => $cid 2485 ); 2486 return true; 2487 } 2488 2489 /** 2490 * Add an embedded stringified attachment. 2491 * This can include images, sounds, and just about any other document type. 2492 * Be sure to set the $type to an image type for images: 2493 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. 2494 * @param string $string The attachment binary data. 2495 * @param string $cid Content ID of the attachment; Use this to reference 2496 * the content when using an embedded image in HTML. 2497 * @param string $name 2498 * @param string $encoding File encoding (see $Encoding). 2499 * @param string $type MIME type. 2500 * @param string $disposition Disposition to use 2501 * @return bool True on successfully adding an attachment 2502 */ 2503 public function addStringEmbeddedImage( 2504 $string, 2505 $cid, 2506 $name = '', 2507 $encoding = 'base64', 2508 $type = '', 2509 $disposition = 'inline' 2510 ) { 2511 //If a MIME type is not specified, try to work it out from the name 2512 if ($type == '') { 2513 $type = self::filenameToType($name); 2514 } 2515 2516 // Append to $attachment array 2517 $this->attachment[] = array( 2518 0 => $string, 2519 1 => $name, 2520 2 => $name, 2521 3 => $encoding, 2522 4 => $type, 2523 5 => true, // isStringAttachment 2524 6 => $disposition, 2525 7 => $cid 2526 ); 2527 return true; 2528 } 2529 2530 /** 2531 * Check if an inline attachment is present. 2532 * @access public 2533 * @return bool 2534 */ 2535 public function inlineImageExists() 2536 { 2537 foreach ($this->attachment as $attachment) { 2538 if ($attachment[6] == 'inline') { 2539 return true; 2540 } 2541 } 2542 return false; 2543 } 2544 2545 /** 2546 * Check if an attachment (non-inline) is present. 2547 * @return bool 2548 */ 2549 public function attachmentExists() 2550 { 2551 foreach ($this->attachment as $attachment) { 2552 if ($attachment[6] == 'attachment') { 2553 return true; 2554 } 2555 } 2556 return false; 2557 } 2558 2559 /** 2560 * Check if this message has an alternative body set. 2561 * @return bool 2562 */ 2563 public function alternativeExists() 2564 { 2565 return !empty($this->AltBody); 2566 } 2567 2568 /** 2569 * Clear all To recipients. 2570 * @return void 2571 */ 2572 public function clearAddresses() 2573 { 2574 foreach ($this->to as $to) { 2575 unset($this->all_recipients[strtolower($to[0])]); 2576 } 2577 $this->to = array(); 2578 } 2579 2580 /** 2581 * Clear all CC recipients. 2582 * @return void 2583 */ 2584 public function clearCCs() 2585 { 2586 foreach ($this->cc as $cc) { 2587 unset($this->all_recipients[strtolower($cc[0])]); 2588 } 2589 $this->cc = array(); 2590 } 2591 2592 /** 2593 * Clear all BCC recipients. 2594 * @return void 2595 */ 2596 public function clearBCCs() 2597 { 2598 foreach ($this->bcc as $bcc) { 2599 unset($this->all_recipients[strtolower($bcc[0])]); 2600 } 2601 $this->bcc = array(); 2602 } 2603 2604 /** 2605 * Clear all ReplyTo recipients. 2606 * @return void 2607 */ 2608 public function clearReplyTos() 2609 { 2610 $this->ReplyTo = array(); 2611 } 2612 2613 /** 2614 * Clear all recipient types. 2615 * @return void 2616 */ 2617 public function clearAllRecipients() 2618 { 2619 $this->to = array(); 2620 $this->cc = array(); 2621 $this->bcc = array(); 2622 $this->all_recipients = array(); 2623 } 2624 2625 /** 2626 * Clear all filesystem, string, and binary attachments. 2627 * @return void 2628 */ 2629 public function clearAttachments() 2630 { 2631 $this->attachment = array(); 2632 } 2633 2634 /** 2635 * Clear all custom headers. 2636 * @return void 2637 */ 2638 public function clearCustomHeaders() 2639 { 2640 $this->CustomHeader = array(); 2641 } 2642 2643 /** 2644 * Add an error message to the error container. 2645 * @access protected 2646 * @param string $msg 2647 * @return void 2648 */ 2649 protected function setError($msg) 2650 { 2651 $this->error_count++; 2652 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { 2653 $lasterror = $this->smtp->getError(); 2654 if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { 2655 $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n"; 2656 } 2657 } 2658 $this->ErrorInfo = $msg; 2659 } 2660 2661 /** 2662 * Return an RFC 822 formatted date. 2663 * @access public 2664 * @return string 2665 * @static 2666 */ 2667 public static function rfcDate() 2668 { 2669 //Set the time zone to whatever the default is to avoid 500 errors 2670 //Will default to UTC if it's not set properly in php.ini 2671 date_default_timezone_set(@date_default_timezone_get()); 2672 return date('D, j M Y H:i:s O'); 2673 } 2674 2675 /** 2676 * Get the server hostname. 2677 * Returns 'localhost.localdomain' if unknown. 2678 * @access protected 2679 * @return string 2680 */ 2681 protected function serverHostname() 2682 { 2683 if (!empty($this->Hostname)) { 2684 $result = $this->Hostname; 2685 } elseif (isset($_SERVER['SERVER_NAME'])) { 2686 $result = $_SERVER['SERVER_NAME']; 2687 } else { 2688 $result = 'localhost.localdomain'; 2689 } 2690 2691 return $result; 2692 } 2693 2694 /** 2695 * Get an error message in the current language. 2696 * @access protected 2697 * @param string $key 2698 * @return string 2699 */ 2700 protected function lang($key) 2701 { 2702 if (count($this->language) < 1) { 2703 $this->setLanguage('en'); // set the default language 2704 } 2705 2706 if (isset($this->language[$key])) { 2707 return $this->language[$key]; 2708 } else { 2709 return 'Language string failed to load: ' . $key; 2710 } 2711 } 2712 2713 /** 2714 * Check if an error occurred. 2715 * @access public 2716 * @return bool True if an error did occur. 2717 */ 2718 public function isError() 2719 { 2720 return ($this->error_count > 0); 2721 } 2722 2723 /** 2724 * Ensure consistent line endings in a string. 2725 * Changes every end of line from CRLF, CR or LF to $this->LE. 2726 * @access public 2727 * @param string $str String to fixEOL 2728 * @return string 2729 */ 2730 public function fixEOL($str) 2731 { 2732 // Normalise to \n 2733 $nstr = str_replace(array("\r\n", "\r"), "\n", $str); 2734 // Now convert LE as needed 2735 if ($this->LE !== "\n") { 2736 $nstr = str_replace("\n", $this->LE, $nstr); 2737 } 2738 return $nstr; 2739 } 2740 2741 /** 2742 * Add a custom header. 2743 * $name value can be overloaded to contain 2744 * both header name and value (name:value) 2745 * @access public 2746 * @param string $name Custom header name 2747 * @param string $value Header value 2748 * @return void 2749 */ 2750 public function addCustomHeader($name, $value = null) 2751 { 2752 if ($value === null) { 2753 // Value passed in as name:value 2754 $this->CustomHeader[] = explode(':', $name, 2); 2755 } else { 2756 $this->CustomHeader[] = array($name, $value); 2757 } 2758 } 2759 2760 /** 2761 * Create a message from an HTML string. 2762 * Automatically makes modifications for inline images and backgrounds 2763 * and creates a plain-text version by converting the HTML. 2764 * Overwrites any existing values in $this->Body and $this->AltBody 2765 * @access public 2766 * @param string $message HTML message string 2767 * @param string $basedir baseline directory for path 2768 * @param bool $advanced Whether to use the advanced HTML to text converter 2769 * @return string $message 2770 */ 2771 public function msgHTML($message, $basedir = '', $advanced = false) 2772 { 2773 preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); 2774 if (isset($images[2])) { 2775 foreach ($images[2] as $i => $url) { 2776 // do not change urls for absolute images (thanks to corvuscorax) 2777 if (!preg_match('#^[A-z]+://#', $url)) { 2778 $filename = basename($url); 2779 $directory = dirname($url); 2780 if ($directory == '.') { 2781 $directory = ''; 2782 } 2783 $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2 2784 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { 2785 $basedir .= '/'; 2786 } 2787 if (strlen($directory) > 1 && substr($directory, -1) != '/') { 2788 $directory .= '/'; 2789 } 2790 if ($this->addEmbeddedImage( 2791 $basedir . $directory . $filename, 2792 $cid, 2793 $filename, 2794 'base64', 2795 self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)) 2796 ) 2797 ) { 2798 $message = preg_replace( 2799 "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui", 2800 $images[1][$i] . "=\"cid:" . $cid . "\"", 2801 $message 2802 ); 2803 } 2804 } 2805 } 2806 } 2807 $this->isHTML(true); 2808 if (empty($this->AltBody)) { 2809 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 2810 } 2811 //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 2812 $this->Body = $this->normalizeBreaks($message); 2813 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 2814 return $this->Body; 2815 } 2816 2817 /** 2818 * Convert an HTML string into plain text. 2819 * @param string $html The HTML text to convert 2820 * @param bool $advanced Should this use the more complex html2text converter or just a simple one? 2821 * @return string 2822 */ 2823 public function html2text($html, $advanced = false) 2824 { 2825 if ($advanced) { 2826 require_once 'extras/class.html2text.php'; 2827 $h = new html2text($html); 2828 return $h->get_text(); 2829 } 2830 return html_entity_decode( 2831 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), 2832 ENT_QUOTES, 2833 $this->CharSet 2834 ); 2835 } 2836 2837 /** 2838 * Get the MIME type for a file extension. 2839 * @param string $ext File extension 2840 * @access public 2841 * @return string MIME type of file. 2842 * @static 2843 */ 2844 public static function _mime_types($ext = '') 2845 { 2846 $mimes = array( 2847 'xl' => 'application/excel', 2848 'hqx' => 'application/mac-binhex40', 2849 'cpt' => 'application/mac-compactpro', 2850 'bin' => 'application/macbinary', 2851 'doc' => 'application/msword', 2852 'word' => 'application/msword', 2853 'class' => 'application/octet-stream', 2854 'dll' => 'application/octet-stream', 2855 'dms' => 'application/octet-stream', 2856 'exe' => 'application/octet-stream', 2857 'lha' => 'application/octet-stream', 2858 'lzh' => 'application/octet-stream', 2859 'psd' => 'application/octet-stream', 2860 'sea' => 'application/octet-stream', 2861 'so' => 'application/octet-stream', 2862 'oda' => 'application/oda', 2863 'pdf' => 'application/pdf', 2864 'ai' => 'application/postscript', 2865 'eps' => 'application/postscript', 2866 'ps' => 'application/postscript', 2867 'smi' => 'application/smil', 2868 'smil' => 'application/smil', 2869 'mif' => 'application/vnd.mif', 2870 'xls' => 'application/vnd.ms-excel', 2871 'ppt' => 'application/vnd.ms-powerpoint', 2872 'wbxml' => 'application/vnd.wap.wbxml', 2873 'wmlc' => 'application/vnd.wap.wmlc', 2874 'dcr' => 'application/x-director', 2875 'dir' => 'application/x-director', 2876 'dxr' => 'application/x-director', 2877 'dvi' => 'application/x-dvi', 2878 'gtar' => 'application/x-gtar', 2879 'php3' => 'application/x-httpd-php', 2880 'php4' => 'application/x-httpd-php', 2881 'php' => 'application/x-httpd-php', 2882 'phtml' => 'application/x-httpd-php', 2883 'phps' => 'application/x-httpd-php-source', 2884 'js' => 'application/x-javascript', 2885 'swf' => 'application/x-shockwave-flash', 2886 'sit' => 'application/x-stuffit', 2887 'tar' => 'application/x-tar', 2888 'tgz' => 'application/x-tar', 2889 'xht' => 'application/xhtml+xml', 2890 'xhtml' => 'application/xhtml+xml', 2891 'zip' => 'application/zip', 2892 'mid' => 'audio/midi', 2893 'midi' => 'audio/midi', 2894 'mp2' => 'audio/mpeg', 2895 'mp3' => 'audio/mpeg', 2896 'mpga' => 'audio/mpeg', 2897 'aif' => 'audio/x-aiff', 2898 'aifc' => 'audio/x-aiff', 2899 'aiff' => 'audio/x-aiff', 2900 'ram' => 'audio/x-pn-realaudio', 2901 'rm' => 'audio/x-pn-realaudio', 2902 'rpm' => 'audio/x-pn-realaudio-plugin', 2903 'ra' => 'audio/x-realaudio', 2904 'wav' => 'audio/x-wav', 2905 'bmp' => 'image/bmp', 2906 'gif' => 'image/gif', 2907 'jpeg' => 'image/jpeg', 2908 'jpe' => 'image/jpeg', 2909 'jpg' => 'image/jpeg', 2910 'png' => 'image/png', 2911 'tiff' => 'image/tiff', 2912 'tif' => 'image/tiff', 2913 'eml' => 'message/rfc822', 2914 'css' => 'text/css', 2915 'html' => 'text/html', 2916 'htm' => 'text/html', 2917 'shtml' => 'text/html', 2918 'log' => 'text/plain', 2919 'text' => 'text/plain', 2920 'txt' => 'text/plain', 2921 'rtx' => 'text/richtext', 2922 'rtf' => 'text/rtf', 2923 'xml' => 'text/xml', 2924 'xsl' => 'text/xml', 2925 'mpeg' => 'video/mpeg', 2926 'mpe' => 'video/mpeg', 2927 'mpg' => 'video/mpeg', 2928 'mov' => 'video/quicktime', 2929 'qt' => 'video/quicktime', 2930 'rv' => 'video/vnd.rn-realvideo', 2931 'avi' => 'video/x-msvideo', 2932 'movie' => 'video/x-sgi-movie' 2933 ); 2934 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream'); 2935 } 2936 2937 /** 2938 * Map a file name to a MIME type. 2939 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. 2940 * @param string $filename A file name or full path, does not need to exist as a file 2941 * @return string 2942 * @static 2943 */ 2944 public static function filenameToType($filename) 2945 { 2946 //In case the path is a URL, strip any query string before getting extension 2947 $qpos = strpos($filename, '?'); 2948 if ($qpos !== false) { 2949 $filename = substr($filename, 0, $qpos); 2950 } 2951 $pathinfo = self::mb_pathinfo($filename); 2952 return self::_mime_types($pathinfo['extension']); 2953 } 2954 2955 /** 2956 * Multi-byte-safe pathinfo replacement. 2957 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. 2958 * Works similarly to the one in PHP >= 5.2.0 2959 * @link http://www.php.net/manual/en/function.pathinfo.php#107461 2960 * @param string $path A filename or path, does not need to exist as a file 2961 * @param integer|string $options Either a PATHINFO_* constant, 2962 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 2963 * @return string|array 2964 * @static 2965 */ 2966 public static function mb_pathinfo($path, $options = null) 2967 { 2968 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); 2969 $m = array(); 2970 preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); 2971 if (array_key_exists(1, $m)) { 2972 $ret['dirname'] = $m[1]; 2973 } 2974 if (array_key_exists(2, $m)) { 2975 $ret['basename'] = $m[2]; 2976 } 2977 if (array_key_exists(5, $m)) { 2978 $ret['extension'] = $m[5]; 2979 } 2980 if (array_key_exists(3, $m)) { 2981 $ret['filename'] = $m[3]; 2982 } 2983 switch ($options) { 2984 case PATHINFO_DIRNAME: 2985 case 'dirname': 2986 return $ret['dirname']; 2987 break; 2988 case PATHINFO_BASENAME: 2989 case 'basename': 2990 return $ret['basename']; 2991 break; 2992 case PATHINFO_EXTENSION: 2993 case 'extension': 2994 return $ret['extension']; 2995 break; 2996 case PATHINFO_FILENAME: 2997 case 'filename': 2998 return $ret['filename']; 2999 break; 3000 default: 3001 return $ret; 3002 } 3003 } 3004 3005 /** 3006 * Set or reset instance properties. 3007 * 3008 * Usage Example: 3009 * $page->set('X-Priority', '3'); 3010 * 3011 * @access public 3012 * @param string $name 3013 * @param mixed $value 3014 * NOTE: will not work with arrays, there are no arrays to set/reset 3015 * @throws phpmailerException 3016 * @return bool 3017 * @todo Should this not be using __set() magic function? 3018 */ 3019 public function set($name, $value = '') 3020 { 3021 try { 3022 if (isset($this->$name)) { 3023 $this->$name = $value; 3024 } else { 3025 throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL); 3026 } 3027 } catch (Exception $e) { 3028 $this->setError($e->getMessage()); 3029 if ($e->getCode() == self::STOP_CRITICAL) { 3030 return false; 3031 } 3032 } 3033 return true; 3034 } 3035 3036 /** 3037 * Strip newlines to prevent header injection. 3038 * @access public 3039 * @param string $str 3040 * @return string 3041 */ 3042 public function secureHeader($str) 3043 { 3044 return trim(str_replace(array("\r", "\n"), '', $str)); 3045 } 3046 3047 /** 3048 * Normalize line breaks in a string. 3049 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. 3050 * Defaults to CRLF (for message bodies) and preserves consecutive breaks. 3051 * @param string $text 3052 * @param string $breaktype What kind of line break to use, defaults to CRLF 3053 * @return string 3054 * @access public 3055 * @static 3056 */ 3057 public static function normalizeBreaks($text, $breaktype = "\r\n") 3058 { 3059 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); 3060 } 3061 3062 3063 /** 3064 * Set the private key file and password for S/MIME signing. 3065 * @access public 3066 * @param string $cert_filename 3067 * @param string $key_filename 3068 * @param string $key_pass Password for private key 3069 */ 3070 public function sign($cert_filename, $key_filename, $key_pass) 3071 { 3072 $this->sign_cert_file = $cert_filename; 3073 $this->sign_key_file = $key_filename; 3074 $this->sign_key_pass = $key_pass; 3075 } 3076 3077 /** 3078 * Quoted-Printable-encode a DKIM header. 3079 * @access public 3080 * @param string $txt 3081 * @return string 3082 */ 3083 public function DKIM_QP($txt) 3084 { 3085 $line = ''; 3086 for ($i = 0; $i < strlen($txt); $i++) { 3087 $ord = ord($txt[$i]); 3088 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { 3089 $line .= $txt[$i]; 3090 } else { 3091 $line .= "=" . sprintf("%02X", $ord); 3092 } 3093 } 3094 return $line; 3095 } 3096 3097 /** 3098 * Generate a DKIM signature. 3099 * @access public 3100 * @param string $s Header 3101 * @throws phpmailerException 3102 * @return string 3103 */ 3104 public function DKIM_Sign($s) 3105 { 3106 if (!defined('PKCS7_TEXT')) { 3107 if ($this->exceptions) { 3108 throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.'); 3109 } 3110 return ''; 3111 } 3112 $privKeyStr = file_get_contents($this->DKIM_private); 3113 if ($this->DKIM_passphrase != '') { 3114 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3115 } else { 3116 $privKey = $privKeyStr; 3117 } 3118 if (openssl_sign($s, $signature, $privKey)) { 3119 return base64_encode($signature); 3120 } 3121 return ''; 3122 } 3123 3124 /** 3125 * Generate a DKIM canonicalization header. 3126 * @access public 3127 * @param string $s Header 3128 * @return string 3129 */ 3130 public function DKIM_HeaderC($s) 3131 { 3132 $s = preg_replace("/\r\n\s+/", " ", $s); 3133 $lines = explode("\r\n", $s); 3134 foreach ($lines as $key => $line) { 3135 list($heading, $value) = explode(":", $line, 2); 3136 $heading = strtolower($heading); 3137 $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces 3138 $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value 3139 } 3140 $s = implode("\r\n", $lines); 3141 return $s; 3142 } 3143 3144 /** 3145 * Generate a DKIM canonicalization body. 3146 * @access public 3147 * @param string $body Message Body 3148 * @return string 3149 */ 3150 public function DKIM_BodyC($body) 3151 { 3152 if ($body == '') { 3153 return "\r\n"; 3154 } 3155 // stabilize line endings 3156 $body = str_replace("\r\n", "\n", $body); 3157 $body = str_replace("\n", "\r\n", $body); 3158 // END stabilize line endings 3159 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { 3160 $body = substr($body, 0, strlen($body) - 2); 3161 } 3162 return $body; 3163 } 3164 3165 /** 3166 * Create the DKIM header and body in a new message header. 3167 * @access public 3168 * @param string $headers_line Header lines 3169 * @param string $subject Subject 3170 * @param string $body Body 3171 * @return string 3172 */ 3173 public function DKIM_Add($headers_line, $subject, $body) 3174 { 3175 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 3176 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 3177 $DKIMquery = 'dns/txt'; // Query method 3178 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 3179 $subject_header = "Subject: $subject"; 3180 $headers = explode($this->LE, $headers_line); 3181 $from_header = ''; 3182 $to_header = ''; 3183 $current = ''; 3184 foreach ($headers as $header) { 3185 if (strpos($header, 'From:') === 0) { 3186 $from_header = $header; 3187 $current = 'from_header'; 3188 } elseif (strpos($header, 'To:') === 0) { 3189 $to_header = $header; 3190 $current = 'to_header'; 3191 } else { 3192 if ($current && strpos($header, ' =?') === 0) { 3193 $current .= $header; 3194 } else { 3195 $current = ''; 3196 } 3197 } 3198 } 3199 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3200 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3201 $subject = str_replace( 3202 '|', 3203 '=7C', 3204 $this->DKIM_QP($subject_header) 3205 ); // Copied header fields (dkim-quoted-printable) 3206 $body = $this->DKIM_BodyC($body); 3207 $DKIMlen = strlen($body); // Length of body 3208 $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body 3209 $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";"; 3210 $dkimhdrs = "DKIM-Signature: v=1; a=" . 3211 $DKIMsignatureType . "; q=" . 3212 $DKIMquery . "; l=" . 3213 $DKIMlen . "; s=" . 3214 $this->DKIM_selector . 3215 ";\r\n" . 3216 "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" . 3217 "\th=From:To:Subject;\r\n" . 3218 "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" . 3219 "\tz=$from\r\n" . 3220 "\t|$to\r\n" . 3221 "\t|$subject;\r\n" . 3222 "\tbh=" . $DKIMb64 . ";\r\n" . 3223 "\tb="; 3224 $toSign = $this->DKIM_HeaderC( 3225 $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs 3226 ); 3227 $signed = $this->DKIM_Sign($toSign); 3228 return $dkimhdrs . $signed . "\r\n"; 3229 } 3230 3231 /** 3232 * Perform a callback. 3233 * @param bool $isSent 3234 * @param string $to 3235 * @param string $cc 3236 * @param string $bcc 3237 * @param string $subject 3238 * @param string $body 3239 * @param string $from 3240 */ 3241 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) 3242 { 3243 if (!empty($this->action_function) && is_callable($this->action_function)) { 3244 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); 3245 call_user_func_array($this->action_function, $params); 3246 } 3247 } 3248 } 3249 3250 /** 3251 * PHPMailer exception handler 41 3252 * @package PHPMailer 42 3253 */ 43 class PHPMailer { 44 45 ///////////////////////////////////////////////// 46 // PROPERTIES, PUBLIC 47 ///////////////////////////////////////////////// 48 49 /** 50 * Email priority (1 = High, 3 = Normal, 5 = low). 51 * @var int 52 */ 53 public $Priority = 3; 54 55 /** 56 * Sets the CharSet of the message. 57 * @var string 58 */ 59 public $CharSet = 'iso-8859-1'; 60 61 /** 62 * Sets the Content-type of the message. 63 * @var string 64 */ 65 public $ContentType = 'text/plain'; 66 67 /** 68 * Sets the Encoding of the message. Options for this are 69 * "8bit", "7bit", "binary", "base64", and "quoted-printable". 70 * @var string 71 */ 72 public $Encoding = '8bit'; 73 74 /** 75 * Holds the most recent mailer error message. 76 * @var string 77 */ 78 public $ErrorInfo = ''; 79 80 /** 81 * Sets the From email address for the message. 82 * @var string 83 */ 84 public $From = 'root@localhost'; 85 86 /** 87 * Sets the From name of the message. 88 * @var string 89 */ 90 public $FromName = 'Root User'; 91 92 /** 93 * Sets the Sender email (Return-Path) of the message. If not empty, 94 * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 95 * @var string 96 */ 97 public $Sender = ''; 98 99 /** 100 * Sets the Return-Path of the message. If empty, it will 101 * be set to either From or Sender. 102 * @var string 103 */ 104 public $ReturnPath = ''; 105 106 /** 107 * Sets the Subject of the message. 108 * @var string 109 */ 110 public $Subject = ''; 111 112 /** 113 * Sets the Body of the message. This can be either an HTML or text body. 114 * If HTML then run IsHTML(true). 115 * @var string 116 */ 117 public $Body = ''; 118 119 /** 120 * Sets the text-only body of the message. This automatically sets the 121 * email to multipart/alternative. This body can be read by mail 122 * clients that do not have HTML email capability such as mutt. Clients 123 * that can read HTML will view the normal Body. 124 * @var string 125 */ 126 public $AltBody = ''; 127 128 /** 129 * Stores the complete compiled MIME message body. 130 * @var string 131 * @access protected 132 */ 133 protected $MIMEBody = ''; 134 135 /** 136 * Stores the complete compiled MIME message headers. 137 * @var string 138 * @access protected 139 */ 140 protected $MIMEHeader = ''; 141 142 /** 143 * Stores the extra header list which CreateHeader() doesn't fold in 144 * @var string 145 * @access protected 146 */ 147 protected $mailHeader = ''; 148 149 /** 150 * Sets word wrapping on the body of the message to a given number of 151 * characters. 152 * @var int 153 */ 154 public $WordWrap = 0; 155 156 /** 157 * Method to send mail: ("mail", "sendmail", or "smtp"). 158 * @var string 159 */ 160 public $Mailer = 'mail'; 161 162 /** 163 * Sets the path of the sendmail program. 164 * @var string 165 */ 166 public $Sendmail = '/usr/sbin/sendmail'; 167 168 /** 169 * Determine if mail() uses a fully sendmail compatible MTA that 170 * supports sendmail's "-oi -f" options 171 * @var boolean 172 */ 173 public $UseSendmailOptions = true; 174 175 /** 176 * Path to PHPMailer plugins. Useful if the SMTP class 177 * is in a different directory than the PHP include path. 178 * @var string 179 */ 180 public $PluginDir = ''; 181 182 /** 183 * Sets the email address that a reading confirmation will be sent. 184 * @var string 185 */ 186 public $ConfirmReadingTo = ''; 187 188 /** 189 * Sets the hostname to use in Message-Id and Received headers 190 * and as default HELO string. If empty, the value returned 191 * by SERVER_NAME is used or 'localhost.localdomain'. 192 * @var string 193 */ 194 public $Hostname = ''; 195 196 /** 197 * Sets the message ID to be used in the Message-Id header. 198 * If empty, a unique id will be generated. 199 * @var string 200 */ 201 public $MessageID = ''; 202 203 /** 204 * Sets the message Date to be used in the Date header. 205 * If empty, the current date will be added. 206 * @var string 207 */ 208 public $MessageDate = ''; 209 210 ///////////////////////////////////////////////// 211 // PROPERTIES FOR SMTP 212 ///////////////////////////////////////////////// 213 214 /** 215 * Sets the SMTP hosts. 216 * 217 * All hosts must be separated by a 218 * semicolon. You can also specify a different port 219 * for each host by using this format: [hostname:port] 220 * (e.g. "smtp1.example.com:25;smtp2.example.com"). 221 * Hosts will be tried in order. 222 * @var string 223 */ 224 public $Host = 'localhost'; 225 226 /** 227 * Sets the default SMTP server port. 228 * @var int 229 */ 230 public $Port = 25; 231 232 /** 233 * Sets the SMTP HELO of the message (Default is $Hostname). 234 * @var string 235 */ 236 public $Helo = ''; 237 238 /** 239 * Sets connection prefix. Options are "", "ssl" or "tls" 240 * @var string 241 */ 242 public $SMTPSecure = ''; 243 244 /** 245 * Sets SMTP authentication. Utilizes the Username and Password variables. 246 * @var bool 247 */ 248 public $SMTPAuth = false; 249 250 /** 251 * Sets SMTP username. 252 * @var string 253 */ 254 public $Username = ''; 255 256 /** 257 * Sets SMTP password. 258 * @var string 259 */ 260 public $Password = ''; 261 262 /** 263 * Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM (default LOGIN) 264 * @var string 265 */ 266 public $AuthType = ''; 267 268 /** 269 * Sets SMTP realm. 270 * @var string 271 */ 272 public $Realm = ''; 273 274 /** 275 * Sets SMTP workstation. 276 * @var string 277 */ 278 public $Workstation = ''; 279 280 /** 281 * Sets the SMTP server timeout in seconds. 282 * This function will not work with the win32 version. 283 * @var int 284 */ 285 public $Timeout = 10; 286 287 /** 288 * Sets SMTP class debugging on or off. 289 * @var bool 290 */ 291 public $SMTPDebug = false; 292 293 /** 294 * Sets the function/method to use for debugging output. 295 * Right now we only honor "echo" or "error_log" 296 * @var string 297 */ 298 public $Debugoutput = "echo"; 299 300 /** 301 * Prevents the SMTP connection from being closed after each mail 302 * sending. If this is set to true then to close the connection 303 * requires an explicit call to SmtpClose(). 304 * @var bool 305 */ 306 public $SMTPKeepAlive = false; 307 308 /** 309 * Provides the ability to have the TO field process individual 310 * emails, instead of sending to entire TO addresses 311 * @var bool 312 */ 313 public $SingleTo = false; 314 315 /** 316 * If SingleTo is true, this provides the array to hold the email addresses 317 * @var bool 318 */ 319 public $SingleToArray = array(); 320 321 /** 322 * Provides the ability to change the generic line ending 323 * NOTE: The default remains '\n'. We force CRLF where we KNOW 324 * it must be used via self::CRLF 325 * @var string 326 */ 327 public $LE = "\n"; 328 329 /** 330 * Used with DKIM Signing 331 * required parameter if DKIM is enabled 332 * 333 * domain selector example domainkey 334 * @var string 335 */ 336 public $DKIM_selector = ''; 337 338 /** 339 * Used with DKIM Signing 340 * required if DKIM is enabled, in format of email address 'you@yourdomain.com' typically used as the source of the email 341 * @var string 342 */ 343 public $DKIM_identity = ''; 344 345 /** 346 * Used with DKIM Signing 347 * optional parameter if your private key requires a passphras 348 * @var string 349 */ 350 public $DKIM_passphrase = ''; 351 352 /** 353 * Used with DKIM Singing 354 * required if DKIM is enabled, in format of email address 'domain.com' 355 * @var string 356 */ 357 public $DKIM_domain = ''; 358 359 /** 360 * Used with DKIM Signing 361 * required if DKIM is enabled, path to private key file 362 * @var string 363 */ 364 public $DKIM_private = ''; 365 366 /** 367 * Callback Action function name. 368 * The function that handles the result of the send email action. 369 * It is called out by Send() for each email sent. 370 * 371 * Value can be: 372 * - 'function_name' for function names 373 * - 'Class::Method' for static method calls 374 * - array($object, 'Method') for calling methods on $object 375 * See http://php.net/is_callable manual page for more details. 376 * 377 * Parameters: 378 * bool $result result of the send action 379 * string $to email address of the recipient 380 * string $cc cc email addresses 381 * string $bcc bcc email addresses 382 * string $subject the subject 383 * string $body the email body 384 * string $from email address of sender 385 * @var string 386 */ 387 public $action_function = ''; //'callbackAction'; 388 389 /** 390 * Sets the PHPMailer Version number 391 * @var string 392 */ 393 public $Version = '5.2.4'; 394 395 /** 396 * What to use in the X-Mailer header 397 * @var string NULL for default, whitespace for None, or actual string to use 398 */ 399 public $XMailer = ''; 400 401 ///////////////////////////////////////////////// 402 // PROPERTIES, PRIVATE AND PROTECTED 403 ///////////////////////////////////////////////// 404 405 /** 406 * @var SMTP An instance of the SMTP sender class 407 * @access protected 408 */ 409 protected $smtp = null; 410 /** 411 * @var array An array of 'to' addresses 412 * @access protected 413 */ 414 protected $to = array(); 415 /** 416 * @var array An array of 'cc' addresses 417 * @access protected 418 */ 419 protected $cc = array(); 420 /** 421 * @var array An array of 'bcc' addresses 422 * @access protected 423 */ 424 protected $bcc = array(); 425 /** 426 * @var array An array of reply-to name and address 427 * @access protected 428 */ 429 protected $ReplyTo = array(); 430 /** 431 * @var array An array of all kinds of addresses: to, cc, bcc, replyto 432 * @access protected 433 */ 434 protected $all_recipients = array(); 435 /** 436 * @var array An array of attachments 437 * @access protected 438 */ 439 protected $attachment = array(); 440 /** 441 * @var array An array of custom headers 442 * @access protected 443 */ 444 protected $CustomHeader = array(); 445 /** 446 * @var string The message's MIME type 447 * @access protected 448 */ 449 protected $message_type = ''; 450 /** 451 * @var array An array of MIME boundary strings 452 * @access protected 453 */ 454 protected $boundary = array(); 455 /** 456 * @var array An array of available languages 457 * @access protected 458 */ 459 protected $language = array(); 460 /** 461 * @var integer The number of errors encountered 462 * @access protected 463 */ 464 protected $error_count = 0; 465 /** 466 * @var string The filename of a DKIM certificate file 467 * @access protected 468 */ 469 protected $sign_cert_file = ''; 470 /** 471 * @var string The filename of a DKIM key file 472 * @access protected 473 */ 474 protected $sign_key_file = ''; 475 /** 476 * @var string The password of a DKIM key 477 * @access protected 478 */ 479 protected $sign_key_pass = ''; 480 /** 481 * @var boolean Whether to throw exceptions for errors 482 * @access protected 483 */ 484 protected $exceptions = false; 485 486 ///////////////////////////////////////////////// 487 // CONSTANTS 488 ///////////////////////////////////////////////// 489 490 const STOP_MESSAGE = 0; // message only, continue processing 491 const STOP_CONTINUE = 1; // message?, likely ok to continue processing 492 const STOP_CRITICAL = 2; // message, plus full stop, critical error reached 493 const CRLF = "\r\n"; // SMTP RFC specified EOL 494 495 ///////////////////////////////////////////////// 496 // METHODS, VARIABLES 497 ///////////////////////////////////////////////// 498 499 /** 500 * Calls actual mail() function, but in a safe_mode aware fashion 501 * Also, unless sendmail_path points to sendmail (or something that 502 * claims to be sendmail), don't pass params (not a perfect fix, 503 * but it will do) 504 * @param string $to To 505 * @param string $subject Subject 506 * @param string $body Message Body 507 * @param string $header Additional Header(s) 508 * @param string $params Params 509 * @access private 510 * @return bool 511 */ 512 private function mail_passthru($to, $subject, $body, $header, $params) { 513 if ( ini_get('safe_mode') || !($this->UseSendmailOptions) ) { 514 $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); 515 } else { 516 $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); 517 } 518 return $rt; 519 } 520 521 /** 522 * Outputs debugging info via user-defined method 523 * @param string $str 524 */ 525 private function edebug($str) { 526 if ($this->Debugoutput == "error_log") { 527 error_log($str); 528 } else { 529 echo $str; 530 } 531 } 532 533 /** 534 * Constructor 535 * @param boolean $exceptions Should we throw external exceptions? 536 */ 537 public function __construct($exceptions = false) { 538 $this->exceptions = ($exceptions == true); 539 } 540 541 /** 542 * Sets message type to HTML. 543 * @param bool $ishtml 544 * @return void 545 */ 546 public function IsHTML($ishtml = true) { 547 if ($ishtml) { 548 $this->ContentType = 'text/html'; 549 } else { 550 $this->ContentType = 'text/plain'; 551 } 552 } 553 554 /** 555 * Sets Mailer to send message using SMTP. 556 * @return void 557 */ 558 public function IsSMTP() { 559 $this->Mailer = 'smtp'; 560 } 561 562 /** 563 * Sets Mailer to send message using PHP mail() function. 564 * @return void 565 */ 566 public function IsMail() { 567 $this->Mailer = 'mail'; 568 } 569 570 /** 571 * Sets Mailer to send message using the $Sendmail program. 572 * @return void 573 */ 574 public function IsSendmail() { 575 if (!stristr(ini_get('sendmail_path'), 'sendmail')) { 576 $this->Sendmail = '/var/qmail/bin/sendmail'; 577 } 578 $this->Mailer = 'sendmail'; 579 } 580 581 /** 582 * Sets Mailer to send message using the qmail MTA. 583 * @return void 584 */ 585 public function IsQmail() { 586 if (stristr(ini_get('sendmail_path'), 'qmail')) { 587 $this->Sendmail = '/var/qmail/bin/sendmail'; 588 } 589 $this->Mailer = 'sendmail'; 590 } 591 592 ///////////////////////////////////////////////// 593 // METHODS, RECIPIENTS 594 ///////////////////////////////////////////////// 595 596 /** 597 * Adds a "To" address. 598 * @param string $address 599 * @param string $name 600 * @return boolean true on success, false if address already used 601 */ 602 public function AddAddress($address, $name = '') { 603 return $this->AddAnAddress('to', $address, $name); 604 } 605 606 /** 607 * Adds a "Cc" address. 608 * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 609 * @param string $address 610 * @param string $name 611 * @return boolean true on success, false if address already used 612 */ 613 public function AddCC($address, $name = '') { 614 return $this->AddAnAddress('cc', $address, $name); 615 } 616 617 /** 618 * Adds a "Bcc" address. 619 * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 620 * @param string $address 621 * @param string $name 622 * @return boolean true on success, false if address already used 623 */ 624 public function AddBCC($address, $name = '') { 625 return $this->AddAnAddress('bcc', $address, $name); 626 } 627 628 /** 629 * Adds a "Reply-to" address. 630 * @param string $address 631 * @param string $name 632 * @return boolean 633 */ 634 public function AddReplyTo($address, $name = '') { 635 return $this->AddAnAddress('Reply-To', $address, $name); 636 } 637 638 /** 639 * Adds an address to one of the recipient arrays 640 * Addresses that have been added already return false, but do not throw exceptions 641 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' 642 * @param string $address The email address to send to 643 * @param string $name 644 * @throws phpmailerException 645 * @return boolean true on success, false if address already used or invalid in some way 646 * @access protected 647 */ 648 protected function AddAnAddress($kind, $address, $name = '') { 649 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { 650 $this->SetError($this->Lang('Invalid recipient array').': '.$kind); 651 if ($this->exceptions) { 652 throw new phpmailerException('Invalid recipient array: ' . $kind); 653 } 654 if ($this->SMTPDebug) { 655 $this->edebug($this->Lang('Invalid recipient array').': '.$kind); 656 } 657 return false; 658 } 659 $address = trim($address); 660 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 661 if (!$this->ValidateAddress($address)) { 662 $this->SetError($this->Lang('invalid_address').': '. $address); 663 if ($this->exceptions) { 664 throw new phpmailerException($this->Lang('invalid_address').': '.$address); 665 } 666 if ($this->SMTPDebug) { 667 $this->edebug($this->Lang('invalid_address').': '.$address); 668 } 669 return false; 670 } 671 if ($kind != 'Reply-To') { 672 if (!isset($this->all_recipients[strtolower($address)])) { 673 array_push($this->$kind, array($address, $name)); 674 $this->all_recipients[strtolower($address)] = true; 675 return true; 676 } 677 } else { 678 if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 679 $this->ReplyTo[strtolower($address)] = array($address, $name); 680 return true; 681 } 682 } 683 return false; 3254 class phpmailerException extends Exception 3255 { 3256 /** 3257 * Prettify error message output 3258 * @return string 3259 */ 3260 public function errorMessage() 3261 { 3262 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; 3263 return $errorMsg; 3264 } 684 3265 } 685 686 /**687 * Set the From and FromName properties688 * @param string $address689 * @param string $name690 * @param int $auto Also set Reply-To and Sender691 * @throws phpmailerException692 * @return boolean693 */694 public function SetFrom($address, $name = '', $auto = 1) {695 $address = trim($address);696 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim697 if (!$this->ValidateAddress($address)) {698 $this->SetError($this->Lang('invalid_address').': '. $address);699 if ($this->exceptions) {700 throw new phpmailerException($this->Lang('invalid_address').': '.$address);701 }702 if ($this->SMTPDebug) {703 $this->edebug($this->Lang('invalid_address').': '.$address);704 }705 return false;706 }707 $this->From = $address;708 $this->FromName = $name;709 if ($auto) {710 if (empty($this->ReplyTo)) {711 $this->AddAnAddress('Reply-To', $address, $name);712 }713 if (empty($this->Sender)) {714 $this->Sender = $address;715 }716 }717 return true;718 }719 720 /**721 * Check that a string looks roughly like an email address should722 * Static so it can be used without instantiation, public so people can overload723 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is724 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to725 * not allow a@b type valid addresses :(726 * Some Versions of PHP break on the regex though, likely due to PCRE, so use727 * the older validation method for those users. (http://php.net/manual/en/pcre.installation.php)728 * @link http://squiloople.com/2009/12/20/email-address-validation/729 * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice.730 * @param string $address The email address to check731 * @return boolean732 * @static733 * @access public734 */735 public static function ValidateAddress($address) {736 if ((defined('PCRE_VERSION')) && (version_compare(PCRE_VERSION, '8.0') >= 0)) {737 return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[ ])+|(?>[ ]*\x0D\x0A)?[ ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){7,})((?6)(?>:(?6)){0,5})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){5,})(?8)?::(?>((?6)(?>:(?6)){0,3}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address);738 } elseif (function_exists('filter_var')) { //Introduced in PHP 5.2739 if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {740 return false;741 } else {742 return true;743 }744 } else {745 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);746 }747 }748 749 /////////////////////////////////////////////////750 // METHODS, MAIL SENDING751 /////////////////////////////////////////////////752 753 /**754 * Creates message and assigns Mailer. If the message is755 * not sent successfully then it returns false. Use the ErrorInfo756 * variable to view description of the error.757 * @throws phpmailerException758 * @return bool759 */760 public function Send() {761 try {762 if(!$this->PreSend()) return false;763 return $this->PostSend();764 } catch (phpmailerException $e) {765 $this->mailHeader = '';766 $this->SetError($e->getMessage());767 if ($this->exceptions) {768 throw $e;769 }770 return false;771 }772 }773 774 /**775 * Prep mail by constructing all message entities776 * @throws phpmailerException777 * @return bool778 */779 public function PreSend() {780 try {781 $this->mailHeader = "";782 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {783 throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);784 }785 786 // Set whether the message is multipart/alternative787 if(!empty($this->AltBody)) {788 $this->ContentType = 'multipart/alternative';789 }790 791 $this->error_count = 0; // reset errors792 $this->SetMessageType();793 //Refuse to send an empty message794 if (empty($this->Body)) {795 throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);796 }797 798 $this->MIMEHeader = $this->CreateHeader();799 $this->MIMEBody = $this->CreateBody();800 801 // To capture the complete message when using mail(), create802 // an extra header list which CreateHeader() doesn't fold in803 if ($this->Mailer == 'mail') {804 if (count($this->to) > 0) {805 $this->mailHeader .= $this->AddrAppend("To", $this->to);806 } else {807 $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;");808 }809 $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject))));810 // if(count($this->cc) > 0) {811 // $this->mailHeader .= $this->AddrAppend("Cc", $this->cc);812 // }813 }814 815 // digitally sign with DKIM if enabled816 if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) {817 $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody);818 $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader;819 }820 821 return true;822 823 } catch (phpmailerException $e) {824 $this->SetError($e->getMessage());825 if ($this->exceptions) {826 throw $e;827 }828 return false;829 }830 }831 832 /**833 * Actual Email transport function834 * Send the email via the selected mechanism835 * @throws phpmailerException836 * @return bool837 */838 public function PostSend() {839 try {840 // Choose the mailer and send through it841 switch($this->Mailer) {842 case 'sendmail':843 return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody);844 case 'smtp':845 return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody);846 case 'mail':847 return $this->MailSend($this->MIMEHeader, $this->MIMEBody);848 default:849 return $this->MailSend($this->MIMEHeader, $this->MIMEBody);850 }851 } catch (phpmailerException $e) {852 $this->SetError($e->getMessage());853 if ($this->exceptions) {854 throw $e;855 }856 if ($this->SMTPDebug) {857 $this->edebug($e->getMessage()."\n");858 }859 }860 return false;861 }862 863 /**864 * Sends mail using the $Sendmail program.865 * @param string $header The message headers866 * @param string $body The message body867 * @throws phpmailerException868 * @access protected869 * @return bool870 */871 protected function SendmailSend($header, $body) {872 if ($this->Sender != '') {873 $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));874 } else {875 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));876 }877 if ($this->SingleTo === true) {878 foreach ($this->SingleToArray as $val) {879 if(!@$mail = popen($sendmail, 'w')) {880 throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);881 }882 fputs($mail, "To: " . $val . "\n");883 fputs($mail, $header);884 fputs($mail, $body);885 $result = pclose($mail);886 // implement call back function if it exists887 $isSent = ($result == 0) ? 1 : 0;888 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body);889 if($result != 0) {890 throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);891 }892 }893 } else {894 if(!@$mail = popen($sendmail, 'w')) {895 throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);896 }897 fputs($mail, $header);898 fputs($mail, $body);899 $result = pclose($mail);900 // implement call back function if it exists901 $isSent = ($result == 0) ? 1 : 0;902 $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body);903 if($result != 0) {904 throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);905 }906 }907 return true;908 }909 910 /**911 * Sends mail using the PHP mail() function.912 * @param string $header The message headers913 * @param string $body The message body914 * @throws phpmailerException915 * @access protected916 * @return bool917 */918 protected function MailSend($header, $body) {919 $toArr = array();920 foreach($this->to as $t) {921 $toArr[] = $this->AddrFormat($t);922 }923 $to = implode(', ', $toArr);924 925 if (empty($this->Sender)) {926 $params = " ";927 } else {928 $params = sprintf("-f%s", $this->Sender);929 }930 if ($this->Sender != '' and !ini_get('safe_mode')) {931 $old_from = ini_get('sendmail_from');932 ini_set('sendmail_from', $this->Sender);933 }934 $rt = false;935 if ($this->SingleTo === true && count($toArr) > 1) {936 foreach ($toArr as $val) {937 $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params);938 // implement call back function if it exists939 $isSent = ($rt == 1) ? 1 : 0;940 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body);941 }942 } else {943 $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params);944 // implement call back function if it exists945 $isSent = ($rt == 1) ? 1 : 0;946 $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body);947 }948 if (isset($old_from)) {949 ini_set('sendmail_from', $old_from);950 }951 if(!$rt) {952 throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);953 }954 return true;955 }956 957 /**958 * Sends mail via SMTP using PhpSMTP959 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.960 * @param string $header The message headers961 * @param string $body The message body962 * @throws phpmailerException963 * @uses SMTP964 * @access protected965 * @return bool966 */967 protected function SmtpSend($header, $body) {968 require_once $this->PluginDir . 'class-smtp.php';969 $bad_rcpt = array();970 971 if(!$this->SmtpConnect()) {972 throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);973 }974 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;975 if(!$this->smtp->Mail($smtp_from)) {976 $this->SetError($this->Lang('from_failed') . $smtp_from . " : " . implode(",",$this->smtp->getError())) ;977 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);978 }979 980 // Attempt to send attach all recipients981 foreach($this->to as $to) {982 if (!$this->smtp->Recipient($to[0])) {983 $bad_rcpt[] = $to[0];984 // implement call back function if it exists985 $isSent = 0;986 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body);987 } else {988 // implement call back function if it exists989 $isSent = 1;990 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body);991 }992 }993 foreach($this->cc as $cc) {994 if (!$this->smtp->Recipient($cc[0])) {995 $bad_rcpt[] = $cc[0];996 // implement call back function if it exists997 $isSent = 0;998 $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body);999 } else {1000 // implement call back function if it exists1001 $isSent = 1;1002 $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body);1003 }1004 }1005 foreach($this->bcc as $bcc) {1006 if (!$this->smtp->Recipient($bcc[0])) {1007 $bad_rcpt[] = $bcc[0];1008 // implement call back function if it exists1009 $isSent = 0;1010 $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body);1011 } else {1012 // implement call back function if it exists1013 $isSent = 1;1014 $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body);1015 }1016 }1017 1018 1019 if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses1020 $badaddresses = implode(', ', $bad_rcpt);1021 throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);1022 }1023 if(!$this->smtp->Data($header . $body)) {1024 throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);1025 }1026 if($this->SMTPKeepAlive == true) {1027 $this->smtp->Reset();1028 } else {1029 $this->smtp->Quit();1030 $this->smtp->Close();1031 }1032 return true;1033 }1034 1035 /**1036 * Initiates a connection to an SMTP server.1037 * Returns false if the operation failed.1038 * @uses SMTP1039 * @access public1040 * @throws phpmailerException1041 * @return bool1042 */1043 public function SmtpConnect() {1044 if(is_null($this->smtp)) {1045 $this->smtp = new SMTP;1046 }1047 1048 $this->smtp->Timeout = $this->Timeout;1049 $this->smtp->do_debug = $this->SMTPDebug;1050 $hosts = explode(';', $this->Host);1051 $index = 0;1052 $connection = $this->smtp->Connected();1053 1054 // Retry while there is no connection1055 try {1056 while($index < count($hosts) && !$connection) {1057 $hostinfo = array();1058 if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {1059 $host = $hostinfo[1];1060 $port = $hostinfo[2];1061 } else {1062 $host = $hosts[$index];1063 $port = $this->Port;1064 }1065 1066 $tls = ($this->SMTPSecure == 'tls');1067 $ssl = ($this->SMTPSecure == 'ssl');1068 1069 if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {1070 1071 $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());1072 $this->smtp->Hello($hello);1073 1074 if ($tls) {1075 if (!$this->smtp->StartTLS()) {1076 throw new phpmailerException($this->Lang('connect_host'));1077 }1078 1079 //We must resend HELO after tls negotiation1080 $this->smtp->Hello($hello);1081 }1082 1083 $connection = true;1084 if ($this->SMTPAuth) {1085 if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType,1086 $this->Realm, $this->Workstation)) {1087 throw new phpmailerException($this->Lang('authenticate'));1088 }1089 }1090 }1091 $index++;1092 if (!$connection) {1093 throw new phpmailerException($this->Lang('connect_host'));1094 }1095 }1096 } catch (phpmailerException $e) {1097 $this->smtp->Reset();1098 if ($this->exceptions) {1099 throw $e;1100 }1101 }1102 return true;1103 }1104 1105 /**1106 * Closes the active SMTP session if one exists.1107 * @return void1108 */1109 public function SmtpClose() {1110 if ($this->smtp !== null) {1111 if($this->smtp->Connected()) {1112 $this->smtp->Quit();1113 $this->smtp->Close();1114 }1115 }1116 }1117 1118 /**1119 * Sets the language for all class error messages.1120 * Returns false if it cannot load the language file. The default language is English.1121 * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")1122 * @param string $lang_path Path to the language file directory1123 * @return bool1124 * @access public1125 */1126 function SetLanguage($langcode = 'en', $lang_path = 'language/') {1127 //Define full set of translatable strings1128 $PHPMAILER_LANG = array(1129 'authenticate' => 'SMTP Error: Could not authenticate.',1130 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',1131 'data_not_accepted' => 'SMTP Error: Data not accepted.',1132 'empty_message' => 'Message body empty',1133 'encoding' => 'Unknown encoding: ',1134 'execute' => 'Could not execute: ',1135 'file_access' => 'Could not access file: ',1136 'file_open' => 'File Error: Could not open file: ',1137 'from_failed' => 'The following From address failed: ',1138 'instantiate' => 'Could not instantiate mail function.',1139 'invalid_address' => 'Invalid address',1140 'mailer_not_supported' => ' mailer is not supported.',1141 'provide_address' => 'You must provide at least one recipient email address.',1142 'recipients_failed' => 'SMTP Error: The following recipients failed: ',1143 'signing' => 'Signing Error: ',1144 'smtp_connect_failed' => 'SMTP Connect() failed.',1145 'smtp_error' => 'SMTP server error: ',1146 'variable_set' => 'Cannot set or reset variable: '1147 );1148 //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!1149 $l = true;1150 if ($langcode != 'en') { //There is no English translation file1151 $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';1152 }1153 $this->language = $PHPMAILER_LANG;1154 return ($l == true); //Returns false if language not found1155 }1156 1157 /**1158 * Return the current array of language strings1159 * @return array1160 */1161 public function GetTranslations() {1162 return $this->language;1163 }1164 1165 /////////////////////////////////////////////////1166 // METHODS, MESSAGE CREATION1167 /////////////////////////////////////////////////1168 1169 /**1170 * Creates recipient headers.1171 * @access public1172 * @param string $type1173 * @param array $addr1174 * @return string1175 */1176 public function AddrAppend($type, $addr) {1177 $addr_str = $type . ': ';1178 $addresses = array();1179 foreach ($addr as $a) {1180 $addresses[] = $this->AddrFormat($a);1181 }1182 $addr_str .= implode(', ', $addresses);1183 $addr_str .= $this->LE;1184 1185 return $addr_str;1186 }1187 1188 /**1189 * Formats an address correctly.1190 * @access public1191 * @param string $addr1192 * @return string1193 */1194 public function AddrFormat($addr) {1195 if (empty($addr[1])) {1196 return $this->SecureHeader($addr[0]);1197 } else {1198 return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";1199 }1200 }1201 1202 /**1203 * Wraps message for use with mailers that do not1204 * automatically perform wrapping and for quoted-printable.1205 * Original written by philippe.1206 * @param string $message The message to wrap1207 * @param integer $length The line length to wrap to1208 * @param boolean $qp_mode Whether to run in Quoted-Printable mode1209 * @access public1210 * @return string1211 */1212 public function WrapText($message, $length, $qp_mode = false) {1213 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;1214 // If utf-8 encoding is used, we will need to make sure we don't1215 // split multibyte characters when we wrap1216 $is_utf8 = (strtolower($this->CharSet) == "utf-8");1217 $lelen = strlen($this->LE);1218 $crlflen = strlen(self::CRLF);1219 1220 $message = $this->FixEOL($message);1221 if (substr($message, -$lelen) == $this->LE) {1222 $message = substr($message, 0, -$lelen);1223 }1224 1225 $line = explode($this->LE, $message); // Magic. We know FixEOL uses $LE1226 $message = '';1227 for ($i = 0 ;$i < count($line); $i++) {1228 $line_part = explode(' ', $line[$i]);1229 $buf = '';1230 for ($e = 0; $e<count($line_part); $e++) {1231 $word = $line_part[$e];1232 if ($qp_mode and (strlen($word) > $length)) {1233 $space_left = $length - strlen($buf) - $crlflen;1234 if ($e != 0) {1235 if ($space_left > 20) {1236 $len = $space_left;1237 if ($is_utf8) {1238 $len = $this->UTF8CharBoundary($word, $len);1239 } elseif (substr($word, $len - 1, 1) == "=") {1240 $len--;1241 } elseif (substr($word, $len - 2, 1) == "=") {1242 $len -= 2;1243 }1244 $part = substr($word, 0, $len);1245 $word = substr($word, $len);1246 $buf .= ' ' . $part;1247 $message .= $buf . sprintf("=%s", self::CRLF);1248 } else {1249 $message .= $buf . $soft_break;1250 }1251 $buf = '';1252 }1253 while (strlen($word) > 0) {1254 $len = $length;1255 if ($is_utf8) {1256 $len = $this->UTF8CharBoundary($word, $len);1257 } elseif (substr($word, $len - 1, 1) == "=") {1258 $len--;1259 } elseif (substr($word, $len - 2, 1) == "=") {1260 $len -= 2;1261 }1262 $part = substr($word, 0, $len);1263 $word = substr($word, $len);1264 1265 if (strlen($word) > 0) {1266 $message .= $part . sprintf("=%s", self::CRLF);1267 } else {1268 $buf = $part;1269 }1270 }1271 } else {1272 $buf_o = $buf;1273 $buf .= ($e == 0) ? $word : (' ' . $word);1274 1275 if (strlen($buf) > $length and $buf_o != '') {1276 $message .= $buf_o . $soft_break;1277 $buf = $word;1278 }1279 }1280 }1281 $message .= $buf . self::CRLF;1282 }1283 1284 return $message;1285 }1286 1287 /**1288 * Finds last character boundary prior to maxLength in a utf-81289 * quoted (printable) encoded string.1290 * Original written by Colin Brown.1291 * @access public1292 * @param string $encodedText utf-8 QP text1293 * @param int $maxLength find last character boundary prior to this length1294 * @return int1295 */1296 public function UTF8CharBoundary($encodedText, $maxLength) {1297 $foundSplitPos = false;1298 $lookBack = 3;1299 while (!$foundSplitPos) {1300 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);1301 $encodedCharPos = strpos($lastChunk, "=");1302 if ($encodedCharPos !== false) {1303 // Found start of encoded character byte within $lookBack block.1304 // Check the encoded byte value (the 2 chars after the '=')1305 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);1306 $dec = hexdec($hex);1307 if ($dec < 128) { // Single byte character.1308 // If the encoded char was found at pos 0, it will fit1309 // otherwise reduce maxLength to start of the encoded char1310 $maxLength = ($encodedCharPos == 0) ? $maxLength :1311 $maxLength - ($lookBack - $encodedCharPos);1312 $foundSplitPos = true;1313 } elseif ($dec >= 192) { // First byte of a multi byte character1314 // Reduce maxLength to split at start of character1315 $maxLength = $maxLength - ($lookBack - $encodedCharPos);1316 $foundSplitPos = true;1317 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back1318 $lookBack += 3;1319 }1320 } else {1321 // No encoded character found1322 $foundSplitPos = true;1323 }1324 }1325 return $maxLength;1326 }1327 1328 1329 /**1330 * Set the body wrapping.1331 * @access public1332 * @return void1333 */1334 public function SetWordWrap() {1335 if($this->WordWrap < 1) {1336 return;1337 }1338 1339 switch($this->message_type) {1340 case 'alt':1341 case 'alt_inline':1342 case 'alt_attach':1343 case 'alt_inline_attach':1344 $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);1345 break;1346 default:1347 $this->Body = $this->WrapText($this->Body, $this->WordWrap);1348 break;1349 }1350 }1351 1352 /**1353 * Assembles message header.1354 * @access public1355 * @return string The assembled header1356 */1357 public function CreateHeader() {1358 $result = '';1359 1360 // Set the boundaries1361 $uniq_id = md5(uniqid(time()));1362 $this->boundary[1] = 'b1_' . $uniq_id;1363 $this->boundary[2] = 'b2_' . $uniq_id;1364 $this->boundary[3] = 'b3_' . $uniq_id;1365 1366 if ($this->MessageDate == '') {1367 $result .= $this->HeaderLine('Date', self::RFCDate());1368 } else {1369 $result .= $this->HeaderLine('Date', $this->MessageDate);1370 }1371 1372 if ($this->ReturnPath) {1373 $result .= $this->HeaderLine('Return-Path', trim($this->ReturnPath));1374 } elseif ($this->Sender == '') {1375 $result .= $this->HeaderLine('Return-Path', trim($this->From));1376 } else {1377 $result .= $this->HeaderLine('Return-Path', trim($this->Sender));1378 }1379 1380 // To be created automatically by mail()1381 if($this->Mailer != 'mail') {1382 if ($this->SingleTo === true) {1383 foreach($this->to as $t) {1384 $this->SingleToArray[] = $this->AddrFormat($t);1385 }1386 } else {1387 if(count($this->to) > 0) {1388 $result .= $this->AddrAppend('To', $this->to);1389 } elseif (count($this->cc) == 0) {1390 $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');1391 }1392 }1393 }1394 1395 $from = array();1396 $from[0][0] = trim($this->From);1397 $from[0][1] = $this->FromName;1398 $result .= $this->AddrAppend('From', $from);1399 1400 // sendmail and mail() extract Cc from the header before sending1401 if(count($this->cc) > 0) {1402 $result .= $this->AddrAppend('Cc', $this->cc);1403 }1404 1405 // sendmail and mail() extract Bcc from the header before sending1406 if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {1407 $result .= $this->AddrAppend('Bcc', $this->bcc);1408 }1409 1410 if(count($this->ReplyTo) > 0) {1411 $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);1412 }1413 1414 // mail() sets the subject itself1415 if($this->Mailer != 'mail') {1416 $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));1417 }1418 1419 if($this->MessageID != '') {1420 $result .= $this->HeaderLine('Message-ID', $this->MessageID);1421 } else {1422 $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);1423 }1424 $result .= $this->HeaderLine('X-Priority', $this->Priority);1425 if ($this->XMailer == '') {1426 $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)');1427 } else {1428 $myXmailer = trim($this->XMailer);1429 if ($myXmailer) {1430 $result .= $this->HeaderLine('X-Mailer', $myXmailer);1431 }1432 }1433 1434 if($this->ConfirmReadingTo != '') {1435 $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');1436 }1437 1438 // Add custom headers1439 for($index = 0; $index < count($this->CustomHeader); $index++) {1440 $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));1441 }1442 if (!$this->sign_key_file) {1443 $result .= $this->HeaderLine('MIME-Version', '1.0');1444 $result .= $this->GetMailMIME();1445 }1446 1447 return $result;1448 }1449 1450 /**1451 * Returns the message MIME.1452 * @access public1453 * @return string1454 */1455 public function GetMailMIME() {1456 $result = '';1457 switch($this->message_type) {1458 case 'inline':1459 $result .= $this->HeaderLine('Content-Type', 'multipart/related;');1460 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1461 break;1462 case 'attach':1463 case 'inline_attach':1464 case 'alt_attach':1465 case 'alt_inline_attach':1466 $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');1467 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1468 break;1469 case 'alt':1470 case 'alt_inline':1471 $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');1472 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');1473 break;1474 default:1475 // Catches case 'plain': and case '':1476 $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);1477 $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet);1478 break;1479 }1480 1481 if($this->Mailer != 'mail') {1482 $result .= $this->LE;1483 }1484 1485 return $result;1486 }1487 1488 /**1489 * Returns the MIME message (headers and body). Only really valid post PreSend().1490 * @access public1491 * @return string1492 */1493 public function GetSentMIMEMessage() {1494 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;1495 }1496 1497 1498 /**1499 * Assembles the message body. Returns an empty string on failure.1500 * @access public1501 * @throws phpmailerException1502 * @return string The assembled message body1503 */1504 public function CreateBody() {1505 $body = '';1506 1507 if ($this->sign_key_file) {1508 $body .= $this->GetMailMIME().$this->LE;1509 }1510 1511 $this->SetWordWrap();1512 1513 switch($this->message_type) {1514 case 'inline':1515 $body .= $this->GetBoundary($this->boundary[1], '', '', '');1516 $body .= $this->EncodeString($this->Body, $this->Encoding);1517 $body .= $this->LE.$this->LE;1518 $body .= $this->AttachAll("inline", $this->boundary[1]);1519 break;1520 case 'attach':1521 $body .= $this->GetBoundary($this->boundary[1], '', '', '');1522 $body .= $this->EncodeString($this->Body, $this->Encoding);1523 $body .= $this->LE.$this->LE;1524 $body .= $this->AttachAll("attachment", $this->boundary[1]);1525 break;1526 case 'inline_attach':1527 $body .= $this->TextLine("--" . $this->boundary[1]);1528 $body .= $this->HeaderLine('Content-Type', 'multipart/related;');1529 $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');1530 $body .= $this->LE;1531 $body .= $this->GetBoundary($this->boundary[2], '', '', '');1532 $body .= $this->EncodeString($this->Body, $this->Encoding);1533 $body .= $this->LE.$this->LE;1534 $body .= $this->AttachAll("inline", $this->boundary[2]);1535 $body .= $this->LE;1536 $body .= $this->AttachAll("attachment", $this->boundary[1]);1537 break;1538 case 'alt':1539 $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');1540 $body .= $this->EncodeString($this->AltBody, $this->Encoding);1541 $body .= $this->LE.$this->LE;1542 $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');1543 $body .= $this->EncodeString($this->Body, $this->Encoding);1544 $body .= $this->LE.$this->LE;1545 $body .= $this->EndBoundary($this->boundary[1]);1546 break;1547 case 'alt_inline':1548 $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');1549 $body .= $this->EncodeString($this->AltBody, $this->Encoding);1550 $body .= $this->LE.$this->LE;1551 $body .= $this->TextLine("--" . $this->boundary[1]);1552 $body .= $this->HeaderLine('Content-Type', 'multipart/related;');1553 $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');1554 $body .= $this->LE;1555 $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '');1556 $body .= $this->EncodeString($this->Body, $this->Encoding);1557 $body .= $this->LE.$this->LE;1558 $body .= $this->AttachAll("inline", $this->boundary[2]);1559 $body .= $this->LE;1560 $body .= $this->EndBoundary($this->boundary[1]);1561 break;1562 case 'alt_attach':1563 $body .= $this->TextLine("--" . $this->boundary[1]);1564 $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');1565 $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');1566 $body .= $this->LE;1567 $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');1568 $body .= $this->EncodeString($this->AltBody, $this->Encoding);1569 $body .= $this->LE.$this->LE;1570 $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '');1571 $body .= $this->EncodeString($this->Body, $this->Encoding);1572 $body .= $this->LE.$this->LE;1573 $body .= $this->EndBoundary($this->boundary[2]);1574 $body .= $this->LE;1575 $body .= $this->AttachAll("attachment", $this->boundary[1]);1576 break;1577 case 'alt_inline_attach':1578 $body .= $this->TextLine("--" . $this->boundary[1]);1579 $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');1580 $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');1581 $body .= $this->LE;1582 $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');1583 $body .= $this->EncodeString($this->AltBody, $this->Encoding);1584 $body .= $this->LE.$this->LE;1585 $body .= $this->TextLine("--" . $this->boundary[2]);1586 $body .= $this->HeaderLine('Content-Type', 'multipart/related;');1587 $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"');1588 $body .= $this->LE;1589 $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', '');1590 $body .= $this->EncodeString($this->Body, $this->Encoding);1591 $body .= $this->LE.$this->LE;1592 $body .= $this->AttachAll("inline", $this->boundary[3]);1593 $body .= $this->LE;1594 $body .= $this->EndBoundary($this->boundary[2]);1595 $body .= $this->LE;1596 $body .= $this->AttachAll("attachment", $this->boundary[1]);1597 break;1598 default:1599 // catch case 'plain' and case ''1600 $body .= $this->EncodeString($this->Body, $this->Encoding);1601 break;1602 }1603 1604 if ($this->IsError()) {1605 $body = '';1606 } elseif ($this->sign_key_file) {1607 try {1608 $file = tempnam('', 'mail');1609 file_put_contents($file, $body); //TODO check this worked1610 $signed = tempnam("", "signed");1611 if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {1612 @unlink($file);1613 $body = file_get_contents($signed);1614 @unlink($signed);1615 } else {1616 @unlink($file);1617 @unlink($signed);1618 throw new phpmailerException($this->Lang("signing").openssl_error_string());1619 }1620 } catch (phpmailerException $e) {1621 $body = '';1622 if ($this->exceptions) {1623 throw $e;1624 }1625 }1626 }1627 1628 return $body;1629 }1630 1631 /**1632 * Returns the start of a message boundary.1633 * @access protected1634 * @param string $boundary1635 * @param string $charSet1636 * @param string $contentType1637 * @param string $encoding1638 * @return string1639 */1640 protected function GetBoundary($boundary, $charSet, $contentType, $encoding) {1641 $result = '';1642 if($charSet == '') {1643 $charSet = $this->CharSet;1644 }1645 if($contentType == '') {1646 $contentType = $this->ContentType;1647 }1648 if($encoding == '') {1649 $encoding = $this->Encoding;1650 }1651 $result .= $this->TextLine('--' . $boundary);1652 $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);1653 $result .= $this->LE;1654 $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);1655 $result .= $this->LE;1656 1657 return $result;1658 }1659 1660 /**1661 * Returns the end of a message boundary.1662 * @access protected1663 * @param string $boundary1664 * @return string1665 */1666 protected function EndBoundary($boundary) {1667 return $this->LE . '--' . $boundary . '--' . $this->LE;1668 }1669 1670 /**1671 * Sets the message type.1672 * @access protected1673 * @return void1674 */1675 protected function SetMessageType() {1676 $this->message_type = array();1677 if($this->AlternativeExists()) $this->message_type[] = "alt";1678 if($this->InlineImageExists()) $this->message_type[] = "inline";1679 if($this->AttachmentExists()) $this->message_type[] = "attach";1680 $this->message_type = implode("_", $this->message_type);1681 if($this->message_type == "") $this->message_type = "plain";1682 }1683 1684 /**1685 * Returns a formatted header line.1686 * @access public1687 * @param string $name1688 * @param string $value1689 * @return string1690 */1691 public function HeaderLine($name, $value) {1692 return $name . ': ' . $value . $this->LE;1693 }1694 1695 /**1696 * Returns a formatted mail line.1697 * @access public1698 * @param string $value1699 * @return string1700 */1701 public function TextLine($value) {1702 return $value . $this->LE;1703 }1704 1705 /////////////////////////////////////////////////1706 // CLASS METHODS, ATTACHMENTS1707 /////////////////////////////////////////////////1708 1709 /**1710 * Adds an attachment from a path on the filesystem.1711 * Returns false if the file could not be found1712 * or accessed.1713 * @param string $path Path to the attachment.1714 * @param string $name Overrides the attachment name.1715 * @param string $encoding File encoding (see $Encoding).1716 * @param string $type File extension (MIME) type.1717 * @throws phpmailerException1718 * @return bool1719 */1720 public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {1721 try {1722 if ( !@is_file($path) ) {1723 throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);1724 }1725 $filename = basename($path);1726 if ( $name == '' ) {1727 $name = $filename;1728 }1729 1730 $this->attachment[] = array(1731 0 => $path,1732 1 => $filename,1733 2 => $name,1734 3 => $encoding,1735 4 => $type,1736 5 => false, // isStringAttachment1737 6 => 'attachment',1738 7 => 01739 );1740 1741 } catch (phpmailerException $e) {1742 $this->SetError($e->getMessage());1743 if ($this->exceptions) {1744 throw $e;1745 }1746 if ($this->SMTPDebug) {1747 $this->edebug($e->getMessage()."\n");1748 }1749 if ( $e->getCode() == self::STOP_CRITICAL ) {1750 return false;1751 }1752 }1753 return true;1754 }1755 1756 /**1757 * Return the current array of attachments1758 * @return array1759 */1760 public function GetAttachments() {1761 return $this->attachment;1762 }1763 1764 /**1765 * Attaches all fs, string, and binary attachments to the message.1766 * Returns an empty string on failure.1767 * @access protected1768 * @param string $disposition_type1769 * @param string $boundary1770 * @return string1771 */1772 protected function AttachAll($disposition_type, $boundary) {1773 // Return text of body1774 $mime = array();1775 $cidUniq = array();1776 $incl = array();1777 1778 // Add all attachments1779 foreach ($this->attachment as $attachment) {1780 // CHECK IF IT IS A VALID DISPOSITION_FILTER1781 if($attachment[6] == $disposition_type) {1782 // Check for string attachment1783 $string = '';1784 $path = '';1785 $bString = $attachment[5];1786 if ($bString) {1787 $string = $attachment[0];1788 } else {1789 $path = $attachment[0];1790 }1791 1792 $inclhash = md5(serialize($attachment));1793 if (in_array($inclhash, $incl)) { continue; }1794 $incl[] = $inclhash;1795 $filename = $attachment[1];1796 $name = $attachment[2];1797 $encoding = $attachment[3];1798 $type = $attachment[4];1799 $disposition = $attachment[6];1800 $cid = $attachment[7];1801 if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }1802 $cidUniq[$cid] = true;1803 1804 $mime[] = sprintf("--%s%s", $boundary, $this->LE);1805 $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);1806 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);1807 1808 if($disposition == 'inline') {1809 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);1810 }1811 1812 $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);1813 1814 // Encode as string attachment1815 if($bString) {1816 $mime[] = $this->EncodeString($string, $encoding);1817 if($this->IsError()) {1818 return '';1819 }1820 $mime[] = $this->LE.$this->LE;1821 } else {1822 $mime[] = $this->EncodeFile($path, $encoding);1823 if($this->IsError()) {1824 return '';1825 }1826 $mime[] = $this->LE.$this->LE;1827 }1828 }1829 }1830 1831 $mime[] = sprintf("--%s--%s", $boundary, $this->LE);1832 1833 return implode("", $mime);1834 }1835 1836 /**1837 * Encodes attachment in requested format.1838 * Returns an empty string on failure.1839 * @param string $path The full path to the file1840 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1841 * @throws phpmailerException1842 * @see EncodeFile()1843 * @access protected1844 * @return string1845 */1846 protected function EncodeFile($path, $encoding = 'base64') {1847 try {1848 if (!is_readable($path)) {1849 throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);1850 }1851 // if (!function_exists('get_magic_quotes')) {1852 // function get_magic_quotes() {1853 // return false;1854 // }1855 // }1856 $magic_quotes = get_magic_quotes_runtime();1857 if ($magic_quotes) {1858 if (version_compare(PHP_VERSION, '5.3.0', '<')) {1859 set_magic_quotes_runtime(0);1860 } else {1861 ini_set('magic_quotes_runtime', 0);1862 }1863 }1864 $file_buffer = file_get_contents($path);1865 $file_buffer = $this->EncodeString($file_buffer, $encoding);1866 if ($magic_quotes) {1867 if (version_compare(PHP_VERSION, '5.3.0', '<')) {1868 set_magic_quotes_runtime($magic_quotes);1869 } else {1870 ini_set('magic_quotes_runtime', $magic_quotes);1871 }1872 }1873 return $file_buffer;1874 } catch (Exception $e) {1875 $this->SetError($e->getMessage());1876 return '';1877 }1878 }1879 1880 /**1881 * Encodes string to requested format.1882 * Returns an empty string on failure.1883 * @param string $str The text to encode1884 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'1885 * @access public1886 * @return string1887 */1888 public function EncodeString($str, $encoding = 'base64') {1889 $encoded = '';1890 switch(strtolower($encoding)) {1891 case 'base64':1892 $encoded = chunk_split(base64_encode($str), 76, $this->LE);1893 break;1894 case '7bit':1895 case '8bit':1896 $encoded = $this->FixEOL($str);1897 //Make sure it ends with a line break1898 if (substr($encoded, -(strlen($this->LE))) != $this->LE)1899 $encoded .= $this->LE;1900 break;1901 case 'binary':1902 $encoded = $str;1903 break;1904 case 'quoted-printable':1905 $encoded = $this->EncodeQP($str);1906 break;1907 default:1908 $this->SetError($this->Lang('encoding') . $encoding);1909 break;1910 }1911 return $encoded;1912 }1913 1914 /**1915 * Encode a header string to best (shortest) of Q, B, quoted or none.1916 * @access public1917 * @param string $str1918 * @param string $position1919 * @return string1920 */1921 public function EncodeHeader($str, $position = 'text') {1922 $x = 0;1923 1924 switch (strtolower($position)) {1925 case 'phrase':1926 if (!preg_match('/[\200-\377]/', $str)) {1927 // Can't use addslashes as we don't know what value has magic_quotes_sybase1928 $encoded = addcslashes($str, "\0..\37\177\\\"");1929 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {1930 return ($encoded);1931 } else {1932 return ("\"$encoded\"");1933 }1934 }1935 $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);1936 break;1937 case 'comment':1938 $x = preg_match_all('/[()"]/', $str, $matches);1939 // Fall-through1940 case 'text':1941 default:1942 $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);1943 break;1944 }1945 1946 if ($x == 0) {1947 return ($str);1948 }1949 1950 $maxlen = 75 - 7 - strlen($this->CharSet);1951 // Try to select the encoding which should produce the shortest output1952 if (strlen($str)/3 < $x) {1953 $encoding = 'B';1954 if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {1955 // Use a custom function which correctly encodes and wraps long1956 // multibyte strings without breaking lines within a character1957 $encoded = $this->Base64EncodeWrapMB($str, "\n");1958 } else {1959 $encoded = base64_encode($str);1960 $maxlen -= $maxlen % 4;1961 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));1962 }1963 } else {1964 $encoding = 'Q';1965 $encoded = $this->EncodeQ($str, $position);1966 $encoded = $this->WrapText($encoded, $maxlen, true);1967 $encoded = str_replace('='.self::CRLF, "\n", trim($encoded));1968 }1969 1970 $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);1971 $encoded = trim(str_replace("\n", $this->LE, $encoded));1972 1973 return $encoded;1974 }1975 1976 /**1977 * Checks if a string contains multibyte characters.1978 * @access public1979 * @param string $str multi-byte text to wrap encode1980 * @return bool1981 */1982 public function HasMultiBytes($str) {1983 if (function_exists('mb_strlen')) {1984 return (strlen($str) > mb_strlen($str, $this->CharSet));1985 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)1986 return false;1987 }1988 }1989 1990 /**1991 * Correctly encodes and wraps long multibyte strings for mail headers1992 * without breaking lines within a character.1993 * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php1994 * @access public1995 * @param string $str multi-byte text to wrap encode1996 * @param string $lf string to use as linefeed/end-of-line1997 * @return string1998 */1999 public function Base64EncodeWrapMB($str, $lf=null) {2000 $start = "=?".$this->CharSet."?B?";2001 $end = "?=";2002 $encoded = "";2003 if ($lf === null) {2004 $lf = $this->LE;2005 }2006 2007 $mb_length = mb_strlen($str, $this->CharSet);2008 // Each line must have length <= 75, including $start and $end2009 $length = 75 - strlen($start) - strlen($end);2010 // Average multi-byte ratio2011 $ratio = $mb_length / strlen($str);2012 // Base64 has a 4:3 ratio2013 $offset = $avgLength = floor($length * $ratio * .75);2014 2015 for ($i = 0; $i < $mb_length; $i += $offset) {2016 $lookBack = 0;2017 2018 do {2019 $offset = $avgLength - $lookBack;2020 $chunk = mb_substr($str, $i, $offset, $this->CharSet);2021 $chunk = base64_encode($chunk);2022 $lookBack++;2023 }2024 while (strlen($chunk) > $length);2025 2026 $encoded .= $chunk . $lf;2027 }2028 2029 // Chomp the last linefeed2030 $encoded = substr($encoded, 0, -strlen($lf));2031 return $encoded;2032 }2033 2034 /**2035 * Encode string to quoted-printable.2036 * Only uses standard PHP, slow, but will always work2037 * @access public2038 * @param string $input2039 * @param integer $line_max Number of chars allowed on a line before wrapping2040 * @param bool $space_conv2041 * @internal param string $string the text to encode2042 * @return string2043 */2044 public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {2045 $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');2046 $lines = preg_split('/(?:\r\n|\r|\n)/', $input);2047 $eol = "\r\n";2048 $escape = '=';2049 $output = '';2050 while( list(, $line) = each($lines) ) {2051 $linlen = strlen($line);2052 $newline = '';2053 for($i = 0; $i < $linlen; $i++) {2054 $c = substr( $line, $i, 1 );2055 $dec = ord( $c );2056 if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E2057 $c = '=2E';2058 }2059 if ( $dec == 32 ) {2060 if ( $i == ( $linlen - 1 ) ) { // convert space at eol only2061 $c = '=20';2062 } else if ( $space_conv ) {2063 $c = '=20';2064 }2065 } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required2066 $h2 = (integer)floor($dec/16);2067 $h1 = (integer)floor($dec%16);2068 $c = $escape.$hex[$h2].$hex[$h1];2069 }2070 if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted2071 $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay2072 $newline = '';2073 // check if newline first character will be point or not2074 if ( $dec == 46 ) {2075 $c = '=2E';2076 }2077 }2078 $newline .= $c;2079 } // end of for2080 $output .= $newline.$eol;2081 } // end of while2082 return $output;2083 }2084 2085 /**2086 * Encode string to RFC2045 (6.7) quoted-printable format2087 * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version2088 * Also results in same content as you started with after decoding2089 * @see EncodeQPphp()2090 * @access public2091 * @param string $string the text to encode2092 * @param integer $line_max Number of chars allowed on a line before wrapping2093 * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function2094 * @return string2095 * @author Marcus Bointon2096 */2097 public function EncodeQP($string, $line_max = 76, $space_conv = false) {2098 if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)2099 return quoted_printable_encode($string);2100 }2101 $filters = stream_get_filters();2102 if (!in_array('convert.*', $filters)) { //Got convert stream filter?2103 return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation2104 }2105 $fp = fopen('php://temp/', 'r+');2106 $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks2107 $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);2108 $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);2109 fputs($fp, $string);2110 rewind($fp);2111 $out = stream_get_contents($fp);2112 stream_filter_remove($s);2113 $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange2114 fclose($fp);2115 return $out;2116 }2117 2118 /**2119 * Encode string to q encoding.2120 * @link http://tools.ietf.org/html/rfc20472121 * @param string $str the text to encode2122 * @param string $position Where the text is going to be used, see the RFC for what that means2123 * @access public2124 * @return string2125 */2126 public function EncodeQ($str, $position = 'text') {2127 //There should not be any EOL in the string2128 $pattern="";2129 $encoded = str_replace(array("\r", "\n"), '', $str);2130 switch (strtolower($position)) {2131 case 'phrase':2132 $pattern = '^A-Za-z0-9!*+\/ -';2133 break;2134 2135 case 'comment':2136 $pattern = '\(\)"';2137 //note that we dont break here!2138 //for this reason we build the $pattern withoud including delimiters and []2139 2140 case 'text':2141 default:2142 //Replace every high ascii, control =, ? and _ characters2143 //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode2144 $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern;2145 break;2146 }2147 2148 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {2149 foreach (array_unique($matches[0]) as $char) {2150 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);2151 }2152 }2153 2154 //Replace every spaces to _ (more readable than =20)2155 return str_replace(' ', '_', $encoded);2156 }2157 2158 2159 /**2160 * Adds a string or binary attachment (non-filesystem) to the list.2161 * This method can be used to attach ascii or binary data,2162 * such as a BLOB record from a database.2163 * @param string $string String attachment data.2164 * @param string $filename Name of the attachment.2165 * @param string $encoding File encoding (see $Encoding).2166 * @param string $type File extension (MIME) type.2167 * @return void2168 */2169 public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {2170 // Append to $attachment array2171 $this->attachment[] = array(2172 0 => $string,2173 1 => $filename,2174 2 => basename($filename),2175 3 => $encoding,2176 4 => $type,2177 5 => true, // isStringAttachment2178 6 => 'attachment',2179 7 => 02180 );2181 }2182 2183 /**2184 * Adds an embedded attachment. This can include images, sounds, and2185 * just about any other document. Make sure to set the $type to an2186 * image type. For JPEG images use "image/jpeg" and for GIF images2187 * use "image/gif".2188 * @param string $path Path to the attachment.2189 * @param string $cid Content ID of the attachment. Use this to identify2190 * the Id for accessing the image in an HTML form.2191 * @param string $name Overrides the attachment name.2192 * @param string $encoding File encoding (see $Encoding).2193 * @param string $type File extension (MIME) type.2194 * @return bool2195 */2196 public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {2197 2198 if ( !@is_file($path) ) {2199 $this->SetError($this->Lang('file_access') . $path);2200 return false;2201 }2202 2203 $filename = basename($path);2204 if ( $name == '' ) {2205 $name = $filename;2206 }2207 2208 // Append to $attachment array2209 $this->attachment[] = array(2210 0 => $path,2211 1 => $filename,2212 2 => $name,2213 3 => $encoding,2214 4 => $type,2215 5 => false, // isStringAttachment2216 6 => 'inline',2217 7 => $cid2218 );2219 2220 return true;2221 }2222 2223 /**2224 * Adds an embedded stringified attachment. This can include images, sounds, and2225 * just about any other document. Make sure to set the $type to an2226 * image type. For JPEG images use "image/jpeg" and for GIF images2227 * use "image/gif".2228 * @param string $string The attachment.2229 * @param string $cid Content ID of the attachment. Use this to identify2230 * the Id for accessing the image in an HTML form.2231 * @param string $name Overrides the attachment name.2232 * @param string $encoding File encoding (see $Encoding).2233 * @param string $type File extension (MIME) type.2234 * @return bool2235 */2236 public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {2237 // Append to $attachment array2238 $this->attachment[] = array(2239 0 => $string,2240 1 => $name,2241 2 => $name,2242 3 => $encoding,2243 4 => $type,2244 5 => true, // isStringAttachment2245 6 => 'inline',2246 7 => $cid2247 );2248 }2249 2250 /**2251 * Returns true if an inline attachment is present.2252 * @access public2253 * @return bool2254 */2255 public function InlineImageExists() {2256 foreach($this->attachment as $attachment) {2257 if ($attachment[6] == 'inline') {2258 return true;2259 }2260 }2261 return false;2262 }2263 2264 /**2265 * Returns true if an attachment (non-inline) is present.2266 * @return bool2267 */2268 public function AttachmentExists() {2269 foreach($this->attachment as $attachment) {2270 if ($attachment[6] == 'attachment') {2271 return true;2272 }2273 }2274 return false;2275 }2276 2277 /**2278 * Does this message have an alternative body set?2279 * @return bool2280 */2281 public function AlternativeExists() {2282 return !empty($this->AltBody);2283 }2284 2285 /////////////////////////////////////////////////2286 // CLASS METHODS, MESSAGE RESET2287 /////////////////////////////////////////////////2288 2289 /**2290 * Clears all recipients assigned in the TO array. Returns void.2291 * @return void2292 */2293 public function ClearAddresses() {2294 foreach($this->to as $to) {2295 unset($this->all_recipients[strtolower($to[0])]);2296 }2297 $this->to = array();2298 }2299 2300 /**2301 * Clears all recipients assigned in the CC array. Returns void.2302 * @return void2303 */2304 public function ClearCCs() {2305 foreach($this->cc as $cc) {2306 unset($this->all_recipients[strtolower($cc[0])]);2307 }2308 $this->cc = array();2309 }2310 2311 /**2312 * Clears all recipients assigned in the BCC array. Returns void.2313 * @return void2314 */2315 public function ClearBCCs() {2316 foreach($this->bcc as $bcc) {2317 unset($this->all_recipients[strtolower($bcc[0])]);2318 }2319 $this->bcc = array();2320 }2321 2322 /**2323 * Clears all recipients assigned in the ReplyTo array. Returns void.2324 * @return void2325 */2326 public function ClearReplyTos() {2327 $this->ReplyTo = array();2328 }2329 2330 /**2331 * Clears all recipients assigned in the TO, CC and BCC2332 * array. Returns void.2333 * @return void2334 */2335 public function ClearAllRecipients() {2336 $this->to = array();2337 $this->cc = array();2338 $this->bcc = array();2339 $this->all_recipients = array();2340 }2341 2342 /**2343 * Clears all previously set filesystem, string, and binary2344 * attachments. Returns void.2345 * @return void2346 */2347 public function ClearAttachments() {2348 $this->attachment = array();2349 }2350 2351 /**2352 * Clears all custom headers. Returns void.2353 * @return void2354 */2355 public function ClearCustomHeaders() {2356 $this->CustomHeader = array();2357 }2358 2359 /////////////////////////////////////////////////2360 // CLASS METHODS, MISCELLANEOUS2361 /////////////////////////////////////////////////2362 2363 /**2364 * Adds the error message to the error container.2365 * @access protected2366 * @param string $msg2367 * @return void2368 */2369 protected function SetError($msg) {2370 $this->error_count++;2371 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {2372 $lasterror = $this->smtp->getError();2373 if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {2374 $msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";2375 }2376 }2377 $this->ErrorInfo = $msg;2378 }2379 2380 /**2381 * Returns the proper RFC 822 formatted date.2382 * @access public2383 * @return string2384 * @static2385 */2386 public static function RFCDate() {2387 $tz = date('Z');2388 $tzs = ($tz < 0) ? '-' : '+';2389 $tz = abs($tz);2390 $tz = (int)($tz/3600)*100 + ($tz%3600)/60;2391 $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);2392 2393 return $result;2394 }2395 2396 /**2397 * Returns the server hostname or 'localhost.localdomain' if unknown.2398 * @access protected2399 * @return string2400 */2401 protected function ServerHostname() {2402 if (!empty($this->Hostname)) {2403 $result = $this->Hostname;2404 } elseif (isset($_SERVER['SERVER_NAME'])) {2405 $result = $_SERVER['SERVER_NAME'];2406 } else {2407 $result = 'localhost.localdomain';2408 }2409 2410 return $result;2411 }2412 2413 /**2414 * Returns a message in the appropriate language.2415 * @access protected2416 * @param string $key2417 * @return string2418 */2419 protected function Lang($key) {2420 if(count($this->language) < 1) {2421 $this->SetLanguage('en'); // set the default language2422 }2423 2424 if(isset($this->language[$key])) {2425 return $this->language[$key];2426 } else {2427 return 'Language string failed to load: ' . $key;2428 }2429 }2430 2431 /**2432 * Returns true if an error occurred.2433 * @access public2434 * @return bool2435 */2436 public function IsError() {2437 return ($this->error_count > 0);2438 }2439 2440 /**2441 * Changes every end of line from CRLF, CR or LF to $this->LE.2442 * @access public2443 * @param string $str String to FixEOL2444 * @return string2445 */2446 public function FixEOL($str) {2447 // condense down to \n2448 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);2449 // Now convert LE as needed2450 if ($this->LE !== "\n") {2451 $nstr = str_replace("\n", $this->LE, $nstr);2452 }2453 return $nstr;2454 }2455 2456 /**2457 * Adds a custom header. $name value can be overloaded to contain2458 * both header name and value (name:value)2459 * @access public2460 * @param string $name custom header name2461 * @param string $value header value2462 * @return void2463 */2464 public function AddCustomHeader($name, $value=null) {2465 if ($value === null) {2466 // Value passed in as name:value2467 $this->CustomHeader[] = explode(':', $name, 2);2468 } else {2469 $this->CustomHeader[] = array($name, $value);2470 }2471 }2472 2473 /**2474 * Evaluates the message and returns modifications for inline images and backgrounds2475 * @access public2476 * @param string $message Text to be HTML modified2477 * @param string $basedir baseline directory for path2478 * @return string $message2479 */2480 public function MsgHTML($message, $basedir = '') {2481 preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);2482 if(isset($images[2])) {2483 foreach($images[2] as $i => $url) {2484 // do not change urls for absolute images (thanks to corvuscorax)2485 if (!preg_match('#^[A-z]+://#', $url)) {2486 $filename = basename($url);2487 $directory = dirname($url);2488 if ($directory == '.') {2489 $directory = '';2490 }2491 $cid = 'cid:' . md5($url);2492 $ext = pathinfo($filename, PATHINFO_EXTENSION);2493 $mimeType = self::_mime_types($ext);2494 if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; }2495 if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; }2496 if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($url), $filename, 'base64', $mimeType) ) {2497 $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message);2498 }2499 }2500 }2501 }2502 $this->IsHTML(true);2503 $this->Body = $message;2504 if (empty($this->AltBody)) {2505 $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message)));2506 if (!empty($textMsg)) {2507 $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet);2508 }2509 }2510 if (empty($this->AltBody)) {2511 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";2512 }2513 return $message;2514 }2515 2516 /**2517 * Gets the MIME type of the embedded or inline image2518 * @param string $ext File extension2519 * @access public2520 * @return string MIME type of ext2521 * @static2522 */2523 public static function _mime_types($ext = '') {2524 $mimes = array(2525 'xl' => 'application/excel',2526 'hqx' => 'application/mac-binhex40',2527 'cpt' => 'application/mac-compactpro',2528 'bin' => 'application/macbinary',2529 'doc' => 'application/msword',2530 'word' => 'application/msword',2531 'class' => 'application/octet-stream',2532 'dll' => 'application/octet-stream',2533 'dms' => 'application/octet-stream',2534 'exe' => 'application/octet-stream',2535 'lha' => 'application/octet-stream',2536 'lzh' => 'application/octet-stream',2537 'psd' => 'application/octet-stream',2538 'sea' => 'application/octet-stream',2539 'so' => 'application/octet-stream',2540 'oda' => 'application/oda',2541 'pdf' => 'application/pdf',2542 'ai' => 'application/postscript',2543 'eps' => 'application/postscript',2544 'ps' => 'application/postscript',2545 'smi' => 'application/smil',2546 'smil' => 'application/smil',2547 'mif' => 'application/vnd.mif',2548 'xls' => 'application/vnd.ms-excel',2549 'ppt' => 'application/vnd.ms-powerpoint',2550 'wbxml' => 'application/vnd.wap.wbxml',2551 'wmlc' => 'application/vnd.wap.wmlc',2552 'dcr' => 'application/x-director',2553 'dir' => 'application/x-director',2554 'dxr' => 'application/x-director',2555 'dvi' => 'application/x-dvi',2556 'gtar' => 'application/x-gtar',2557 'php3' => 'application/x-httpd-php',2558 'php4' => 'application/x-httpd-php',2559 'php' => 'application/x-httpd-php',2560 'phtml' => 'application/x-httpd-php',2561 'phps' => 'application/x-httpd-php-source',2562 'js' => 'application/x-javascript',2563 'swf' => 'application/x-shockwave-flash',2564 'sit' => 'application/x-stuffit',2565 'tar' => 'application/x-tar',2566 'tgz' => 'application/x-tar',2567 'xht' => 'application/xhtml+xml',2568 'xhtml' => 'application/xhtml+xml',2569 'zip' => 'application/zip',2570 'mid' => 'audio/midi',2571 'midi' => 'audio/midi',2572 'mp2' => 'audio/mpeg',2573 'mp3' => 'audio/mpeg',2574 'mpga' => 'audio/mpeg',2575 'aif' => 'audio/x-aiff',2576 'aifc' => 'audio/x-aiff',2577 'aiff' => 'audio/x-aiff',2578 'ram' => 'audio/x-pn-realaudio',2579 'rm' => 'audio/x-pn-realaudio',2580 'rpm' => 'audio/x-pn-realaudio-plugin',2581 'ra' => 'audio/x-realaudio',2582 'wav' => 'audio/x-wav',2583 'bmp' => 'image/bmp',2584 'gif' => 'image/gif',2585 'jpeg' => 'image/jpeg',2586 'jpe' => 'image/jpeg',2587 'jpg' => 'image/jpeg',2588 'png' => 'image/png',2589 'tiff' => 'image/tiff',2590 'tif' => 'image/tiff',2591 'eml' => 'message/rfc822',2592 'css' => 'text/css',2593 'html' => 'text/html',2594 'htm' => 'text/html',2595 'shtml' => 'text/html',2596 'log' => 'text/plain',2597 'text' => 'text/plain',2598 'txt' => 'text/plain',2599 'rtx' => 'text/richtext',2600 'rtf' => 'text/rtf',2601 'xml' => 'text/xml',2602 'xsl' => 'text/xml',2603 'mpeg' => 'video/mpeg',2604 'mpe' => 'video/mpeg',2605 'mpg' => 'video/mpeg',2606 'mov' => 'video/quicktime',2607 'qt' => 'video/quicktime',2608 'rv' => 'video/vnd.rn-realvideo',2609 'avi' => 'video/x-msvideo',2610 'movie' => 'video/x-sgi-movie'2611 );2612 return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];2613 }2614 2615 /**2616 * Set (or reset) Class Objects (variables)2617 *2618 * Usage Example:2619 * $page->set('X-Priority', '3');2620 *2621 * @access public2622 * @param string $name Parameter Name2623 * @param mixed $value Parameter Value2624 * NOTE: will not work with arrays, there are no arrays to set/reset2625 * @throws phpmailerException2626 * @return bool2627 * @todo Should this not be using __set() magic function?2628 */2629 public function set($name, $value = '') {2630 try {2631 if (isset($this->$name) ) {2632 $this->$name = $value;2633 } else {2634 throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);2635 }2636 } catch (Exception $e) {2637 $this->SetError($e->getMessage());2638 if ($e->getCode() == self::STOP_CRITICAL) {2639 return false;2640 }2641 }2642 return true;2643 }2644 2645 /**2646 * Strips newlines to prevent header injection.2647 * @access public2648 * @param string $str String2649 * @return string2650 */2651 public function SecureHeader($str) {2652 return trim(str_replace(array("\r", "\n"), '', $str));2653 }2654 2655 /**2656 * Set the private key file and password to sign the message.2657 *2658 * @access public2659 * @param $cert_filename2660 * @param string $key_filename Parameter File Name2661 * @param string $key_pass Password for private key2662 */2663 public function Sign($cert_filename, $key_filename, $key_pass) {2664 $this->sign_cert_file = $cert_filename;2665 $this->sign_key_file = $key_filename;2666 $this->sign_key_pass = $key_pass;2667 }2668 2669 /**2670 * Set the private key file and password to sign the message.2671 *2672 * @access public2673 * @param string $txt2674 * @return string2675 */2676 public function DKIM_QP($txt) {2677 $line = '';2678 for ($i = 0; $i < strlen($txt); $i++) {2679 $ord = ord($txt[$i]);2680 if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {2681 $line .= $txt[$i];2682 } else {2683 $line .= "=".sprintf("%02X", $ord);2684 }2685 }2686 return $line;2687 }2688 2689 /**2690 * Generate DKIM signature2691 *2692 * @access public2693 * @param string $s Header2694 * @return string2695 */2696 public function DKIM_Sign($s) {2697 $privKeyStr = file_get_contents($this->DKIM_private);2698 if ($this->DKIM_passphrase != '') {2699 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);2700 } else {2701 $privKey = $privKeyStr;2702 }2703 if (openssl_sign($s, $signature, $privKey)) {2704 return base64_encode($signature);2705 }2706 return '';2707 }2708 2709 /**2710 * Generate DKIM Canonicalization Header2711 *2712 * @access public2713 * @param string $s Header2714 * @return string2715 */2716 public function DKIM_HeaderC($s) {2717 $s = preg_replace("/\r\n\s+/", " ", $s);2718 $lines = explode("\r\n", $s);2719 foreach ($lines as $key => $line) {2720 list($heading, $value) = explode(":", $line, 2);2721 $heading = strtolower($heading);2722 $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces2723 $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value2724 }2725 $s = implode("\r\n", $lines);2726 return $s;2727 }2728 2729 /**2730 * Generate DKIM Canonicalization Body2731 *2732 * @access public2733 * @param string $body Message Body2734 * @return string2735 */2736 public function DKIM_BodyC($body) {2737 if ($body == '') return "\r\n";2738 // stabilize line endings2739 $body = str_replace("\r\n", "\n", $body);2740 $body = str_replace("\n", "\r\n", $body);2741 // END stabilize line endings2742 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {2743 $body = substr($body, 0, strlen($body) - 2);2744 }2745 return $body;2746 }2747 2748 /**2749 * Create the DKIM header, body, as new header2750 *2751 * @access public2752 * @param string $headers_line Header lines2753 * @param string $subject Subject2754 * @param string $body Body2755 * @return string2756 */2757 public function DKIM_Add($headers_line, $subject, $body) {2758 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms2759 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body2760 $DKIMquery = 'dns/txt'; // Query method2761 $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)2762 $subject_header = "Subject: $subject";2763 $headers = explode($this->LE, $headers_line);2764 $from_header = "";2765 $to_header = "";2766 foreach($headers as $header) {2767 if (strpos($header, 'From:') === 0) {2768 $from_header = $header;2769 } elseif (strpos($header, 'To:') === 0) {2770 $to_header = $header;2771 }2772 }2773 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));2774 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));2775 $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable2776 $body = $this->DKIM_BodyC($body);2777 $DKIMlen = strlen($body) ; // Length of body2778 $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body2779 $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";2780 $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".2781 "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".2782 "\th=From:To:Subject;\r\n".2783 "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".2784 "\tz=$from\r\n".2785 "\t|$to\r\n".2786 "\t|$subject;\r\n".2787 "\tbh=" . $DKIMb64 . ";\r\n".2788 "\tb=";2789 $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);2790 $signed = $this->DKIM_Sign($toSign);2791 return "X-PHPMAILER-DKIM: code.google.com/a/apache-extras.org/p/phpmailer/\r\n".$dkimhdrs.$signed."\r\n";2792 }2793 2794 /**2795 * Perform callback2796 * @param boolean $isSent2797 * @param string $to2798 * @param string $cc2799 * @param string $bcc2800 * @param string $subject2801 * @param string $body2802 * @param string $from2803 */2804 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) {2805 if (!empty($this->action_function) && is_callable($this->action_function)) {2806 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);2807 call_user_func_array($this->action_function, $params);2808 }2809 }2810 }2811 2812 /**2813 * Exception handler for PHPMailer2814 * @package PHPMailer2815 */2816 class phpmailerException extends Exception {2817 /**2818 * Prettify error message output2819 * @return string2820 */2821 public function errorMessage() {2822 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";2823 return $errorMsg;2824 }2825 }2826 ?> -
trunk/src/wp-includes/class-smtp.php
r23522 r27385 1 1 <?php 2 /*~ class.smtp.php3 .---------------------------------------------------------------------------.4 | Software: PHPMailer - PHP email class |5 | Version: 5.2.4 |6 | Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ |7 | ------------------------------------------------------------------------- |8 | Admin: Jim Jagielski (project admininistrator) |9 | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |10 | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net |11 | : Jim Jagielski (jimjag) jimjag@gmail.com |12 | Founder: Brent R. Matzelle (original founder) |13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. |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 */24 25 2 /** 26 * PHPMailer - PHP SMTP email transport class 27 * NOTE: Designed for use with PHP version 5 and up 28 * @package PHPMailer 29 * @author Andy Prevost 30 * @author Marcus Bointon 3 * PHPMailer RFC821 SMTP email transport class. 4 * Version 5.2.7 5 * PHP version 5.0.0 6 * @category PHP 7 * @package PHPMailer 8 * @link https://github.com/PHPMailer/PHPMailer/ 9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> 10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 12 * @copyright 2013 Marcus Bointon 31 13 * @copyright 2004 - 2008 Andy Prevost 32 * @author Jim Jagielski33 14 * @copyright 2010 - 2012 Jim Jagielski 34 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)15 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 35 16 */ 36 17 37 18 /** 38 * PHP RFC821 SMTP client19 * PHPMailer RFC821 SMTP email transport class. 39 20 * 40 * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error. 41 * SMTP also provides some utility methods for sending mail to an SMTP server. 42 * @author Chris Ryan 43 * @package PHPMailer 21 * Implements RFC 821 SMTP commands 22 * and provides some utility methods for sending mail to an SMTP server. 23 * 24 * PHP Version 5.0.0 25 * 26 * @category PHP 27 * @package PHPMailer 28 * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php 29 * @author Chris Ryan <unknown@example.com> 30 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 31 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 44 32 */ 45 33 46 class SMTP { 47 /** 48 * SMTP server port 49 * @var int 50 */ 51 public $SMTP_PORT = 25; 52 53 /** 54 * SMTP reply line ending (don't change) 55 * @var string 56 */ 57 public $CRLF = "\r\n"; 58 59 /** 60 * Sets whether debugging is turned on 61 * @var bool 62 */ 63 public $do_debug; // the level of debug to perform 64 65 /** 66 * Sets the function/method to use for debugging output. 67 * Right now we only honor "echo" or "error_log" 68 * @var string 69 */ 70 public $Debugoutput = "echo"; 71 72 /** 73 * Sets VERP use on/off (default is off) 74 * @var bool 75 */ 76 public $do_verp = false; 77 78 /** 79 * Sets the SMTP timeout value for reads, in seconds 80 * @var int 81 */ 82 public $Timeout = 15; 83 84 /** 85 * Sets the SMTP timelimit value for reads, in seconds 86 * @var int 87 */ 88 public $Timelimit = 30; 89 90 /** 91 * Sets the SMTP PHPMailer Version number 92 * @var string 93 */ 94 public $Version = '5.2.4'; 95 96 ///////////////////////////////////////////////// 97 // PROPERTIES, PRIVATE AND PROTECTED 98 ///////////////////////////////////////////////// 99 100 /** 101 * @var resource The socket to the server 102 */ 103 private $smtp_conn; 104 /** 105 * @var string Error message, if any, for the last call 106 */ 107 private $error; 108 /** 109 * @var string The reply the server sent to us for HELO 110 */ 111 private $helo_rply; 112 113 /** 114 * Outputs debugging info via user-defined method 115 * @param string $str 116 */ 117 private function edebug($str) { 118 if ($this->Debugoutput == "error_log") { 119 error_log($str); 120 } else { 121 echo $str; 122 } 123 } 124 125 /** 126 * Initialize the class so that the data is in a known state. 127 * @access public 128 * @return SMTP 129 */ 130 public function __construct() { 131 $this->smtp_conn = 0; 132 $this->error = null; 133 $this->helo_rply = null; 134 135 $this->do_debug = 0; 136 } 137 138 ///////////////////////////////////////////////// 139 // CONNECTION FUNCTIONS 140 ///////////////////////////////////////////////// 141 142 /** 143 * Connect to the server specified on the port specified. 144 * If the port is not specified use the default SMTP_PORT. 145 * If tval is specified then a connection will try and be 146 * established with the server for that number of seconds. 147 * If tval is not specified the default is 30 seconds to 148 * try on the connection. 149 * 150 * SMTP CODE SUCCESS: 220 151 * SMTP CODE FAILURE: 421 152 * @access public 153 * @param string $host 154 * @param int $port 155 * @param int $tval 156 * @return bool 157 */ 158 public function Connect($host, $port = 0, $tval = 30) { 159 // set the error val to null so there is no confusion 160 $this->error = null; 161 162 // make sure we are __not__ connected 163 if($this->connected()) { 164 // already connected, generate error 165 $this->error = array("error" => "Already connected to a server"); 166 return false; 167 } 168 169 if(empty($port)) { 170 $port = $this->SMTP_PORT; 171 } 172 173 // connect to the smtp server 174 $this->smtp_conn = @fsockopen($host, // the host of the server 175 $port, // the port to use 176 $errno, // error number if any 177 $errstr, // error message if any 178 $tval); // give up after ? secs 179 // verify we connected properly 180 if(empty($this->smtp_conn)) { 181 $this->error = array("error" => "Failed to connect to server", 182 "errno" => $errno, 183 "errstr" => $errstr); 184 if($this->do_debug >= 1) { 185 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />'); 186 } 187 return false; 188 } 189 190 // SMTP server can take longer to respond, give longer timeout for first read 191 // Windows does not have support for this timeout function 192 if(substr(PHP_OS, 0, 3) != "WIN") { 193 $max = ini_get('max_execution_time'); 194 if ($max != 0 && $tval > $max) { // don't bother if unlimited 195 @set_time_limit($tval); 196 } 197 stream_set_timeout($this->smtp_conn, $tval, 0); 198 } 199 200 // get any announcement 201 $announce = $this->get_lines(); 202 203 if($this->do_debug >= 2) { 204 $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />'); 205 } 206 207 return true; 208 } 209 210 /** 211 * Initiate a TLS communication with the server. 212 * 213 * SMTP CODE 220 Ready to start TLS 214 * SMTP CODE 501 Syntax error (no parameters allowed) 215 * SMTP CODE 454 TLS not available due to temporary reason 216 * @access public 217 * @return bool success 218 */ 219 public function StartTLS() { 220 $this->error = null; # to avoid confusion 221 222 if(!$this->connected()) { 223 $this->error = array("error" => "Called StartTLS() without being connected"); 224 return false; 225 } 226 227 fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); 228 229 $rply = $this->get_lines(); 230 $code = substr($rply,0,3); 231 232 if($this->do_debug >= 2) { 233 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 234 } 235 236 if($code != 220) { 237 $this->error = 238 array("error" => "STARTTLS not accepted from server", 239 "smtp_code" => $code, 240 "smtp_msg" => substr($rply,4)); 241 if($this->do_debug >= 1) { 242 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 243 } 244 return false; 245 } 246 247 // Begin encrypted connection 248 if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { 249 return false; 250 } 251 252 return true; 253 } 254 255 /** 256 * Performs SMTP authentication. Must be run after running the 257 * Hello() method. Returns true if successfully authenticated. 258 * @access public 259 * @param string $username 260 * @param string $password 261 * @param string $authtype 262 * @param string $realm 263 * @param string $workstation 264 * @return bool 265 */ 266 public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { 267 if (empty($authtype)) { 268 $authtype = 'LOGIN'; 269 } 270 271 switch ($authtype) { 272 case 'PLAIN': 273 // Start authentication 274 fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF); 275 276 $rply = $this->get_lines(); 277 $code = substr($rply,0,3); 278 279 if($code != 334) { 280 $this->error = 281 array("error" => "AUTH not accepted from server", 282 "smtp_code" => $code, 283 "smtp_msg" => substr($rply,4)); 284 if($this->do_debug >= 1) { 285 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 286 } 287 return false; 288 } 289 // Send encoded username and password 290 fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF); 291 292 $rply = $this->get_lines(); 293 $code = substr($rply,0,3); 294 295 if($code != 235) { 296 $this->error = 297 array("error" => "Authentication not accepted from server", 298 "smtp_code" => $code, 299 "smtp_msg" => substr($rply,4)); 300 if($this->do_debug >= 1) { 301 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 302 } 303 return false; 304 } 305 break; 306 case 'LOGIN': 307 // Start authentication 308 fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); 309 310 $rply = $this->get_lines(); 311 $code = substr($rply,0,3); 312 313 if($code != 334) { 314 $this->error = 315 array("error" => "AUTH not accepted from server", 316 "smtp_code" => $code, 317 "smtp_msg" => substr($rply,4)); 318 if($this->do_debug >= 1) { 319 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 320 } 321 return false; 322 } 323 324 // Send encoded username 325 fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); 326 327 $rply = $this->get_lines(); 328 $code = substr($rply,0,3); 329 330 if($code != 334) { 331 $this->error = 332 array("error" => "Username not accepted from server", 333 "smtp_code" => $code, 334 "smtp_msg" => substr($rply,4)); 335 if($this->do_debug >= 1) { 336 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 337 } 338 return false; 339 } 340 341 // Send encoded password 342 fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); 343 344 $rply = $this->get_lines(); 345 $code = substr($rply,0,3); 346 347 if($code != 235) { 348 $this->error = 349 array("error" => "Password not accepted from server", 350 "smtp_code" => $code, 351 "smtp_msg" => substr($rply,4)); 352 if($this->do_debug >= 1) { 353 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 354 } 355 return false; 356 } 357 break; 358 case 'NTLM': 359 /* 360 * ntlm_sasl_client.php 361 ** Bundled with Permission 362 ** 363 ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 364 ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 34 class SMTP 35 { 36 /** 37 * The PHPMailer SMTP Version number. 38 */ 39 const VERSION = '5.2.7'; 40 41 /** 42 * SMTP line break constant. 43 */ 44 const CRLF = "\r\n"; 45 46 /** 47 * The SMTP port to use if one is not specified. 48 */ 49 const DEFAULT_SMTP_PORT = 25; 50 51 /** 52 * The PHPMailer SMTP Version number. 53 * @type string 54 * @deprecated This should be a constant 55 * @see SMTP::VERSION 56 */ 57 public $Version = '5.2.7'; 58 59 /** 60 * SMTP server port number. 61 * @type int 62 * @deprecated This is only ever ued as default value, so should be a constant 63 * @see SMTP::DEFAULT_SMTP_PORT 64 */ 65 public $SMTP_PORT = 25; 66 67 /** 68 * SMTP reply line ending 69 * @type string 70 * @deprecated Use the class constant instead 71 * @see SMTP::CRLF 72 */ 73 public $CRLF = "\r\n"; 74 75 /** 76 * Debug output level. 77 * Options: 0 for no output, 1 for commands, 2 for data and commands 78 * @type int 79 */ 80 public $do_debug = 0; 81 82 /** 83 * The function/method to use for debugging output. 84 * Options: 'echo', 'html' or 'error_log' 85 * @type string 86 */ 87 public $Debugoutput = 'echo'; 88 89 /** 90 * Whether to use VERP. 91 * @type bool 92 */ 93 public $do_verp = false; 94 95 /** 96 * The SMTP timeout value for reads, in seconds. 97 * @type int 98 */ 99 public $Timeout = 15; 100 101 /** 102 * The SMTP timelimit value for reads, in seconds. 103 * @type int 104 */ 105 public $Timelimit = 30; 106 107 /** 108 * The socket for the server connection. 109 * @type resource 110 */ 111 protected $smtp_conn; 112 113 /** 114 * Error message, if any, for the last call. 115 * @type string 116 */ 117 protected $error = ''; 118 119 /** 120 * The reply the server sent to us for HELO. 121 * @type string 122 */ 123 protected $helo_rply = ''; 124 125 /** 126 * The most recent reply received from the server. 127 * @type string 128 */ 129 protected $last_reply = ''; 130 131 /** 132 * Constructor. 133 * @access public 134 */ 135 public function __construct() 136 { 137 $this->smtp_conn = 0; 138 $this->error = null; 139 $this->helo_rply = null; 140 141 $this->do_debug = 0; 142 } 143 144 /** 145 * Output debugging info via a user-selected method. 146 * @param string $str Debug string to output 147 * @return void 148 */ 149 protected function edebug($str) 150 { 151 switch ($this->Debugoutput) { 152 case 'error_log': 153 //Don't output, just log 154 error_log($str); 155 break; 156 case 'html': 157 //Cleans up output a bit for a better looking, HTML-safe output 158 echo htmlentities( 159 preg_replace('/[\r\n]+/', '', $str), 160 ENT_QUOTES, 161 'UTF-8' 162 ) 163 . "<br>\n"; 164 break; 165 case 'echo': 166 default: 167 //Just echoes whatever was received 168 echo $str; 169 } 170 } 171 172 /** 173 * Connect to an SMTP server. 174 * @param string $host SMTP server IP or host name 175 * @param int $port The port number to connect to 176 * @param int $timeout How long to wait for the connection to open 177 * @param array $options An array of options for stream_context_create() 178 * @access public 179 * @return bool 180 */ 181 public function connect($host, $port = null, $timeout = 30, $options = array()) 182 { 183 // Clear errors to avoid confusion 184 $this->error = null; 185 186 // Make sure we are __not__ connected 187 if ($this->connected()) { 188 // Already connected, generate error 189 $this->error = array('error' => 'Already connected to a server'); 190 return false; 191 } 192 193 if (empty($port)) { 194 $port = self::DEFAULT_SMTP_PORT; 195 } 196 197 // Connect to the SMTP server 198 $errno = 0; 199 $errstr = ''; 200 $socket_context = stream_context_create($options); 201 //Suppress errors; connection failures are handled at a higher level 202 $this->smtp_conn = @stream_socket_client( 203 $host . ":" . $port, 204 $errno, 205 $errstr, 206 $timeout, 207 STREAM_CLIENT_CONNECT, 208 $socket_context 209 ); 210 211 // Verify we connected properly 212 if (empty($this->smtp_conn)) { 213 $this->error = array( 214 'error' => 'Failed to connect to server', 215 'errno' => $errno, 216 'errstr' => $errstr 217 ); 218 if ($this->do_debug >= 1) { 219 $this->edebug( 220 'SMTP -> ERROR: ' . $this->error['error'] 221 . ": $errstr ($errno)" 222 ); 223 } 224 return false; 225 } 226 227 // SMTP server can take longer to respond, give longer timeout for first read 228 // Windows does not have support for this timeout function 229 if (substr(PHP_OS, 0, 3) != 'WIN') { 230 $max = ini_get('max_execution_time'); 231 if ($max != 0 && $timeout > $max) { // Don't bother if unlimited 232 @set_time_limit($timeout); 233 } 234 stream_set_timeout($this->smtp_conn, $timeout, 0); 235 } 236 237 // Get any announcement 238 $announce = $this->get_lines(); 239 240 if ($this->do_debug >= 2) { 241 $this->edebug('SMTP -> FROM SERVER:' . $announce); 242 } 243 244 return true; 245 } 246 247 /** 248 * Initiate a TLS (encrypted) session. 249 * @access public 250 * @return bool 251 */ 252 public function startTLS() 253 { 254 if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) { 255 return false; 256 } 257 // Begin encrypted connection 258 if (!stream_socket_enable_crypto( 259 $this->smtp_conn, 260 true, 261 STREAM_CRYPTO_METHOD_TLS_CLIENT 262 ) 263 ) { 264 return false; 265 } 266 return true; 267 } 268 269 /** 270 * Perform SMTP authentication. 271 * Must be run after hello(). 272 * @see hello() 273 * @param string $username The user name 274 * @param string $password The password 275 * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) 276 * @param string $realm The auth realm for NTLM 277 * @param string $workstation The auth workstation for NTLM 278 * @access public 279 * @return bool True if successfully authenticated. 280 */ 281 public function authenticate( 282 $username, 283 $password, 284 $authtype = 'LOGIN', 285 $realm = '', 286 $workstation = '' 287 ) { 288 if (empty($authtype)) { 289 $authtype = 'LOGIN'; 290 } 291 292 switch ($authtype) { 293 case 'PLAIN': 294 // Start authentication 295 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { 296 return false; 297 } 298 // Send encoded username and password 299 if (!$this->sendCommand( 300 'User & Password', 301 base64_encode("\0" . $username . "\0" . $password), 302 235 303 ) 304 ) { 305 return false; 306 } 307 break; 308 case 'LOGIN': 309 // Start authentication 310 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { 311 return false; 312 } 313 if (!$this->sendCommand("Username", base64_encode($username), 334)) { 314 return false; 315 } 316 if (!$this->sendCommand("Password", base64_encode($password), 235)) { 317 return false; 318 } 319 break; 320 case 'NTLM': 321 /* 322 * ntlm_sasl_client.php 323 * Bundled with Permission 324 * 325 * How to telnet in windows: 326 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 327 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 328 */ 329 require_once 'extras/ntlm_sasl_client.php'; 330 $temp = new stdClass(); 331 $ntlm_client = new ntlm_sasl_client_class; 332 //Check that functions are available 333 if (!$ntlm_client->Initialize($temp)) { 334 $this->error = array('error' => $temp->error); 335 if ($this->do_debug >= 1) { 336 $this->edebug( 337 'You need to enable some modules in your php.ini file: ' 338 . $this->error['error'] 339 ); 340 } 341 return false; 342 } 343 //msg1 344 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 345 346 if (!$this->sendCommand( 347 'AUTH NTLM', 348 'AUTH NTLM ' . base64_encode($msg1), 349 334 350 ) 351 ) { 352 return false; 353 } 354 355 //Though 0 based, there is a white space after the 3 digit number 356 //msg2 357 $challenge = substr($this->last_reply, 3); 358 $challenge = base64_decode($challenge); 359 $ntlm_res = $ntlm_client->NTLMResponse( 360 substr($challenge, 24, 8), 361 $password 362 ); 363 //msg3 364 $msg3 = $ntlm_client->TypeMsg3( 365 $ntlm_res, 366 $username, 367 $realm, 368 $workstation 369 ); 370 // send encoded username 371 return $this->sendCommand('Username', base64_encode($msg3), 235); 372 break; 373 case 'CRAM-MD5': 374 // Start authentication 375 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { 376 return false; 377 } 378 // Get the challenge 379 $challenge = base64_decode(substr($this->last_reply, 4)); 380 381 // Build the response 382 $response = $username . ' ' . $this->hmac($challenge, $password); 383 384 // send encoded credentials 385 return $this->sendCommand('Username', base64_encode($response), 235); 386 break; 387 } 388 return true; 389 } 390 391 /** 392 * Calculate an MD5 HMAC hash. 393 * Works like hash_hmac('md5', $data, $key) 394 * in case that function is not available 395 * @param string $data The data to hash 396 * @param string $key The key to hash with 397 * @access protected 398 * @return string 399 */ 400 protected function hmac($data, $key) 401 { 402 if (function_exists('hash_hmac')) { 403 return hash_hmac('md5', $data, $key); 404 } 405 406 // The following borrowed from 407 // http://php.net/manual/en/function.mhash.php#27225 408 409 // RFC 2104 HMAC implementation for php. 410 // Creates an md5 HMAC. 411 // Eliminates the need to install mhash to compute a HMAC 412 // Hacked by Lance Rushing 413 414 $b = 64; // byte length for md5 415 if (strlen($key) > $b) { 416 $key = pack('H*', md5($key)); 417 } 418 $key = str_pad($key, $b, chr(0x00)); 419 $ipad = str_pad('', $b, chr(0x36)); 420 $opad = str_pad('', $b, chr(0x5c)); 421 $k_ipad = $key ^ $ipad; 422 $k_opad = $key ^ $opad; 423 424 return md5($k_opad . pack('H*', md5($k_ipad . $data))); 425 } 426 427 /** 428 * Check connection state. 429 * @access public 430 * @return bool True if connected. 431 */ 432 public function connected() 433 { 434 if (!empty($this->smtp_conn)) { 435 $sock_status = stream_get_meta_data($this->smtp_conn); 436 if ($sock_status['eof']) { 437 // the socket is valid but we are not connected 438 if ($this->do_debug >= 1) { 439 $this->edebug( 440 'SMTP -> NOTICE: EOF caught while checking if connected' 441 ); 442 } 443 $this->close(); 444 return false; 445 } 446 return true; // everything looks good 447 } 448 return false; 449 } 450 451 /** 452 * Close the socket and clean up the state of the class. 453 * Don't use this function without first trying to use QUIT. 454 * @see quit() 455 * @access public 456 * @return void 457 */ 458 public function close() 459 { 460 $this->error = null; // so there is no confusion 461 $this->helo_rply = null; 462 if (!empty($this->smtp_conn)) { 463 // close the connection and cleanup 464 fclose($this->smtp_conn); 465 $this->smtp_conn = 0; 466 } 467 } 468 469 /** 470 * Send an SMTP DATA command. 471 * Issues a data command and sends the msg_data to the server, 472 * finializing the mail transaction. $msg_data is the message 473 * that is to be send with the headers. Each header needs to be 474 * on a single line followed by a <CRLF> with the message headers 475 * and the message body being separated by and additional <CRLF>. 476 * Implements rfc 821: DATA <CRLF> 477 * @param string $msg_data Message data to send 478 * @access public 479 * @return bool 480 */ 481 public function data($msg_data) 482 { 483 if (!$this->sendCommand('DATA', 'DATA', 354)) { 484 return false; 485 } 486 487 /* The server is ready to accept data! 488 * according to rfc821 we should not send more than 1000 489 * including the CRLF 490 * characters on a single line so we will break the data up 491 * into lines by \r and/or \n then if needed we will break 492 * each of those into smaller lines to fit within the limit. 493 * in addition we will be looking for lines that start with 494 * a period '.' and append and additional period '.' to that 495 * line. NOTE: this does not count towards limit. 365 496 */ 366 require_once('ntlm_sasl_client.php'); 367 $temp = new stdClass(); 368 $ntlm_client = new ntlm_sasl_client_class; 369 if(! $ntlm_client->Initialize($temp)){//let's test if every function its available 370 $this->error = array("error" => $temp->error); 371 if($this->do_debug >= 1) { 372 $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF); 373 } 497 498 // Normalize the line breaks before exploding 499 $msg_data = str_replace("\r\n", "\n", $msg_data); 500 $msg_data = str_replace("\r", "\n", $msg_data); 501 $lines = explode("\n", $msg_data); 502 503 /* We need to find a good way to determine if headers are 504 * in the msg_data or if it is a straight msg body 505 * currently I am assuming rfc822 definitions of msg headers 506 * and if the first field of the first line (':' separated) 507 * does not contain a space then it _should_ be a header 508 * and we can process all lines before a blank "" line as 509 * headers. 510 */ 511 512 $field = substr($lines[0], 0, strpos($lines[0], ':')); 513 $in_headers = false; 514 if (!empty($field) && !strstr($field, ' ')) { 515 $in_headers = true; 516 } 517 518 //RFC 2822 section 2.1.1 limit 519 $max_line_length = 998; 520 521 foreach ($lines as $line) { 522 $lines_out = null; 523 if ($line == '' && $in_headers) { 524 $in_headers = false; 525 } 526 // ok we need to break this line up into several smaller lines 527 while (strlen($line) > $max_line_length) { 528 $pos = strrpos(substr($line, 0, $max_line_length), ' '); 529 530 // Patch to fix DOS attack 531 if (!$pos) { 532 $pos = $max_line_length - 1; 533 $lines_out[] = substr($line, 0, $pos); 534 $line = substr($line, $pos); 535 } else { 536 $lines_out[] = substr($line, 0, $pos); 537 $line = substr($line, $pos + 1); 538 } 539 540 /* If processing headers add a LWSP-char to the front of new line 541 * rfc822 on long msg headers 542 */ 543 if ($in_headers) { 544 $line = "\t" . $line; 545 } 546 } 547 $lines_out[] = $line; 548 549 // send the lines to the server 550 while (list(, $line_out) = @each($lines_out)) { 551 if (strlen($line_out) > 0) { 552 if (substr($line_out, 0, 1) == '.') { 553 $line_out = '.' . $line_out; 554 } 555 } 556 $this->client_send($line_out . self::CRLF); 557 } 558 } 559 560 // Message data has been sent, complete the command 561 return $this->sendCommand('DATA END', '.', 250); 562 } 563 564 /** 565 * Send an SMTP HELO or EHLO command. 566 * Used to identify the sending server to the receiving server. 567 * This makes sure that client and server are in a known state. 568 * Implements from RFC 821: HELO <SP> <domain> <CRLF> 569 * and RFC 2821 EHLO. 570 * @param string $host The host name or IP to connect to 571 * @access public 572 * @return bool 573 */ 574 public function hello($host = '') 575 { 576 // Try extended hello first (RFC 2821) 577 if (!$this->sendHello('EHLO', $host)) { 578 if (!$this->sendHello('HELO', $host)) { 579 return false; 580 } 581 } 582 583 return true; 584 } 585 586 /** 587 * Send an SMTP HELO or EHLO command. 588 * Low-level implementation used by hello() 589 * @see hello() 590 * @param string $hello The HELO string 591 * @param string $host The hostname to say we are 592 * @access protected 593 * @return bool 594 */ 595 protected function sendHello($hello, $host) 596 { 597 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 598 $this->helo_rply = $this->last_reply; 599 return $noerror; 600 } 601 602 /** 603 * Send an SMTP MAIL command. 604 * Starts a mail transaction from the email address specified in 605 * $from. Returns true if successful or false otherwise. If True 606 * the mail transaction is started and then one or more recipient 607 * commands may be called followed by a data command. 608 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 609 * @param string $from Source address of this message 610 * @access public 611 * @return bool 612 */ 613 public function mail($from) 614 { 615 $useVerp = ($this->do_verp ? ' XVERP' : ''); 616 return $this->sendCommand( 617 'MAIL FROM', 618 'MAIL FROM:<' . $from . '>' . $useVerp, 619 250 620 ); 621 } 622 623 /** 624 * Send an SMTP QUIT command. 625 * Closes the socket if there is no error or the $close_on_error argument is true. 626 * Implements from rfc 821: QUIT <CRLF> 627 * @param bool $close_on_error Should the connection close if an error occurs? 628 * @access public 629 * @return bool 630 */ 631 public function quit($close_on_error = true) 632 { 633 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 634 $e = $this->error; //Save any error 635 if ($noerror or $close_on_error) { 636 $this->close(); 637 $this->error = $e; //Restore any error from the quit command 638 } 639 return $noerror; 640 } 641 642 /** 643 * Send an SMTP RCPT command. 644 * Sets the TO argument to $to. 645 * Returns true if the recipient was accepted false if it was rejected. 646 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 647 * @param string $to The address the message is being sent to 648 * @access public 649 * @return bool 650 */ 651 public function recipient($to) 652 { 653 return $this->sendCommand( 654 'RCPT TO ', 655 'RCPT TO:<' . $to . '>', 656 array(250, 251) 657 ); 658 } 659 660 /** 661 * Send an SMTP RSET command. 662 * Abort any transaction that is currently in progress. 663 * Implements rfc 821: RSET <CRLF> 664 * @access public 665 * @return bool True on success. 666 */ 667 public function reset() 668 { 669 return $this->sendCommand('RSET', 'RSET', 250); 670 } 671 672 /** 673 * Send a command to an SMTP server and check its return code. 674 * @param string $command The command name - not sent to the server 675 * @param string $commandstring The actual command to send 676 * @param int|array $expect One or more expected integer success codes 677 * @access protected 678 * @return bool True on success. 679 */ 680 protected function sendCommand($command, $commandstring, $expect) 681 { 682 if (!$this->connected()) { 683 $this->error = array( 684 "error" => "Called $command without being connected" 685 ); 374 686 return false; 375 687 } 376 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 377 378 fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF); 379 380 $rply = $this->get_lines(); 381 $code = substr($rply,0,3); 382 383 384 if($code != 334) { 385 $this->error = 386 array("error" => "AUTH not accepted from server", 387 "smtp_code" => $code, 388 "smtp_msg" => substr($rply,4)); 389 if($this->do_debug >= 1) { 390 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); 688 $this->client_send($commandstring . self::CRLF); 689 690 $reply = $this->get_lines(); 691 $code = substr($reply, 0, 3); 692 693 if ($this->do_debug >= 2) { 694 $this->edebug('SMTP -> FROM SERVER:' . $reply); 695 } 696 697 if (!in_array($code, (array)$expect)) { 698 $this->last_reply = null; 699 $this->error = array( 700 "error" => "$command command failed", 701 "smtp_code" => $code, 702 "detail" => substr($reply, 4) 703 ); 704 if ($this->do_debug >= 1) { 705 $this->edebug( 706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply 707 ); 391 708 } 392 709 return false; 393 710 } 394 395 $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2 396 $challange = base64_decode($challange); 397 $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password); 398 $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3 399 // Send encoded username 400 fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF); 401 402 $rply = $this->get_lines(); 403 $code = substr($rply,0,3); 404 405 if($code != 235) { 406 $this->error = 407 array("error" => "Could not authenticate", 408 "smtp_code" => $code, 409 "smtp_msg" => substr($rply,4)); 410 if($this->do_debug >= 1) { 411 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); 412 } 413 return false; 414 } 415 break; 416 } 417 return true; 418 } 419 420 /** 421 * Returns true if connected to a server otherwise false 422 * @access public 423 * @return bool 424 */ 425 public function Connected() { 426 if(!empty($this->smtp_conn)) { 427 $sock_status = socket_get_status($this->smtp_conn); 428 if($sock_status["eof"]) { 429 // the socket is valid but we are not connected 430 if($this->do_debug >= 1) { 431 $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"); 432 } 433 $this->Close(); 711 712 $this->last_reply = $reply; 713 $this->error = null; 714 return true; 715 } 716 717 /** 718 * Send an SMTP SAML command. 719 * Starts a mail transaction from the email address specified in $from. 720 * Returns true if successful or false otherwise. If True 721 * the mail transaction is started and then one or more recipient 722 * commands may be called followed by a data command. This command 723 * will send the message to the users terminal if they are logged 724 * in and send them an email. 725 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 726 * @param string $from The address the message is from 727 * @access public 728 * @return bool 729 */ 730 public function sendAndMail($from) 731 { 732 return $this->sendCommand("SAML", "SAML FROM:$from", 250); 733 } 734 735 /** 736 * Send an SMTP VRFY command. 737 * @param string $name The name to verify 738 * @access public 739 * @return bool 740 */ 741 public function verify($name) 742 { 743 return $this->sendCommand("VRFY", "VRFY $name", array(250, 251)); 744 } 745 746 /** 747 * Send an SMTP NOOP command. 748 * Used to keep keep-alives alive, doesn't actually do anything 749 * @access public 750 * @return bool 751 */ 752 public function noop() 753 { 754 return $this->sendCommand("NOOP", "NOOP", 250); 755 } 756 757 /** 758 * Send an SMTP TURN command. 759 * This is an optional command for SMTP that this class does not support. 760 * This method is here to make the RFC821 Definition 761 * complete for this class and __may__ be implemented in future 762 * Implements from rfc 821: TURN <CRLF> 763 * @access public 764 * @return bool 765 */ 766 public function turn() 767 { 768 $this->error = array( 769 'error' => 'The SMTP TURN command is not implemented' 770 ); 771 if ($this->do_debug >= 1) { 772 $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); 773 } 434 774 return false; 435 } 436 return true; // everything looks good 437 } 438 return false; 439 } 440 441 /** 442 * Closes the socket and cleans up the state of the class. 443 * It is not considered good to use this function without 444 * first trying to use QUIT. 445 * @access public 446 * @return void 447 */ 448 public function Close() { 449 $this->error = null; // so there is no confusion 450 $this->helo_rply = null; 451 if(!empty($this->smtp_conn)) { 452 // close the connection and cleanup 453 fclose($this->smtp_conn); 454 $this->smtp_conn = 0; 455 } 456 } 457 458 ///////////////////////////////////////////////// 459 // SMTP COMMANDS 460 ///////////////////////////////////////////////// 461 462 /** 463 * Issues a data command and sends the msg_data to the server 464 * finializing the mail transaction. $msg_data is the message 465 * that is to be send with the headers. Each header needs to be 466 * on a single line followed by a <CRLF> with the message headers 467 * and the message body being seperated by and additional <CRLF>. 468 * 469 * Implements rfc 821: DATA <CRLF> 470 * 471 * SMTP CODE INTERMEDIATE: 354 472 * [data] 473 * <CRLF>.<CRLF> 474 * SMTP CODE SUCCESS: 250 475 * SMTP CODE FAILURE: 552,554,451,452 476 * SMTP CODE FAILURE: 451,554 477 * SMTP CODE ERROR : 500,501,503,421 478 * @access public 479 * @param string $msg_data 480 * @return bool 481 */ 482 public function Data($msg_data) { 483 $this->error = null; // so no confusion is caused 484 485 if(!$this->connected()) { 486 $this->error = array( 487 "error" => "Called Data() without being connected"); 488 return false; 489 } 490 491 fputs($this->smtp_conn,"DATA" . $this->CRLF); 492 493 $rply = $this->get_lines(); 494 $code = substr($rply,0,3); 495 496 if($this->do_debug >= 2) { 497 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 498 } 499 500 if($code != 354) { 501 $this->error = 502 array("error" => "DATA command not accepted from server", 503 "smtp_code" => $code, 504 "smtp_msg" => substr($rply,4)); 505 if($this->do_debug >= 1) { 506 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 507 } 508 return false; 509 } 510 511 /* the server is ready to accept data! 512 * according to rfc 821 we should not send more than 1000 513 * including the CRLF 514 * characters on a single line so we will break the data up 515 * into lines by \r and/or \n then if needed we will break 516 * each of those into smaller lines to fit within the limit. 517 * in addition we will be looking for lines that start with 518 * a period '.' and append and additional period '.' to that 519 * line. NOTE: this does not count towards limit. 520 */ 521 522 // normalize the line breaks so we know the explode works 523 $msg_data = str_replace("\r\n","\n",$msg_data); 524 $msg_data = str_replace("\r","\n",$msg_data); 525 $lines = explode("\n",$msg_data); 526 527 /* we need to find a good way to determine is headers are 528 * in the msg_data or if it is a straight msg body 529 * currently I am assuming rfc 822 definitions of msg headers 530 * and if the first field of the first line (':' sperated) 531 * does not contain a space then it _should_ be a header 532 * and we can process all lines before a blank "" line as 533 * headers. 534 */ 535 536 $field = substr($lines[0],0,strpos($lines[0],":")); 537 $in_headers = false; 538 if(!empty($field) && !strstr($field," ")) { 539 $in_headers = true; 540 } 541 542 $max_line_length = 998; // used below; set here for ease in change 543 544 while(list(,$line) = @each($lines)) { 545 $lines_out = null; 546 if($line == "" && $in_headers) { 547 $in_headers = false; 548 } 549 // ok we need to break this line up into several smaller lines 550 while(strlen($line) > $max_line_length) { 551 $pos = strrpos(substr($line,0,$max_line_length)," "); 552 553 // Patch to fix DOS attack 554 if(!$pos) { 555 $pos = $max_line_length - 1; 556 $lines_out[] = substr($line,0,$pos); 557 $line = substr($line,$pos); 558 } else { 559 $lines_out[] = substr($line,0,$pos); 560 $line = substr($line,$pos + 1); 561 } 562 563 /* if processing headers add a LWSP-char to the front of new line 564 * rfc 822 on long msg headers 565 */ 566 if($in_headers) { 567 $line = "\t" . $line; 568 } 569 } 570 $lines_out[] = $line; 571 572 // send the lines to the server 573 while(list(,$line_out) = @each($lines_out)) { 574 if(strlen($line_out) > 0) 575 { 576 if(substr($line_out, 0, 1) == ".") { 577 $line_out = "." . $line_out; 578 } 579 } 580 fputs($this->smtp_conn,$line_out . $this->CRLF); 581 } 582 } 583 584 // message data has been sent 585 fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); 586 587 $rply = $this->get_lines(); 588 $code = substr($rply,0,3); 589 590 if($this->do_debug >= 2) { 591 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 592 } 593 594 if($code != 250) { 595 $this->error = 596 array("error" => "DATA not accepted from server", 597 "smtp_code" => $code, 598 "smtp_msg" => substr($rply,4)); 599 if($this->do_debug >= 1) { 600 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 601 } 602 return false; 603 } 604 return true; 605 } 606 607 /** 608 * Sends the HELO command to the smtp server. 609 * This makes sure that we and the server are in 610 * the same known state. 611 * 612 * Implements from rfc 821: HELO <SP> <domain> <CRLF> 613 * 614 * SMTP CODE SUCCESS: 250 615 * SMTP CODE ERROR : 500, 501, 504, 421 616 * @access public 617 * @param string $host 618 * @return bool 619 */ 620 public function Hello($host = '') { 621 $this->error = null; // so no confusion is caused 622 623 if(!$this->connected()) { 624 $this->error = array( 625 "error" => "Called Hello() without being connected"); 626 return false; 627 } 628 629 // if hostname for HELO was not specified send default 630 if(empty($host)) { 631 // determine appropriate default to send to server 632 $host = "localhost"; 633 } 634 635 // Send extended hello first (RFC 2821) 636 if(!$this->SendHello("EHLO", $host)) { 637 if(!$this->SendHello("HELO", $host)) { 638 return false; 639 } 640 } 641 642 return true; 643 } 644 645 /** 646 * Sends a HELO/EHLO command. 647 * @access private 648 * @param string $hello 649 * @param string $host 650 * @return bool 651 */ 652 private function SendHello($hello, $host) { 653 fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); 654 655 $rply = $this->get_lines(); 656 $code = substr($rply,0,3); 657 658 if($this->do_debug >= 2) { 659 $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '<br />'); 660 } 661 662 if($code != 250) { 663 $this->error = 664 array("error" => $hello . " not accepted from server", 665 "smtp_code" => $code, 666 "smtp_msg" => substr($rply,4)); 667 if($this->do_debug >= 1) { 668 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 669 } 670 return false; 671 } 672 673 $this->helo_rply = $rply; 674 675 return true; 676 } 677 678 /** 679 * Starts a mail transaction from the email address specified in 680 * $from. Returns true if successful or false otherwise. If True 681 * the mail transaction is started and then one or more Recipient 682 * commands may be called followed by a Data command. 683 * 684 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 685 * 686 * SMTP CODE SUCCESS: 250 687 * SMTP CODE SUCCESS: 552,451,452 688 * SMTP CODE SUCCESS: 500,501,421 689 * @access public 690 * @param string $from 691 * @return bool 692 */ 693 public function Mail($from) { 694 $this->error = null; // so no confusion is caused 695 696 if(!$this->connected()) { 697 $this->error = array( 698 "error" => "Called Mail() without being connected"); 699 return false; 700 } 701 702 $useVerp = ($this->do_verp ? " XVERP" : ""); 703 fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); 704 705 $rply = $this->get_lines(); 706 $code = substr($rply,0,3); 707 708 if($this->do_debug >= 2) { 709 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 710 } 711 712 if($code != 250) { 713 $this->error = 714 array("error" => "MAIL not accepted from server", 715 "smtp_code" => $code, 716 "smtp_msg" => substr($rply,4)); 717 if($this->do_debug >= 1) { 718 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 719 } 720 return false; 721 } 722 return true; 723 } 724 725 /** 726 * Sends the quit command to the server and then closes the socket 727 * if there is no error or the $close_on_error argument is true. 728 * 729 * Implements from rfc 821: QUIT <CRLF> 730 * 731 * SMTP CODE SUCCESS: 221 732 * SMTP CODE ERROR : 500 733 * @access public 734 * @param bool $close_on_error 735 * @return bool 736 */ 737 public function Quit($close_on_error = true) { 738 $this->error = null; // so there is no confusion 739 740 if(!$this->connected()) { 741 $this->error = array( 742 "error" => "Called Quit() without being connected"); 743 return false; 744 } 745 746 // send the quit command to the server 747 fputs($this->smtp_conn,"quit" . $this->CRLF); 748 749 // get any good-bye messages 750 $byemsg = $this->get_lines(); 751 752 if($this->do_debug >= 2) { 753 $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '<br />'); 754 } 755 756 $rval = true; 757 $e = null; 758 759 $code = substr($byemsg,0,3); 760 if($code != 221) { 761 // use e as a tmp var cause Close will overwrite $this->error 762 $e = array("error" => "SMTP server rejected quit command", 763 "smtp_code" => $code, 764 "smtp_rply" => substr($byemsg,4)); 765 $rval = false; 766 if($this->do_debug >= 1) { 767 $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '<br />'); 768 } 769 } 770 771 if(empty($e) || $close_on_error) { 772 $this->Close(); 773 } 774 775 return $rval; 776 } 777 778 /** 779 * Sends the command RCPT to the SMTP server with the TO: argument of $to. 780 * Returns true if the recipient was accepted false if it was rejected. 781 * 782 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 783 * 784 * SMTP CODE SUCCESS: 250,251 785 * SMTP CODE FAILURE: 550,551,552,553,450,451,452 786 * SMTP CODE ERROR : 500,501,503,421 787 * @access public 788 * @param string $to 789 * @return bool 790 */ 791 public function Recipient($to) { 792 $this->error = null; // so no confusion is caused 793 794 if(!$this->connected()) { 795 $this->error = array( 796 "error" => "Called Recipient() without being connected"); 797 return false; 798 } 799 800 fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); 801 802 $rply = $this->get_lines(); 803 $code = substr($rply,0,3); 804 805 if($this->do_debug >= 2) { 806 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 807 } 808 809 if($code != 250 && $code != 251) { 810 $this->error = 811 array("error" => "RCPT not accepted from server", 812 "smtp_code" => $code, 813 "smtp_msg" => substr($rply,4)); 814 if($this->do_debug >= 1) { 815 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 816 } 817 return false; 818 } 819 return true; 820 } 821 822 /** 823 * Sends the RSET command to abort and transaction that is 824 * currently in progress. Returns true if successful false 825 * otherwise. 826 * 827 * Implements rfc 821: RSET <CRLF> 828 * 829 * SMTP CODE SUCCESS: 250 830 * SMTP CODE ERROR : 500,501,504,421 831 * @access public 832 * @return bool 833 */ 834 public function Reset() { 835 $this->error = null; // so no confusion is caused 836 837 if(!$this->connected()) { 838 $this->error = array( 839 "error" => "Called Reset() without being connected"); 840 return false; 841 } 842 843 fputs($this->smtp_conn,"RSET" . $this->CRLF); 844 845 $rply = $this->get_lines(); 846 $code = substr($rply,0,3); 847 848 if($this->do_debug >= 2) { 849 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 850 } 851 852 if($code != 250) { 853 $this->error = 854 array("error" => "RSET failed", 855 "smtp_code" => $code, 856 "smtp_msg" => substr($rply,4)); 857 if($this->do_debug >= 1) { 858 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 859 } 860 return false; 861 } 862 863 return true; 864 } 865 866 /** 867 * Starts a mail transaction from the email address specified in 868 * $from. Returns true if successful or false otherwise. If True 869 * the mail transaction is started and then one or more Recipient 870 * commands may be called followed by a Data command. This command 871 * will send the message to the users terminal if they are logged 872 * in and send them an email. 873 * 874 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 875 * 876 * SMTP CODE SUCCESS: 250 877 * SMTP CODE SUCCESS: 552,451,452 878 * SMTP CODE SUCCESS: 500,501,502,421 879 * @access public 880 * @param string $from 881 * @return bool 882 */ 883 public function SendAndMail($from) { 884 $this->error = null; // so no confusion is caused 885 886 if(!$this->connected()) { 887 $this->error = array( 888 "error" => "Called SendAndMail() without being connected"); 889 return false; 890 } 891 892 fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); 893 894 $rply = $this->get_lines(); 895 $code = substr($rply,0,3); 896 897 if($this->do_debug >= 2) { 898 $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />'); 899 } 900 901 if($code != 250) { 902 $this->error = 903 array("error" => "SAML not accepted from server", 904 "smtp_code" => $code, 905 "smtp_msg" => substr($rply,4)); 906 if($this->do_debug >= 1) { 907 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />'); 908 } 909 return false; 910 } 911 return true; 912 } 913 914 /** 915 * This is an optional command for SMTP that this class does not 916 * support. This method is here to make the RFC821 Definition 917 * complete for this class and __may__ be implimented in the future 918 * 919 * Implements from rfc 821: TURN <CRLF> 920 * 921 * SMTP CODE SUCCESS: 250 922 * SMTP CODE FAILURE: 502 923 * SMTP CODE ERROR : 500, 503 924 * @access public 925 * @return bool 926 */ 927 public function Turn() { 928 $this->error = array("error" => "This method, TURN, of the SMTP ". 929 "is not implemented"); 930 if($this->do_debug >= 1) { 931 $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '<br />'); 932 } 933 return false; 934 } 935 936 /** 937 * Get the current error 938 * @access public 939 * @return array 940 */ 941 public function getError() { 942 return $this->error; 943 } 944 945 ///////////////////////////////////////////////// 946 // INTERNAL FUNCTIONS 947 ///////////////////////////////////////////////// 948 949 /** 950 * Read in as many lines as possible 951 * either before eof or socket timeout occurs on the operation. 952 * With SMTP we can tell if we have more lines to read if the 953 * 4th character is '-' symbol. If it is a space then we don't 954 * need to read anything else. 955 * @access private 956 * @return string 957 */ 958 private function get_lines() { 959 $data = ""; 960 $endtime = 0; 961 /* If for some reason the fp is bad, don't inf loop */ 962 if (!is_resource($this->smtp_conn)) { 963 return $data; 964 } 965 stream_set_timeout($this->smtp_conn, $this->Timeout); 966 if ($this->Timelimit > 0) { 967 $endtime = time() + $this->Timelimit; 968 } 969 while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 970 $str = @fgets($this->smtp_conn,515); 971 if($this->do_debug >= 4) { 972 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />'); 973 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />'); 974 } 975 $data .= $str; 976 if($this->do_debug >= 4) { 977 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />'); 978 } 979 // if 4th character is a space, we are done reading, break the loop 980 if(substr($str,3,1) == " ") { break; } 981 // Timed-out? Log and break 982 $info = stream_get_meta_data($this->smtp_conn); 983 if ($info['timed_out']) { 984 if($this->do_debug >= 4) { 985 $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds) <br />"); 986 } 987 break; 988 } 989 // Now check if reads took too long 990 if ($endtime) { 991 if (time() > $endtime) { 992 if($this->do_debug >= 4) { 993 $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds) <br />"); 994 } 995 break; 996 } 997 } 998 } 999 return $data; 1000 } 1001 775 } 776 777 /** 778 * Send raw data to the server. 779 * @param string $data The data to send 780 * @access public 781 * @return int|bool The number of bytes sent to the server or FALSE on error 782 */ 783 public function client_send($data) 784 { 785 if ($this->do_debug >= 1) { 786 $this->edebug("CLIENT -> SMTP: $data"); 787 } 788 return fwrite($this->smtp_conn, $data); 789 } 790 791 /** 792 * Get the latest error. 793 * @access public 794 * @return array 795 */ 796 public function getError() 797 { 798 return $this->error; 799 } 800 801 /** 802 * Get the last reply from the server. 803 * @access public 804 * @return string 805 */ 806 public function getLastReply() 807 { 808 return $this->last_reply; 809 } 810 811 /** 812 * Read the SMTP server's response. 813 * Either before eof or socket timeout occurs on the operation. 814 * With SMTP we can tell if we have more lines to read if the 815 * 4th character is '-' symbol. If it is a space then we don't 816 * need to read anything else. 817 * @access protected 818 * @return string 819 */ 820 protected function get_lines() 821 { 822 $data = ''; 823 $endtime = 0; 824 // If the connection is bad, give up now 825 if (!is_resource($this->smtp_conn)) { 826 return $data; 827 } 828 stream_set_timeout($this->smtp_conn, $this->Timeout); 829 if ($this->Timelimit > 0) { 830 $endtime = time() + $this->Timelimit; 831 } 832 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 833 $str = @fgets($this->smtp_conn, 515); 834 if ($this->do_debug >= 4) { 835 $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); 836 $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); 837 } 838 $data .= $str; 839 if ($this->do_debug >= 4) { 840 $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); 841 } 842 // if 4th character is a space, we are done reading, break the loop 843 if (substr($str, 3, 1) == ' ') { 844 break; 845 } 846 // Timed-out? Log and break 847 $info = stream_get_meta_data($this->smtp_conn); 848 if ($info['timed_out']) { 849 if ($this->do_debug >= 4) { 850 $this->edebug( 851 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' 852 ); 853 } 854 break; 855 } 856 // Now check if reads took too long 857 if ($endtime) { 858 if (time() > $endtime) { 859 if ($this->do_debug >= 4) { 860 $this->edebug( 861 'SMTP -> get_lines(): timelimit reached (' 862 . $this->Timelimit . ' sec)' 863 ); 864 } 865 break; 866 } 867 } 868 } 869 return $data; 870 } 871 872 /** 873 * Enable or disable VERP address generation. 874 * @param bool $enabled 875 */ 876 public function setVerp($enabled = false) 877 { 878 $this->do_verp = $enabled; 879 } 880 881 /** 882 * Get VERP address generation mode. 883 * @return bool 884 */ 885 public function getVerp() 886 { 887 return $this->do_verp; 888 } 889 890 /** 891 * Set debug output method. 892 * @param string $method The function/method to use for debugging output. 893 */ 894 public function setDebugOutput($method = 'echo') 895 { 896 $this->Debugoutput = $method; 897 } 898 899 /** 900 * Get debug output method. 901 * @return string 902 */ 903 public function getDebugOutput() 904 { 905 return $this->Debugoutput; 906 } 907 908 /** 909 * Set debug output level. 910 * @param int $level 911 */ 912 public function setDebugLevel($level = 0) 913 { 914 $this->do_debug = $level; 915 } 916 917 /** 918 * Get debug output level. 919 * @return int 920 */ 921 public function getDebugLevel() 922 { 923 return $this->do_debug; 924 } 925 926 /** 927 * Set SMTP timeout. 928 * @param int $timeout 929 */ 930 public function setTimeout($timeout = 0) 931 { 932 $this->Timeout = $timeout; 933 } 934 935 /** 936 * Get SMTP timeout. 937 * @return int 938 */ 939 public function getTimeout() 940 { 941 return $this->Timeout; 942 } 1002 943 } 1003 ?> -
trunk/tests/phpunit/includes/mock-mailer.php
r25002 r27385 5 5 var $mock_sent = array(); 6 6 7 // override the Send function so it doesn't actually send anything 8 function Send() { 7 /** 8 * Override send() so mail isn't actually sent. 9 */ 10 function send() { 9 11 try { 10 if ( ! $this-> PreSend() )12 if ( ! $this->preSend() ) 11 13 return false; 12 14
Note: See TracChangeset
for help on using the changeset viewer.