Ticket #28909: 28909v3.diff
File 28909v3.diff, 164.8 KB (added by , 9 years ago) |
---|
-
class-phpmailer.php
1 1 <?php 2 2 /** 3 3 * PHPMailer - PHP email creation and transport class. 4 * PHP Version 5.0.0 5 * Version 5.2.7 4 * PHP Version 5 6 5 * @package PHPMailer 7 * @link https://github.com/PHPMailer/PHPMailer/ 8 * @author Marcus Bointon ( coolbru) <phpmailer@synchromedia.co.uk>6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 9 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 10 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 11 10 * @author Brent R. Matzelle (original founder) 12 * @copyright 201 3Marcus Bointon11 * @copyright 2012 - 2014 Marcus Bointon 13 12 * @copyright 2010 - 2012 Jim Jagielski 14 13 * @copyright 2004 - 2009 Andy Prevost 15 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License … … 18 17 * FITNESS FOR A PARTICULAR PURPOSE. 19 18 */ 20 19 21 if (version_compare(PHP_VERSION, '5.0.0', '<')) {22 exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");23 }24 25 20 /** 26 21 * PHPMailer - PHP email creation and transport class. 27 * PHP Version 5.0.028 22 * @package PHPMailer 29 * @author Marcus Bointon ( coolbru) <phpmailer@synchromedia.co.uk>23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 30 24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 31 25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 32 26 * @author Brent R. Matzelle (original founder) 33 * @copyright 2013 Marcus Bointon34 * @copyright 2010 - 2012 Jim Jagielski35 * @copyright 2004 - 2009 Andy Prevost36 27 */ 37 28 class PHPMailer 38 29 { … … 40 31 * The PHPMailer Version number. 41 32 * @type string 42 33 */ 43 public $Version = '5.2. 7';34 public $Version = '5.2.10'; 44 35 45 36 /** 46 37 * Email priority. 47 38 * Options: 1 = High, 3 = Normal, 5 = low. 48 * @type int 39 * @type integer 49 40 */ 50 41 public $Priority = 3; 51 42 … … 97 88 * The Return-Path of the message. 98 89 * If empty, it will be set to either From or Sender. 99 90 * @type string 91 * @deprecated Email senders should never set a return-path header; 92 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. 93 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference 100 94 */ 101 95 public $ReturnPath = ''; 102 96 … … 155 149 156 150 /** 157 151 * Word-wrap the message body to this number of chars. 158 * @type int 152 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. 153 * @type integer 159 154 */ 160 155 public $WordWrap = 0; 161 156 … … 175 170 /** 176 171 * Whether mail() uses a fully sendmail-compatible MTA. 177 172 * One which supports sendmail's "-oi -f" options. 178 * @type bool 173 * @type boolean 179 174 */ 180 175 public $UseSendmailOptions = true; 181 176 … … 222 217 * You can also specify a different port 223 218 * for each host by using this format: [hostname:port] 224 219 * (e.g. "smtp1.example.com:25;smtp2.example.com"). 220 * You can also specify encryption type, for example: 221 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). 225 222 * Hosts will be tried in order. 226 223 * @type string 227 224 */ … … 229 226 230 227 /** 231 228 * The default SMTP server port. 232 * @type int 233 * @T odoWhy is this needed when the SMTP class takes care of it?229 * @type integer 230 * @TODO Why is this needed when the SMTP class takes care of it? 234 231 */ 235 232 public $Port = 25; 236 233 … … 243 240 public $Helo = ''; 244 241 245 242 /** 246 * The secure connection prefix.247 * Options: "", "ssl" or "tls"243 * What kind of encryption to use on the SMTP connection. 244 * Options: '', 'ssl' or 'tls' 248 245 * @type string 249 246 */ 250 247 public $SMTPSecure = ''; 251 248 252 249 /** 250 * Whether to enable TLS encryption automatically if a server supports it, 251 * even if `SMTPSecure` is not set to 'tls'. 252 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. 253 * @type boolean 254 */ 255 public $SMTPAutoTLS = true; 256 257 /** 253 258 * Whether to use SMTP authentication. 254 259 * Uses the Username and Password properties. 255 * @type bool 260 * @type boolean 256 261 * @see PHPMailer::$Username 257 262 * @see PHPMailer::$Password 258 263 */ … … 259 264 public $SMTPAuth = false; 260 265 261 266 /** 267 * Options array passed to stream_context_create when connecting via SMTP. 268 * @type array 269 */ 270 public $SMTPOptions = array(); 271 272 /** 262 273 * SMTP username. 263 274 * @type string 264 275 */ … … 293 304 294 305 /** 295 306 * The SMTP server timeout in seconds. 296 * @type int 307 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 308 * @type integer 297 309 */ 298 public $Timeout = 10;310 public $Timeout = 300; 299 311 300 312 /** 301 313 * SMTP class debug output mode. 302 * Options: 0 = off, 1 = commands, 2 = commands and data 303 * @type int 314 * Debug output level. 315 * Options: 316 * * `0` No output 317 * * `1` Commands 318 * * `2` Data and commands 319 * * `3` As 2 plus connection status 320 * * `4` Low-level data output 321 * @type integer 304 322 * @see SMTP::$do_debug 305 323 */ 306 324 public $SMTPDebug = 0; 307 325 308 326 /** 309 * The function/method to use for debugging output. 310 * Options: "echo" or "error_log" 311 * @type string 327 * How to handle debug output. 328 * Options: 329 * * `echo` Output plain-text as-is, appropriate for CLI 330 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 331 * * `error_log` Output to error log as configured in php.ini 332 * 333 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 334 * <code> 335 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 336 * </code> 337 * @type string|callable 312 338 * @see SMTP::$Debugoutput 313 339 */ 314 public $Debugoutput = "echo";340 public $Debugoutput = 'echo'; 315 341 316 342 /** 317 343 * Whether to keep SMTP connection open after each message. 318 344 * If this is set to true then to close the connection 319 345 * requires an explicit call to smtpClose(). 320 * @type bool 346 * @type boolean 321 347 */ 322 348 public $SMTPKeepAlive = false; 323 349 … … 324 350 /** 325 351 * Whether to split multiple to addresses into multiple messages 326 352 * or send them all in one message. 327 * @type bool 353 * @type boolean 328 354 */ 329 355 public $SingleTo = false; 330 356 … … 331 357 /** 332 358 * Storage for addresses when SingleTo is enabled. 333 359 * @type array 334 * @ todoThis should really not be public360 * @TODO This should really not be public 335 361 */ 336 362 public $SingleToArray = array(); 337 363 … … 339 365 * Whether to generate VERP addresses on send. 340 366 * Only applicable when sending via SMTP. 341 367 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 342 * @type bool 368 * @link http://www.postfix.org/VERP_README.html Postfix VERP info 369 * @type boolean 343 370 */ 344 371 public $do_verp = false; 345 372 346 373 /** 347 374 * Whether to allow sending messages with an empty body. 348 * @type bool 375 * @type boolean 349 376 */ 350 377 public $AllowEmpty = false; 351 378 … … 396 423 * The function that handles the result of the send email action. 397 424 * It is called out by send() for each email sent. 398 425 * 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. 426 * Value can be any php callable: http://www.php.net/is_callable 404 427 * 405 428 * Parameters: 406 * bool 429 * boolean $result result of the send action 407 430 * string $to email address of the recipient 408 431 * string $cc cc email addresses 409 432 * string $bcc bcc email addresses … … 410 433 * string $subject the subject 411 434 * string $body the email body 412 435 * string $from email address of sender 413 *414 436 * @type string 415 437 */ 416 438 public $action_function = ''; 417 439 418 440 /** 419 * What to usein the X-Mailer header.420 * Options: null for default, whitespace for none, or a string to use441 * What to put in the X-Mailer header. 442 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use 421 443 * @type string 422 444 */ 423 445 public $XMailer = ''; … … 459 481 460 482 /** 461 483 * An array of all kinds of addresses. 462 * Includes all of $to, $cc, $bcc , $replyto484 * Includes all of $to, $cc, $bcc 463 485 * @type array 464 486 * @access protected 465 487 */ … … 529 551 protected $sign_key_file = ''; 530 552 531 553 /** 554 * The optional S/MIME extra certificates ("CA Chain") file path. 555 * @type string 556 * @access protected 557 */ 558 protected $sign_extracerts_file = ''; 559 560 /** 532 561 * The S/MIME password for the key. 533 562 * Used only if the key is encrypted. 534 563 * @type string … … 538 567 539 568 /** 540 569 * Whether to throw exceptions for errors. 541 * @type bool 570 * @type boolean 542 571 * @access protected 543 572 */ 544 573 protected $exceptions = false; 545 574 546 575 /** 547 * Error severity: message only, continue processing 576 * Unique ID used for message ID and boundaries. 577 * @type string 578 * @access protected 548 579 */ 580 protected $uniqueid = ''; 581 582 /** 583 * Error severity: message only, continue processing. 584 */ 549 585 const STOP_MESSAGE = 0; 550 586 551 587 /** 552 * Error severity: message, likely ok to continue processing 588 * Error severity: message, likely ok to continue processing. 553 589 */ 554 590 const STOP_CONTINUE = 1; 555 591 556 592 /** 557 * Error severity: message, plus full stop, critical error reached 593 * Error severity: message, plus full stop, critical error reached. 558 594 */ 559 595 const STOP_CRITICAL = 2; 560 596 561 597 /** 562 * SMTP RFC standard line ending 598 * SMTP RFC standard line ending. 563 599 */ 564 600 const CRLF = "\r\n"; 565 601 566 602 /** 567 * Constructor568 * @ param bool $exceptions Should we throw external exceptions?603 * The maximum line length allowed by RFC 2822 section 2.1.1 604 * @type integer 569 605 */ 606 const MAX_LINE_LENGTH = 998; 607 608 /** 609 * Constructor. 610 * @param boolean $exceptions Should we throw external exceptions? 611 */ 570 612 public function __construct($exceptions = false) 571 613 { 572 $this->exceptions = ( $exceptions == true);614 $this->exceptions = (boolean)$exceptions; 573 615 } 574 616 575 617 /** … … 577 619 */ 578 620 public function __destruct() 579 621 { 580 if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely 622 //Close any open SMTP connection nicely 623 if ($this->Mailer == 'smtp') { 581 624 $this->smtpClose(); 582 625 } 583 626 } … … 593 636 * @param string $header Additional Header(s) 594 637 * @param string $params Params 595 638 * @access private 596 * @return bool 639 * @return boolean 597 640 */ 598 641 private function mailPassthru($to, $subject, $body, $header, $params) 599 642 { 643 //Check overloading of mail function to avoid double-encoding 644 if (ini_get('mbstring.func_overload') & 1) { 645 $subject = $this->secureHeader($subject); 646 } else { 647 $subject = $this->encodeHeader($this->secureHeader($subject)); 648 } 600 649 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { 601 $r t = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);650 $result = @mail($to, $subject, $body, $header); 602 651 } else { 603 $r t = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);652 $result = @mail($to, $subject, $body, $header, $params); 604 653 } 605 return $r t;654 return $result; 606 655 } 607 656 608 657 /** 609 658 * Output debugging info via user-defined method. 610 * Only if debug output is enabled.659 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). 611 660 * @see PHPMailer::$Debugoutput 612 661 * @see PHPMailer::$SMTPDebug 613 662 * @param string $str … … 614 663 */ 615 664 protected function edebug($str) 616 665 { 617 if ( !$this->SMTPDebug) {666 if ($this->SMTPDebug <= 0) { 618 667 return; 619 668 } 669 //Avoid clash with built-in function names 670 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 671 call_user_func($this->Debugoutput, $str, $this->SMTPDebug); 672 return; 673 } 620 674 switch ($this->Debugoutput) { 621 675 case 'error_log': 676 //Don't output, just log 622 677 error_log($str); 623 678 break; 624 679 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"; 680 //Cleans up output a bit for a better looking, HTML-safe output 681 echo htmlentities( 682 preg_replace('/[\r\n]+/', '', $str), 683 ENT_QUOTES, 684 'UTF-8' 685 ) 686 . "<br>\n"; 627 687 break; 628 688 case 'echo': 629 689 default: 630 //Just echoes exactly what was received 631 echo $str; 690 //Normalize line breaks 691 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 692 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 693 "\n", 694 "\n \t ", 695 trim($str) 696 ) . "\n"; 632 697 } 633 698 } 634 699 635 700 /** 636 701 * Sets message type to HTML or plain. 637 * @param bool $ishtml True for HTML mode.702 * @param boolean $isHtml True for HTML mode. 638 703 * @return void 639 704 */ 640 public function isHTML($is html = true)705 public function isHTML($isHtml = true) 641 706 { 642 if ($is html) {707 if ($isHtml) { 643 708 $this->ContentType = 'text/html'; 644 709 } else { 645 710 $this->ContentType = 'text/plain'; … … 670 735 */ 671 736 public function isSendmail() 672 737 { 673 if (!stristr(ini_get('sendmail_path'), 'sendmail')) { 674 $this->Sendmail = '/var/qmail/bin/sendmail'; 738 $ini_sendmail_path = ini_get('sendmail_path'); 739 740 if (!stristr($ini_sendmail_path, 'sendmail')) { 741 $this->Sendmail = '/usr/sbin/sendmail'; 742 } else { 743 $this->Sendmail = $ini_sendmail_path; 675 744 } 676 745 $this->Mailer = 'sendmail'; 677 746 } … … 682 751 */ 683 752 public function isQmail() 684 753 { 685 if (stristr(ini_get('sendmail_path'), 'qmail')) { 686 $this->Sendmail = '/var/qmail/bin/sendmail'; 754 $ini_sendmail_path = ini_get('sendmail_path'); 755 756 if (!stristr($ini_sendmail_path, 'qmail')) { 757 $this->Sendmail = '/var/qmail/bin/qmail-inject'; 758 } else { 759 $this->Sendmail = $ini_sendmail_path; 687 760 } 688 $this->Mailer = ' sendmail';761 $this->Mailer = 'qmail'; 689 762 } 690 763 691 764 /** … … 692 765 * Add a "To" address. 693 766 * @param string $address 694 767 * @param string $name 695 * @return bool true on success, false if address already used768 * @return boolean true on success, false if address already used 696 769 */ 697 770 public function addAddress($address, $name = '') 698 771 { … … 704 777 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 705 778 * @param string $address 706 779 * @param string $name 707 * @return bool true on success, false if address already used780 * @return boolean true on success, false if address already used 708 781 */ 709 782 public function addCC($address, $name = '') 710 783 { … … 716 789 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 717 790 * @param string $address 718 791 * @param string $name 719 * @return bool true on success, false if address already used792 * @return boolean true on success, false if address already used 720 793 */ 721 794 public function addBCC($address, $name = '') 722 795 { … … 727 800 * Add a "Reply-to" address. 728 801 * @param string $address 729 802 * @param string $name 730 * @return bool 803 * @return boolean 731 804 */ 732 805 public function addReplyTo($address, $name = '') 733 806 { … … 741 814 * @param string $address The email address to send to 742 815 * @param string $name 743 816 * @throws phpmailerException 744 * @return bool true on success, false if address already used or invalid in some way817 * @return boolean true on success, false if address already used or invalid in some way 745 818 * @access protected 746 819 */ 747 820 protected function addAnAddress($kind, $address, $name = '') … … 748 821 { 749 822 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { 750 823 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind); 824 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind); 751 825 if ($this->exceptions) { 752 826 throw new phpmailerException('Invalid recipient array: ' . $kind); 753 827 } 754 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);755 828 return false; 756 829 } 757 830 $address = trim($address); … … 758 831 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 759 832 if (!$this->validateAddress($address)) { 760 833 $this->setError($this->lang('invalid_address') . ': ' . $address); 834 $this->edebug($this->lang('invalid_address') . ': ' . $address); 761 835 if ($this->exceptions) { 762 836 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); 763 837 } 764 $this->edebug($this->lang('invalid_address') . ': ' . $address);765 838 return false; 766 839 } 767 840 if ($kind != 'Reply-To') { … … 783 856 * Set the From and FromName properties. 784 857 * @param string $address 785 858 * @param string $name 786 * @param bool $auto Whether to also set the Sender address, defaults to true859 * @param boolean $auto Whether to also set the Sender address, defaults to true 787 860 * @throws phpmailerException 788 * @return bool 861 * @return boolean 789 862 */ 790 863 public function setFrom($address, $name = '', $auto = true) 791 864 { … … 793 866 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 794 867 if (!$this->validateAddress($address)) { 795 868 $this->setError($this->lang('invalid_address') . ': ' . $address); 869 $this->edebug($this->lang('invalid_address') . ': ' . $address); 796 870 if ($this->exceptions) { 797 871 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); 798 872 } 799 $this->edebug($this->lang('invalid_address') . ': ' . $address);800 873 return false; 801 874 } 802 875 $this->From = $address; … … 825 898 * Check that a string looks like an email address. 826 899 * @param string $address The email address to check 827 900 * @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 901 * * `auto` Pick strictest one automatically; 902 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; 903 * * `pcre` Use old PCRE implementation; 904 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains; 905 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. 906 * * `noregex` Don't use a regex: super fast, really dumb. 907 * @return boolean 834 908 * @static 835 909 * @access public 836 910 */ 837 911 public static function validateAddress($address, $patternselect = 'auto') 838 912 { 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 disabled844 if (version_compare(PCRE_VERSION, '8.0 ') >= 0) {913 if (!$patternselect or $patternselect == 'auto') { 914 //Check this constant first so it works when extension_loaded() is disabled by safe mode 915 //Constant was added in PHP 5.2.4 916 if (defined('PCRE_VERSION')) { 917 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 918 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { 845 919 $patternselect = 'pcre8'; 846 920 } else { 847 921 $patternselect = 'pcre'; 848 922 } 923 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { 924 //Fall back to older PCRE 925 $patternselect = 'pcre'; 849 926 } else { 850 927 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension 851 928 if (version_compare(PHP_VERSION, '5.2.0') >= 0) { … … 858 935 switch ($patternselect) { 859 936 case 'pcre8': 860 937 /** 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 :( 938 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. 864 939 * @link http://squiloople.com/2009/12/20/email-address-validation/ 865 940 * @copyright 2009-2010 Michael Rushton 866 941 * Feel free to use and redistribute this code. But please keep this copyright notice. 867 942 */ 868 return (bool )preg_match(943 return (boolean)preg_match( 869 944 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . 870 945 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . 871 946 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . … … 877 952 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', 878 953 $address 879 954 ); 880 break;881 955 case 'pcre': 882 956 //An older regex that doesn't need a recent PCRE 883 return (bool )preg_match(957 return (boolean)preg_match( 884 958 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . 885 959 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . 886 960 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . … … 893 967 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', 894 968 $address 895 969 ); 896 break; 897 case 'php': 898 default: 899 return (bool)filter_var($address, FILTER_VALIDATE_EMAIL); 900 break; 970 case 'html5': 971 /** 972 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. 973 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) 974 */ 975 return (boolean)preg_match( 976 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . 977 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', 978 $address 979 ); 901 980 case 'noregex': 902 981 //No PCRE! Do something _very_ approximate! 903 982 //Check the address is 3 chars or longer and contains an @ that's not the first or last char … … 904 983 return (strlen($address) >= 3 905 984 and strpos($address, '@') >= 1 906 985 and strpos($address, '@') != strlen($address) - 1); 907 break; 986 case 'php': 987 default: 988 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); 908 989 } 909 990 } 910 991 … … 911 992 /** 912 993 * Create a message and send it. 913 994 * Uses the sending method specified by $Mailer. 914 * Returns false on error - Use the ErrorInfo variable to view description of the error.915 995 * @throws phpmailerException 916 * @return bool 996 * @return boolean false on error - See the ErrorInfo property for details of the error. 917 997 */ 918 998 public function send() 919 999 { … … 922 1002 return false; 923 1003 } 924 1004 return $this->postSend(); 925 } catch (phpmailerException $e ) {1005 } catch (phpmailerException $exc) { 926 1006 $this->mailHeader = ''; 927 $this->setError($e ->getMessage());1007 $this->setError($exc->getMessage()); 928 1008 if ($this->exceptions) { 929 throw $e ;1009 throw $exc; 930 1010 } 931 1011 return false; 932 1012 } … … 935 1015 /** 936 1016 * Prepare a message for sending. 937 1017 * @throws phpmailerException 938 * @return bool 1018 * @return boolean 939 1019 */ 940 1020 public function preSend() 941 1021 { 942 1022 try { 943 $this->mailHeader = "";1023 $this->mailHeader = ''; 944 1024 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { 945 1025 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); 946 1026 } … … 950 1030 $this->ContentType = 'multipart/alternative'; 951 1031 } 952 1032 953 $this->error_count = 0; // reset errors1033 $this->error_count = 0; // Reset errors 954 1034 $this->setMessageType(); 955 1035 // Refuse to send an empty message unless we are specifically allowing it 956 1036 if (!$this->AllowEmpty and empty($this->Body)) { … … 957 1037 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); 958 1038 } 959 1039 1040 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) 1041 $this->MIMEHeader = ''; 1042 $this->MIMEBody = $this->createBody(); 1043 // createBody may have added some headers, so retain them 1044 $tempheaders = $this->MIMEHeader; 960 1045 $this->MIMEHeader = $this->createHeader(); 961 $this->MIME Body = $this->createBody();1046 $this->MIMEHeader .= $tempheaders; 962 1047 963 1048 // To capture the complete message when using mail(), create 964 1049 // an extra header list which createHeader() doesn't fold in 965 1050 if ($this->Mailer == 'mail') { 966 1051 if (count($this->to) > 0) { 967 $this->mailHeader .= $this->addrAppend( "To", $this->to);1052 $this->mailHeader .= $this->addrAppend('To', $this->to); 968 1053 } else { 969 $this->mailHeader .= $this->headerLine( "To", "undisclosed-recipients:;");1054 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); 970 1055 } 971 1056 $this->mailHeader .= $this->headerLine( 972 1057 'Subject', … … 978 1063 if (!empty($this->DKIM_domain) 979 1064 && !empty($this->DKIM_private) 980 1065 && !empty($this->DKIM_selector) 981 && !empty($this->DKIM_domain)982 1066 && file_exists($this->DKIM_private)) { 983 1067 $header_dkim = $this->DKIM_Add( 984 1068 $this->MIMEHeader . $this->mailHeader, … … 989 1073 str_replace("\r\n", "\n", $header_dkim) . self::CRLF; 990 1074 } 991 1075 return true; 992 993 } catch (phpmailerException $e) { 994 $this->setError($e->getMessage()); 1076 } catch (phpmailerException $exc) { 1077 $this->setError($exc->getMessage()); 995 1078 if ($this->exceptions) { 996 throw $e ;1079 throw $exc; 997 1080 } 998 1081 return false; 999 1082 } … … 1003 1086 * Actually send a message. 1004 1087 * Send the email via the selected mechanism 1005 1088 * @throws phpmailerException 1006 * @return bool 1089 * @return boolean 1007 1090 */ 1008 1091 public function postSend() 1009 1092 { … … 1011 1094 // Choose the mailer and send through it 1012 1095 switch ($this->Mailer) { 1013 1096 case 'sendmail': 1097 case 'qmail': 1014 1098 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); 1015 1099 case 'smtp': 1016 1100 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); … … 1017 1101 case 'mail': 1018 1102 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1019 1103 default: 1104 $sendMethod = $this->Mailer.'Send'; 1105 if (method_exists($this, $sendMethod)) { 1106 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); 1107 } 1108 1020 1109 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1021 1110 } 1022 } catch (phpmailerException $e) { 1023 $this->setError($e->getMessage()); 1111 } catch (phpmailerException $exc) { 1112 $this->setError($exc->getMessage()); 1113 $this->edebug($exc->getMessage()); 1024 1114 if ($this->exceptions) { 1025 throw $e ;1115 throw $exc; 1026 1116 } 1027 $this->edebug($e->getMessage() . "\n");1028 1117 } 1029 1118 return false; 1030 1119 } … … 1036 1125 * @see PHPMailer::$Sendmail 1037 1126 * @throws phpmailerException 1038 1127 * @access protected 1039 * @return bool 1128 * @return boolean 1040 1129 */ 1041 1130 protected function sendmailSend($header, $body) 1042 1131 { 1043 1132 if ($this->Sender != '') { 1044 $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1133 if ($this->Mailer == 'qmail') { 1134 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1135 } else { 1136 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1137 } 1045 1138 } else { 1046 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 1139 if ($this->Mailer == 'qmail') { 1140 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); 1141 } else { 1142 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); 1143 } 1047 1144 } 1048 if ($this->SingleTo === true) {1049 foreach ($this->SingleToArray as $ val) {1145 if ($this->SingleTo) { 1146 foreach ($this->SingleToArray as $toAddr) { 1050 1147 if (!@$mail = popen($sendmail, 'w')) { 1051 1148 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1052 1149 } 1053 fputs($mail, "To: " . $val. "\n");1150 fputs($mail, 'To: ' . $toAddr . "\n"); 1054 1151 fputs($mail, $header); 1055 1152 fputs($mail, $body); 1056 1153 $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); 1154 $this->doCallback( 1155 ($result == 0), 1156 array($toAddr), 1157 $this->cc, 1158 $this->bcc, 1159 $this->Subject, 1160 $body, 1161 $this->From 1162 ); 1060 1163 if ($result != 0) { 1061 1164 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1062 1165 } … … 1068 1171 fputs($mail, $header); 1069 1172 fputs($mail, $body); 1070 1173 $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); 1174 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1074 1175 if ($result != 0) { 1075 1176 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1076 1177 } … … 1085 1186 * @link http://www.php.net/manual/en/book.mail.php 1086 1187 * @throws phpmailerException 1087 1188 * @access protected 1088 * @return bool 1189 * @return boolean 1089 1190 */ 1090 1191 protected function mailSend($header, $body) 1091 1192 { 1092 1193 $toArr = array(); 1093 foreach ($this->to as $t ) {1094 $toArr[] = $this->addrFormat($t );1194 foreach ($this->to as $toaddr) { 1195 $toArr[] = $this->addrFormat($toaddr); 1095 1196 } 1096 1197 $to = implode(', ', $toArr); 1097 1198 1098 1199 if (empty($this->Sender)) { 1099 $params = " ";1200 $params = ' '; 1100 1201 } else { 1101 $params = sprintf( "-f%s", $this->Sender);1202 $params = sprintf('-f%s', $this->Sender); 1102 1203 } 1103 1204 if ($this->Sender != '' and !ini_get('safe_mode')) { 1104 1205 $old_from = ini_get('sendmail_from'); 1105 1206 ini_set('sendmail_from', $this->Sender); 1106 1207 } 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); 1208 $result = false; 1209 if ($this->SingleTo && count($toArr) > 1) { 1210 foreach ($toArr as $toAddr) { 1211 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 1212 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1114 1213 } 1115 1214 } 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); 1215 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); 1216 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1120 1217 } 1121 1218 if (isset($old_from)) { 1122 1219 ini_set('sendmail_from', $old_from); 1123 1220 } 1124 if (!$r t) {1221 if (!$result) { 1125 1222 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); 1126 1223 } 1127 1224 return true; … … 1135 1232 public function getSMTPInstance() 1136 1233 { 1137 1234 if (!is_object($this->smtp)) { 1138 require_once 'class-smtp.php';1235 require_once( 'class-smtp.php' ); 1139 1236 $this->smtp = new SMTP; 1140 1237 } 1141 1238 return $this->smtp; … … 1151 1248 * @throws phpmailerException 1152 1249 * @uses SMTP 1153 1250 * @access protected 1154 * @return bool 1251 * @return boolean 1155 1252 */ 1156 1253 protected function smtpSend($header, $body) 1157 1254 { 1158 1255 $bad_rcpt = array(); 1159 1160 if (!$this->smtpConnect()) { 1256 if (!$this->smtpConnect($this->SMTPOptions)) { 1161 1257 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1162 1258 } 1163 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; 1259 if ('' == $this->Sender) { 1260 $smtp_from = $this->From; 1261 } else { 1262 $smtp_from = $this->Sender; 1263 } 1164 1264 if (!$this->smtp->mail($smtp_from)) { 1165 1265 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); 1166 1266 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); 1167 1267 } 1168 1268 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; 1269 // Attempt to send to all recipients 1270 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { 1271 foreach ($togroup as $to) { 1272 if (!$this->smtp->recipient($to[0])) { 1273 $error = $this->smtp->getError(); 1274 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); 1275 $isSent = false; 1276 } else { 1277 $isSent = true; 1278 } 1279 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); 1176 1280 } 1177 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);1178 1281 } 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 1282 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)) { 1283 // Only send the DATA command if we have viable recipients 1284 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { 1202 1285 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); 1203 1286 } 1204 if ($this->SMTPKeepAlive == true) {1287 if ($this->SMTPKeepAlive) { 1205 1288 $this->smtp->reset(); 1206 1289 } else { 1207 1290 $this->smtp->quit(); 1208 1291 $this->smtp->close(); 1209 1292 } 1293 //Create error message for any bad addresses 1294 if (count($bad_rcpt) > 0) { 1295 $errstr = ''; 1296 foreach ($bad_rcpt as $bad) { 1297 $errstr .= $bad['to'] . ': ' . $bad['error']; 1298 } 1299 throw new phpmailerException( 1300 $this->lang('recipients_failed') . $errstr, 1301 self::STOP_CONTINUE 1302 ); 1303 } 1210 1304 return true; 1211 1305 } 1212 1306 … … 1217 1311 * @uses SMTP 1218 1312 * @access public 1219 1313 * @throws phpmailerException 1220 * @return bool 1314 * @return boolean 1221 1315 */ 1222 1316 public function smtpConnect($options = array()) 1223 1317 { … … 1225 1319 $this->smtp = $this->getSMTPInstance(); 1226 1320 } 1227 1321 1228 // Already connected?1322 // Already connected? 1229 1323 if ($this->smtp->connected()) { 1230 1324 return true; 1231 1325 } … … 1234 1328 $this->smtp->setDebugLevel($this->SMTPDebug); 1235 1329 $this->smtp->setDebugOutput($this->Debugoutput); 1236 1330 $this->smtp->setVerp($this->do_verp); 1237 $tls = ($this->SMTPSecure == 'tls');1238 $ssl = ($this->SMTPSecure == 'ssl');1239 1331 $hosts = explode(';', $this->Host); 1240 1332 $lastexception = null; 1241 1333 1242 1334 foreach ($hosts as $hostentry) { 1243 1335 $hostinfo = array(); 1244 $host = $hostentry; 1336 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { 1337 // Not a valid host entry 1338 continue; 1339 } 1340 // $hostinfo[2]: optional ssl or tls prefix 1341 // $hostinfo[3]: the hostname 1342 // $hostinfo[4]: optional port number 1343 // The host string prefix can temporarily override the current setting for SMTPSecure 1344 // If it's not specified, the default value is used 1345 $prefix = ''; 1346 $secure = $this->SMTPSecure; 1347 $tls = ($this->SMTPSecure == 'tls'); 1348 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { 1349 $prefix = 'ssl://'; 1350 $tls = false; // Can't have SSL and TLS at the same time 1351 $secure = 'ssl'; 1352 } elseif ($hostinfo[2] == 'tls') { 1353 $tls = true; 1354 // tls doesn't use a prefix 1355 $secure = 'tls'; 1356 } 1357 //Do we need the OpenSSL extension? 1358 $sslext = defined('OPENSSL_ALGO_SHA1'); 1359 if ('tls' === $secure or 'ssl' === $secure) { 1360 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled 1361 if (!$sslext) { 1362 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); 1363 } 1364 } 1365 $host = $hostinfo[3]; 1245 1366 $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]; 1367 $tport = (integer)$hostinfo[4]; 1368 if ($tport > 0 and $tport < 65536) { 1369 $port = $tport; 1254 1370 } 1255 if ($this->smtp->connect( ($ssl ? 'ssl://' : ''). $host, $port, $this->Timeout, $options)) {1371 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { 1256 1372 try { 1257 1373 if ($this->Helo) { 1258 1374 $hello = $this->Helo; … … 1260 1376 $hello = $this->serverHostname(); 1261 1377 } 1262 1378 $this->smtp->hello($hello); 1263 1379 //Automatically enable TLS encryption if: 1380 // * it's not disabled 1381 // * we have openssl extension 1382 // * we are not already using SSL 1383 // * the server offers STARTTLS 1384 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { 1385 $tls = true; 1386 } 1264 1387 if ($tls) { 1265 1388 if (!$this->smtp->startTLS()) { 1266 1389 throw new phpmailerException($this->lang('connect_host')); 1267 1390 } 1268 // We must resend HELO after tls negotiation1391 // We must resend HELO after tls negotiation 1269 1392 $this->smtp->hello($hello); 1270 1393 } 1271 1394 if ($this->SMTPAuth) { … … 1281 1404 } 1282 1405 } 1283 1406 return true; 1284 } catch (phpmailerException $e) { 1285 $lastexception = $e; 1286 //We must have connected, but then failed TLS or Auth, so close connection nicely 1407 } catch (phpmailerException $exc) { 1408 $lastexception = $exc; 1409 $this->edebug($exc->getMessage()); 1410 // We must have connected, but then failed TLS or Auth, so close connection nicely 1287 1411 $this->smtp->quit(); 1288 1412 } 1289 1413 } 1290 1414 } 1291 // If we get here, all connection attempts have failed, so close connection hard1415 // If we get here, all connection attempts have failed, so close connection hard 1292 1416 $this->smtp->close(); 1293 // As we've caught all exceptions, just report whatever the last one was1417 // As we've caught all exceptions, just report whatever the last one was 1294 1418 if ($this->exceptions and !is_null($lastexception)) { 1295 1419 throw $lastexception; 1296 1420 } … … 1317 1441 * The default language is English. 1318 1442 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 1319 1443 * @param string $lang_path Path to the language file directory, with trailing separator (slash) 1320 * @return bool 1444 * @return boolean 1321 1445 * @access public 1322 1446 */ 1323 public function setLanguage($langcode = 'en', $lang_path = ' language/')1447 public function setLanguage($langcode = 'en', $lang_path = '') 1324 1448 { 1325 // Define full set of translatable strings1449 // Define full set of translatable strings in English 1326 1450 $PHPMAILER_LANG = array( 1327 1451 'authenticate' => 'SMTP Error: Could not authenticate.', 1328 1452 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', … … 1341 1465 'signing' => 'Signing Error: ', 1342 1466 'smtp_connect_failed' => 'SMTP connect() failed.', 1343 1467 'smtp_error' => 'SMTP server error: ', 1344 'variable_set' => 'Cannot set or reset variable: ' 1468 'variable_set' => 'Cannot set or reset variable: ', 1469 'extension_missing' => 'Extension missing: ' 1345 1470 ); 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'; 1471 if (empty($lang_path)) { 1472 // Calculate an absolute path so it can work if CWD is not here 1473 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 1351 1474 } 1475 $foundlang = true; 1476 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1477 // There is no English translation file 1478 if ($langcode != 'en') { 1479 // Make sure language file path is readable 1480 if (!is_readable($lang_file)) { 1481 $foundlang = false; 1482 } else { 1483 // Overwrite language-specific strings. 1484 // This way we'll never have missing translation keys. 1485 $foundlang = include $lang_file; 1486 } 1487 } 1352 1488 $this->language = $PHPMAILER_LANG; 1353 return ( $l == true); //Returns false if language not found1489 return (boolean)$foundlang; // Returns false if language not found 1354 1490 } 1355 1491 1356 1492 /** … … 1375 1511 public function addrAppend($type, $addr) 1376 1512 { 1377 1513 $addresses = array(); 1378 foreach ($addr as $a ) {1379 $addresses[] = $this->addrFormat($a );1514 foreach ($addr as $address) { 1515 $addresses[] = $this->addrFormat($address); 1380 1516 } 1381 1517 return $type . ': ' . implode(', ', $addresses) . $this->LE; 1382 1518 } … … 1393 1529 if (empty($addr[1])) { // No name provided 1394 1530 return $this->secureHeader($addr[0]); 1395 1531 } else { 1396 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <". $this->secureHeader(1532 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 1397 1533 $addr[0] 1398 ) . ">";1534 ) . '>'; 1399 1535 } 1400 1536 } 1401 1537 … … 1406 1542 * Original written by philippe. 1407 1543 * @param string $message The message to wrap 1408 1544 * @param integer $length The line length to wrap to 1409 * @param bool $qp_mode Whether to run in Quoted-Printable mode1545 * @param boolean $qp_mode Whether to run in Quoted-Printable mode 1410 1546 * @access public 1411 1547 * @return string 1412 1548 */ 1413 1549 public function wrapText($message, $length, $qp_mode = false) 1414 1550 { 1415 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 1551 if ($qp_mode) { 1552 $soft_break = sprintf(' =%s', $this->LE); 1553 } else { 1554 $soft_break = $this->LE; 1555 } 1416 1556 // If utf-8 encoding is used, we will need to make sure we don't 1417 1557 // split multibyte characters when we wrap 1418 $is_utf8 = (strtolower($this->CharSet) == "utf-8");1558 $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); 1419 1559 $lelen = strlen($this->LE); 1420 1560 $crlflen = strlen(self::CRLF); 1421 1561 1422 1562 $message = $this->fixEOL($message); 1563 //Remove a trailing line break 1423 1564 if (substr($message, -$lelen) == $this->LE) { 1424 1565 $message = substr($message, 0, -$lelen); 1425 1566 } 1426 1567 1427 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE 1568 //Split message into lines 1569 $lines = explode($this->LE, $message); 1570 //Message will be rebuilt in here 1428 1571 $message = ''; 1429 for ($i = 0; $i < count($line); $i++) {1430 $ line_part = explode(' ', $line[$i]);1572 foreach ($lines as $line) { 1573 $words = explode(' ', $line); 1431 1574 $buf = ''; 1432 for ($e = 0; $e < count($line_part); $e++) {1433 $word = $line_part[$e];1575 $firstword = true; 1576 foreach ($words as $word) { 1434 1577 if ($qp_mode and (strlen($word) > $length)) { 1435 1578 $space_left = $length - strlen($buf) - $crlflen; 1436 if ( $e != 0) {1579 if (!$firstword) { 1437 1580 if ($space_left > 20) { 1438 1581 $len = $space_left; 1439 1582 if ($is_utf8) { 1440 1583 $len = $this->utf8CharBoundary($word, $len); 1441 } elseif (substr($word, $len - 1, 1) == "=") {1584 } elseif (substr($word, $len - 1, 1) == '=') { 1442 1585 $len--; 1443 } elseif (substr($word, $len - 2, 1) == "=") {1586 } elseif (substr($word, $len - 2, 1) == '=') { 1444 1587 $len -= 2; 1445 1588 } 1446 1589 $part = substr($word, 0, $len); 1447 1590 $word = substr($word, $len); 1448 1591 $buf .= ' ' . $part; 1449 $message .= $buf . sprintf( "=%s", self::CRLF);1592 $message .= $buf . sprintf('=%s', self::CRLF); 1450 1593 } else { 1451 1594 $message .= $buf . $soft_break; 1452 1595 } … … 1459 1602 $len = $length; 1460 1603 if ($is_utf8) { 1461 1604 $len = $this->utf8CharBoundary($word, $len); 1462 } elseif (substr($word, $len - 1, 1) == "=") {1605 } elseif (substr($word, $len - 1, 1) == '=') { 1463 1606 $len--; 1464 } elseif (substr($word, $len - 2, 1) == "=") {1607 } elseif (substr($word, $len - 2, 1) == '=') { 1465 1608 $len -= 2; 1466 1609 } 1467 1610 $part = substr($word, 0, $len); … … 1468 1611 $word = substr($word, $len); 1469 1612 1470 1613 if (strlen($word) > 0) { 1471 $message .= $part . sprintf( "=%s", self::CRLF);1614 $message .= $part . sprintf('=%s', self::CRLF); 1472 1615 } else { 1473 1616 $buf = $part; 1474 1617 } … … 1475 1618 } 1476 1619 } else { 1477 1620 $buf_o = $buf; 1478 $buf .= ($e == 0) ? $word : (' ' . $word); 1621 if (!$firstword) { 1622 $buf .= ' '; 1623 } 1624 $buf .= $word; 1479 1625 1480 1626 if (strlen($buf) > $length and $buf_o != '') { 1481 1627 $message .= $buf_o . $soft_break; … … 1482 1628 $buf = $word; 1483 1629 } 1484 1630 } 1631 $firstword = false; 1485 1632 } 1486 1633 $message .= $buf . self::CRLF; 1487 1634 } … … 1491 1638 1492 1639 /** 1493 1640 * Find the last character boundary prior to $maxLength in a utf-8 1494 * quoted (printable)encoded string.1641 * quoted-printable encoded string. 1495 1642 * Original written by Colin Brown. 1496 1643 * @access public 1497 1644 * @param string $encodedText utf-8 QP text 1498 * @param int $maxLength findlast character boundary prior to this length1499 * @return int 1645 * @param integer $maxLength Find the last character boundary prior to this length 1646 * @return integer 1500 1647 */ 1501 1648 public function utf8CharBoundary($encodedText, $maxLength) 1502 1649 { … … 1504 1651 $lookBack = 3; 1505 1652 while (!$foundSplitPos) { 1506 1653 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 1507 $encodedCharPos = strpos($lastChunk, "=");1508 if ( $encodedCharPos !== false) {1654 $encodedCharPos = strpos($lastChunk, '='); 1655 if (false !== $encodedCharPos) { 1509 1656 // Found start of encoded character byte within $lookBack block. 1510 1657 // Check the encoded byte value (the 2 chars after the '=') 1511 1658 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 1512 1659 $dec = hexdec($hex); 1513 if ($dec < 128) { // Single byte character. 1660 if ($dec < 128) { 1661 // Single byte character. 1514 1662 // If the encoded char was found at pos 0, it will fit 1515 1663 // otherwise reduce maxLength to start of the encoded char 1516 $maxLength = ($encodedCharPos == 0) ? $maxLength : 1517 $maxLength - ($lookBack - $encodedCharPos); 1664 if ($encodedCharPos > 0) { 1665 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1666 } 1518 1667 $foundSplitPos = true; 1519 } elseif ($dec >= 192) { // First byte of a multi byte character 1668 } elseif ($dec >= 192) { 1669 // First byte of a multi byte character 1520 1670 // Reduce maxLength to split at start of character 1521 1671 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1522 1672 $foundSplitPos = true; 1523 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back 1673 } elseif ($dec < 192) { 1674 // Middle byte of a multi byte character, look further back 1524 1675 $lookBack += 3; 1525 1676 } 1526 1677 } else { … … 1531 1682 return $maxLength; 1532 1683 } 1533 1684 1534 1535 1685 /** 1536 * Set the body wrapping. 1686 * Apply word wrapping to the message body. 1687 * Wraps the message body to the number of chars set in the WordWrap property. 1688 * You should only do this to plain-text bodies as wrapping HTML tags may break them. 1689 * This is called automatically by createBody(), so you don't need to call it yourself. 1537 1690 * @access public 1538 1691 * @return void 1539 1692 */ … … 1565 1718 { 1566 1719 $result = ''; 1567 1720 1568 // Set the boundaries1569 $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 1721 if ($this->MessageDate == '') { 1575 $result .= $this->headerLine('Date', self::rfcDate()); 1576 } else { 1577 $result .= $this->headerLine('Date', $this->MessageDate); 1722 $this->MessageDate = self::rfcDate(); 1578 1723 } 1724 $result .= $this->headerLine('Date', $this->MessageDate); 1579 1725 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 1726 1588 1727 // 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 );1728 if ($this->SingleTo) { 1729 if ($this->Mailer != 'mail') { 1730 foreach ($this->to as $toaddr) { 1731 $this->SingleToArray[] = $this->addrFormat($toaddr); 1593 1732 } 1594 } else { 1595 if (count($this->to) > 0) { 1733 } 1734 } else { 1735 if (count($this->to) > 0) { 1736 if ($this->Mailer != 'mail') { 1596 1737 $result .= $this->addrAppend('To', $this->to); 1597 } elseif (count($this->cc) == 0) {1598 $result .= $this->headerLine('To', 'undisclosed-recipients:;');1599 1738 } 1739 } elseif (count($this->cc) == 0) { 1740 $result .= $this->headerLine('To', 'undisclosed-recipients:;'); 1600 1741 } 1601 1742 } 1602 1743 … … 1608 1749 } 1609 1750 1610 1751 // sendmail and mail() extract Bcc from the header before sending 1611 if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { 1752 if (( 1753 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' 1754 ) 1755 and count($this->bcc) > 0 1756 ) { 1612 1757 $result .= $this->addrAppend('Bcc', $this->bcc); 1613 1758 } 1614 1759 … … 1624 1769 if ($this->MessageID != '') { 1625 1770 $this->lastMessageID = $this->MessageID; 1626 1771 } else { 1627 $this->lastMessageID = sprintf( "<%s@%s>", $uniq_id, $this->ServerHostname());1772 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname()); 1628 1773 } 1629 $result .= $this-> HeaderLine('Message-ID', $this->lastMessageID);1774 $result .= $this->headerLine('Message-ID', $this->lastMessageID); 1630 1775 $result .= $this->headerLine('X-Priority', $this->Priority); 1631 1776 if ($this->XMailer == '') { 1632 1777 $result .= $this->headerLine( … … 1645 1790 } 1646 1791 1647 1792 // Add custom headers 1648 for ($index = 0; $index < count($this->CustomHeader); $index++) {1793 foreach ($this->CustomHeader as $header) { 1649 1794 $result .= $this->headerLine( 1650 trim($ this->CustomHeader[$index][0]),1651 $this->encodeHeader(trim($ this->CustomHeader[$index][1]))1795 trim($header[0]), 1796 $this->encodeHeader(trim($header[1])) 1652 1797 ); 1653 1798 } 1654 1799 if (!$this->sign_key_file) { … … 1667 1812 public function getMailMIME() 1668 1813 { 1669 1814 $result = ''; 1815 $ismultipart = true; 1670 1816 switch ($this->message_type) { 1671 1817 case 'inline': 1672 1818 $result .= $this->headerLine('Content-Type', 'multipart/related;'); … … 1687 1833 default: 1688 1834 // Catches case 'plain': and case '': 1689 1835 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); 1836 $ismultipart = false; 1690 1837 break; 1691 1838 } 1692 // RFC1341 part 5 says 7bit is assumed if not specified1839 // RFC1341 part 5 says 7bit is assumed if not specified 1693 1840 if ($this->Encoding != '7bit') { 1694 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 1841 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE 1842 if ($ismultipart) { 1843 if ($this->Encoding == '8bit') { 1844 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); 1845 } 1846 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible 1847 } else { 1848 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 1849 } 1695 1850 } 1696 1851 1697 1852 if ($this->Mailer != 'mail') { … … 1704 1859 /** 1705 1860 * Returns the whole MIME message. 1706 1861 * Includes complete headers and body. 1707 * Only valid post PreSend().1708 * @see PHPMailer:: PreSend()1862 * Only valid post preSend(). 1863 * @see PHPMailer::preSend() 1709 1864 * @access public 1710 1865 * @return string 1711 1866 */ … … 1714 1869 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 1715 1870 } 1716 1871 1717 1718 1872 /** 1719 1873 * Assemble the message body. 1720 1874 * Returns an empty string on failure. … … 1725 1879 public function createBody() 1726 1880 { 1727 1881 $body = ''; 1882 //Create unique IDs and preset boundaries 1883 $this->uniqueid = md5(uniqid(time())); 1884 $this->boundary[1] = 'b1_' . $this->uniqueid; 1885 $this->boundary[2] = 'b2_' . $this->uniqueid; 1886 $this->boundary[3] = 'b3_' . $this->uniqueid; 1728 1887 1729 1888 if ($this->sign_key_file) { 1730 1889 $body .= $this->getMailMIME() . $this->LE; … … 1732 1891 1733 1892 $this->setWordWrap(); 1734 1893 1894 $bodyEncoding = $this->Encoding; 1895 $bodyCharSet = $this->CharSet; 1896 //Can we do a 7-bit downgrade? 1897 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 1898 $bodyEncoding = '7bit'; 1899 $bodyCharSet = 'us-ascii'; 1900 } 1901 //If lines are too long, change to quoted-printable transfer encoding 1902 if (self::hasLineLongerThanMax($this->Body)) { 1903 $this->Encoding = 'quoted-printable'; 1904 $bodyEncoding = 'quoted-printable'; 1905 } 1906 1907 $altBodyEncoding = $this->Encoding; 1908 $altBodyCharSet = $this->CharSet; 1909 //Can we do a 7-bit downgrade? 1910 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 1911 $altBodyEncoding = '7bit'; 1912 $altBodyCharSet = 'us-ascii'; 1913 } 1914 //If lines are too long, change to quoted-printable transfer encoding 1915 if (self::hasLineLongerThanMax($this->AltBody)) { 1916 $altBodyEncoding = 'quoted-printable'; 1917 } 1918 //Use this as a preamble in all multipart message types 1919 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; 1735 1920 switch ($this->message_type) { 1736 1921 case 'inline': 1737 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1738 $body .= $this->encodeString($this->Body, $this->Encoding); 1922 $body .= $mimepre; 1923 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 1924 $body .= $this->encodeString($this->Body, $bodyEncoding); 1739 1925 $body .= $this->LE . $this->LE; 1740 1926 $body .= $this->attachAll('inline', $this->boundary[1]); 1741 1927 break; 1742 1928 case 'attach': 1743 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1744 $body .= $this->encodeString($this->Body, $this->Encoding); 1929 $body .= $mimepre; 1930 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 1931 $body .= $this->encodeString($this->Body, $bodyEncoding); 1745 1932 $body .= $this->LE . $this->LE; 1746 1933 $body .= $this->attachAll('attachment', $this->boundary[1]); 1747 1934 break; 1748 1935 case 'inline_attach': 1936 $body .= $mimepre; 1749 1937 $body .= $this->textLine('--' . $this->boundary[1]); 1750 1938 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1751 1939 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1752 1940 $body .= $this->LE; 1753 $body .= $this->getBoundary($this->boundary[2], '', '', '');1754 $body .= $this->encodeString($this->Body, $ this->Encoding);1941 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); 1942 $body .= $this->encodeString($this->Body, $bodyEncoding); 1755 1943 $body .= $this->LE . $this->LE; 1756 1944 $body .= $this->attachAll('inline', $this->boundary[2]); 1757 1945 $body .= $this->LE; … … 1758 1946 $body .= $this->attachAll('attachment', $this->boundary[1]); 1759 1947 break; 1760 1948 case 'alt': 1761 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1762 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1949 $body .= $mimepre; 1950 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1951 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1763 1952 $body .= $this->LE . $this->LE; 1764 $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');1765 $body .= $this->encodeString($this->Body, $ this->Encoding);1953 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); 1954 $body .= $this->encodeString($this->Body, $bodyEncoding); 1766 1955 $body .= $this->LE . $this->LE; 1767 1956 if (!empty($this->Ical)) { 1768 1957 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); … … 1772 1961 $body .= $this->endBoundary($this->boundary[1]); 1773 1962 break; 1774 1963 case 'alt_inline': 1775 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1776 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1964 $body .= $mimepre; 1965 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1966 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1777 1967 $body .= $this->LE . $this->LE; 1778 1968 $body .= $this->textLine('--' . $this->boundary[1]); 1779 1969 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1780 1970 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1781 1971 $body .= $this->LE; 1782 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');1783 $body .= $this->encodeString($this->Body, $ this->Encoding);1972 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 1973 $body .= $this->encodeString($this->Body, $bodyEncoding); 1784 1974 $body .= $this->LE . $this->LE; 1785 1975 $body .= $this->attachAll('inline', $this->boundary[2]); 1786 1976 $body .= $this->LE; … … 1787 1977 $body .= $this->endBoundary($this->boundary[1]); 1788 1978 break; 1789 1979 case 'alt_attach': 1980 $body .= $mimepre; 1790 1981 $body .= $this->textLine('--' . $this->boundary[1]); 1791 1982 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1792 1983 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1793 1984 $body .= $this->LE; 1794 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');1795 $body .= $this->encodeString($this->AltBody, $ this->Encoding);1985 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1986 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1796 1987 $body .= $this->LE . $this->LE; 1797 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');1798 $body .= $this->encodeString($this->Body, $ this->Encoding);1988 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 1989 $body .= $this->encodeString($this->Body, $bodyEncoding); 1799 1990 $body .= $this->LE . $this->LE; 1800 1991 $body .= $this->endBoundary($this->boundary[2]); 1801 1992 $body .= $this->LE; … … 1802 1993 $body .= $this->attachAll('attachment', $this->boundary[1]); 1803 1994 break; 1804 1995 case 'alt_inline_attach': 1996 $body .= $mimepre; 1805 1997 $body .= $this->textLine('--' . $this->boundary[1]); 1806 1998 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1807 1999 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1808 2000 $body .= $this->LE; 1809 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');1810 $body .= $this->encodeString($this->AltBody, $ this->Encoding);2001 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2002 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1811 2003 $body .= $this->LE . $this->LE; 1812 2004 $body .= $this->textLine('--' . $this->boundary[2]); 1813 2005 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1814 2006 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); 1815 2007 $body .= $this->LE; 1816 $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');1817 $body .= $this->encodeString($this->Body, $ this->Encoding);2008 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); 2009 $body .= $this->encodeString($this->Body, $bodyEncoding); 1818 2010 $body .= $this->LE . $this->LE; 1819 2011 $body .= $this->attachAll('inline', $this->boundary[3]); 1820 2012 $body .= $this->LE; … … 1824 2016 break; 1825 2017 default: 1826 2018 // catch case 'plain' and case '' 1827 $body .= $this->encodeString($this->Body, $ this->Encoding);2019 $body .= $this->encodeString($this->Body, $bodyEncoding); 1828 2020 break; 1829 2021 } 1830 2022 … … 1833 2025 } elseif ($this->sign_key_file) { 1834 2026 try { 1835 2027 if (!defined('PKCS7_TEXT')) { 1836 throw new phpmailerException($this->lang(' signing') . ' OpenSSL extension missing.');2028 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 1837 2029 } 2030 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 1838 2031 $file = tempnam(sys_get_temp_dir(), 'mail'); 1839 file_put_contents($file, $body); //TODO check this worked 2032 if (false === file_put_contents($file, $body)) { 2033 throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); 2034 } 1840 2035 $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 ) { 2036 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 2037 if (empty($this->sign_extracerts_file)) { 2038 $sign = @openssl_pkcs7_sign( 2039 $file, 2040 $signed, 2041 'file://' . realpath($this->sign_cert_file), 2042 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2043 null 2044 ); 2045 } else { 2046 $sign = @openssl_pkcs7_sign( 2047 $file, 2048 $signed, 2049 'file://' . realpath($this->sign_cert_file), 2050 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2051 null, 2052 PKCS7_DETACHED, 2053 $this->sign_extracerts_file 2054 ); 2055 } 2056 if ($sign) { 1849 2057 @unlink($file); 1850 2058 $body = file_get_contents($signed); 1851 2059 @unlink($signed); 2060 //The message returned by openssl contains both headers and body, so need to split them up 2061 $parts = explode("\n\n", $body, 2); 2062 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; 2063 $body = $parts[1]; 1852 2064 } else { 1853 2065 @unlink($file); 1854 2066 @unlink($signed); 1855 2067 throw new phpmailerException($this->lang('signing') . openssl_error_string()); 1856 2068 } 1857 } catch (phpmailerException $e ) {2069 } catch (phpmailerException $exc) { 1858 2070 $body = ''; 1859 2071 if ($this->exceptions) { 1860 throw $e ;2072 throw $exc; 1861 2073 } 1862 2074 } 1863 2075 } … … 1886 2098 $encoding = $this->Encoding; 1887 2099 } 1888 2100 $result .= $this->textLine('--' . $boundary); 1889 $result .= sprintf( "Content-Type: %s; charset=%s", $contentType, $charSet);2101 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); 1890 2102 $result .= $this->LE; 1891 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2103 // RFC1341 part 5 says 7bit is assumed if not specified 2104 if ($encoding != '7bit') { 2105 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2106 } 1892 2107 $result .= $this->LE; 1893 2108 1894 2109 return $result; … … 1914 2129 */ 1915 2130 protected function setMessageType() 1916 2131 { 1917 $t his->message_type = array();2132 $type = array(); 1918 2133 if ($this->alternativeExists()) { 1919 $t his->message_type[] = "alt";2134 $type[] = 'alt'; 1920 2135 } 1921 2136 if ($this->inlineImageExists()) { 1922 $t his->message_type[] = "inline";2137 $type[] = 'inline'; 1923 2138 } 1924 2139 if ($this->attachmentExists()) { 1925 $t his->message_type[] = "attach";2140 $type[] = 'attach'; 1926 2141 } 1927 $this->message_type = implode( "_", $this->message_type);1928 if ($this->message_type == "") {1929 $this->message_type = "plain";2142 $this->message_type = implode('_', $type); 2143 if ($this->message_type == '') { 2144 $this->message_type = 'plain'; 1930 2145 } 1931 2146 } 1932 2147 … … 1962 2177 * @param string $type File extension (MIME) type. 1963 2178 * @param string $disposition Disposition to use 1964 2179 * @throws phpmailerException 1965 * @return bool 2180 * @return boolean 1966 2181 */ 1967 2182 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') 1968 2183 { … … 1971 2186 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); 1972 2187 } 1973 2188 1974 // If a MIME type is not specified, try to work it out from the file name2189 // If a MIME type is not specified, try to work it out from the file name 1975 2190 if ($type == '') { 1976 2191 $type = self::filenameToType($path); 1977 2192 } … … 1992 2207 7 => 0 1993 2208 ); 1994 2209 1995 } catch (phpmailerException $e) { 1996 $this->setError($e->getMessage()); 2210 } catch (phpmailerException $exc) { 2211 $this->setError($exc->getMessage()); 2212 $this->edebug($exc->getMessage()); 1997 2213 if ($this->exceptions) { 1998 throw $e ;2214 throw $exc; 1999 2215 } 2000 $this->edebug($e->getMessage() . "\n");2001 2216 return false; 2002 2217 } 2003 2218 return true; … … 2056 2271 } 2057 2272 $cidUniq[$cid] = true; 2058 2273 2059 $mime[] = sprintf( "--%s%s", $boundary, $this->LE);2274 $mime[] = sprintf('--%s%s', $boundary, $this->LE); 2060 2275 $mime[] = sprintf( 2061 "Content-Type: %s; name=\"%s\"%s",2276 'Content-Type: %s; name="%s"%s', 2062 2277 $type, 2063 2278 $this->encodeHeader($this->secureHeader($name)), 2064 2279 $this->LE 2065 2280 ); 2066 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 2281 // RFC1341 part 5 says 7bit is assumed if not specified 2282 if ($encoding != '7bit') { 2283 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); 2284 } 2067 2285 2068 2286 if ($disposition == 'inline') { 2069 $mime[] = sprintf( "Content-ID: <%s>%s", $cid, $this->LE);2287 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); 2070 2288 } 2071 2289 2072 2290 // If a filename contains any of these chars, it should be quoted, … … 2074 2292 // Fixes a warning in IETF's msglint MIME checker 2075 2293 // Allow for bypassing the Content-Disposition header totally 2076 2294 if (!(empty($disposition))) { 2077 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { 2295 $encoded_name = $this->encodeHeader($this->secureHeader($name)); 2296 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { 2078 2297 $mime[] = sprintf( 2079 "Content-Disposition: %s; filename=\"%s\"%s",2298 'Content-Disposition: %s; filename="%s"%s', 2080 2299 $disposition, 2081 $ this->encodeHeader($this->secureHeader($name)),2300 $encoded_name, 2082 2301 $this->LE . $this->LE 2083 2302 ); 2084 2303 } else { 2085 2304 $mime[] = sprintf( 2086 "Content-Disposition: %s; filename=%s%s",2305 'Content-Disposition: %s; filename=%s%s', 2087 2306 $disposition, 2088 $ this->encodeHeader($this->secureHeader($name)),2307 $encoded_name, 2089 2308 $this->LE . $this->LE 2090 2309 ); 2091 2310 } … … 2110 2329 } 2111 2330 } 2112 2331 2113 $mime[] = sprintf( "--%s--%s", $boundary, $this->LE);2332 $mime[] = sprintf('--%s--%s', $boundary, $this->LE); 2114 2333 2115 return implode( "", $mime);2334 return implode('', $mime); 2116 2335 } 2117 2336 2118 2337 /** … … 2134 2353 $magic_quotes = get_magic_quotes_runtime(); 2135 2354 if ($magic_quotes) { 2136 2355 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2137 set_magic_quotes_runtime( 0);2356 set_magic_quotes_runtime(false); 2138 2357 } else { 2139 ini_set('magic_quotes_runtime', 0); 2358 //Doesn't exist in PHP 5.4, but we don't need to check because 2359 //get_magic_quotes_runtime always returns false in 5.4+ 2360 //so it will never get here 2361 ini_set('magic_quotes_runtime', false); 2140 2362 } 2141 2363 } 2142 2364 $file_buffer = file_get_contents($path); … … 2149 2371 } 2150 2372 } 2151 2373 return $file_buffer; 2152 } catch (Exception $e ) {2153 $this->setError($e ->getMessage());2374 } catch (Exception $exc) { 2375 $this->setError($exc->getMessage()); 2154 2376 return ''; 2155 2377 } 2156 2378 } … … 2173 2395 case '7bit': 2174 2396 case '8bit': 2175 2397 $encoded = $this->fixEOL($str); 2176 // Make sure it ends with a line break2398 // Make sure it ends with a line break 2177 2399 if (substr($encoded, -(strlen($this->LE))) != $this->LE) { 2178 2400 $encoded .= $this->LE; 2179 2401 } … … 2201 2423 */ 2202 2424 public function encodeHeader($str, $position = 'text') 2203 2425 { 2204 $ x= 0;2426 $matchcount = 0; 2205 2427 switch (strtolower($position)) { 2206 2428 case 'phrase': 2207 2429 if (!preg_match('/[\200-\377]/', $str)) { 2208 // Can't use addslashes as we don't know what value hasmagic_quotes_sybase2430 // Can't use addslashes as we don't know the value of magic_quotes_sybase 2209 2431 $encoded = addcslashes($str, "\0..\37\177\\\""); 2210 2432 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 2211 2433 return ($encoded); … … 2213 2435 return ("\"$encoded\""); 2214 2436 } 2215 2437 } 2216 $ x= preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);2438 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 2217 2439 break; 2218 2440 /** @noinspection PhpMissingBreakStatementInspection */ 2219 2441 case 'comment': 2220 $ x= preg_match_all('/[()"]/', $str, $matches);2442 $matchcount = preg_match_all('/[()"]/', $str, $matches); 2221 2443 // Intentional fall-through 2222 2444 case 'text': 2223 2445 default: 2224 $ x+= preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);2446 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 2225 2447 break; 2226 2448 } 2227 2449 2228 if ($x == 0) { //There are no chars that need encoding 2450 //There are no chars that need encoding 2451 if ($matchcount == 0) { 2229 2452 return ($str); 2230 2453 } 2231 2454 2232 2455 $maxlen = 75 - 7 - strlen($this->CharSet); 2233 2456 // 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 efficient2457 if ($matchcount > strlen($str) / 3) { 2458 // More than a third of the content will need encoding, so B encoding will be most efficient 2236 2459 $encoding = 'B'; 2237 2460 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { 2238 2461 // Use a custom function which correctly encodes and wraps long … … 2250 2473 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); 2251 2474 } 2252 2475 2253 $encoded = preg_replace('/^(.*)$/m', " =?". $this->CharSet . "?$encoding?\\1?=", $encoded);2476 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 2254 2477 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 2255 2478 2256 2479 return $encoded; … … 2260 2483 * Check if a string contains multi-byte characters. 2261 2484 * @access public 2262 2485 * @param string $str multi-byte text to wrap encode 2263 * @return bool 2486 * @return boolean 2264 2487 */ 2265 2488 public function hasMultiBytes($str) 2266 2489 { … … 2272 2495 } 2273 2496 2274 2497 /** 2498 * Does a string contain any 8-bit chars (in any charset)? 2499 * @param string $text 2500 * @return boolean 2501 */ 2502 public function has8bitChars($text) 2503 { 2504 return (boolean)preg_match('/[\x80-\xFF]/', $text); 2505 } 2506 2507 /** 2275 2508 * Encode and wrap long multibyte strings for mail headers 2276 2509 * 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 2510 * Adapted from a function by paravoid 2511 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 2278 2512 * @access public 2279 2513 * @param string $str multi-byte text to wrap encode 2280 * @param string $l fstring to use as linefeed/end-of-line2514 * @param string $linebreak string to use as linefeed/end-of-line 2281 2515 * @return string 2282 2516 */ 2283 public function base64EncodeWrapMB($str, $l f= null)2517 public function base64EncodeWrapMB($str, $linebreak = null) 2284 2518 { 2285 $start = "=?" . $this->CharSet . "?B?";2286 $end = "?=";2287 $encoded = "";2288 if ($l f=== null) {2289 $l f= $this->LE;2519 $start = '=?' . $this->CharSet . '?B?'; 2520 $end = '?='; 2521 $encoded = ''; 2522 if ($linebreak === null) { 2523 $linebreak = $this->LE; 2290 2524 } 2291 2525 2292 2526 $mb_length = mb_strlen($str, $this->CharSet); … … 2305 2539 $chunk = base64_encode($chunk); 2306 2540 $lookBack++; 2307 2541 } while (strlen($chunk) > $length); 2308 $encoded .= $chunk . $l f;2542 $encoded .= $chunk . $linebreak; 2309 2543 } 2310 2544 2311 2545 // Chomp the last linefeed 2312 $encoded = substr($encoded, 0, -strlen($l f));2546 $encoded = substr($encoded, 0, -strlen($linebreak)); 2313 2547 return $encoded; 2314 2548 } 2315 2549 … … 2320 2554 * @param string $string The text to encode 2321 2555 * @param integer $line_max Number of chars allowed on a line before wrapping 2322 2556 * @return string 2323 * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#894172557 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment 2324 2558 */ 2325 2559 public function encodeQP($string, $line_max = 76) 2326 2560 { 2327 if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) 2328 return quoted_printable_encode($string); 2561 // Use native function if it's available (>= PHP5.3) 2562 if (function_exists('quoted_printable_encode')) { 2563 return $this->fixEOL(quoted_printable_encode($string)); 2329 2564 } 2330 // Fall back to a pure PHP implementation2565 // Fall back to a pure PHP implementation 2331 2566 $string = str_replace( 2332 2567 array('%20', '%0D%0A.', '%0D%0A', '%'), 2333 2568 array(' ', "\r\n=2E", "\r\n", '='), … … 2334 2569 rawurlencode($string) 2335 2570 ); 2336 2571 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); 2337 return $ string;2572 return $this->fixEOL($string); 2338 2573 } 2339 2574 2340 2575 /** … … 2343 2578 * @access public 2344 2579 * @param string $string 2345 2580 * @param integer $line_max 2346 * @param bool $space_conv2581 * @param boolean $space_conv 2347 2582 * @return string 2348 2583 * @deprecated Use encodeQP instead. 2349 2584 */ … … 2365 2600 */ 2366 2601 public function encodeQ($str, $position = 'text') 2367 2602 { 2368 // There should not be any EOL in the string2603 // There should not be any EOL in the string 2369 2604 $pattern = ''; 2370 2605 $encoded = str_replace(array("\r", "\n"), '', $str); 2371 2606 switch (strtolower($position)) { 2372 2607 case 'phrase': 2373 // RFC 2047 section 5.32608 // RFC 2047 section 5.3 2374 2609 $pattern = '^A-Za-z0-9!*+\/ -'; 2375 2610 break; 2376 2611 /** @noinspection PhpMissingBreakStatementInspection */ 2377 2612 case 'comment': 2378 // RFC 2047 section 5.22613 // RFC 2047 section 5.2 2379 2614 $pattern = '\(\)"'; 2380 // intentional fall-through2381 // for this reason we build the $pattern without including delimiters and []2615 // intentional fall-through 2616 // for this reason we build the $pattern without including delimiters and [] 2382 2617 case 'text': 2383 2618 default: 2384 // RFC 2047 section 5.12385 // Replace every high ascii, control, =, ? and _ characters2619 // RFC 2047 section 5.1 2620 // Replace every high ascii, control, =, ? and _ characters 2386 2621 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; 2387 2622 break; 2388 2623 } 2389 2624 $matches = array(); 2390 2625 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { 2391 // If the string contains an '=', make sure it's the first thing we replace2392 // so as to avoid double-encoding2393 $ s= array_search('=', $matches[0]);2394 if ( $s !== false) {2395 unset($matches[0][$ s]);2626 // If the string contains an '=', make sure it's the first thing we replace 2627 // so as to avoid double-encoding 2628 $eqkey = array_search('=', $matches[0]); 2629 if (false !== $eqkey) { 2630 unset($matches[0][$eqkey]); 2396 2631 array_unshift($matches[0], '='); 2397 2632 } 2398 2633 foreach (array_unique($matches[0]) as $char) { … … 2399 2634 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); 2400 2635 } 2401 2636 } 2402 // Replace every spaces to _ (more readable than =20)2637 // Replace every spaces to _ (more readable than =20) 2403 2638 return str_replace(' ', '_', $encoded); 2404 2639 } 2405 2640 … … 2422 2657 $type = '', 2423 2658 $disposition = 'attachment' 2424 2659 ) { 2425 // If a MIME type is not specified, try to work it out from the file name2660 // If a MIME type is not specified, try to work it out from the file name 2426 2661 if ($type == '') { 2427 2662 $type = self::filenameToType($filename); 2428 2663 } … … 2442 2677 /** 2443 2678 * Add an embedded (inline) attachment from a file. 2444 2679 * This can include images, sounds, and just about any other document type. 2445 * These differ from 'regular' attachm ants in that they are intended to be2680 * These differ from 'regular' attachments in that they are intended to be 2446 2681 * displayed inline with the message, not just attached for download. 2447 2682 * This is used in HTML messages that embed the images 2448 2683 * the HTML refers to using the $cid value. … … 2453 2688 * @param string $encoding File encoding (see $Encoding). 2454 2689 * @param string $type File MIME type. 2455 2690 * @param string $disposition Disposition to use 2456 * @return bool True on successfully adding an attachment2691 * @return boolean True on successfully adding an attachment 2457 2692 */ 2458 2693 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') 2459 2694 { … … 2462 2697 return false; 2463 2698 } 2464 2699 2465 // If a MIME type is not specified, try to work it out from the file name2700 // If a MIME type is not specified, try to work it out from the file name 2466 2701 if ($type == '') { 2467 2702 $type = self::filenameToType($path); 2468 2703 } … … 2498 2733 * @param string $encoding File encoding (see $Encoding). 2499 2734 * @param string $type MIME type. 2500 2735 * @param string $disposition Disposition to use 2501 * @return bool True on successfully adding an attachment2736 * @return boolean True on successfully adding an attachment 2502 2737 */ 2503 2738 public function addStringEmbeddedImage( 2504 2739 $string, … … 2508 2743 $type = '', 2509 2744 $disposition = 'inline' 2510 2745 ) { 2511 // If a MIME type is not specified, try to work it out from the name2746 // If a MIME type is not specified, try to work it out from the name 2512 2747 if ($type == '') { 2513 2748 $type = self::filenameToType($name); 2514 2749 } … … 2530 2765 /** 2531 2766 * Check if an inline attachment is present. 2532 2767 * @access public 2533 * @return bool 2768 * @return boolean 2534 2769 */ 2535 2770 public function inlineImageExists() 2536 2771 { … … 2544 2779 2545 2780 /** 2546 2781 * Check if an attachment (non-inline) is present. 2547 * @return bool 2782 * @return boolean 2548 2783 */ 2549 2784 public function attachmentExists() 2550 2785 { … … 2558 2793 2559 2794 /** 2560 2795 * Check if this message has an alternative body set. 2561 * @return bool 2796 * @return boolean 2562 2797 */ 2563 2798 public function alternativeExists() 2564 2799 { … … 2651 2886 $this->error_count++; 2652 2887 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { 2653 2888 $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"; 2889 if (!empty($lasterror['error'])) { 2890 $msg .= $this->lang('smtp_error') . $lasterror['error']; 2891 if (!empty($lasterror['detail'])) { 2892 $msg .= ' Detail: '. $lasterror['detail']; 2893 } 2894 if (!empty($lasterror['smtp_code'])) { 2895 $msg .= ' SMTP code: ' . $lasterror['smtp_code']; 2896 } 2897 if (!empty($lasterror['smtp_code_ex'])) { 2898 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; 2899 } 2656 2900 } 2657 2901 } 2658 2902 $this->ErrorInfo = $msg; … … 2666 2910 */ 2667 2911 public static function rfcDate() 2668 2912 { 2669 // Set the time zone to whatever the default is to avoid 500 errors2670 // Will default to UTC if it's not set properly in php.ini2913 // Set the time zone to whatever the default is to avoid 500 errors 2914 // Will default to UTC if it's not set properly in php.ini 2671 2915 date_default_timezone_set(@date_default_timezone_get()); 2672 2916 return date('D, j M Y H:i:s O'); 2673 2917 } … … 2680 2924 */ 2681 2925 protected function serverHostname() 2682 2926 { 2927 $result = 'localhost.localdomain'; 2683 2928 if (!empty($this->Hostname)) { 2684 2929 $result = $this->Hostname; 2685 } elseif (isset($_SERVER ['SERVER_NAME'])) {2930 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { 2686 2931 $result = $_SERVER['SERVER_NAME']; 2687 } else { 2688 $result = 'localhost.localdomain'; 2932 } elseif (function_exists('gethostname') && gethostname() !== false) { 2933 $result = gethostname(); 2934 } elseif (php_uname('n') !== false) { 2935 $result = php_uname('n'); 2689 2936 } 2690 2691 2937 return $result; 2692 2938 } 2693 2939 … … 2703 2949 $this->setLanguage('en'); // set the default language 2704 2950 } 2705 2951 2706 if (isset($this->language[$key])) { 2952 if (array_key_exists($key, $this->language)) { 2953 if ($key == 'smtp_connect_failed') { 2954 //Include a link to troubleshooting docs on SMTP connection failure 2955 //this is by far the biggest cause of support questions 2956 //but it's usually not PHPMailer's fault. 2957 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; 2958 } 2707 2959 return $this->language[$key]; 2708 2960 } else { 2709 return 'Language string failed to load: ' . $key; 2961 //Return the key as a fallback 2962 return $key; 2710 2963 } 2711 2964 } 2712 2965 … … 2713 2966 /** 2714 2967 * Check if an error occurred. 2715 2968 * @access public 2716 * @return bool True if an error did occur.2969 * @return boolean True if an error did occur. 2717 2970 */ 2718 2971 public function isError() 2719 2972 { … … 2758 3011 } 2759 3012 2760 3013 /** 3014 * Returns all custom headers 3015 * 3016 * @return array 3017 */ 3018 public function getCustomHeaders() 3019 { 3020 return $this->CustomHeader; 3021 } 3022 3023 /** 2761 3024 * Create a message from an HTML string. 2762 3025 * Automatically makes modifications for inline images and backgrounds 2763 3026 * and creates a plain-text version by converting the HTML. … … 2765 3028 * @access public 2766 3029 * @param string $message HTML message string 2767 3030 * @param string $basedir baseline directory for path 2768 * @param bool $advanced Whether to use the advanced HTML to text converter 3031 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3032 * or your own custom converter @see html2text() 2769 3033 * @return string $message 2770 3034 */ 2771 3035 public function msgHTML($message, $basedir = '', $advanced = false) 2772 3036 { 2773 preg_match_all( "/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);3037 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); 2774 3038 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)) { 3039 foreach ($images[2] as $imgindex => $url) { 3040 // Convert data URIs into embedded images 3041 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { 3042 $data = substr($url, strpos($url, ',')); 3043 if ($match[2]) { 3044 $data = base64_decode($data); 3045 } else { 3046 $data = rawurldecode($data); 3047 } 3048 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3049 if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) { 3050 $message = str_replace( 3051 $images[0][$imgindex], 3052 $images[1][$imgindex] . '="cid:' . $cid . '"', 3053 $message 3054 ); 3055 } 3056 } elseif (!preg_match('#^[A-z]+://#', $url)) { 3057 // Do not change urls for absolute images (thanks to corvuscorax) 2778 3058 $filename = basename($url); 2779 3059 $directory = dirname($url); 2780 3060 if ($directory == '.') { 2781 3061 $directory = ''; 2782 3062 } 2783 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 23063 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 2784 3064 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { 2785 3065 $basedir .= '/'; 2786 3066 } … … 2792 3072 $cid, 2793 3073 $filename, 2794 3074 'base64', 2795 self::_mime_types( self::mb_pathinfo($filename, PATHINFO_EXTENSION))3075 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) 2796 3076 ) 2797 3077 ) { 2798 3078 $message = preg_replace( 2799 "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",2800 $images[1][$i ] . "=\"cid:" . $cid . "\"",3079 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', 3080 $images[1][$imgindex] . '="cid:' . $cid . '"', 2801 3081 $message 2802 3082 ); 2803 3083 } … … 2805 3085 } 2806 3086 } 2807 3087 $this->isHTML(true); 3088 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 3089 $this->Body = $this->normalizeBreaks($message); 3090 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 2808 3091 if (empty($this->AltBody)) { 2809 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 3092 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 3093 self::CRLF . self::CRLF; 2810 3094 } 2811 //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better2812 $this->Body = $this->normalizeBreaks($message);2813 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));2814 3095 return $this->Body; 2815 3096 } 2816 3097 2817 3098 /** 2818 3099 * Convert an HTML string into plain text. 3100 * This is used by msgHTML(). 3101 * Note - older versions of this function used a bundled advanced converter 3102 * which was been removed for license reasons in #232 3103 * Example usage: 3104 * <code> 3105 * // Use default conversion 3106 * $plain = $mail->html2text($html); 3107 * // Use your own custom converter 3108 * $plain = $mail->html2text($html, function($html) { 3109 * $converter = new MyHtml2text($html); 3110 * return $converter->get_text(); 3111 * }); 3112 * </code> 2819 3113 * @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? 3114 * @param boolean|callable $advanced Any boolean value to use the internal converter, 3115 * or provide your own callable for custom conversion. 2821 3116 * @return string 2822 3117 */ 2823 3118 public function html2text($html, $advanced = false) 2824 3119 { 2825 if ($advanced) { 2826 require_once 'extras/class.html2text.php'; 2827 $h = new html2text($html); 2828 return $h->get_text(); 3120 if (is_callable($advanced)) { 3121 return call_user_func($advanced, $html); 2829 3122 } 2830 3123 return html_entity_decode( 2831 3124 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), … … 2844 3137 public static function _mime_types($ext = '') 2845 3138 { 2846 3139 $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', 3140 'xl' => 'application/excel', 3141 'js' => 'application/javascript', 3142 'hqx' => 'application/mac-binhex40', 3143 'cpt' => 'application/mac-compactpro', 3144 'bin' => 'application/macbinary', 3145 'doc' => 'application/msword', 3146 'word' => 'application/msword', 2853 3147 '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',3148 'dll' => 'application/octet-stream', 3149 'dms' => 'application/octet-stream', 3150 'exe' => 'application/octet-stream', 3151 'lha' => 'application/octet-stream', 3152 'lzh' => 'application/octet-stream', 3153 'psd' => 'application/octet-stream', 3154 'sea' => 'application/octet-stream', 3155 'so' => 'application/octet-stream', 3156 'oda' => 'application/oda', 3157 'pdf' => 'application/pdf', 3158 'ai' => 'application/postscript', 3159 'eps' => 'application/postscript', 3160 'ps' => 'application/postscript', 3161 'smi' => 'application/smil', 3162 'smil' => 'application/smil', 3163 'mif' => 'application/vnd.mif', 3164 'xls' => 'application/vnd.ms-excel', 3165 'ppt' => 'application/vnd.ms-powerpoint', 2872 3166 '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',3167 'wmlc' => 'application/vnd.wap.wmlc', 3168 'dcr' => 'application/x-director', 3169 'dir' => 'application/x-director', 3170 'dxr' => 'application/x-director', 3171 'dvi' => 'application/x-dvi', 3172 'gtar' => 'application/x-gtar', 3173 'php3' => 'application/x-httpd-php', 3174 'php4' => 'application/x-httpd-php', 3175 'php' => 'application/x-httpd-php', 2882 3176 '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', 3177 'phps' => 'application/x-httpd-php-source', 3178 'swf' => 'application/x-shockwave-flash', 3179 'sit' => 'application/x-stuffit', 3180 'tar' => 'application/x-tar', 3181 'tgz' => 'application/x-tar', 3182 'xht' => 'application/xhtml+xml', 2890 3183 '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',3184 'zip' => 'application/zip', 3185 'mid' => 'audio/midi', 3186 'midi' => 'audio/midi', 3187 'mp2' => 'audio/mpeg', 3188 'mp3' => 'audio/mpeg', 3189 'mpga' => 'audio/mpeg', 3190 'aif' => 'audio/x-aiff', 3191 'aifc' => 'audio/x-aiff', 3192 'aiff' => 'audio/x-aiff', 3193 'ram' => 'audio/x-pn-realaudio', 3194 'rm' => 'audio/x-pn-realaudio', 3195 'rpm' => 'audio/x-pn-realaudio-plugin', 3196 'ra' => 'audio/x-realaudio', 3197 'wav' => 'audio/x-wav', 3198 'bmp' => 'image/bmp', 3199 'gif' => 'image/gif', 3200 'jpeg' => 'image/jpeg', 3201 'jpe' => 'image/jpeg', 3202 'jpg' => 'image/jpeg', 3203 'png' => 'image/png', 3204 'tiff' => 'image/tiff', 3205 'tif' => 'image/tiff', 3206 'eml' => 'message/rfc822', 3207 'css' => 'text/css', 3208 'html' => 'text/html', 3209 'htm' => 'text/html', 2917 3210 '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', 3211 'log' => 'text/plain', 3212 'text' => 'text/plain', 3213 'txt' => 'text/plain', 3214 'rtx' => 'text/richtext', 3215 'rtf' => 'text/rtf', 3216 'vcf' => 'text/vcard', 3217 'vcard' => 'text/vcard', 3218 'xml' => 'text/xml', 3219 'xsl' => 'text/xml', 3220 'mpeg' => 'video/mpeg', 3221 'mpe' => 'video/mpeg', 3222 'mpg' => 'video/mpeg', 3223 'mov' => 'video/quicktime', 3224 'qt' => 'video/quicktime', 3225 'rv' => 'video/vnd.rn-realvideo', 3226 'avi' => 'video/x-msvideo', 2932 3227 'movie' => 'video/x-sgi-movie' 2933 3228 ); 2934 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream'); 3229 if (array_key_exists(strtolower($ext), $mimes)) { 3230 return $mimes[strtolower($ext)]; 3231 } 3232 return 'application/octet-stream'; 2935 3233 } 2936 3234 2937 3235 /** … … 2943 3241 */ 2944 3242 public static function filenameToType($filename) 2945 3243 { 2946 // In case the path is a URL, strip any query string before getting extension3244 // In case the path is a URL, strip any query string before getting extension 2947 3245 $qpos = strpos($filename, '?'); 2948 if ( $qpos !== false) {3246 if (false !== $qpos) { 2949 3247 $filename = substr($filename, 0, $qpos); 2950 3248 } 2951 3249 $pathinfo = self::mb_pathinfo($filename); … … 2966 3264 public static function mb_pathinfo($path, $options = null) 2967 3265 { 2968 3266 $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]; 3267 $pathinfo = array(); 3268 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { 3269 if (array_key_exists(1, $pathinfo)) { 3270 $ret['dirname'] = $pathinfo[1]; 3271 } 3272 if (array_key_exists(2, $pathinfo)) { 3273 $ret['basename'] = $pathinfo[2]; 3274 } 3275 if (array_key_exists(5, $pathinfo)) { 3276 $ret['extension'] = $pathinfo[5]; 3277 } 3278 if (array_key_exists(3, $pathinfo)) { 3279 $ret['filename'] = $pathinfo[3]; 3280 } 2973 3281 } 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 3282 switch ($options) { 2984 3283 case PATHINFO_DIRNAME: 2985 3284 case 'dirname': 2986 3285 return $ret['dirname']; 2987 break;2988 3286 case PATHINFO_BASENAME: 2989 3287 case 'basename': 2990 3288 return $ret['basename']; 2991 break;2992 3289 case PATHINFO_EXTENSION: 2993 3290 case 'extension': 2994 3291 return $ret['extension']; 2995 break;2996 3292 case PATHINFO_FILENAME: 2997 3293 case 'filename': 2998 3294 return $ret['filename']; 2999 break;3000 3295 default: 3001 3296 return $ret; 3002 3297 } … … 3004 3299 3005 3300 /** 3006 3301 * Set or reset instance properties. 3007 * 3302 * You should avoid this function - it's more verbose, less efficient, more error-prone and 3303 * harder to debug than setting properties directly. 3008 3304 * Usage Example: 3009 * $page->set('X-Priority', '3'); 3010 * 3305 * `$mail->set('SMTPSecure', 'tls');` 3306 * is the same as: 3307 * `$mail->SMTPSecure = 'tls';` 3011 3308 * @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? 3309 * @param string $name The property name to set 3310 * @param mixed $value The value to set the property to 3311 * @return boolean 3312 * @TODO Should this not be using the __set() magic function? 3018 3313 */ 3019 3314 public function set($name, $value = '') 3020 3315 { 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 } 3316 if (property_exists($this, $name)) { 3317 $this->$name = $value; 3318 return true; 3319 } else { 3320 $this->setError($this->lang('variable_set') . $name); 3321 return false; 3032 3322 } 3033 return true;3034 3323 } 3035 3324 3036 3325 /** … … 3061 3350 3062 3351 3063 3352 /** 3064 * Set the p rivate key fileand password for S/MIME signing.3353 * Set the public and private key files and password for S/MIME signing. 3065 3354 * @access public 3066 3355 * @param string $cert_filename 3067 3356 * @param string $key_filename 3068 3357 * @param string $key_pass Password for private key 3358 * @param string $extracerts_filename Optional path to chain certificate 3069 3359 */ 3070 public function sign($cert_filename, $key_filename, $key_pass )3360 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') 3071 3361 { 3072 3362 $this->sign_cert_file = $cert_filename; 3073 3363 $this->sign_key_file = $key_filename; 3074 3364 $this->sign_key_pass = $key_pass; 3365 $this->sign_extracerts_file = $extracerts_filename; 3075 3366 } 3076 3367 3077 3368 /** … … 3088 3379 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { 3089 3380 $line .= $txt[$i]; 3090 3381 } else { 3091 $line .= "=" . sprintf("%02X", $ord);3382 $line .= '=' . sprintf('%02X', $ord); 3092 3383 } 3093 3384 } 3094 3385 return $line; … … 3097 3388 /** 3098 3389 * Generate a DKIM signature. 3099 3390 * @access public 3100 * @param string $s 3391 * @param string $signHeader 3101 3392 * @throws phpmailerException 3102 3393 * @return string 3103 3394 */ 3104 public function DKIM_Sign($s )3395 public function DKIM_Sign($signHeader) 3105 3396 { 3106 3397 if (!defined('PKCS7_TEXT')) { 3107 3398 if ($this->exceptions) { 3108 throw new phpmailerException($this->lang( "signing") . ' OpenSSL extension missing.');3399 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 3109 3400 } 3110 3401 return ''; 3111 3402 } … … 3115 3406 } else { 3116 3407 $privKey = $privKeyStr; 3117 3408 } 3118 if (openssl_sign($s , $signature, $privKey)) {3409 if (openssl_sign($signHeader, $signature, $privKey)) { 3119 3410 return base64_encode($signature); 3120 3411 } 3121 3412 return ''; … … 3124 3415 /** 3125 3416 * Generate a DKIM canonicalization header. 3126 3417 * @access public 3127 * @param string $s Header3418 * @param string $signHeader Header 3128 3419 * @return string 3129 3420 */ 3130 public function DKIM_HeaderC($s )3421 public function DKIM_HeaderC($signHeader) 3131 3422 { 3132 $s = preg_replace("/\r\n\s+/", " ", $s);3133 $lines = explode("\r\n", $s );3423 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); 3424 $lines = explode("\r\n", $signHeader); 3134 3425 foreach ($lines as $key => $line) { 3135 list($heading, $value) = explode( ":", $line, 2);3426 list($heading, $value) = explode(':', $line, 2); 3136 3427 $heading = strtolower($heading); 3137 $value = preg_replace( "/\s+/", " ", $value); // Compress useless spaces3138 $lines[$key] = $heading . ":". trim($value); // Don't forget to remove WSP around the value3428 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces 3429 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 3139 3430 } 3140 $s = implode("\r\n", $lines);3141 return $s ;3431 $signHeader = implode("\r\n", $lines); 3432 return $signHeader; 3142 3433 } 3143 3434 3144 3435 /** … … 3189 3480 $to_header = $header; 3190 3481 $current = 'to_header'; 3191 3482 } else { 3192 if ( $current&& strpos($header, ' =?') === 0) {3193 $ current .= $header;3483 if (!empty($$current) && strpos($header, ' =?') === 0) { 3484 $$current .= $header; 3194 3485 } else { 3195 3486 $current = ''; 3196 3487 } … … 3205 3496 ); // Copied header fields (dkim-quoted-printable) 3206 3497 $body = $this->DKIM_BodyC($body); 3207 3498 $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=" . 3499 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body 3500 if ('' == $this->DKIM_identity) { 3501 $ident = ''; 3502 } else { 3503 $ident = ' i=' . $this->DKIM_identity . ';'; 3504 } 3505 $dkimhdrs = 'DKIM-Signature: v=1; a=' . 3506 $DKIMsignatureType . '; q=' . 3507 $DKIMquery . '; l=' . 3508 $DKIMlen . '; s=' . 3214 3509 $this->DKIM_selector . 3215 3510 ";\r\n" . 3216 "\tt=" . $DKIMtime . "; c=". $DKIMcanonicalization . ";\r\n" .3511 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3217 3512 "\th=From:To:Subject;\r\n" . 3218 "\td=" . $this->DKIM_domain . ";". $ident . "\r\n" .3513 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3219 3514 "\tz=$from\r\n" . 3220 3515 "\t|$to\r\n" . 3221 3516 "\t|$subject;\r\n" . … … 3229 3524 } 3230 3525 3231 3526 /** 3527 * Detect if a string contains a line longer than the maximum line length allowed. 3528 * @param string $str 3529 * @return boolean 3530 * @static 3531 */ 3532 public static function hasLineLongerThanMax($str) 3533 { 3534 //+2 to include CRLF line break for a 1000 total 3535 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); 3536 } 3537 3538 /** 3539 * Allows for public read access to 'to' property. 3540 * @access public 3541 * @return array 3542 */ 3543 public function getToAddresses() 3544 { 3545 return $this->to; 3546 } 3547 3548 /** 3549 * Allows for public read access to 'cc' property. 3550 * @access public 3551 * @return array 3552 */ 3553 public function getCcAddresses() 3554 { 3555 return $this->cc; 3556 } 3557 3558 /** 3559 * Allows for public read access to 'bcc' property. 3560 * @access public 3561 * @return array 3562 */ 3563 public function getBccAddresses() 3564 { 3565 return $this->bcc; 3566 } 3567 3568 /** 3569 * Allows for public read access to 'ReplyTo' property. 3570 * @access public 3571 * @return array 3572 */ 3573 public function getReplyToAddresses() 3574 { 3575 return $this->ReplyTo; 3576 } 3577 3578 /** 3579 * Allows for public read access to 'all_recipients' property. 3580 * @access public 3581 * @return array 3582 */ 3583 public function getAllRecipientAddresses() 3584 { 3585 return $this->all_recipients; 3586 } 3587 3588 /** 3232 3589 * Perform a callback. 3233 * @param bool $isSent3234 * @param string$to3235 * @param string$cc3236 * @param string$bcc3590 * @param boolean $isSent 3591 * @param array $to 3592 * @param array $cc 3593 * @param array $bcc 3237 3594 * @param string $subject 3238 3595 * @param string $body 3239 3596 * @param string $from 3240 3597 */ 3241 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)3598 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) 3242 3599 { 3243 3600 if (!empty($this->action_function) && is_callable($this->action_function)) { 3244 3601 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); -
class-smtp.php
1 1 <?php 2 2 /** 3 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> 4 * PHP Version 5 5 * @package PHPMailer 6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 10 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 11 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 12 * @ copyright 2013 Marcus Bointon13 * @copyright 20 04 - 2008 Andy Prevost10 * @author Brent R. Matzelle (original founder) 11 * @copyright 2014 Marcus Bointon 14 12 * @copyright 2010 - 2012 Jim Jagielski 15 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 13 * @copyright 2004 - 2009 Andy Prevost 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 15 * @note This program is distributed in the hope that it will be useful - WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. 16 18 */ 17 19 18 20 /** 19 21 * PHPMailer RFC821 SMTP email transport class. 20 * 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) 22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. 23 * @package PHPMailer 24 * @author Chris Ryan 25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 32 26 */ 33 34 27 class SMTP 35 28 { 36 29 /** 37 * The PHPMailer SMTP Version number. 30 * The PHPMailer SMTP version number. 31 * @type string 38 32 */ 39 const VERSION = '5.2. 7';33 const VERSION = '5.2.10'; 40 34 41 35 /** 42 36 * SMTP line break constant. 37 * @type string 43 38 */ 44 39 const CRLF = "\r\n"; 45 40 46 41 /** 47 42 * The SMTP port to use if one is not specified. 43 * @type integer 48 44 */ 49 45 const DEFAULT_SMTP_PORT = 25; 50 46 51 47 /** 48 * The maximum line length allowed by RFC 2822 section 2.1.1 49 * @type integer 50 */ 51 const MAX_LINE_LENGTH = 998; 52 53 /** 54 * Debug level for no output 55 */ 56 const DEBUG_OFF = 0; 57 58 /** 59 * Debug level to show client -> server messages 60 */ 61 const DEBUG_CLIENT = 1; 62 63 /** 64 * Debug level to show client -> server and server -> client messages 65 */ 66 const DEBUG_SERVER = 2; 67 68 /** 69 * Debug level to show connection status, client -> server and server -> client messages 70 */ 71 const DEBUG_CONNECTION = 3; 72 73 /** 74 * Debug level to show all messages 75 */ 76 const DEBUG_LOWLEVEL = 4; 77 78 /** 52 79 * The PHPMailer SMTP Version number. 53 80 * @type string 54 * @deprecated This should be a constant81 * @deprecated Use the `VERSION` constant instead 55 82 * @see SMTP::VERSION 56 83 */ 57 public $Version = '5.2. 7';84 public $Version = '5.2.10'; 58 85 59 86 /** 60 87 * SMTP server port number. 61 * @type int 62 * @deprecated This is only ever u ed as default value, so should be a constant88 * @type integer 89 * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead 63 90 * @see SMTP::DEFAULT_SMTP_PORT 64 91 */ 65 92 public $SMTP_PORT = 25; 66 93 67 94 /** 68 * SMTP reply line ending 95 * SMTP reply line ending. 69 96 * @type string 70 * @deprecated Use the classconstant instead97 * @deprecated Use the `CRLF` constant instead 71 98 * @see SMTP::CRLF 72 99 */ 73 100 public $CRLF = "\r\n"; … … 74 101 75 102 /** 76 103 * Debug output level. 77 * Options: 0 for no output, 1 for commands, 2 for data and commands 78 * @type int 104 * Options: 105 * * self::DEBUG_OFF (`0`) No debug output, default 106 * * self::DEBUG_CLIENT (`1`) Client commands 107 * * self::DEBUG_SERVER (`2`) Client commands and server responses 108 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status 109 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages 110 * @type integer 79 111 */ 80 public $do_debug = 0;112 public $do_debug = self::DEBUG_OFF; 81 113 82 114 /** 83 * The function/method to use for debugging output. 84 * Options: 'echo', 'html' or 'error_log' 85 * @type string 115 * How to handle debug output. 116 * Options: 117 * * `echo` Output plain-text as-is, appropriate for CLI 118 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 119 * * `error_log` Output to error log as configured in php.ini 120 * 121 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 122 * <code> 123 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 124 * </code> 125 * @type string|callable 86 126 */ 87 127 public $Debugoutput = 'echo'; 88 128 89 129 /** 90 130 * Whether to use VERP. 91 * @type bool 131 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 132 * @link http://www.postfix.org/VERP_README.html Info on VERP 133 * @type boolean 92 134 */ 93 135 public $do_verp = false; 94 136 95 137 /** 96 * The SMTP timeout value for reads, in seconds. 97 * @type int 138 * The timeout value for connection, in seconds. 139 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 140 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. 141 * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 142 * @type integer 98 143 */ 99 public $Timeout = 15;144 public $Timeout = 300; 100 145 101 146 /** 102 * The SMTP timelimit value for reads, in seconds. 103 * @type int 147 * How long to wait for commands to complete, in seconds. 148 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 149 * @type integer 104 150 */ 105 public $Timelimit = 30 ;151 public $Timelimit = 300; 106 152 107 153 /** 108 154 * The socket for the server connection. … … 111 157 protected $smtp_conn; 112 158 113 159 /** 114 * Error message, if any, for the last call.115 * @type string160 * Error information, if any, for the last SMTP command. 161 * @type array 116 162 */ 117 protected $error = ''; 163 protected $error = array( 164 'error' => '', 165 'detail' => '', 166 'smtp_code' => '', 167 'smtp_code_ex' => '' 168 ); 118 169 119 170 /** 120 171 * The reply the server sent to us for HELO. 121 * @type string 172 * If null, no HELO string has yet been received. 173 * @type string|null 122 174 */ 123 protected $helo_rply = '';175 protected $helo_rply = null; 124 176 125 177 /** 178 * The set of SMTP extensions sent in reply to EHLO command. 179 * Indexes of the array are extension names. 180 * Value at index 'HELO' or 'EHLO' (according to command that was sent) 181 * represents the server name. In case of HELO it is the only element of the array. 182 * Other values can be boolean TRUE or an array containing extension options. 183 * If null, no HELO/EHLO string has yet been received. 184 * @type array|null 185 */ 186 protected $server_caps = null; 187 188 /** 126 189 * The most recent reply received from the server. 127 190 * @type string 128 191 */ … … 129 192 protected $last_reply = ''; 130 193 131 194 /** 132 * Constructor.133 * @access public134 */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 195 * Output debugging info via a user-selected method. 196 * @see SMTP::$Debugoutput 197 * @see SMTP::$do_debug 146 198 * @param string $str Debug string to output 199 * @param integer $level The debug level of this message; see DEBUG_* constants 147 200 * @return void 148 201 */ 149 protected function edebug($str )202 protected function edebug($str, $level = 0) 150 203 { 204 if ($level > $this->do_debug) { 205 return; 206 } 207 //Avoid clash with built-in function names 208 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 209 call_user_func($this->Debugoutput, $str, $this->do_debug); 210 return; 211 } 151 212 switch ($this->Debugoutput) { 152 213 case 'error_log': 153 214 //Don't output, just log … … 164 225 break; 165 226 case 'echo': 166 227 default: 167 //Just echoes whatever was received 168 echo $str; 228 //Normalize line breaks 229 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 230 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 231 "\n", 232 "\n \t ", 233 trim($str) 234 )."\n"; 169 235 } 170 236 } 171 237 172 238 /** 173 239 * Connect to an SMTP server. 174 * @param string $host 175 * @param int $portThe port number to connect to176 * @param int $timeout How long to wait for the connection to open240 * @param string $host SMTP server IP or host name 241 * @param integer $port The port number to connect to 242 * @param integer $timeout How long to wait for the connection to open 177 243 * @param array $options An array of options for stream_context_create() 178 244 * @access public 179 * @return bool 245 * @return boolean 180 246 */ 181 247 public function connect($host, $port = null, $timeout = 30, $options = array()) 182 248 { 249 static $streamok; 250 //This is enabled by default since 5.0.0 but some providers disable it 251 //Check this once and cache the result 252 if (is_null($streamok)) { 253 $streamok = function_exists('stream_socket_client'); 254 } 183 255 // Clear errors to avoid confusion 184 $this->error = null; 185 256 $this->setError(''); 186 257 // Make sure we are __not__ connected 187 258 if ($this->connected()) { 188 259 // Already connected, generate error 189 $this-> error = array('error' =>'Already connected to a server');260 $this->setError('Already connected to a server'); 190 261 return false; 191 262 } 192 193 263 if (empty($port)) { 194 264 $port = self::DEFAULT_SMTP_PORT; 195 265 } 196 197 266 // Connect to the SMTP server 267 $this->edebug( 268 "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), 269 self::DEBUG_CONNECTION 270 ); 198 271 $errno = 0; 199 272 $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 273 if ($streamok) { 274 $socket_context = stream_context_create($options); 275 //Suppress errors; connection failures are handled at a higher level 276 $this->smtp_conn = @stream_socket_client( 277 $host . ":" . $port, 278 $errno, 279 $errstr, 280 $timeout, 281 STREAM_CLIENT_CONNECT, 282 $socket_context 283 ); 284 } else { 285 //Fall back to fsockopen which should work in more places, but is missing some features 286 $this->edebug( 287 "Connection: stream_socket_client not available, falling back to fsockopen", 288 self::DEBUG_CONNECTION 289 ); 290 $this->smtp_conn = fsockopen( 291 $host, 292 $port, 293 $errno, 294 $errstr, 295 $timeout 296 ); 297 } 211 298 // 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' =>$errstr299 if (!is_resource($this->smtp_conn)) { 300 $this->setError( 301 'Failed to connect to server', 302 $errno, 303 $errstr 217 304 ); 218 if ($this->do_debug >= 1) { 219 $this->edebug( 220 'SMTP -> ERROR: ' . $this->error['error'] 221 . ": $errstr ($errno)" 222 ); 223 } 305 $this->edebug( 306 'SMTP ERROR: ' . $this->error['error'] 307 . ": $errstr ($errno)", 308 self::DEBUG_CLIENT 309 ); 224 310 return false; 225 311 } 226 312 $this->edebug('Connection: opened', self::DEBUG_CONNECTION); 227 313 // SMTP server can take longer to respond, give longer timeout for first read 228 314 // Windows does not have support for this timeout function 229 315 if (substr(PHP_OS, 0, 3) != 'WIN') { 230 316 $max = ini_get('max_execution_time'); 231 if ($max != 0 && $timeout > $max) { // Don't bother if unlimited 317 // Don't bother if unlimited 318 if ($max != 0 && $timeout > $max) { 232 319 @set_time_limit($timeout); 233 320 } 234 321 stream_set_timeout($this->smtp_conn, $timeout, 0); 235 322 } 236 237 323 // Get any announcement 238 324 $announce = $this->get_lines(); 239 240 if ($this->do_debug >= 2) { 241 $this->edebug('SMTP -> FROM SERVER:' . $announce); 242 } 243 325 $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); 244 326 return true; 245 327 } 246 328 … … 247 329 /** 248 330 * Initiate a TLS (encrypted) session. 249 331 * @access public 250 * @return bool 332 * @return boolean 251 333 */ 252 334 public function startTLS() 253 335 { 254 if (!$this->sendCommand( "STARTTLS", "STARTTLS", 220)) {336 if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 255 337 return false; 256 338 } 257 339 // Begin encrypted connection … … 259 341 $this->smtp_conn, 260 342 true, 261 343 STREAM_CRYPTO_METHOD_TLS_CLIENT 262 ) 263 ) { 344 )) { 264 345 return false; 265 346 } 266 347 return true; … … 276 357 * @param string $realm The auth realm for NTLM 277 358 * @param string $workstation The auth workstation for NTLM 278 359 * @access public 279 * @return bool True if successfully authenticated.360 * @return boolean True if successfully authenticated. 280 361 */ 281 362 public function authenticate( 282 363 $username, 283 364 $password, 284 $authtype = 'LOGIN',365 $authtype = null, 285 366 $realm = '', 286 367 $workstation = '' 287 368 ) { 288 if (empty($authtype)) { 369 if (!$this->server_caps) { 370 $this->setError('Authentication is not allowed before HELO/EHLO'); 371 return false; 372 } 373 374 if (array_key_exists('EHLO', $this->server_caps)) { 375 // SMTP extensions are available. Let's try to find a proper authentication method 376 377 if (!array_key_exists('AUTH', $this->server_caps)) { 378 $this->setError('Authentication is not allowed at this stage'); 379 // 'at this stage' means that auth may be allowed after the stage changes 380 // e.g. after STARTTLS 381 return false; 382 } 383 384 self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); 385 self::edebug( 386 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), 387 self::DEBUG_LOWLEVEL 388 ); 389 390 if (empty($authtype)) { 391 foreach (array('LOGIN', 'CRAM-MD5', 'PLAIN') as $method) { 392 if (in_array($method, $this->server_caps['AUTH'])) { 393 $authtype = $method; 394 break; 395 } 396 } 397 if (empty($authtype)) { 398 $this->setError('No supported authentication methods found'); 399 return false; 400 } 401 self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); 402 } 403 404 if (!in_array($authtype, $this->server_caps['AUTH'])) { 405 $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); 406 return false; 407 } 408 } elseif (empty($authtype)) { 289 409 $authtype = 'LOGIN'; 290 410 } 291 292 411 switch ($authtype) { 293 412 case 'PLAIN': 294 413 // Start authentication … … 317 436 return false; 318 437 } 319 438 break; 320 case 'NTLM':321 /*322 * ntlm_sasl_client.php323 * Bundled with Permission324 *325 * How to telnet in windows:326 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx327 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication328 */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 available333 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 //msg1344 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1345 346 if (!$this->sendCommand(347 'AUTH NTLM',348 'AUTH NTLM ' . base64_encode($msg1),349 334350 )351 ) {352 return false;353 }354 355 //Though 0 based, there is a white space after the 3 digit number356 //msg2357 $challenge = substr($this->last_reply, 3);358 $challenge = base64_decode($challenge);359 $ntlm_res = $ntlm_client->NTLMResponse(360 substr($challenge, 24, 8),361 $password362 );363 //msg3364 $msg3 = $ntlm_client->TypeMsg3(365 $ntlm_res,366 $username,367 $realm,368 $workstation369 );370 // send encoded username371 return $this->sendCommand('Username', base64_encode($msg3), 235);372 break;373 439 case 'CRAM-MD5': 374 440 // Start authentication 375 441 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { … … 383 449 384 450 // send encoded credentials 385 451 return $this->sendCommand('Username', base64_encode($response), 235); 386 break; 452 default: 453 $this->setError("Authentication method \"$authtype\" is not supported"); 454 return false; 387 455 } 388 456 return true; 389 457 } … … 411 479 // Eliminates the need to install mhash to compute a HMAC 412 480 // by Lance Rushing 413 481 414 $b = 64; // byte length for md5415 if (strlen($key) > $b ) {482 $bytelen = 64; // byte length for md5 483 if (strlen($key) > $bytelen) { 416 484 $key = pack('H*', md5($key)); 417 485 } 418 $key = str_pad($key, $b , chr(0x00));419 $ipad = str_pad('', $b , chr(0x36));420 $opad = str_pad('', $b , chr(0x5c));486 $key = str_pad($key, $bytelen, chr(0x00)); 487 $ipad = str_pad('', $bytelen, chr(0x36)); 488 $opad = str_pad('', $bytelen, chr(0x5c)); 421 489 $k_ipad = $key ^ $ipad; 422 490 $k_opad = $key ^ $opad; 423 491 … … 427 495 /** 428 496 * Check connection state. 429 497 * @access public 430 * @return bool True if connected.498 * @return boolean True if connected. 431 499 */ 432 500 public function connected() 433 501 { 434 if ( !empty($this->smtp_conn)) {502 if (is_resource($this->smtp_conn)) { 435 503 $sock_status = stream_get_meta_data($this->smtp_conn); 436 504 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 } 505 // The socket is valid but we are not connected 506 $this->edebug( 507 'SMTP NOTICE: EOF caught while checking if connected', 508 self::DEBUG_CLIENT 509 ); 443 510 $this->close(); 444 511 return false; 445 512 } … … 457 524 */ 458 525 public function close() 459 526 { 460 $this->error = null; // so there is no confusion 527 $this->setError(''); 528 $this->server_caps = null; 461 529 $this->helo_rply = null; 462 if ( !empty($this->smtp_conn)) {530 if (is_resource($this->smtp_conn)) { 463 531 // close the connection and cleanup 464 532 fclose($this->smtp_conn); 465 $this->smtp_conn = 0; 533 $this->smtp_conn = null; //Makes for cleaner serialization 534 $this->edebug('Connection: closed', self::DEBUG_CONNECTION); 466 535 } 467 536 } 468 537 … … 476 545 * Implements rfc 821: DATA <CRLF> 477 546 * @param string $msg_data Message data to send 478 547 * @access public 479 * @return bool 548 * @return boolean 480 549 */ 481 550 public function data($msg_data) 482 551 { 552 //This will use the standard timelimit 483 553 if (!$this->sendCommand('DATA', 'DATA', 354)) { 484 554 return false; 485 555 } 486 556 487 557 /* 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. 558 * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) 559 * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into 560 * smaller lines to fit within the limit. 561 * We will also look for lines that start with a '.' and prepend an additional '.'. 562 * NOTE: this does not count towards line-length limit. 496 563 */ 497 564 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); 565 // Normalize line breaks before exploding 566 $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); 502 567 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. 568 /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field 569 * of the first line (':' separated) does not contain a space then it _should_ be a header and we will 570 * process all lines before a blank line as headers. 510 571 */ 511 572 512 573 $field = substr($lines[0], 0, strpos($lines[0], ':')); 513 574 $in_headers = false; 514 if (!empty($field) && !strstr($field, ' ')) {575 if (!empty($field) && strpos($field, ' ') === false) { 515 576 $in_headers = true; 516 577 } 517 578 518 //RFC 2822 section 2.1.1 limit519 $max_line_length = 998;520 521 579 foreach ($lines as $line) { 522 $lines_out = null;523 if ($ line == '' && $in_headers) {580 $lines_out = array(); 581 if ($in_headers and $line == '') { 524 582 $in_headers = false; 525 583 } 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 584 //Break this line up into several smaller lines if it's too long 585 //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), 586 while (isset($line[self::MAX_LINE_LENGTH])) { 587 //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on 588 //so as to avoid breaking in the middle of a word 589 $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); 590 //Deliberately matches both false and 0 531 591 if (!$pos) { 532 $pos = $max_line_length - 1; 592 //No nice break found, add a hard break 593 $pos = self::MAX_LINE_LENGTH - 1; 533 594 $lines_out[] = substr($line, 0, $pos); 534 595 $line = substr($line, $pos); 535 596 } else { 597 //Break at the found point 536 598 $lines_out[] = substr($line, 0, $pos); 599 //Move along by the amount we dealt with 537 600 $line = substr($line, $pos + 1); 538 601 } 539 540 /* If processing headers add a LWSP-char to the front of new line 541 * rfc822 on long msg headers 542 */ 602 //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 543 603 if ($in_headers) { 544 604 $line = "\t" . $line; 545 605 } … … 546 606 } 547 607 $lines_out[] = $line; 548 608 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 } 609 //Send the lines to the server 610 foreach ($lines_out as $line_out) { 611 //RFC2821 section 4.5.2 612 if (!empty($line_out) and $line_out[0] == '.') { 613 $line_out = '.' . $line_out; 555 614 } 556 615 $this->client_send($line_out . self::CRLF); 557 616 } 558 617 } 559 618 560 // Message data has been sent, complete the command 561 return $this->sendCommand('DATA END', '.', 250); 619 //Message data has been sent, complete the command 620 //Increase timelimit for end of DATA command 621 $savetimelimit = $this->Timelimit; 622 $this->Timelimit = $this->Timelimit * 2; 623 $result = $this->sendCommand('DATA END', '.', 250); 624 //Restore timelimit 625 $this->Timelimit = $savetimelimit; 626 return $result; 562 627 } 563 628 564 629 /** … … 565 630 * Send an SMTP HELO or EHLO command. 566 631 * Used to identify the sending server to the receiving server. 567 632 * This makes sure that client and server are in a known state. 568 * Implements fromRFC 821: HELO <SP> <domain> <CRLF>633 * Implements RFC 821: HELO <SP> <domain> <CRLF> 569 634 * and RFC 2821 EHLO. 570 635 * @param string $host The host name or IP to connect to 571 636 * @access public 572 * @return bool 637 * @return boolean 573 638 */ 574 639 public function hello($host = '') 575 640 { 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; 641 //Try extended hello first (RFC 2821) 642 return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); 584 643 } 585 644 586 645 /** … … 588 647 * Low-level implementation used by hello() 589 648 * @see hello() 590 649 * @param string $hello The HELO string 591 * @param string $host 650 * @param string $host The hostname to say we are 592 651 * @access protected 593 * @return bool 652 * @return boolean 594 653 */ 595 654 protected function sendHello($hello, $host) 596 655 { 597 656 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 598 657 $this->helo_rply = $this->last_reply; 658 if ($noerror) { 659 $this->parseHelloFields($hello); 660 } else { 661 $this->server_caps = null; 662 } 599 663 return $noerror; 600 664 } 601 665 602 666 /** 667 * Parse a reply to HELO/EHLO command to discover server extensions. 668 * In case of HELO, the only parameter that can be discovered is a server name. 669 * @access protected 670 * @param string $type - 'HELO' or 'EHLO' 671 */ 672 protected function parseHelloFields($type) 673 { 674 $this->server_caps = array(); 675 $lines = explode("\n", $this->last_reply); 676 foreach ($lines as $n => $s) { 677 $s = trim(substr($s, 4)); 678 if (!$s) { 679 continue; 680 } 681 $fields = explode(' ', $s); 682 if (!empty($fields)) { 683 if (!$n) { 684 $name = $type; 685 $fields = $fields[0]; 686 } else { 687 $name = array_shift($fields); 688 if ($name == 'SIZE') { 689 $fields = ($fields) ? $fields[0] : 0; 690 } 691 } 692 $this->server_caps[$name] = ($fields ? $fields : true); 693 } 694 } 695 } 696 697 /** 603 698 * Send an SMTP MAIL command. 604 699 * Starts a mail transaction from the email address specified in 605 700 * $from. Returns true if successful or false otherwise. If True … … 608 703 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 609 704 * @param string $from Source address of this message 610 705 * @access public 611 * @return bool 706 * @return boolean 612 707 */ 613 708 public function mail($from) 614 709 { … … 624 719 * Send an SMTP QUIT command. 625 720 * Closes the socket if there is no error or the $close_on_error argument is true. 626 721 * Implements from rfc 821: QUIT <CRLF> 627 * @param bool $close_on_error Should the connection close if an error occurs?722 * @param boolean $close_on_error Should the connection close if an error occurs? 628 723 * @access public 629 * @return bool 724 * @return boolean 630 725 */ 631 726 public function quit($close_on_error = true) 632 727 { 633 728 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 634 $e = $this->error; //Save any error729 $err = $this->error; //Save any error 635 730 if ($noerror or $close_on_error) { 636 731 $this->close(); 637 $this->error = $e ; //Restore any error from the quit command732 $this->error = $err; //Restore any error from the quit command 638 733 } 639 734 return $noerror; 640 735 } … … 641 736 642 737 /** 643 738 * Send an SMTP RCPT command. 644 * Sets the TO argument to $to .739 * Sets the TO argument to $toaddr. 645 740 * Returns true if the recipient was accepted false if it was rejected. 646 741 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 647 * @param string $to The address the message is being sent to742 * @param string $toaddr The address the message is being sent to 648 743 * @access public 649 * @return bool 744 * @return boolean 650 745 */ 651 public function recipient($to )746 public function recipient($toaddr) 652 747 { 653 748 return $this->sendCommand( 654 'RCPT TO 655 'RCPT TO:<' . $to . '>',749 'RCPT TO', 750 'RCPT TO:<' . $toaddr . '>', 656 751 array(250, 251) 657 752 ); 658 753 } … … 662 757 * Abort any transaction that is currently in progress. 663 758 * Implements rfc 821: RSET <CRLF> 664 759 * @access public 665 * @return bool True on success.760 * @return boolean True on success. 666 761 */ 667 762 public function reset() 668 763 { … … 673 768 * Send a command to an SMTP server and check its return code. 674 769 * @param string $command The command name - not sent to the server 675 770 * @param string $commandstring The actual command to send 676 * @param int |array $expect One or more expected integer success codes771 * @param integer|array $expect One or more expected integer success codes 677 772 * @access protected 678 * @return bool True on success.773 * @return boolean True on success. 679 774 */ 680 775 protected function sendCommand($command, $commandstring, $expect) 681 776 { 682 777 if (!$this->connected()) { 683 $this->error = array( 684 "error" => "Called $command without being connected" 685 ); 778 $this->setError("Called $command without being connected"); 686 779 return false; 687 780 } 688 781 $this->client_send($commandstring . self::CRLF); 689 782 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); 783 $this->last_reply = $this->get_lines(); 784 // Fetch SMTP code and possible error code explanation 785 $matches = array(); 786 if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { 787 $code = $matches[1]; 788 $code_ex = (count($matches) > 2 ? $matches[2] : null); 789 // Cut off error code from each response line 790 $detail = preg_replace( 791 "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", 792 '', 793 $this->last_reply 794 ); 795 } else { 796 // Fall back to simple parsing if regex fails 797 $code = substr($this->last_reply, 0, 3); 798 $code_ex = null; 799 $detail = substr($this->last_reply, 4); 695 800 } 696 801 802 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 803 697 804 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)805 $this->setError( 806 "$command command failed", 807 $detail, 808 $code, 809 $code_ex 703 810 ); 704 if ($this->do_debug >= 1) { 705 $this->edebug( 706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply 707 ); 708 } 811 $this->edebug( 812 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, 813 self::DEBUG_CLIENT 814 ); 709 815 return false; 710 816 } 711 817 712 $this->last_reply = $reply; 713 $this->error = null; 818 $this->setError(''); 714 819 return true; 715 820 } 716 821 … … 725 830 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 726 831 * @param string $from The address the message is from 727 832 * @access public 728 * @return bool 833 * @return boolean 729 834 */ 730 835 public function sendAndMail($from) 731 836 { 732 return $this->sendCommand( "SAML", "SAML FROM:$from", 250);837 return $this->sendCommand('SAML', "SAML FROM:$from", 250); 733 838 } 734 839 735 840 /** … … 736 841 * Send an SMTP VRFY command. 737 842 * @param string $name The name to verify 738 843 * @access public 739 * @return bool 844 * @return boolean 740 845 */ 741 846 public function verify($name) 742 847 { 743 return $this->sendCommand( "VRFY", "VRFY $name", array(250, 251));848 return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); 744 849 } 745 850 746 851 /** … … 747 852 * Send an SMTP NOOP command. 748 853 * Used to keep keep-alives alive, doesn't actually do anything 749 854 * @access public 750 * @return bool 855 * @return boolean 751 856 */ 752 857 public function noop() 753 858 { 754 return $this->sendCommand( "NOOP", "NOOP", 250);859 return $this->sendCommand('NOOP', 'NOOP', 250); 755 860 } 756 861 757 862 /** 758 863 * Send an SMTP TURN command. 759 864 * 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 future865 * This method is here to make the RFC821 Definition complete for this class 866 * and _may_ be implemented in future 762 867 * Implements from rfc 821: TURN <CRLF> 763 868 * @access public 764 * @return bool 869 * @return boolean 765 870 */ 766 871 public function turn() 767 872 { 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 } 873 $this->setError('The SMTP TURN command is not implemented'); 874 $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); 774 875 return false; 775 876 } 776 877 … … 778 879 * Send raw data to the server. 779 880 * @param string $data The data to send 780 881 * @access public 781 * @return int |bool The number of bytes sent to the server or FALSEon error882 * @return integer|boolean The number of bytes sent to the server or false on error 782 883 */ 783 884 public function client_send($data) 784 885 { 785 if ($this->do_debug >= 1) { 786 $this->edebug("CLIENT -> SMTP: $data"); 787 } 886 $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); 788 887 return fwrite($this->smtp_conn, $data); 789 888 } 790 889 … … 799 898 } 800 899 801 900 /** 901 * Get SMTP extensions available on the server 902 * @access public 903 * @return array|null 904 */ 905 public function getServerExtList() 906 { 907 return $this->server_caps; 908 } 909 910 /** 911 * A multipurpose method 912 * The method works in three ways, dependent on argument value and current state 913 * 1. HELO/EHLO was not sent - returns null and set up $this->error 914 * 2. HELO was sent 915 * $name = 'HELO': returns server name 916 * $name = 'EHLO': returns boolean false 917 * $name = any string: returns null and set up $this->error 918 * 3. EHLO was sent 919 * $name = 'HELO'|'EHLO': returns server name 920 * $name = any string: if extension $name exists, returns boolean True 921 * or its options. Otherwise returns boolean False 922 * In other words, one can use this method to detect 3 conditions: 923 * - null returned: handshake was not or we don't know about ext (refer to $this->error) 924 * - false returned: the requested feature exactly not exists 925 * - positive value returned: the requested feature exists 926 * @param string $name Name of SMTP extension or 'HELO'|'EHLO' 927 * @return mixed 928 */ 929 public function getServerExt($name) 930 { 931 if (!$this->server_caps) { 932 $this->setError('No HELO/EHLO was sent'); 933 return null; 934 } 935 936 // the tight logic knot ;) 937 if (!array_key_exists($name, $this->server_caps)) { 938 if ($name == 'HELO') { 939 return $this->server_caps['EHLO']; 940 } 941 if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { 942 return false; 943 } 944 $this->setError('HELO handshake was used. Client knows nothing about server extensions'); 945 return null; 946 } 947 948 return $this->server_caps[$name]; 949 } 950 951 /** 802 952 * Get the last reply from the server. 803 953 * @access public 804 954 * @return string … … 819 969 */ 820 970 protected function get_lines() 821 971 { 972 // If the connection is bad, give up straight away 973 if (!is_resource($this->smtp_conn)) { 974 return ''; 975 } 822 976 $data = ''; 823 977 $endtime = 0; 824 // If the connection is bad, give up now825 if (!is_resource($this->smtp_conn)) {826 return $data;827 }828 978 stream_set_timeout($this->smtp_conn, $this->Timeout); 829 979 if ($this->Timelimit > 0) { 830 980 $endtime = time() + $this->Timelimit; … … 831 981 } 832 982 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 833 983 $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 } 984 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"", self::DEBUG_LOWLEVEL); 985 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); 838 986 $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) == ' ') { 987 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); 988 // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen 989 if ((isset($str[3]) and $str[3] == ' ')) { 844 990 break; 845 991 } 846 992 // Timed-out? Log and break 847 993 $info = stream_get_meta_data($this->smtp_conn); 848 994 if ($info['timed_out']) { 849 if ($this->do_debug >= 4) { 850 $this->edebug( 851 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' 852 ); 853 } 995 $this->edebug( 996 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', 997 self::DEBUG_LOWLEVEL 998 ); 854 999 break; 855 1000 } 856 1001 // 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 } 1002 if ($endtime and time() > $endtime) { 1003 $this->edebug( 1004 'SMTP -> get_lines(): timelimit reached ('. 1005 $this->Timelimit . ' sec)', 1006 self::DEBUG_LOWLEVEL 1007 ); 1008 break; 867 1009 } 868 1010 } 869 1011 return $data; … … 871 1013 872 1014 /** 873 1015 * Enable or disable VERP address generation. 874 * @param bool $enabled1016 * @param boolean $enabled 875 1017 */ 876 1018 public function setVerp($enabled = false) 877 1019 { … … 880 1022 881 1023 /** 882 1024 * Get VERP address generation mode. 883 * @return bool 1025 * @return boolean 884 1026 */ 885 1027 public function getVerp() 886 1028 { … … 888 1030 } 889 1031 890 1032 /** 1033 * Set error messages and codes. 1034 * @param string $message The error message 1035 * @param string $detail Further detail on the error 1036 * @param string $smtp_code An associated SMTP error code 1037 * @param string $smtp_code_ex Extended SMTP code 1038 */ 1039 protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') 1040 { 1041 $this->error = array( 1042 'error' => $message, 1043 'detail' => $detail, 1044 'smtp_code' => $smtp_code, 1045 'smtp_code_ex' => $smtp_code_ex 1046 ); 1047 } 1048 1049 /** 891 1050 * Set debug output method. 892 * @param string $method The function/method to use for debugging output.1051 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. 893 1052 */ 894 1053 public function setDebugOutput($method = 'echo') 895 1054 { … … 907 1066 908 1067 /** 909 1068 * Set debug output level. 910 * @param int $level1069 * @param integer $level 911 1070 */ 912 1071 public function setDebugLevel($level = 0) 913 1072 { … … 916 1075 917 1076 /** 918 1077 * Get debug output level. 919 * @return int 1078 * @return integer 920 1079 */ 921 1080 public function getDebugLevel() 922 1081 { … … 925 1084 926 1085 /** 927 1086 * Set SMTP timeout. 928 * @param int $timeout1087 * @param integer $timeout 929 1088 */ 930 1089 public function setTimeout($timeout = 0) 931 1090 { … … 934 1093 935 1094 /** 936 1095 * Get SMTP timeout. 937 * @return int 1096 * @return integer 938 1097 */ 939 1098 public function getTimeout() 940 1099 {