Changeset 33124
- Timestamp:
- 07/08/2015 05:15:02 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-phpmailer.php
r27385 r33124 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 … … 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 … … 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; … … 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 = ''; … … 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; … … 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; … … 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 … … 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; … … 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 = ''; 248 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; 251 256 252 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 … … 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 … … 294 305 /** 295 306 * The SMTP server timeout in seconds. 296 * @type int 297 */ 298 public $Timeout = 10; 307 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 308 * @type integer 309 */ 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 */ … … 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 /** … … 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; … … 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; … … 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(); … … 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; … … 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; … … 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 … … 411 434 * string $body the email body 412 435 * string $from email address of sender 413 *414 436 * @type string 415 437 */ … … 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 */ … … 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 … … 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. … … 539 568 /** 540 569 * Whether to throw exceptions for errors. 541 * @type bool 570 * @type boolean 542 571 * @access protected 543 572 */ … … 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 579 */ 580 protected $uniqueid = ''; 581 582 /** 583 * Error severity: message only, continue processing. 548 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 * Constructor 568 * @param bool $exceptions Should we throw external exceptions? 603 * The maximum line length allowed by RFC 2822 section 2.1.1 604 * @type integer 605 */ 606 const MAX_LINE_LENGTH = 998; 607 608 /** 609 * Constructor. 610 * @param boolean $exceptions Should we throw external exceptions? 569 611 */ 570 612 public function __construct($exceptions = false) 571 613 { 572 $this->exceptions = ( $exceptions == true);614 $this->exceptions = (boolean)$exceptions; 573 615 } 574 616 … … 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 } … … 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);604 } 605 return $r t;652 $result = @mail($to, $subject, $body, $header, $params); 653 } 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 … … 615 664 protected function edebug($str) 616 665 { 617 if (!$this->SMTPDebug) { 666 if ($this->SMTPDebug <= 0) { 667 return; 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); 618 672 return; 619 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 } … … 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)641 { 642 if ($is html) {705 public function isHTML($isHtml = true) 706 { 707 if ($isHtml) { 643 708 $this->ContentType = 'text/html'; 644 709 } else { … … 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'; … … 683 752 public function isQmail() 684 753 { 685 if (stristr(ini_get('sendmail_path'), 'qmail')) { 686 $this->Sendmail = '/var/qmail/bin/sendmail'; 687 } 688 $this->Mailer = '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; 760 } 761 $this->Mailer = 'qmail'; 689 762 } 690 763 … … 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 = '') … … 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 = '') … … 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 = '') … … 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 = '') … … 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 */ … … 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 } … … 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 } … … 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) … … 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 } … … 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 … … 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 … … 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)' . … … 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]))*")' . … … 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! … … 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 } … … 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() … … 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; … … 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); … … 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 … … 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 … … 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( … … 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( … … 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; … … 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 { 1093 echo 'called'; 1010 1094 try { 1011 1095 // Choose the mailer and send through it 1012 1096 switch ($this->Mailer) { 1013 1097 case 'sendmail': 1098 case 'qmail': 1014 1099 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); 1015 1100 case 'smtp': … … 1018 1103 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1019 1104 default: 1105 $sendMethod = $this->Mailer.'Send'; 1106 if (method_exists($this, $sendMethod)) { 1107 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); 1108 } 1109 1020 1110 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1021 1111 } 1022 } catch (phpmailerException $e) { 1023 $this->setError($e->getMessage()); 1112 } catch (phpmailerException $exc) { 1113 $this->setError($exc->getMessage()); 1114 $this->edebug($exc->getMessage()); 1024 1115 if ($this->exceptions) { 1025 throw $e; 1026 } 1027 $this->edebug($e->getMessage() . "\n"); 1116 throw $exc; 1117 } 1028 1118 } 1029 1119 return false; … … 1037 1127 * @throws phpmailerException 1038 1128 * @access protected 1039 * @return bool 1129 * @return boolean 1040 1130 */ 1041 1131 protected function sendmailSend($header, $body) 1042 1132 { 1043 1133 if ($this->Sender != '') { 1044 $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1134 if ($this->Mailer == 'qmail') { 1135 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1136 } else { 1137 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1138 } 1045 1139 } else { 1046 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 1047 } 1048 if ($this->SingleTo === true) { 1049 foreach ($this->SingleToArray as $val) { 1140 if ($this->Mailer == 'qmail') { 1141 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); 1142 } else { 1143 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); 1144 } 1145 } 1146 if ($this->SingleTo) { 1147 foreach ($this->SingleToArray as $toAddr) { 1050 1148 if (!@$mail = popen($sendmail, 'w')) { 1051 1149 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1052 1150 } 1053 fputs($mail, "To: " . $val. "\n");1151 fputs($mail, 'To: ' . $toAddr . "\n"); 1054 1152 fputs($mail, $header); 1055 1153 fputs($mail, $body); 1056 1154 $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); 1155 $this->doCallback( 1156 ($result == 0), 1157 array($toAddr), 1158 $this->cc, 1159 $this->bcc, 1160 $this->Subject, 1161 $body, 1162 $this->From 1163 ); 1060 1164 if ($result != 0) { 1061 1165 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); … … 1069 1173 fputs($mail, $body); 1070 1174 $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); 1175 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1074 1176 if ($result != 0) { 1075 1177 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); … … 1086 1188 * @throws phpmailerException 1087 1189 * @access protected 1088 * @return bool 1190 * @return boolean 1089 1191 */ 1090 1192 protected function mailSend($header, $body) 1091 1193 { 1092 1194 $toArr = array(); 1093 foreach ($this->to as $t ) {1094 $toArr[] = $this->addrFormat($t );1195 foreach ($this->to as $toaddr) { 1196 $toArr[] = $this->addrFormat($toaddr); 1095 1197 } 1096 1198 $to = implode(', ', $toArr); 1097 1199 1098 1200 if (empty($this->Sender)) { 1099 $params = " ";1201 $params = ' '; 1100 1202 } else { 1101 $params = sprintf( "-f%s", $this->Sender);1203 $params = sprintf('-f%s', $this->Sender); 1102 1204 } 1103 1205 if ($this->Sender != '' and !ini_get('safe_mode')) { … … 1105 1207 ini_set('sendmail_from', $this->Sender); 1106 1208 } 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); 1209 $result = false; 1210 if ($this->SingleTo && count($toArr) > 1) { 1211 foreach ($toArr as $toAddr) { 1212 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 1213 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1114 1214 } 1115 1215 } 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); 1216 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); 1217 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1120 1218 } 1121 1219 if (isset($old_from)) { 1122 1220 ini_set('sendmail_from', $old_from); 1123 1221 } 1124 if (!$r t) {1222 if (!$result) { 1125 1223 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); 1126 1224 } … … 1136 1234 { 1137 1235 if (!is_object($this->smtp)) { 1138 require_once 'class-smtp.php';1236 require_once( 'class-smtp.php' ); 1139 1237 $this->smtp = new SMTP; 1140 1238 } … … 1152 1250 * @uses SMTP 1153 1251 * @access protected 1154 * @return bool 1252 * @return boolean 1155 1253 */ 1156 1254 protected function smtpSend($header, $body) 1157 1255 { 1158 1256 $bad_rcpt = array(); 1159 1160 if (!$this->smtpConnect()) { 1257 if (!$this->smtpConnect($this->SMTPOptions)) { 1161 1258 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1162 1259 } 1163 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; 1260 if ('' == $this->Sender) { 1261 $smtp_from = $this->From; 1262 } else { 1263 $smtp_from = $this->Sender; 1264 } 1164 1265 if (!$this->smtp->mail($smtp_from)) { 1165 1266 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); … … 1167 1268 } 1168 1269 1169 // Attempt to send attach all recipients 1170 foreach ($this->to as $to) { 1171 if (!$this->smtp->recipient($to[0])) { 1172 $bad_rcpt[] = $to[0]; 1173 $isSent = 0; 1174 } else { 1175 $isSent = 1; 1176 } 1177 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From); 1178 } 1179 foreach ($this->cc as $cc) { 1180 if (!$this->smtp->recipient($cc[0])) { 1181 $bad_rcpt[] = $cc[0]; 1182 $isSent = 0; 1183 } else { 1184 $isSent = 1; 1185 } 1186 $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From); 1187 } 1188 foreach ($this->bcc as $bcc) { 1189 if (!$this->smtp->recipient($bcc[0])) { 1190 $bad_rcpt[] = $bcc[0]; 1191 $isSent = 0; 1192 } else { 1193 $isSent = 1; 1194 } 1195 $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From); 1196 } 1197 1198 if (count($bad_rcpt) > 0) { //Create error message for any bad addresses 1199 throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt)); 1200 } 1201 if (!$this->smtp->data($header . $body)) { 1270 // Attempt to send to all recipients 1271 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { 1272 foreach ($togroup as $to) { 1273 if (!$this->smtp->recipient($to[0])) { 1274 $error = $this->smtp->getError(); 1275 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); 1276 $isSent = false; 1277 } else { 1278 $isSent = true; 1279 } 1280 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); 1281 } 1282 } 1283 1284 // Only send the DATA command if we have viable recipients 1285 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { 1202 1286 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); 1203 1287 } 1204 if ($this->SMTPKeepAlive == true) {1288 if ($this->SMTPKeepAlive) { 1205 1289 $this->smtp->reset(); 1206 1290 } else { … … 1208 1292 $this->smtp->close(); 1209 1293 } 1294 //Create error message for any bad addresses 1295 if (count($bad_rcpt) > 0) { 1296 $errstr = ''; 1297 foreach ($bad_rcpt as $bad) { 1298 $errstr .= $bad['to'] . ': ' . $bad['error']; 1299 } 1300 throw new phpmailerException( 1301 $this->lang('recipients_failed') . $errstr, 1302 self::STOP_CONTINUE 1303 ); 1304 } 1210 1305 return true; 1211 1306 } … … 1218 1313 * @access public 1219 1314 * @throws phpmailerException 1220 * @return bool 1315 * @return boolean 1221 1316 */ 1222 1317 public function smtpConnect($options = array()) … … 1226 1321 } 1227 1322 1228 // Already connected?1323 // Already connected? 1229 1324 if ($this->smtp->connected()) { 1230 1325 return true; … … 1235 1330 $this->smtp->setDebugOutput($this->Debugoutput); 1236 1331 $this->smtp->setVerp($this->do_verp); 1237 $tls = ($this->SMTPSecure == 'tls');1238 $ssl = ($this->SMTPSecure == 'ssl');1239 1332 $hosts = explode(';', $this->Host); 1240 1333 $lastexception = null; … … 1242 1335 foreach ($hosts as $hostentry) { 1243 1336 $hostinfo = array(); 1244 $host = $hostentry; 1337 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { 1338 // Not a valid host entry 1339 continue; 1340 } 1341 // $hostinfo[2]: optional ssl or tls prefix 1342 // $hostinfo[3]: the hostname 1343 // $hostinfo[4]: optional port number 1344 // The host string prefix can temporarily override the current setting for SMTPSecure 1345 // If it's not specified, the default value is used 1346 $prefix = ''; 1347 $secure = $this->SMTPSecure; 1348 $tls = ($this->SMTPSecure == 'tls'); 1349 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { 1350 $prefix = 'ssl://'; 1351 $tls = false; // Can't have SSL and TLS at the same time 1352 $secure = 'ssl'; 1353 } elseif ($hostinfo[2] == 'tls') { 1354 $tls = true; 1355 // tls doesn't use a prefix 1356 $secure = 'tls'; 1357 } 1358 //Do we need the OpenSSL extension? 1359 $sslext = defined('OPENSSL_ALGO_SHA1'); 1360 if ('tls' === $secure or 'ssl' === $secure) { 1361 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled 1362 if (!$sslext) { 1363 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); 1364 } 1365 } 1366 $host = $hostinfo[3]; 1245 1367 $port = $this->Port; 1246 if (preg_match( 1247 '/^(.+):([0-9]+)$/', 1248 $hostentry, 1249 $hostinfo 1250 ) 1251 ) { //If $hostentry contains 'address:port', override default 1252 $host = $hostinfo[1]; 1253 $port = $hostinfo[2]; 1254 } 1255 if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) { 1368 $tport = (integer)$hostinfo[4]; 1369 if ($tport > 0 and $tport < 65536) { 1370 $port = $tport; 1371 } 1372 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { 1256 1373 try { 1257 1374 if ($this->Helo) { … … 1261 1378 } 1262 1379 $this->smtp->hello($hello); 1263 1380 //Automatically enable TLS encryption if: 1381 // * it's not disabled 1382 // * we have openssl extension 1383 // * we are not already using SSL 1384 // * the server offers STARTTLS 1385 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { 1386 $tls = true; 1387 } 1264 1388 if ($tls) { 1265 1389 if (!$this->smtp->startTLS()) { 1266 1390 throw new phpmailerException($this->lang('connect_host')); 1267 1391 } 1268 // We must resend HELO after tls negotiation1392 // We must resend HELO after tls negotiation 1269 1393 $this->smtp->hello($hello); 1270 1394 } … … 1282 1406 } 1283 1407 return true; 1284 } catch (phpmailerException $e) { 1285 $lastexception = $e; 1286 //We must have connected, but then failed TLS or Auth, so close connection nicely 1408 } catch (phpmailerException $exc) { 1409 $lastexception = $exc; 1410 $this->edebug($exc->getMessage()); 1411 // We must have connected, but then failed TLS or Auth, so close connection nicely 1287 1412 $this->smtp->quit(); 1288 1413 } 1289 1414 } 1290 1415 } 1291 // If we get here, all connection attempts have failed, so close connection hard1416 // If we get here, all connection attempts have failed, so close connection hard 1292 1417 $this->smtp->close(); 1293 // As we've caught all exceptions, just report whatever the last one was1418 // As we've caught all exceptions, just report whatever the last one was 1294 1419 if ($this->exceptions and !is_null($lastexception)) { 1295 1420 throw $lastexception; … … 1318 1443 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 1319 1444 * @param string $lang_path Path to the language file directory, with trailing separator (slash) 1320 * @return bool 1321 * @access public 1322 */ 1323 public function setLanguage($langcode = 'en', $lang_path = ' language/')1324 { 1325 // Define full set of translatable strings1445 * @return boolean 1446 * @access public 1447 */ 1448 public function setLanguage($langcode = 'en', $lang_path = '') 1449 { 1450 // Define full set of translatable strings in English 1326 1451 $PHPMAILER_LANG = array( 1327 1452 'authenticate' => 'SMTP Error: Could not authenticate.', … … 1342 1467 'smtp_connect_failed' => 'SMTP connect() failed.', 1343 1468 'smtp_error' => 'SMTP server error: ', 1344 'variable_set' => 'Cannot set or reset variable: ' 1469 'variable_set' => 'Cannot set or reset variable: ', 1470 'extension_missing' => 'Extension missing: ' 1345 1471 ); 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'; 1472 if (empty($lang_path)) { 1473 // Calculate an absolute path so it can work if CWD is not here 1474 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 1475 } 1476 $foundlang = true; 1477 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1478 // There is no English translation file 1479 if ($langcode != 'en') { 1480 // Make sure language file path is readable 1481 if (!is_readable($lang_file)) { 1482 $foundlang = false; 1483 } else { 1484 // Overwrite language-specific strings. 1485 // This way we'll never have missing translation keys. 1486 $foundlang = include $lang_file; 1487 } 1351 1488 } 1352 1489 $this->language = $PHPMAILER_LANG; 1353 return ( $l == true); //Returns false if language not found1490 return (boolean)$foundlang; // Returns false if language not found 1354 1491 } 1355 1492 … … 1376 1513 { 1377 1514 $addresses = array(); 1378 foreach ($addr as $a ) {1379 $addresses[] = $this->addrFormat($a );1515 foreach ($addr as $address) { 1516 $addresses[] = $this->addrFormat($address); 1380 1517 } 1381 1518 return $type . ': ' . implode(', ', $addresses) . $this->LE; … … 1394 1531 return $this->secureHeader($addr[0]); 1395 1532 } else { 1396 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <". $this->secureHeader(1533 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 1397 1534 $addr[0] 1398 ) . ">";1535 ) . '>'; 1399 1536 } 1400 1537 } … … 1407 1544 * @param string $message The message to wrap 1408 1545 * @param integer $length The line length to wrap to 1409 * @param bool $qp_mode Whether to run in Quoted-Printable mode1546 * @param boolean $qp_mode Whether to run in Quoted-Printable mode 1410 1547 * @access public 1411 1548 * @return string … … 1413 1550 public function wrapText($message, $length, $qp_mode = false) 1414 1551 { 1415 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 1552 if ($qp_mode) { 1553 $soft_break = sprintf(' =%s', $this->LE); 1554 } else { 1555 $soft_break = $this->LE; 1556 } 1416 1557 // If utf-8 encoding is used, we will need to make sure we don't 1417 1558 // split multibyte characters when we wrap 1418 $is_utf8 = (strtolower($this->CharSet) == "utf-8");1559 $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); 1419 1560 $lelen = strlen($this->LE); 1420 1561 $crlflen = strlen(self::CRLF); 1421 1562 1422 1563 $message = $this->fixEOL($message); 1564 //Remove a trailing line break 1423 1565 if (substr($message, -$lelen) == $this->LE) { 1424 1566 $message = substr($message, 0, -$lelen); 1425 1567 } 1426 1568 1427 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE 1569 //Split message into lines 1570 $lines = explode($this->LE, $message); 1571 //Message will be rebuilt in here 1428 1572 $message = ''; 1429 for ($i = 0; $i < count($line); $i++) {1430 $ line_part = explode(' ', $line[$i]);1573 foreach ($lines as $line) { 1574 $words = explode(' ', $line); 1431 1575 $buf = ''; 1432 for ($e = 0; $e < count($line_part); $e++) {1433 $word = $line_part[$e];1576 $firstword = true; 1577 foreach ($words as $word) { 1434 1578 if ($qp_mode and (strlen($word) > $length)) { 1435 1579 $space_left = $length - strlen($buf) - $crlflen; 1436 if ( $e != 0) {1580 if (!$firstword) { 1437 1581 if ($space_left > 20) { 1438 1582 $len = $space_left; 1439 1583 if ($is_utf8) { 1440 1584 $len = $this->utf8CharBoundary($word, $len); 1441 } elseif (substr($word, $len - 1, 1) == "=") {1585 } elseif (substr($word, $len - 1, 1) == '=') { 1442 1586 $len--; 1443 } elseif (substr($word, $len - 2, 1) == "=") {1587 } elseif (substr($word, $len - 2, 1) == '=') { 1444 1588 $len -= 2; 1445 1589 } … … 1447 1591 $word = substr($word, $len); 1448 1592 $buf .= ' ' . $part; 1449 $message .= $buf . sprintf( "=%s", self::CRLF);1593 $message .= $buf . sprintf('=%s', self::CRLF); 1450 1594 } else { 1451 1595 $message .= $buf . $soft_break; … … 1460 1604 if ($is_utf8) { 1461 1605 $len = $this->utf8CharBoundary($word, $len); 1462 } elseif (substr($word, $len - 1, 1) == "=") {1606 } elseif (substr($word, $len - 1, 1) == '=') { 1463 1607 $len--; 1464 } elseif (substr($word, $len - 2, 1) == "=") {1608 } elseif (substr($word, $len - 2, 1) == '=') { 1465 1609 $len -= 2; 1466 1610 } … … 1469 1613 1470 1614 if (strlen($word) > 0) { 1471 $message .= $part . sprintf( "=%s", self::CRLF);1615 $message .= $part . sprintf('=%s', self::CRLF); 1472 1616 } else { 1473 1617 $buf = $part; … … 1476 1620 } else { 1477 1621 $buf_o = $buf; 1478 $buf .= ($e == 0) ? $word : (' ' . $word); 1622 if (!$firstword) { 1623 $buf .= ' '; 1624 } 1625 $buf .= $word; 1479 1626 1480 1627 if (strlen($buf) > $length and $buf_o != '') { … … 1483 1630 } 1484 1631 } 1632 $firstword = false; 1485 1633 } 1486 1634 $message .= $buf . self::CRLF; … … 1492 1640 /** 1493 1641 * Find the last character boundary prior to $maxLength in a utf-8 1494 * quoted (printable)encoded string.1642 * quoted-printable encoded string. 1495 1643 * Original written by Colin Brown. 1496 1644 * @access public 1497 1645 * @param string $encodedText utf-8 QP text 1498 * @param int $maxLength findlast character boundary prior to this length1499 * @return int 1646 * @param integer $maxLength Find the last character boundary prior to this length 1647 * @return integer 1500 1648 */ 1501 1649 public function utf8CharBoundary($encodedText, $maxLength) … … 1505 1653 while (!$foundSplitPos) { 1506 1654 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 1507 $encodedCharPos = strpos($lastChunk, "=");1508 if ( $encodedCharPos !== false) {1655 $encodedCharPos = strpos($lastChunk, '='); 1656 if (false !== $encodedCharPos) { 1509 1657 // Found start of encoded character byte within $lookBack block. 1510 1658 // Check the encoded byte value (the 2 chars after the '=') 1511 1659 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 1512 1660 $dec = hexdec($hex); 1513 if ($dec < 128) { // Single byte character. 1661 if ($dec < 128) { 1662 // Single byte character. 1514 1663 // If the encoded char was found at pos 0, it will fit 1515 1664 // otherwise reduce maxLength to start of the encoded char 1516 $maxLength = ($encodedCharPos == 0) ? $maxLength : 1517 $maxLength - ($lookBack - $encodedCharPos); 1665 if ($encodedCharPos > 0) { 1666 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1667 } 1518 1668 $foundSplitPos = true; 1519 } elseif ($dec >= 192) { // First byte of a multi byte character 1669 } elseif ($dec >= 192) { 1670 // First byte of a multi byte character 1520 1671 // Reduce maxLength to split at start of character 1521 1672 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1522 1673 $foundSplitPos = true; 1523 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back 1674 } elseif ($dec < 192) { 1675 // Middle byte of a multi byte character, look further back 1524 1676 $lookBack += 3; 1525 1677 } … … 1532 1684 } 1533 1685 1534 1535 /** 1536 * Set the body wrapping. 1686 /** 1687 * Apply word wrapping to the message body. 1688 * Wraps the message body to the number of chars set in the WordWrap property. 1689 * You should only do this to plain-text bodies as wrapping HTML tags may break them. 1690 * This is called automatically by createBody(), so you don't need to call it yourself. 1537 1691 * @access public 1538 1692 * @return void … … 1566 1720 $result = ''; 1567 1721 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 1722 if ($this->MessageDate == '') { 1575 $result .= $this->headerLine('Date', self::rfcDate()); 1723 $this->MessageDate = self::rfcDate(); 1724 } 1725 $result .= $this->headerLine('Date', $this->MessageDate); 1726 1727 1728 // To be created automatically by mail() 1729 if ($this->SingleTo) { 1730 if ($this->Mailer != 'mail') { 1731 foreach ($this->to as $toaddr) { 1732 $this->SingleToArray[] = $this->addrFormat($toaddr); 1733 } 1734 } 1576 1735 } else { 1577 $result .= $this->headerLine('Date', $this->MessageDate); 1578 } 1579 1580 if ($this->ReturnPath) { 1581 $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>'); 1582 } elseif ($this->Sender == '') { 1583 $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>'); 1584 } else { 1585 $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>'); 1586 } 1587 1588 // To be created automatically by mail() 1589 if ($this->Mailer != 'mail') { 1590 if ($this->SingleTo === true) { 1591 foreach ($this->to as $t) { 1592 $this->SingleToArray[] = $this->addrFormat($t); 1736 if (count($this->to) > 0) { 1737 if ($this->Mailer != 'mail') { 1738 $result .= $this->addrAppend('To', $this->to); 1593 1739 } 1594 } else { 1595 if (count($this->to) > 0) { 1596 $result .= $this->addrAppend('To', $this->to); 1597 } elseif (count($this->cc) == 0) { 1598 $result .= $this->headerLine('To', 'undisclosed-recipients:;'); 1599 } 1740 } elseif (count($this->cc) == 0) { 1741 $result .= $this->headerLine('To', 'undisclosed-recipients:;'); 1600 1742 } 1601 1743 } … … 1609 1751 1610 1752 // sendmail and mail() extract Bcc from the header before sending 1611 if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { 1753 if (( 1754 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' 1755 ) 1756 and count($this->bcc) > 0 1757 ) { 1612 1758 $result .= $this->addrAppend('Bcc', $this->bcc); 1613 1759 } … … 1625 1771 $this->lastMessageID = $this->MessageID; 1626 1772 } else { 1627 $this->lastMessageID = sprintf( "<%s@%s>", $uniq_id, $this->ServerHostname());1628 } 1629 $result .= $this-> HeaderLine('Message-ID', $this->lastMessageID);1773 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname()); 1774 } 1775 $result .= $this->headerLine('Message-ID', $this->lastMessageID); 1630 1776 $result .= $this->headerLine('X-Priority', $this->Priority); 1631 1777 if ($this->XMailer == '') { … … 1646 1792 1647 1793 // Add custom headers 1648 for ($index = 0; $index < count($this->CustomHeader); $index++) {1794 foreach ($this->CustomHeader as $header) { 1649 1795 $result .= $this->headerLine( 1650 trim($ this->CustomHeader[$index][0]),1651 $this->encodeHeader(trim($ this->CustomHeader[$index][1]))1796 trim($header[0]), 1797 $this->encodeHeader(trim($header[1])) 1652 1798 ); 1653 1799 } … … 1668 1814 { 1669 1815 $result = ''; 1816 $ismultipart = true; 1670 1817 switch ($this->message_type) { 1671 1818 case 'inline': … … 1688 1835 // Catches case 'plain': and case '': 1689 1836 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); 1837 $ismultipart = false; 1690 1838 break; 1691 1839 } 1692 // RFC1341 part 5 says 7bit is assumed if not specified1840 // RFC1341 part 5 says 7bit is assumed if not specified 1693 1841 if ($this->Encoding != '7bit') { 1694 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 1842 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE 1843 if ($ismultipart) { 1844 if ($this->Encoding == '8bit') { 1845 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); 1846 } 1847 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible 1848 } else { 1849 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 1850 } 1695 1851 } 1696 1852 … … 1705 1861 * Returns the whole MIME message. 1706 1862 * Includes complete headers and body. 1707 * Only valid post PreSend().1708 * @see PHPMailer:: PreSend()1863 * Only valid post preSend(). 1864 * @see PHPMailer::preSend() 1709 1865 * @access public 1710 1866 * @return string … … 1714 1870 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 1715 1871 } 1716 1717 1872 1718 1873 /** … … 1726 1881 { 1727 1882 $body = ''; 1883 //Create unique IDs and preset boundaries 1884 $this->uniqueid = md5(uniqid(time())); 1885 $this->boundary[1] = 'b1_' . $this->uniqueid; 1886 $this->boundary[2] = 'b2_' . $this->uniqueid; 1887 $this->boundary[3] = 'b3_' . $this->uniqueid; 1728 1888 1729 1889 if ($this->sign_key_file) { … … 1733 1893 $this->setWordWrap(); 1734 1894 1895 $bodyEncoding = $this->Encoding; 1896 $bodyCharSet = $this->CharSet; 1897 //Can we do a 7-bit downgrade? 1898 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 1899 $bodyEncoding = '7bit'; 1900 $bodyCharSet = 'us-ascii'; 1901 } 1902 //If lines are too long, change to quoted-printable transfer encoding 1903 if (self::hasLineLongerThanMax($this->Body)) { 1904 $this->Encoding = 'quoted-printable'; 1905 $bodyEncoding = 'quoted-printable'; 1906 } 1907 1908 $altBodyEncoding = $this->Encoding; 1909 $altBodyCharSet = $this->CharSet; 1910 //Can we do a 7-bit downgrade? 1911 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 1912 $altBodyEncoding = '7bit'; 1913 $altBodyCharSet = 'us-ascii'; 1914 } 1915 //If lines are too long, change to quoted-printable transfer encoding 1916 if (self::hasLineLongerThanMax($this->AltBody)) { 1917 $altBodyEncoding = 'quoted-printable'; 1918 } 1919 //Use this as a preamble in all multipart message types 1920 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; 1735 1921 switch ($this->message_type) { 1736 1922 case 'inline': 1737 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1738 $body .= $this->encodeString($this->Body, $this->Encoding); 1923 $body .= $mimepre; 1924 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 1925 $body .= $this->encodeString($this->Body, $bodyEncoding); 1739 1926 $body .= $this->LE . $this->LE; 1740 1927 $body .= $this->attachAll('inline', $this->boundary[1]); 1741 1928 break; 1742 1929 case 'attach': 1743 $body .= $this->getBoundary($this->boundary[1], '', '', ''); 1744 $body .= $this->encodeString($this->Body, $this->Encoding); 1930 $body .= $mimepre; 1931 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 1932 $body .= $this->encodeString($this->Body, $bodyEncoding); 1745 1933 $body .= $this->LE . $this->LE; 1746 1934 $body .= $this->attachAll('attachment', $this->boundary[1]); 1747 1935 break; 1748 1936 case 'inline_attach': 1937 $body .= $mimepre; 1749 1938 $body .= $this->textLine('--' . $this->boundary[1]); 1750 1939 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 1751 1940 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1752 1941 $body .= $this->LE; 1753 $body .= $this->getBoundary($this->boundary[2], '', '', '');1754 $body .= $this->encodeString($this->Body, $ this->Encoding);1942 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); 1943 $body .= $this->encodeString($this->Body, $bodyEncoding); 1755 1944 $body .= $this->LE . $this->LE; 1756 1945 $body .= $this->attachAll('inline', $this->boundary[2]); … … 1759 1948 break; 1760 1949 case 'alt': 1761 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1762 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1950 $body .= $mimepre; 1951 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1952 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1763 1953 $body .= $this->LE . $this->LE; 1764 $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');1765 $body .= $this->encodeString($this->Body, $ this->Encoding);1954 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); 1955 $body .= $this->encodeString($this->Body, $bodyEncoding); 1766 1956 $body .= $this->LE . $this->LE; 1767 1957 if (!empty($this->Ical)) { … … 1773 1963 break; 1774 1964 case 'alt_inline': 1775 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); 1776 $body .= $this->encodeString($this->AltBody, $this->Encoding); 1965 $body .= $mimepre; 1966 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1967 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1777 1968 $body .= $this->LE . $this->LE; 1778 1969 $body .= $this->textLine('--' . $this->boundary[1]); … … 1780 1971 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1781 1972 $body .= $this->LE; 1782 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');1783 $body .= $this->encodeString($this->Body, $ this->Encoding);1973 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 1974 $body .= $this->encodeString($this->Body, $bodyEncoding); 1784 1975 $body .= $this->LE . $this->LE; 1785 1976 $body .= $this->attachAll('inline', $this->boundary[2]); … … 1788 1979 break; 1789 1980 case 'alt_attach': 1981 $body .= $mimepre; 1790 1982 $body .= $this->textLine('--' . $this->boundary[1]); 1791 1983 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1792 1984 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1793 1985 $body .= $this->LE; 1794 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');1795 $body .= $this->encodeString($this->AltBody, $ this->Encoding);1986 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 1987 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1796 1988 $body .= $this->LE . $this->LE; 1797 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');1798 $body .= $this->encodeString($this->Body, $ this->Encoding);1989 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 1990 $body .= $this->encodeString($this->Body, $bodyEncoding); 1799 1991 $body .= $this->LE . $this->LE; 1800 1992 $body .= $this->endBoundary($this->boundary[2]); … … 1803 1995 break; 1804 1996 case 'alt_inline_attach': 1997 $body .= $mimepre; 1805 1998 $body .= $this->textLine('--' . $this->boundary[1]); 1806 1999 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 1807 2000 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 1808 2001 $body .= $this->LE; 1809 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');1810 $body .= $this->encodeString($this->AltBody, $ this->Encoding);2002 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2003 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 1811 2004 $body .= $this->LE . $this->LE; 1812 2005 $body .= $this->textLine('--' . $this->boundary[2]); … … 1814 2007 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); 1815 2008 $body .= $this->LE; 1816 $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');1817 $body .= $this->encodeString($this->Body, $ this->Encoding);2009 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); 2010 $body .= $this->encodeString($this->Body, $bodyEncoding); 1818 2011 $body .= $this->LE . $this->LE; 1819 2012 $body .= $this->attachAll('inline', $this->boundary[3]); … … 1825 2018 default: 1826 2019 // catch case 'plain' and case '' 1827 $body .= $this->encodeString($this->Body, $ this->Encoding);2020 $body .= $this->encodeString($this->Body, $bodyEncoding); 1828 2021 break; 1829 2022 } … … 1834 2027 try { 1835 2028 if (!defined('PKCS7_TEXT')) { 1836 throw new phpmailerException($this->lang(' signing') . ' OpenSSL extension missing.');2029 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 1837 2030 } 2031 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 1838 2032 $file = tempnam(sys_get_temp_dir(), 'mail'); 1839 file_put_contents($file, $body); //TODO check this worked 2033 if (false === file_put_contents($file, $body)) { 2034 throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); 2035 } 1840 2036 $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 ) { 2037 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 2038 if (empty($this->sign_extracerts_file)) { 2039 $sign = @openssl_pkcs7_sign( 2040 $file, 2041 $signed, 2042 'file://' . realpath($this->sign_cert_file), 2043 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2044 null 2045 ); 2046 } else { 2047 $sign = @openssl_pkcs7_sign( 2048 $file, 2049 $signed, 2050 'file://' . realpath($this->sign_cert_file), 2051 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2052 null, 2053 PKCS7_DETACHED, 2054 $this->sign_extracerts_file 2055 ); 2056 } 2057 if ($sign) { 1849 2058 @unlink($file); 1850 2059 $body = file_get_contents($signed); 1851 2060 @unlink($signed); 2061 //The message returned by openssl contains both headers and body, so need to split them up 2062 $parts = explode("\n\n", $body, 2); 2063 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; 2064 $body = $parts[1]; 1852 2065 } else { 1853 2066 @unlink($file); … … 1855 2068 throw new phpmailerException($this->lang('signing') . openssl_error_string()); 1856 2069 } 1857 } catch (phpmailerException $e ) {2070 } catch (phpmailerException $exc) { 1858 2071 $body = ''; 1859 2072 if ($this->exceptions) { 1860 throw $e ;2073 throw $exc; 1861 2074 } 1862 2075 } … … 1887 2100 } 1888 2101 $result .= $this->textLine('--' . $boundary); 1889 $result .= sprintf( "Content-Type: %s; charset=%s", $contentType, $charSet);2102 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); 1890 2103 $result .= $this->LE; 1891 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2104 // RFC1341 part 5 says 7bit is assumed if not specified 2105 if ($encoding != '7bit') { 2106 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2107 } 1892 2108 $result .= $this->LE; 1893 2109 … … 1915 2131 protected function setMessageType() 1916 2132 { 1917 $t his->message_type = array();2133 $type = array(); 1918 2134 if ($this->alternativeExists()) { 1919 $t his->message_type[] = "alt";2135 $type[] = 'alt'; 1920 2136 } 1921 2137 if ($this->inlineImageExists()) { 1922 $t his->message_type[] = "inline";2138 $type[] = 'inline'; 1923 2139 } 1924 2140 if ($this->attachmentExists()) { 1925 $t his->message_type[] = "attach";1926 } 1927 $this->message_type = implode( "_", $this->message_type);1928 if ($this->message_type == "") {1929 $this->message_type = "plain";2141 $type[] = 'attach'; 2142 } 2143 $this->message_type = implode('_', $type); 2144 if ($this->message_type == '') { 2145 $this->message_type = 'plain'; 1930 2146 } 1931 2147 } … … 1963 2179 * @param string $disposition Disposition to use 1964 2180 * @throws phpmailerException 1965 * @return bool 2181 * @return boolean 1966 2182 */ 1967 2183 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') … … 1972 2188 } 1973 2189 1974 // If a MIME type is not specified, try to work it out from the file name2190 // If a MIME type is not specified, try to work it out from the file name 1975 2191 if ($type == '') { 1976 2192 $type = self::filenameToType($path); … … 1993 2209 ); 1994 2210 1995 } catch (phpmailerException $e) { 1996 $this->setError($e->getMessage()); 2211 } catch (phpmailerException $exc) { 2212 $this->setError($exc->getMessage()); 2213 $this->edebug($exc->getMessage()); 1997 2214 if ($this->exceptions) { 1998 throw $e; 1999 } 2000 $this->edebug($e->getMessage() . "\n"); 2215 throw $exc; 2216 } 2001 2217 return false; 2002 2218 } … … 2057 2273 $cidUniq[$cid] = true; 2058 2274 2059 $mime[] = sprintf( "--%s%s", $boundary, $this->LE);2275 $mime[] = sprintf('--%s%s', $boundary, $this->LE); 2060 2276 $mime[] = sprintf( 2061 "Content-Type: %s; name=\"%s\"%s",2277 'Content-Type: %s; name="%s"%s', 2062 2278 $type, 2063 2279 $this->encodeHeader($this->secureHeader($name)), 2064 2280 $this->LE 2065 2281 ); 2066 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 2282 // RFC1341 part 5 says 7bit is assumed if not specified 2283 if ($encoding != '7bit') { 2284 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); 2285 } 2067 2286 2068 2287 if ($disposition == 'inline') { 2069 $mime[] = sprintf( "Content-ID: <%s>%s", $cid, $this->LE);2288 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); 2070 2289 } 2071 2290 … … 2075 2294 // Allow for bypassing the Content-Disposition header totally 2076 2295 if (!(empty($disposition))) { 2077 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { 2296 $encoded_name = $this->encodeHeader($this->secureHeader($name)); 2297 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { 2078 2298 $mime[] = sprintf( 2079 "Content-Disposition: %s; filename=\"%s\"%s",2299 'Content-Disposition: %s; filename="%s"%s', 2080 2300 $disposition, 2081 $ this->encodeHeader($this->secureHeader($name)),2301 $encoded_name, 2082 2302 $this->LE . $this->LE 2083 2303 ); 2084 2304 } else { 2085 2305 $mime[] = sprintf( 2086 "Content-Disposition: %s; filename=%s%s",2306 'Content-Disposition: %s; filename=%s%s', 2087 2307 $disposition, 2088 $ this->encodeHeader($this->secureHeader($name)),2308 $encoded_name, 2089 2309 $this->LE . $this->LE 2090 2310 ); … … 2111 2331 } 2112 2332 2113 $mime[] = sprintf( "--%s--%s", $boundary, $this->LE);2114 2115 return implode( "", $mime);2333 $mime[] = sprintf('--%s--%s', $boundary, $this->LE); 2334 2335 return implode('', $mime); 2116 2336 } 2117 2337 … … 2135 2355 if ($magic_quotes) { 2136 2356 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2137 set_magic_quotes_runtime( 0);2357 set_magic_quotes_runtime(false); 2138 2358 } else { 2139 ini_set('magic_quotes_runtime', 0); 2359 //Doesn't exist in PHP 5.4, but we don't need to check because 2360 //get_magic_quotes_runtime always returns false in 5.4+ 2361 //so it will never get here 2362 ini_set('magic_quotes_runtime', false); 2140 2363 } 2141 2364 } … … 2150 2373 } 2151 2374 return $file_buffer; 2152 } catch (Exception $e ) {2153 $this->setError($e ->getMessage());2375 } catch (Exception $exc) { 2376 $this->setError($exc->getMessage()); 2154 2377 return ''; 2155 2378 } … … 2174 2397 case '8bit': 2175 2398 $encoded = $this->fixEOL($str); 2176 // Make sure it ends with a line break2399 // Make sure it ends with a line break 2177 2400 if (substr($encoded, -(strlen($this->LE))) != $this->LE) { 2178 2401 $encoded .= $this->LE; … … 2202 2425 public function encodeHeader($str, $position = 'text') 2203 2426 { 2204 $ x= 0;2427 $matchcount = 0; 2205 2428 switch (strtolower($position)) { 2206 2429 case 'phrase': 2207 2430 if (!preg_match('/[\200-\377]/', $str)) { 2208 // Can't use addslashes as we don't know what value hasmagic_quotes_sybase2431 // Can't use addslashes as we don't know the value of magic_quotes_sybase 2209 2432 $encoded = addcslashes($str, "\0..\37\177\\\""); 2210 2433 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { … … 2214 2437 } 2215 2438 } 2216 $ x= preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);2439 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 2217 2440 break; 2218 2441 /** @noinspection PhpMissingBreakStatementInspection */ 2219 2442 case 'comment': 2220 $ x= preg_match_all('/[()"]/', $str, $matches);2443 $matchcount = preg_match_all('/[()"]/', $str, $matches); 2221 2444 // Intentional fall-through 2222 2445 case 'text': 2223 2446 default: 2224 $ x+= preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);2447 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 2225 2448 break; 2226 2449 } 2227 2450 2228 if ($x == 0) { //There are no chars that need encoding 2451 //There are no chars that need encoding 2452 if ($matchcount == 0) { 2229 2453 return ($str); 2230 2454 } … … 2232 2456 $maxlen = 75 - 7 - strlen($this->CharSet); 2233 2457 // 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 efficient2458 if ($matchcount > strlen($str) / 3) { 2459 // More than a third of the content will need encoding, so B encoding will be most efficient 2236 2460 $encoding = 'B'; 2237 2461 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { … … 2251 2475 } 2252 2476 2253 $encoded = preg_replace('/^(.*)$/m', " =?". $this->CharSet . "?$encoding?\\1?=", $encoded);2477 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 2254 2478 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 2255 2479 … … 2261 2485 * @access public 2262 2486 * @param string $str multi-byte text to wrap encode 2263 * @return bool 2487 * @return boolean 2264 2488 */ 2265 2489 public function hasMultiBytes($str) … … 2273 2497 2274 2498 /** 2499 * Does a string contain any 8-bit chars (in any charset)? 2500 * @param string $text 2501 * @return boolean 2502 */ 2503 public function has8bitChars($text) 2504 { 2505 return (boolean)preg_match('/[\x80-\xFF]/', $text); 2506 } 2507 2508 /** 2275 2509 * Encode and wrap long multibyte strings for mail headers 2276 2510 * 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 2511 * Adapted from a function by paravoid 2512 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 2278 2513 * @access public 2279 2514 * @param string $str multi-byte text to wrap encode 2280 * @param string $l fstring to use as linefeed/end-of-line2515 * @param string $linebreak string to use as linefeed/end-of-line 2281 2516 * @return string 2282 2517 */ 2283 public function base64EncodeWrapMB($str, $l f= null)2284 { 2285 $start = "=?" . $this->CharSet . "?B?";2286 $end = "?=";2287 $encoded = "";2288 if ($l f=== null) {2289 $l f= $this->LE;2518 public function base64EncodeWrapMB($str, $linebreak = null) 2519 { 2520 $start = '=?' . $this->CharSet . '?B?'; 2521 $end = '?='; 2522 $encoded = ''; 2523 if ($linebreak === null) { 2524 $linebreak = $this->LE; 2290 2525 } 2291 2526 … … 2306 2541 $lookBack++; 2307 2542 } while (strlen($chunk) > $length); 2308 $encoded .= $chunk . $l f;2543 $encoded .= $chunk . $linebreak; 2309 2544 } 2310 2545 2311 2546 // Chomp the last linefeed 2312 $encoded = substr($encoded, 0, -strlen($l f));2547 $encoded = substr($encoded, 0, -strlen($linebreak)); 2313 2548 return $encoded; 2314 2549 } … … 2321 2556 * @param integer $line_max Number of chars allowed on a line before wrapping 2322 2557 * @return string 2323 * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#894172558 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment 2324 2559 */ 2325 2560 public function encodeQP($string, $line_max = 76) 2326 2561 { 2327 if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) 2328 return quoted_printable_encode($string); 2329 } 2330 //Fall back to a pure PHP implementation 2562 // Use native function if it's available (>= PHP5.3) 2563 if (function_exists('quoted_printable_encode')) { 2564 return $this->fixEOL(quoted_printable_encode($string)); 2565 } 2566 // Fall back to a pure PHP implementation 2331 2567 $string = str_replace( 2332 2568 array('%20', '%0D%0A.', '%0D%0A', '%'), … … 2335 2571 ); 2336 2572 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); 2337 return $ string;2573 return $this->fixEOL($string); 2338 2574 } 2339 2575 … … 2344 2580 * @param string $string 2345 2581 * @param integer $line_max 2346 * @param bool $space_conv2582 * @param boolean $space_conv 2347 2583 * @return string 2348 2584 * @deprecated Use encodeQP instead. … … 2366 2602 public function encodeQ($str, $position = 'text') 2367 2603 { 2368 // There should not be any EOL in the string2604 // There should not be any EOL in the string 2369 2605 $pattern = ''; 2370 2606 $encoded = str_replace(array("\r", "\n"), '', $str); 2371 2607 switch (strtolower($position)) { 2372 2608 case 'phrase': 2373 // RFC 2047 section 5.32609 // RFC 2047 section 5.3 2374 2610 $pattern = '^A-Za-z0-9!*+\/ -'; 2375 2611 break; 2376 2612 /** @noinspection PhpMissingBreakStatementInspection */ 2377 2613 case 'comment': 2378 // RFC 2047 section 5.22614 // RFC 2047 section 5.2 2379 2615 $pattern = '\(\)"'; 2380 // intentional fall-through2381 // for this reason we build the $pattern without including delimiters and []2616 // intentional fall-through 2617 // for this reason we build the $pattern without including delimiters and [] 2382 2618 case 'text': 2383 2619 default: 2384 // RFC 2047 section 5.12385 // Replace every high ascii, control, =, ? and _ characters2620 // RFC 2047 section 5.1 2621 // Replace every high ascii, control, =, ? and _ characters 2386 2622 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; 2387 2623 break; … … 2389 2625 $matches = array(); 2390 2626 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]);2627 // If the string contains an '=', make sure it's the first thing we replace 2628 // so as to avoid double-encoding 2629 $eqkey = array_search('=', $matches[0]); 2630 if (false !== $eqkey) { 2631 unset($matches[0][$eqkey]); 2396 2632 array_unshift($matches[0], '='); 2397 2633 } … … 2400 2636 } 2401 2637 } 2402 // Replace every spaces to _ (more readable than =20)2638 // Replace every spaces to _ (more readable than =20) 2403 2639 return str_replace(' ', '_', $encoded); 2404 2640 } … … 2423 2659 $disposition = 'attachment' 2424 2660 ) { 2425 // If a MIME type is not specified, try to work it out from the file name2661 // If a MIME type is not specified, try to work it out from the file name 2426 2662 if ($type == '') { 2427 2663 $type = self::filenameToType($filename); … … 2443 2679 * Add an embedded (inline) attachment from a file. 2444 2680 * 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 be2681 * These differ from 'regular' attachments in that they are intended to be 2446 2682 * displayed inline with the message, not just attached for download. 2447 2683 * This is used in HTML messages that embed the images … … 2454 2690 * @param string $type File MIME type. 2455 2691 * @param string $disposition Disposition to use 2456 * @return bool True on successfully adding an attachment2692 * @return boolean True on successfully adding an attachment 2457 2693 */ 2458 2694 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') … … 2463 2699 } 2464 2700 2465 // If a MIME type is not specified, try to work it out from the file name2701 // If a MIME type is not specified, try to work it out from the file name 2466 2702 if ($type == '') { 2467 2703 $type = self::filenameToType($path); … … 2499 2735 * @param string $type MIME type. 2500 2736 * @param string $disposition Disposition to use 2501 * @return bool True on successfully adding an attachment2737 * @return boolean True on successfully adding an attachment 2502 2738 */ 2503 2739 public function addStringEmbeddedImage( … … 2509 2745 $disposition = 'inline' 2510 2746 ) { 2511 // If a MIME type is not specified, try to work it out from the name2747 // If a MIME type is not specified, try to work it out from the name 2512 2748 if ($type == '') { 2513 2749 $type = self::filenameToType($name); … … 2531 2767 * Check if an inline attachment is present. 2532 2768 * @access public 2533 * @return bool 2769 * @return boolean 2534 2770 */ 2535 2771 public function inlineImageExists() … … 2545 2781 /** 2546 2782 * Check if an attachment (non-inline) is present. 2547 * @return bool 2783 * @return boolean 2548 2784 */ 2549 2785 public function attachmentExists() … … 2559 2795 /** 2560 2796 * Check if this message has an alternative body set. 2561 * @return bool 2797 * @return boolean 2562 2798 */ 2563 2799 public function alternativeExists() … … 2652 2888 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { 2653 2889 $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"; 2890 if (!empty($lasterror['error'])) { 2891 $msg .= $this->lang('smtp_error') . $lasterror['error']; 2892 if (!empty($lasterror['detail'])) { 2893 $msg .= ' Detail: '. $lasterror['detail']; 2894 } 2895 if (!empty($lasterror['smtp_code'])) { 2896 $msg .= ' SMTP code: ' . $lasterror['smtp_code']; 2897 } 2898 if (!empty($lasterror['smtp_code_ex'])) { 2899 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; 2900 } 2656 2901 } 2657 2902 } … … 2667 2912 public static function rfcDate() 2668 2913 { 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.ini2914 // Set the time zone to whatever the default is to avoid 500 errors 2915 // Will default to UTC if it's not set properly in php.ini 2671 2916 date_default_timezone_set(@date_default_timezone_get()); 2672 2917 return date('D, j M Y H:i:s O'); … … 2681 2926 protected function serverHostname() 2682 2927 { 2928 $result = 'localhost.localdomain'; 2683 2929 if (!empty($this->Hostname)) { 2684 2930 $result = $this->Hostname; 2685 } elseif (isset($_SERVER ['SERVER_NAME'])) {2931 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { 2686 2932 $result = $_SERVER['SERVER_NAME']; 2687 } else { 2688 $result = 'localhost.localdomain'; 2689 } 2690 2933 } elseif (function_exists('gethostname') && gethostname() !== false) { 2934 $result = gethostname(); 2935 } elseif (php_uname('n') !== false) { 2936 $result = php_uname('n'); 2937 } 2691 2938 return $result; 2692 2939 } … … 2704 2951 } 2705 2952 2706 if (isset($this->language[$key])) { 2953 if (array_key_exists($key, $this->language)) { 2954 if ($key == 'smtp_connect_failed') { 2955 //Include a link to troubleshooting docs on SMTP connection failure 2956 //this is by far the biggest cause of support questions 2957 //but it's usually not PHPMailer's fault. 2958 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; 2959 } 2707 2960 return $this->language[$key]; 2708 2961 } else { 2709 return 'Language string failed to load: ' . $key; 2962 //Return the key as a fallback 2963 return $key; 2710 2964 } 2711 2965 } … … 2714 2968 * Check if an error occurred. 2715 2969 * @access public 2716 * @return bool True if an error did occur.2970 * @return boolean True if an error did occur. 2717 2971 */ 2718 2972 public function isError() … … 2759 3013 2760 3014 /** 3015 * Returns all custom headers 3016 * 3017 * @return array 3018 */ 3019 public function getCustomHeaders() 3020 { 3021 return $this->CustomHeader; 3022 } 3023 3024 /** 2761 3025 * Create a message from an HTML string. 2762 3026 * Automatically makes modifications for inline images and backgrounds … … 2766 3030 * @param string $message HTML message string 2767 3031 * @param string $basedir baseline directory for path 2768 * @param bool $advanced Whether to use the advanced HTML to text converter 3032 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3033 * or your own custom converter @see html2text() 2769 3034 * @return string $message 2770 3035 */ 2771 3036 public function msgHTML($message, $basedir = '', $advanced = false) 2772 3037 { 2773 preg_match_all( "/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);3038 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); 2774 3039 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)) { 3040 foreach ($images[2] as $imgindex => $url) { 3041 // Convert data URIs into embedded images 3042 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { 3043 $data = substr($url, strpos($url, ',')); 3044 if ($match[2]) { 3045 $data = base64_decode($data); 3046 } else { 3047 $data = rawurldecode($data); 3048 } 3049 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3050 if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) { 3051 $message = str_replace( 3052 $images[0][$imgindex], 3053 $images[1][$imgindex] . '="cid:' . $cid . '"', 3054 $message 3055 ); 3056 } 3057 } elseif (!preg_match('#^[A-z]+://#', $url)) { 3058 // Do not change urls for absolute images (thanks to corvuscorax) 2778 3059 $filename = basename($url); 2779 3060 $directory = dirname($url); … … 2781 3062 $directory = ''; 2782 3063 } 2783 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 23064 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 2784 3065 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { 2785 3066 $basedir .= '/'; … … 2793 3074 $filename, 2794 3075 'base64', 2795 self::_mime_types( self::mb_pathinfo($filename, PATHINFO_EXTENSION))3076 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) 2796 3077 ) 2797 3078 ) { 2798 3079 $message = preg_replace( 2799 "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",2800 $images[1][$i ] . "=\"cid:" . $cid . "\"",3080 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', 3081 $images[1][$imgindex] . '="cid:' . $cid . '"', 2801 3082 $message 2802 3083 ); … … 2806 3087 } 2807 3088 $this->isHTML(true); 2808 if (empty($this->AltBody)) { 2809 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 2810 } 2811 //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 3089 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 2812 3090 $this->Body = $this->normalizeBreaks($message); 2813 3091 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 3092 if (empty($this->AltBody)) { 3093 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 3094 self::CRLF . self::CRLF; 3095 } 2814 3096 return $this->Body; 2815 3097 } … … 2817 3099 /** 2818 3100 * Convert an HTML string into plain text. 3101 * This is used by msgHTML(). 3102 * Note - older versions of this function used a bundled advanced converter 3103 * which was been removed for license reasons in #232 3104 * Example usage: 3105 * <code> 3106 * // Use default conversion 3107 * $plain = $mail->html2text($html); 3108 * // Use your own custom converter 3109 * $plain = $mail->html2text($html, function($html) { 3110 * $converter = new MyHtml2text($html); 3111 * return $converter->get_text(); 3112 * }); 3113 * </code> 2819 3114 * @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? 3115 * @param boolean|callable $advanced Any boolean value to use the internal converter, 3116 * or provide your own callable for custom conversion. 2821 3117 * @return string 2822 3118 */ 2823 3119 public function html2text($html, $advanced = false) 2824 3120 { 2825 if ($advanced) { 2826 require_once 'extras/class.html2text.php'; 2827 $h = new html2text($html); 2828 return $h->get_text(); 3121 if (is_callable($advanced)) { 3122 return call_user_func($advanced, $html); 2829 3123 } 2830 3124 return html_entity_decode( … … 2845 3139 { 2846 3140 $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', 3141 'xl' => 'application/excel', 3142 'js' => 'application/javascript', 3143 'hqx' => 'application/mac-binhex40', 3144 'cpt' => 'application/mac-compactpro', 3145 'bin' => 'application/macbinary', 3146 'doc' => 'application/msword', 3147 'word' => 'application/msword', 2853 3148 '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',3149 'dll' => 'application/octet-stream', 3150 'dms' => 'application/octet-stream', 3151 'exe' => 'application/octet-stream', 3152 'lha' => 'application/octet-stream', 3153 'lzh' => 'application/octet-stream', 3154 'psd' => 'application/octet-stream', 3155 'sea' => 'application/octet-stream', 3156 'so' => 'application/octet-stream', 3157 'oda' => 'application/oda', 3158 'pdf' => 'application/pdf', 3159 'ai' => 'application/postscript', 3160 'eps' => 'application/postscript', 3161 'ps' => 'application/postscript', 3162 'smi' => 'application/smil', 3163 'smil' => 'application/smil', 3164 'mif' => 'application/vnd.mif', 3165 'xls' => 'application/vnd.ms-excel', 3166 'ppt' => 'application/vnd.ms-powerpoint', 2872 3167 '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',3168 'wmlc' => 'application/vnd.wap.wmlc', 3169 'dcr' => 'application/x-director', 3170 'dir' => 'application/x-director', 3171 'dxr' => 'application/x-director', 3172 'dvi' => 'application/x-dvi', 3173 'gtar' => 'application/x-gtar', 3174 'php3' => 'application/x-httpd-php', 3175 'php4' => 'application/x-httpd-php', 3176 'php' => 'application/x-httpd-php', 2882 3177 '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', 3178 'phps' => 'application/x-httpd-php-source', 3179 'swf' => 'application/x-shockwave-flash', 3180 'sit' => 'application/x-stuffit', 3181 'tar' => 'application/x-tar', 3182 'tgz' => 'application/x-tar', 3183 'xht' => 'application/xhtml+xml', 2890 3184 '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',3185 'zip' => 'application/zip', 3186 'mid' => 'audio/midi', 3187 'midi' => 'audio/midi', 3188 'mp2' => 'audio/mpeg', 3189 'mp3' => 'audio/mpeg', 3190 'mpga' => 'audio/mpeg', 3191 'aif' => 'audio/x-aiff', 3192 'aifc' => 'audio/x-aiff', 3193 'aiff' => 'audio/x-aiff', 3194 'ram' => 'audio/x-pn-realaudio', 3195 'rm' => 'audio/x-pn-realaudio', 3196 'rpm' => 'audio/x-pn-realaudio-plugin', 3197 'ra' => 'audio/x-realaudio', 3198 'wav' => 'audio/x-wav', 3199 'bmp' => 'image/bmp', 3200 'gif' => 'image/gif', 3201 'jpeg' => 'image/jpeg', 3202 'jpe' => 'image/jpeg', 3203 'jpg' => 'image/jpeg', 3204 'png' => 'image/png', 3205 'tiff' => 'image/tiff', 3206 'tif' => 'image/tiff', 3207 'eml' => 'message/rfc822', 3208 'css' => 'text/css', 3209 'html' => 'text/html', 3210 'htm' => 'text/html', 2917 3211 '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', 3212 'log' => 'text/plain', 3213 'text' => 'text/plain', 3214 'txt' => 'text/plain', 3215 'rtx' => 'text/richtext', 3216 'rtf' => 'text/rtf', 3217 'vcf' => 'text/vcard', 3218 'vcard' => 'text/vcard', 3219 'xml' => 'text/xml', 3220 'xsl' => 'text/xml', 3221 'mpeg' => 'video/mpeg', 3222 'mpe' => 'video/mpeg', 3223 'mpg' => 'video/mpeg', 3224 'mov' => 'video/quicktime', 3225 'qt' => 'video/quicktime', 3226 'rv' => 'video/vnd.rn-realvideo', 3227 'avi' => 'video/x-msvideo', 2932 3228 'movie' => 'video/x-sgi-movie' 2933 3229 ); 2934 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream'); 3230 if (array_key_exists(strtolower($ext), $mimes)) { 3231 return $mimes[strtolower($ext)]; 3232 } 3233 return 'application/octet-stream'; 2935 3234 } 2936 3235 … … 2944 3243 public static function filenameToType($filename) 2945 3244 { 2946 // In case the path is a URL, strip any query string before getting extension3245 // In case the path is a URL, strip any query string before getting extension 2947 3246 $qpos = strpos($filename, '?'); 2948 if ( $qpos !== false) {3247 if (false !== $qpos) { 2949 3248 $filename = substr($filename, 0, $qpos); 2950 3249 } … … 2967 3266 { 2968 3267 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); 2969 $m = array(); 2970 preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); 2971 if (array_key_exists(1, $m)) { 2972 $ret['dirname'] = $m[1]; 2973 } 2974 if (array_key_exists(2, $m)) { 2975 $ret['basename'] = $m[2]; 2976 } 2977 if (array_key_exists(5, $m)) { 2978 $ret['extension'] = $m[5]; 2979 } 2980 if (array_key_exists(3, $m)) { 2981 $ret['filename'] = $m[3]; 3268 $pathinfo = array(); 3269 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { 3270 if (array_key_exists(1, $pathinfo)) { 3271 $ret['dirname'] = $pathinfo[1]; 3272 } 3273 if (array_key_exists(2, $pathinfo)) { 3274 $ret['basename'] = $pathinfo[2]; 3275 } 3276 if (array_key_exists(5, $pathinfo)) { 3277 $ret['extension'] = $pathinfo[5]; 3278 } 3279 if (array_key_exists(3, $pathinfo)) { 3280 $ret['filename'] = $pathinfo[3]; 3281 } 2982 3282 } 2983 3283 switch ($options) { … … 2985 3285 case 'dirname': 2986 3286 return $ret['dirname']; 2987 break;2988 3287 case PATHINFO_BASENAME: 2989 3288 case 'basename': 2990 3289 return $ret['basename']; 2991 break;2992 3290 case PATHINFO_EXTENSION: 2993 3291 case 'extension': 2994 3292 return $ret['extension']; 2995 break;2996 3293 case PATHINFO_FILENAME: 2997 3294 case 'filename': 2998 3295 return $ret['filename']; 2999 break;3000 3296 default: 3001 3297 return $ret; … … 3005 3301 /** 3006 3302 * Set or reset instance properties. 3007 * 3303 * You should avoid this function - it's more verbose, less efficient, more error-prone and 3304 * harder to debug than setting properties directly. 3008 3305 * Usage Example: 3009 * $page->set('X-Priority', '3'); 3010 * 3011 * @access public 3012 * @param string $name 3013 * @param mixed $value 3014 * NOTE: will not work with arrays, there are no arrays to set/reset 3015 * @throws phpmailerException 3016 * @return bool 3017 * @todo Should this not be using __set() magic function? 3306 * `$mail->set('SMTPSecure', 'tls');` 3307 * is the same as: 3308 * `$mail->SMTPSecure = 'tls';` 3309 * @access public 3310 * @param string $name The property name to set 3311 * @param mixed $value The value to set the property to 3312 * @return boolean 3313 * @TODO Should this not be using the __set() magic function? 3018 3314 */ 3019 3315 public function set($name, $value = '') 3020 3316 { 3021 try { 3022 if (isset($this->$name)) { 3023 $this->$name = $value; 3024 } else { 3025 throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL); 3026 } 3027 } catch (Exception $e) { 3028 $this->setError($e->getMessage()); 3029 if ($e->getCode() == self::STOP_CRITICAL) { 3030 return false; 3031 } 3032 } 3033 return true; 3317 if (property_exists($this, $name)) { 3318 $this->$name = $value; 3319 return true; 3320 } else { 3321 $this->setError($this->lang('variable_set') . $name); 3322 return false; 3323 } 3034 3324 } 3035 3325 … … 3062 3352 3063 3353 /** 3064 * Set the p rivate key fileand password for S/MIME signing.3354 * Set the public and private key files and password for S/MIME signing. 3065 3355 * @access public 3066 3356 * @param string $cert_filename 3067 3357 * @param string $key_filename 3068 3358 * @param string $key_pass Password for private key 3069 */ 3070 public function sign($cert_filename, $key_filename, $key_pass) 3359 * @param string $extracerts_filename Optional path to chain certificate 3360 */ 3361 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') 3071 3362 { 3072 3363 $this->sign_cert_file = $cert_filename; 3073 3364 $this->sign_key_file = $key_filename; 3074 3365 $this->sign_key_pass = $key_pass; 3366 $this->sign_extracerts_file = $extracerts_filename; 3075 3367 } 3076 3368 … … 3089 3381 $line .= $txt[$i]; 3090 3382 } else { 3091 $line .= "=" . sprintf("%02X", $ord);3383 $line .= '=' . sprintf('%02X', $ord); 3092 3384 } 3093 3385 } … … 3098 3390 * Generate a DKIM signature. 3099 3391 * @access public 3100 * @param string $s 3392 * @param string $signHeader 3101 3393 * @throws phpmailerException 3102 3394 * @return string 3103 3395 */ 3104 public function DKIM_Sign($s )3396 public function DKIM_Sign($signHeader) 3105 3397 { 3106 3398 if (!defined('PKCS7_TEXT')) { 3107 3399 if ($this->exceptions) { 3108 throw new phpmailerException($this->lang( "signing") . ' OpenSSL extension missing.');3400 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 3109 3401 } 3110 3402 return ''; … … 3116 3408 $privKey = $privKeyStr; 3117 3409 } 3118 if (openssl_sign($s , $signature, $privKey)) {3410 if (openssl_sign($signHeader, $signature, $privKey)) { 3119 3411 return base64_encode($signature); 3120 3412 } … … 3125 3417 * Generate a DKIM canonicalization header. 3126 3418 * @access public 3127 * @param string $s Header3419 * @param string $signHeader Header 3128 3420 * @return string 3129 3421 */ 3130 public function DKIM_HeaderC($s )3131 { 3132 $s = preg_replace("/\r\n\s+/", " ", $s);3133 $lines = explode("\r\n", $s );3422 public function DKIM_HeaderC($signHeader) 3423 { 3424 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); 3425 $lines = explode("\r\n", $signHeader); 3134 3426 foreach ($lines as $key => $line) { 3135 list($heading, $value) = explode( ":", $line, 2);3427 list($heading, $value) = explode(':', $line, 2); 3136 3428 $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 value3139 } 3140 $s = implode("\r\n", $lines);3141 return $s ;3429 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces 3430 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 3431 } 3432 $signHeader = implode("\r\n", $lines); 3433 return $signHeader; 3142 3434 } 3143 3435 … … 3190 3482 $current = 'to_header'; 3191 3483 } else { 3192 if ( $current&& strpos($header, ' =?') === 0) {3193 $ current .= $header;3484 if (!empty($$current) && strpos($header, ' =?') === 0) { 3485 $$current .= $header; 3194 3486 } else { 3195 3487 $current = ''; … … 3206 3498 $body = $this->DKIM_BodyC($body); 3207 3499 $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=" . 3500 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body 3501 if ('' == $this->DKIM_identity) { 3502 $ident = ''; 3503 } else { 3504 $ident = ' i=' . $this->DKIM_identity . ';'; 3505 } 3506 $dkimhdrs = 'DKIM-Signature: v=1; a=' . 3507 $DKIMsignatureType . '; q=' . 3508 $DKIMquery . '; l=' . 3509 $DKIMlen . '; s=' . 3214 3510 $this->DKIM_selector . 3215 3511 ";\r\n" . 3216 "\tt=" . $DKIMtime . "; c=". $DKIMcanonicalization . ";\r\n" .3512 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3217 3513 "\th=From:To:Subject;\r\n" . 3218 "\td=" . $this->DKIM_domain . ";". $ident . "\r\n" .3514 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3219 3515 "\tz=$from\r\n" . 3220 3516 "\t|$to\r\n" . … … 3230 3526 3231 3527 /** 3528 * Detect if a string contains a line longer than the maximum line length allowed. 3529 * @param string $str 3530 * @return boolean 3531 * @static 3532 */ 3533 public static function hasLineLongerThanMax($str) 3534 { 3535 //+2 to include CRLF line break for a 1000 total 3536 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); 3537 } 3538 3539 /** 3540 * Allows for public read access to 'to' property. 3541 * @access public 3542 * @return array 3543 */ 3544 public function getToAddresses() 3545 { 3546 return $this->to; 3547 } 3548 3549 /** 3550 * Allows for public read access to 'cc' property. 3551 * @access public 3552 * @return array 3553 */ 3554 public function getCcAddresses() 3555 { 3556 return $this->cc; 3557 } 3558 3559 /** 3560 * Allows for public read access to 'bcc' property. 3561 * @access public 3562 * @return array 3563 */ 3564 public function getBccAddresses() 3565 { 3566 return $this->bcc; 3567 } 3568 3569 /** 3570 * Allows for public read access to 'ReplyTo' property. 3571 * @access public 3572 * @return array 3573 */ 3574 public function getReplyToAddresses() 3575 { 3576 return $this->ReplyTo; 3577 } 3578 3579 /** 3580 * Allows for public read access to 'all_recipients' property. 3581 * @access public 3582 * @return array 3583 */ 3584 public function getAllRecipientAddresses() 3585 { 3586 return $this->all_recipients; 3587 } 3588 3589 /** 3232 3590 * Perform a callback. 3233 * @param bool $isSent3234 * @param string$to3235 * @param string$cc3236 * @param string$bcc3591 * @param boolean $isSent 3592 * @param array $to 3593 * @param array $cc 3594 * @param array $bcc 3237 3595 * @param string $subject 3238 3596 * @param string $body 3239 3597 * @param string $from 3240 3598 */ 3241 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)3599 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) 3242 3600 { 3243 3601 if (!empty($this->action_function) && is_callable($this->action_function)) { -
trunk/src/wp-includes/class-smtp.php
r29783 r33124 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. 38 */ 39 const VERSION = '5.2.7'; 30 * The PHPMailer SMTP version number. 31 * @type string 32 */ 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"; … … 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; 46 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; 50 77 51 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 */ … … 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 */ … … 75 102 /** 76 103 * Debug output level. 77 * Options: 0 for no output, 1 for commands, 2 for data and commands 78 * @type int 79 */ 80 public $do_debug = 0; 81 82 /** 83 * The function/method to use for debugging output. 84 * Options: 'echo', 'html' or 'error_log' 85 * @type string 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 111 */ 112 public $do_debug = self::DEBUG_OFF; 113 114 /** 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'; … … 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 98 */ 99 public $Timeout = 15; 100 101 /** 102 * The SMTP timelimit value for reads, in seconds. 103 * @type int 104 */ 105 public $Timelimit = 30; 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 143 */ 144 public $Timeout = 300; 145 146 /** 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 150 */ 151 public $Timelimit = 300; 106 152 107 153 /** … … 112 158 113 159 /** 114 * Error message, if any, for the last call. 115 * @type string 116 */ 117 protected $error = ''; 160 * Error information, if any, for the last SMTP command. 161 * @type array 162 */ 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 122 */ 123 protected $helo_rply = ''; 172 * If null, no HELO string has yet been received. 173 * @type string|null 174 */ 175 protected $helo_rply = null; 176 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; 124 187 125 188 /** … … 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) 150 { 202 protected function edebug($str, $level = 0) 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': … … 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 } … … 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 } … … 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 } … … 260 342 true, 261 343 STREAM_CRYPTO_METHOD_TLS_CLIENT 262 ) 263 ) { 344 )) { 264 345 return false; 265 346 } … … 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': … … 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 … … 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; … … 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; … … 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; … … 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 } … … 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; … … 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); 502 503 /* We need to find a good way to determine if headers are 504 * in the msg_data or if it is a straight msg body 505 * currently I am assuming rfc822 definitions of msg headers 506 * and if the first field of the first line (':' separated) 507 * does not contain a space then it _should_ be a header 508 * and we can process all lines before a blank "" line as 509 * headers. 565 // Normalize line breaks before exploding 566 $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); 567 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; … … 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); … … 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 … … 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 … … 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) … … 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; 664 } 665 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 } 600 695 } 601 696 … … 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) … … 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?628 * @access public 629 * @return bool 722 * @param boolean $close_on_error Should the connection close if an error occurs? 723 * @access public 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; … … 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 to648 * @access public 649 * @return bool 650 */ 651 public function recipient($to )742 * @param string $toaddr The address the message is being sent to 743 * @access public 744 * @return boolean 745 */ 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 ); … … 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() … … 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" 778 $this->setError("Called $command without being connected"); 779 return false; 780 } 781 $this->client_send($commandstring . self::CRLF); 782 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); 800 } 801 802 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 803 804 if (!in_array($code, (array)$expect)) { 805 $this->setError( 806 "$command command failed", 807 $detail, 808 $code, 809 $code_ex 810 ); 811 $this->edebug( 812 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, 813 self::DEBUG_CLIENT 685 814 ); 686 815 return false; 687 816 } 688 $this->client_send($commandstring . self::CRLF); 689 690 $reply = $this->get_lines(); 691 $code = substr($reply, 0, 3); 692 693 if ($this->do_debug >= 2) { 694 $this->edebug('SMTP -> FROM SERVER:' . $reply); 695 } 696 697 if (!in_array($code, (array)$expect)) { 698 $this->last_reply = null; 699 $this->error = array( 700 "error" => "$command command failed", 701 "smtp_code" => $code, 702 "detail" => substr($reply, 4) 703 ); 704 if ($this->do_debug >= 1) { 705 $this->edebug( 706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply 707 ); 708 } 709 return false; 710 } 711 712 $this->last_reply = $reply; 713 $this->error = null; 817 818 $this->setError(''); 714 819 return true; 715 820 } … … 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 … … 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 … … 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 … … 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 } … … 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 } … … 797 896 { 798 897 return $this->error; 898 } 899 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]; 799 949 } 800 950 … … 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) { … … 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 } … … 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 } … … 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) … … 881 1023 /** 882 1024 * Get VERP address generation mode. 883 * @return bool 1025 * @return boolean 884 1026 */ 885 1027 public function getVerp() … … 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') … … 908 1067 /** 909 1068 * Set debug output level. 910 * @param int $level1069 * @param integer $level 911 1070 */ 912 1071 public function setDebugLevel($level = 0) … … 917 1076 /** 918 1077 * Get debug output level. 919 * @return int 1078 * @return integer 920 1079 */ 921 1080 public function getDebugLevel() … … 926 1085 /** 927 1086 * Set SMTP timeout. 928 * @param int $timeout1087 * @param integer $timeout 929 1088 */ 930 1089 public function setTimeout($timeout = 0) … … 935 1094 /** 936 1095 * Get SMTP timeout. 937 * @return int 1096 * @return integer 938 1097 */ 939 1098 public function getTimeout() -
trunk/tests/phpunit/includes/mock-mailer.php
r27385 r33124 5 5 var $mock_sent = array(); 6 6 7 function preSend() { 8 $this->Encoding = '8bit'; 9 return parent::preSend(); 10 } 11 7 12 /** 8 * Override send() so mail isn't actually sent.13 * Override postSend() so mail isn't actually sent. 9 14 */ 10 function send() { 11 try { 12 if ( ! $this->preSend() ) 13 return false; 15 function postSend() { 16 $this->mock_sent[] = array( 17 'to' => $this->to, 18 'cc' => $this->cc, 19 'bcc' => $this->bcc, 20 'header' => $this->MIMEHeader, 21 'body' => $this->MIMEBody, 22 ); 14 23 15 $this->mock_sent[] = array( 16 'to' => $this->to, 17 'cc' => $this->cc, 18 'bcc' => $this->bcc, 19 'header' => $this->MIMEHeader, 20 'body' => $this->MIMEBody, 21 ); 22 23 return true; 24 } catch ( phpmailerException $e ) { 25 return false; 26 } 24 return true; 27 25 } 28 26 } -
trunk/tests/phpunit/tests/mail.php
r32839 r33124 8 8 parent::setUp(); 9 9 unset( $GLOBALS['phpmailer']->mock_sent ); 10 } 11 12 /** 13 * Send a mail with a 1000 char long line. 14 * 15 * `PHPMailer::createBody()` will set `$this->Encoding = 'quoted-printable'` (away from it's default of 8bit) 16 * when it encounters a line longer than 999 characters. But PHPMailer doesn't clean up after itself / presets 17 * all variables, which means that following tests would fail. To solve this issue we set `$this->Encoding` 18 * back to 8bit in `MockPHPMailer::preSend`. 19 * 20 */ 21 function test_wp_mail_break_it() { 22 $content = str_repeat( 'A', 1000 ); 23 wp_mail( "admin@example.org", 'Looong line testing', $content); 10 24 } 11 25
Note: See TracChangeset
for help on using the changeset viewer.