[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * PHPMailer - PHP email creation and transport class. 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> 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 10 * @author Brent R. Matzelle (original founder) 11 * @copyright 2012 - 2014 Marcus Bointon 12 * @copyright 2010 - 2012 Jim Jagielski 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. 18 */ 19 20 /** 21 * PHPMailer - PHP email creation and transport class. 22 * @package PHPMailer 23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 26 * @author Brent R. Matzelle (original founder) 27 */ 28 class PHPMailer 29 { 30 /** 31 * The PHPMailer Version number. 32 * @var string 33 */ 34 public $Version = '5.2.14'; 35 36 /** 37 * Email priority. 38 * Options: null (default), 1 = High, 3 = Normal, 5 = low. 39 * When null, the header is not set at all. 40 * @var integer 41 */ 42 public $Priority = null; 43 44 /** 45 * The character set of the message. 46 * @var string 47 */ 48 public $CharSet = 'iso-8859-1'; 49 50 /** 51 * The MIME Content-type of the message. 52 * @var string 53 */ 54 public $ContentType = 'text/plain'; 55 56 /** 57 * The message encoding. 58 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". 59 * @var string 60 */ 61 public $Encoding = '8bit'; 62 63 /** 64 * Holds the most recent mailer error message. 65 * @var string 66 */ 67 public $ErrorInfo = ''; 68 69 /** 70 * The From email address for the message. 71 * @var string 72 */ 73 public $From = 'root@localhost'; 74 75 /** 76 * The From name of the message. 77 * @var string 78 */ 79 public $FromName = 'Root User'; 80 81 /** 82 * The Sender email (Return-Path) of the message. 83 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. 84 * @var string 85 */ 86 public $Sender = ''; 87 88 /** 89 * The Return-Path of the message. 90 * If empty, it will be set to either From or Sender. 91 * @var string 92 * @deprecated Email senders should never set a return-path header; 93 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. 94 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference 95 */ 96 public $ReturnPath = ''; 97 98 /** 99 * The Subject of the message. 100 * @var string 101 */ 102 public $Subject = ''; 103 104 /** 105 * An HTML or plain text message body. 106 * If HTML then call isHTML(true). 107 * @var string 108 */ 109 public $Body = ''; 110 111 /** 112 * The plain-text message body. 113 * This body can be read by mail clients that do not have HTML email 114 * capability such as mutt & Eudora. 115 * Clients that can read HTML will view the normal Body. 116 * @var string 117 */ 118 public $AltBody = ''; 119 120 /** 121 * An iCal message part body. 122 * Only supported in simple alt or alt_inline message types 123 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator 124 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ 125 * @link http://kigkonsult.se/iCalcreator/ 126 * @var string 127 */ 128 public $Ical = ''; 129 130 /** 131 * The complete compiled MIME message body. 132 * @access protected 133 * @var string 134 */ 135 protected $MIMEBody = ''; 136 137 /** 138 * The complete compiled MIME message headers. 139 * @var string 140 * @access protected 141 */ 142 protected $MIMEHeader = ''; 143 144 /** 145 * Extra headers that createHeader() doesn't fold in. 146 * @var string 147 * @access protected 148 */ 149 protected $mailHeader = ''; 150 151 /** 152 * Word-wrap the message body to this number of chars. 153 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. 154 * @var integer 155 */ 156 public $WordWrap = 0; 157 158 /** 159 * Which method to use to send mail. 160 * Options: "mail", "sendmail", or "smtp". 161 * @var string 162 */ 163 public $Mailer = 'mail'; 164 165 /** 166 * The path to the sendmail program. 167 * @var string 168 */ 169 public $Sendmail = '/usr/sbin/sendmail'; 170 171 /** 172 * Whether mail() uses a fully sendmail-compatible MTA. 173 * One which supports sendmail's "-oi -f" options. 174 * @var boolean 175 */ 176 public $UseSendmailOptions = true; 177 178 /** 179 * Path to PHPMailer plugins. 180 * Useful if the SMTP class is not in the PHP include path. 181 * @var string 182 * @deprecated Should not be needed now there is an autoloader. 183 */ 184 public $PluginDir = ''; 185 186 /** 187 * The email address that a reading confirmation should be sent to, also known as read receipt. 188 * @var string 189 */ 190 public $ConfirmReadingTo = ''; 191 192 /** 193 * The hostname to use in the Message-ID header and as default HELO string. 194 * If empty, PHPMailer attempts to find one with, in order, 195 * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value 196 * 'localhost.localdomain'. 197 * @var string 198 */ 199 public $Hostname = ''; 200 201 /** 202 * An ID to be used in the Message-ID header. 203 * If empty, a unique id will be generated. 204 * @var string 205 */ 206 public $MessageID = ''; 207 208 /** 209 * The message Date to be used in the Date header. 210 * If empty, the current date will be added. 211 * @var string 212 */ 213 public $MessageDate = ''; 214 215 /** 216 * SMTP hosts. 217 * Either a single hostname or multiple semicolon-delimited hostnames. 218 * You can also specify a different port 219 * for each host by using this format: [hostname:port] 220 * (e.g. "smtp1.example.com:25;smtp2.example.com"). 221 * You can also specify encryption type, for example: 222 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). 223 * Hosts will be tried in order. 224 * @var string 225 */ 226 public $Host = 'localhost'; 227 228 /** 229 * The default SMTP server port. 230 * @var integer 231 * @TODO Why is this needed when the SMTP class takes care of it? 232 */ 233 public $Port = 25; 234 235 /** 236 * The SMTP HELO of the message. 237 * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find 238 * one with the same method described above for $Hostname. 239 * @var string 240 * @see PHPMailer::$Hostname 241 */ 242 public $Helo = ''; 243 244 /** 245 * What kind of encryption to use on the SMTP connection. 246 * Options: '', 'ssl' or 'tls' 247 * @var string 248 */ 249 public $SMTPSecure = ''; 250 251 /** 252 * Whether to enable TLS encryption automatically if a server supports it, 253 * even if `SMTPSecure` is not set to 'tls'. 254 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. 255 * @var boolean 256 */ 257 public $SMTPAutoTLS = true; 258 259 /** 260 * Whether to use SMTP authentication. 261 * Uses the Username and Password properties. 262 * @var boolean 263 * @see PHPMailer::$Username 264 * @see PHPMailer::$Password 265 */ 266 public $SMTPAuth = false; 267 268 /** 269 * Options array passed to stream_context_create when connecting via SMTP. 270 * @var array 271 */ 272 public $SMTPOptions = array(); 273 274 /** 275 * SMTP username. 276 * @var string 277 */ 278 public $Username = ''; 279 280 /** 281 * SMTP password. 282 * @var string 283 */ 284 public $Password = ''; 285 286 /** 287 * SMTP auth type. 288 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 289 * @var string 290 */ 291 public $AuthType = ''; 292 293 /** 294 * SMTP realm. 295 * Used for NTLM auth 296 * @var string 297 */ 298 public $Realm = ''; 299 300 /** 301 * SMTP workstation. 302 * Used for NTLM auth 303 * @var string 304 */ 305 public $Workstation = ''; 306 307 /** 308 * The SMTP server timeout in seconds. 309 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 310 * @var integer 311 */ 312 public $Timeout = 300; 313 314 /** 315 * SMTP class debug output mode. 316 * Debug output level. 317 * Options: 318 * * `0` No output 319 * * `1` Commands 320 * * `2` Data and commands 321 * * `3` As 2 plus connection status 322 * * `4` Low-level data output 323 * @var integer 324 * @see SMTP::$do_debug 325 */ 326 public $SMTPDebug = 0; 327 328 /** 329 * How to handle debug output. 330 * Options: 331 * * `echo` Output plain-text as-is, appropriate for CLI 332 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 333 * * `error_log` Output to error log as configured in php.ini 334 * 335 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 336 * <code> 337 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 338 * </code> 339 * @var string|callable 340 * @see SMTP::$Debugoutput 341 */ 342 public $Debugoutput = 'echo'; 343 344 /** 345 * Whether to keep SMTP connection open after each message. 346 * If this is set to true then to close the connection 347 * requires an explicit call to smtpClose(). 348 * @var boolean 349 */ 350 public $SMTPKeepAlive = false; 351 352 /** 353 * Whether to split multiple to addresses into multiple messages 354 * or send them all in one message. 355 * @var boolean 356 */ 357 public $SingleTo = false; 358 359 /** 360 * Storage for addresses when SingleTo is enabled. 361 * @var array 362 * @TODO This should really not be public 363 */ 364 public $SingleToArray = array(); 365 366 /** 367 * Whether to generate VERP addresses on send. 368 * Only applicable when sending via SMTP. 369 * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path 370 * @link http://www.postfix.org/VERP_README.html Postfix VERP info 371 * @var boolean 372 */ 373 public $do_verp = false; 374 375 /** 376 * Whether to allow sending messages with an empty body. 377 * @var boolean 378 */ 379 public $AllowEmpty = false; 380 381 /** 382 * The default line ending. 383 * @note The default remains "\n". We force CRLF where we know 384 * it must be used via self::CRLF. 385 * @var string 386 */ 387 public $LE = "\n"; 388 389 /** 390 * DKIM selector. 391 * @var string 392 */ 393 public $DKIM_selector = ''; 394 395 /** 396 * DKIM Identity. 397 * Usually the email address used as the source of the email 398 * @var string 399 */ 400 public $DKIM_identity = ''; 401 402 /** 403 * DKIM passphrase. 404 * Used if your key is encrypted. 405 * @var string 406 */ 407 public $DKIM_passphrase = ''; 408 409 /** 410 * DKIM signing domain name. 411 * @example 'example.com' 412 * @var string 413 */ 414 public $DKIM_domain = ''; 415 416 /** 417 * DKIM private key file path. 418 * @var string 419 */ 420 public $DKIM_private = ''; 421 422 /** 423 * Callback Action function name. 424 * 425 * The function that handles the result of the send email action. 426 * It is called out by send() for each email sent. 427 * 428 * Value can be any php callable: http://www.php.net/is_callable 429 * 430 * Parameters: 431 * boolean $result result of the send action 432 * string $to email address of the recipient 433 * string $cc cc email addresses 434 * string $bcc bcc email addresses 435 * string $subject the subject 436 * string $body the email body 437 * string $from email address of sender 438 * @var string 439 */ 440 public $action_function = ''; 441 442 /** 443 * What to put in the X-Mailer header. 444 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use 445 * @var string 446 */ 447 public $XMailer = ''; 448 449 /** 450 * An instance of the SMTP sender class. 451 * @var SMTP 452 * @access protected 453 */ 454 protected $smtp = null; 455 456 /** 457 * The array of 'to' names and addresses. 458 * @var array 459 * @access protected 460 */ 461 protected $to = array(); 462 463 /** 464 * The array of 'cc' names and addresses. 465 * @var array 466 * @access protected 467 */ 468 protected $cc = array(); 469 470 /** 471 * The array of 'bcc' names and addresses. 472 * @var array 473 * @access protected 474 */ 475 protected $bcc = array(); 476 477 /** 478 * The array of reply-to names and addresses. 479 * @var array 480 * @access protected 481 */ 482 protected $ReplyTo = array(); 483 484 /** 485 * An array of all kinds of addresses. 486 * Includes all of $to, $cc, $bcc 487 * @var array 488 * @access protected 489 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc 490 */ 491 protected $all_recipients = array(); 492 493 /** 494 * An array of names and addresses queued for validation. 495 * In send(), valid and non duplicate entries are moved to $all_recipients 496 * and one of $to, $cc, or $bcc. 497 * This array is used only for addresses with IDN. 498 * @var array 499 * @access protected 500 * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc 501 * @see PHPMailer::$all_recipients 502 */ 503 protected $RecipientsQueue = array(); 504 505 /** 506 * An array of reply-to names and addresses queued for validation. 507 * In send(), valid and non duplicate entries are moved to $ReplyTo. 508 * This array is used only for addresses with IDN. 509 * @var array 510 * @access protected 511 * @see PHPMailer::$ReplyTo 512 */ 513 protected $ReplyToQueue = array(); 514 515 /** 516 * The array of attachments. 517 * @var array 518 * @access protected 519 */ 520 protected $attachment = array(); 521 522 /** 523 * The array of custom headers. 524 * @var array 525 * @access protected 526 */ 527 protected $CustomHeader = array(); 528 529 /** 530 * The most recent Message-ID (including angular brackets). 531 * @var string 532 * @access protected 533 */ 534 protected $lastMessageID = ''; 535 536 /** 537 * The message's MIME type. 538 * @var string 539 * @access protected 540 */ 541 protected $message_type = ''; 542 543 /** 544 * The array of MIME boundary strings. 545 * @var array 546 * @access protected 547 */ 548 protected $boundary = array(); 549 550 /** 551 * The array of available languages. 552 * @var array 553 * @access protected 554 */ 555 protected $language = array(); 556 557 /** 558 * The number of errors encountered. 559 * @var integer 560 * @access protected 561 */ 562 protected $error_count = 0; 563 564 /** 565 * The S/MIME certificate file path. 566 * @var string 567 * @access protected 568 */ 569 protected $sign_cert_file = ''; 570 571 /** 572 * The S/MIME key file path. 573 * @var string 574 * @access protected 575 */ 576 protected $sign_key_file = ''; 577 578 /** 579 * The optional S/MIME extra certificates ("CA Chain") file path. 580 * @var string 581 * @access protected 582 */ 583 protected $sign_extracerts_file = ''; 584 585 /** 586 * The S/MIME password for the key. 587 * Used only if the key is encrypted. 588 * @var string 589 * @access protected 590 */ 591 protected $sign_key_pass = ''; 592 593 /** 594 * Whether to throw exceptions for errors. 595 * @var boolean 596 * @access protected 597 */ 598 protected $exceptions = false; 599 600 /** 601 * Unique ID used for message ID and boundaries. 602 * @var string 603 * @access protected 604 */ 605 protected $uniqueid = ''; 606 607 /** 608 * Error severity: message only, continue processing. 609 */ 610 const STOP_MESSAGE = 0; 611 612 /** 613 * Error severity: message, likely ok to continue processing. 614 */ 615 const STOP_CONTINUE = 1; 616 617 /** 618 * Error severity: message, plus full stop, critical error reached. 619 */ 620 const STOP_CRITICAL = 2; 621 622 /** 623 * SMTP RFC standard line ending. 624 */ 625 const CRLF = "\r\n"; 626 627 /** 628 * The maximum line length allowed by RFC 2822 section 2.1.1 629 * @var integer 630 */ 631 const MAX_LINE_LENGTH = 998; 632 633 /** 634 * Constructor. 635 * @param boolean $exceptions Should we throw external exceptions? 636 */ 637 public function __construct($exceptions = false) 638 { 639 $this->exceptions = (boolean)$exceptions; 640 } 641 642 /** 643 * Destructor. 644 */ 645 public function __destruct() 646 { 647 //Close any open SMTP connection nicely 648 if ($this->Mailer == 'smtp') { 649 $this->smtpClose(); 650 } 651 } 652 653 /** 654 * Call mail() in a safe_mode-aware fashion. 655 * Also, unless sendmail_path points to sendmail (or something that 656 * claims to be sendmail), don't pass params (not a perfect fix, 657 * but it will do) 658 * @param string $to To 659 * @param string $subject Subject 660 * @param string $body Message Body 661 * @param string $header Additional Header(s) 662 * @param string $params Params 663 * @access private 664 * @return boolean 665 */ 666 private function mailPassthru($to, $subject, $body, $header, $params) 667 { 668 //Check overloading of mail function to avoid double-encoding 669 if (ini_get('mbstring.func_overload') & 1) { 670 $subject = $this->secureHeader($subject); 671 } else { 672 $subject = $this->encodeHeader($this->secureHeader($subject)); 673 } 674 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { 675 $result = @mail($to, $subject, $body, $header); 676 } else { 677 $result = @mail($to, $subject, $body, $header, $params); 678 } 679 return $result; 680 } 681 682 /** 683 * Output debugging info via user-defined method. 684 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). 685 * @see PHPMailer::$Debugoutput 686 * @see PHPMailer::$SMTPDebug 687 * @param string $str 688 */ 689 protected function edebug($str) 690 { 691 if ($this->SMTPDebug <= 0) { 692 return; 693 } 694 //Avoid clash with built-in function names 695 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 696 call_user_func($this->Debugoutput, $str, $this->SMTPDebug); 697 return; 698 } 699 switch ($this->Debugoutput) { 700 case 'error_log': 701 //Don't output, just log 702 error_log($str); 703 break; 704 case 'html': 705 //Cleans up output a bit for a better looking, HTML-safe output 706 echo htmlentities( 707 preg_replace('/[\r\n]+/', '', $str), 708 ENT_QUOTES, 709 'UTF-8' 710 ) 711 . "<br>\n"; 712 break; 713 case 'echo': 714 default: 715 //Normalize line breaks 716 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 717 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 718 "\n", 719 "\n \t ", 720 trim($str) 721 ) . "\n"; 722 } 723 } 724 725 /** 726 * Sets message type to HTML or plain. 727 * @param boolean $isHtml True for HTML mode. 728 * @return void 729 */ 730 public function isHTML($isHtml = true) 731 { 732 if ($isHtml) { 733 $this->ContentType = 'text/html'; 734 } else { 735 $this->ContentType = 'text/plain'; 736 } 737 } 738 739 /** 740 * Send messages using SMTP. 741 * @return void 742 */ 743 public function isSMTP() 744 { 745 $this->Mailer = 'smtp'; 746 } 747 748 /** 749 * Send messages using PHP's mail() function. 750 * @return void 751 */ 752 public function isMail() 753 { 754 $this->Mailer = 'mail'; 755 } 756 757 /** 758 * Send messages using $Sendmail. 759 * @return void 760 */ 761 public function isSendmail() 762 { 763 $ini_sendmail_path = ini_get('sendmail_path'); 764 765 if (!stristr($ini_sendmail_path, 'sendmail')) { 766 $this->Sendmail = '/usr/sbin/sendmail'; 767 } else { 768 $this->Sendmail = $ini_sendmail_path; 769 } 770 $this->Mailer = 'sendmail'; 771 } 772 773 /** 774 * Send messages using qmail. 775 * @return void 776 */ 777 public function isQmail() 778 { 779 $ini_sendmail_path = ini_get('sendmail_path'); 780 781 if (!stristr($ini_sendmail_path, 'qmail')) { 782 $this->Sendmail = '/var/qmail/bin/qmail-inject'; 783 } else { 784 $this->Sendmail = $ini_sendmail_path; 785 } 786 $this->Mailer = 'qmail'; 787 } 788 789 /** 790 * Add a "To" address. 791 * @param string $address The email address to send to 792 * @param string $name 793 * @return boolean true on success, false if address already used or invalid in some way 794 */ 795 public function addAddress($address, $name = '') 796 { 797 return $this->addOrEnqueueAnAddress('to', $address, $name); 798 } 799 800 /** 801 * Add a "CC" address. 802 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 803 * @param string $address The email address to send to 804 * @param string $name 805 * @return boolean true on success, false if address already used or invalid in some way 806 */ 807 public function addCC($address, $name = '') 808 { 809 return $this->addOrEnqueueAnAddress('cc', $address, $name); 810 } 811 812 /** 813 * Add a "BCC" address. 814 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. 815 * @param string $address The email address to send to 816 * @param string $name 817 * @return boolean true on success, false if address already used or invalid in some way 818 */ 819 public function addBCC($address, $name = '') 820 { 821 return $this->addOrEnqueueAnAddress('bcc', $address, $name); 822 } 823 824 /** 825 * Add a "Reply-To" address. 826 * @param string $address The email address to reply to 827 * @param string $name 828 * @return boolean true on success, false if address already used or invalid in some way 829 */ 830 public function addReplyTo($address, $name = '') 831 { 832 return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); 833 } 834 835 /** 836 * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer 837 * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still 838 * be modified after calling this function), addition of such addresses is delayed until send(). 839 * Addresses that have been added already return false, but do not throw exceptions. 840 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 841 * @param string $address The email address to send, resp. to reply to 842 * @param string $name 843 * @throws phpmailerException 844 * @return boolean true on success, false if address already used or invalid in some way 845 * @access protected 846 */ 847 protected function addOrEnqueueAnAddress($kind, $address, $name) 848 { 849 $address = trim($address); 850 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 851 if (($pos = strrpos($address, '@')) === false) { 852 // At-sign is misssing. 853 $error_message = $this->lang('invalid_address') . $address; 854 $this->setError($error_message); 855 $this->edebug($error_message); 856 if ($this->exceptions) { 857 throw new phpmailerException($error_message); 858 } 859 return false; 860 } 861 $params = array($kind, $address, $name); 862 // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. 863 if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { 864 if ($kind != 'Reply-To') { 865 if (!array_key_exists($address, $this->RecipientsQueue)) { 866 $this->RecipientsQueue[$address] = $params; 867 return true; 868 } 869 } else { 870 if (!array_key_exists($address, $this->ReplyToQueue)) { 871 $this->ReplyToQueue[$address] = $params; 872 return true; 873 } 874 } 875 return false; 876 } 877 // Immediately add standard addresses without IDN. 878 return call_user_func_array(array($this, 'addAnAddress'), $params); 879 } 880 881 /** 882 * Add an address to one of the recipient arrays or to the ReplyTo array. 883 * Addresses that have been added already return false, but do not throw exceptions. 884 * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' 885 * @param string $address The email address to send, resp. to reply to 886 * @param string $name 887 * @throws phpmailerException 888 * @return boolean true on success, false if address already used or invalid in some way 889 * @access protected 890 */ 891 protected function addAnAddress($kind, $address, $name = '') 892 { 893 if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { 894 $error_message = $this->lang('Invalid recipient kind: ') . $kind; 895 $this->setError($error_message); 896 $this->edebug($error_message); 897 if ($this->exceptions) { 898 throw new phpmailerException($error_message); 899 } 900 return false; 901 } 902 if (!$this->validateAddress($address)) { 903 $error_message = $this->lang('invalid_address') . $address; 904 $this->setError($error_message); 905 $this->edebug($error_message); 906 if ($this->exceptions) { 907 throw new phpmailerException($error_message); 908 } 909 return false; 910 } 911 if ($kind != 'Reply-To') { 912 if (!array_key_exists(strtolower($address), $this->all_recipients)) { 913 array_push($this->$kind, array($address, $name)); 914 $this->all_recipients[strtolower($address)] = true; 915 return true; 916 } 917 } else { 918 if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 919 $this->ReplyTo[strtolower($address)] = array($address, $name); 920 return true; 921 } 922 } 923 return false; 924 } 925 926 /** 927 * Parse and validate a string containing one or more RFC822-style comma-separated email addresses 928 * of the form "display name <address>" into an array of name/address pairs. 929 * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. 930 * Note that quotes in the name part are removed. 931 * @param string $addrstr The address list string 932 * @param bool $useimap Whether to use the IMAP extension to parse the list 933 * @return array 934 * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation 935 */ 936 public function parseAddresses($addrstr, $useimap = true) 937 { 938 $addresses = array(); 939 if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { 940 //Use this built-in parser if it's available 941 $list = imap_rfc822_parse_adrlist($addrstr, ''); 942 foreach ($list as $address) { 943 if ($address->host != '.SYNTAX-ERROR.') { 944 if ($this->validateAddress($address->mailbox . '@' . $address->host)) { 945 $addresses[] = array( 946 'name' => (property_exists($address, 'personal') ? $address->personal : ''), 947 'address' => $address->mailbox . '@' . $address->host 948 ); 949 } 950 } 951 } 952 } else { 953 //Use this simpler parser 954 $list = explode(',', $addrstr); 955 foreach ($list as $address) { 956 $address = trim($address); 957 //Is there a separate name part? 958 if (strpos($address, '<') === false) { 959 //No separate name, just use the whole thing 960 if ($this->validateAddress($address)) { 961 $addresses[] = array( 962 'name' => '', 963 'address' => $address 964 ); 965 } 966 } else { 967 list($name, $email) = explode('<', $address); 968 $email = trim(str_replace('>', '', $email)); 969 if ($this->validateAddress($email)) { 970 $addresses[] = array( 971 'name' => trim(str_replace(array('"', "'"), '', $name)), 972 'address' => $email 973 ); 974 } 975 } 976 } 977 } 978 return $addresses; 979 } 980 981 /** 982 * Set the From and FromName properties. 983 * @param string $address 984 * @param string $name 985 * @param boolean $auto Whether to also set the Sender address, defaults to true 986 * @throws phpmailerException 987 * @return boolean 988 */ 989 public function setFrom($address, $name = '', $auto = true) 990 { 991 $address = trim($address); 992 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 993 // Don't validate now addresses with IDN. Will be done in send(). 994 if (($pos = strrpos($address, '@')) === false or 995 (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and 996 !$this->validateAddress($address)) { 997 $error_message = $this->lang('invalid_address') . $address; 998 $this->setError($error_message); 999 $this->edebug($error_message); 1000 if ($this->exceptions) { 1001 throw new phpmailerException($error_message); 1002 } 1003 return false; 1004 } 1005 $this->From = $address; 1006 $this->FromName = $name; 1007 if ($auto) { 1008 if (empty($this->Sender)) { 1009 $this->Sender = $address; 1010 } 1011 } 1012 return true; 1013 } 1014 1015 /** 1016 * Return the Message-ID header of the last email. 1017 * Technically this is the value from the last time the headers were created, 1018 * but it's also the message ID of the last sent message except in 1019 * pathological cases. 1020 * @return string 1021 */ 1022 public function getLastMessageID() 1023 { 1024 return $this->lastMessageID; 1025 } 1026 1027 /** 1028 * Check that a string looks like an email address. 1029 * @param string $address The email address to check 1030 * @param string $patternselect A selector for the validation pattern to use : 1031 * * `auto` Pick best pattern automatically; 1032 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; 1033 * * `pcre` Use old PCRE implementation; 1034 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; 1035 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. 1036 * * `noregex` Don't use a regex: super fast, really dumb. 1037 * @return boolean 1038 * @static 1039 * @access public 1040 */ 1041 public static function validateAddress($address, $patternselect = 'auto') 1042 { 1043 //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 1044 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { 1045 return false; 1046 } 1047 if (!$patternselect or $patternselect == 'auto') { 1048 //Check this constant first so it works when extension_loaded() is disabled by safe mode 1049 //Constant was added in PHP 5.2.4 1050 if (defined('PCRE_VERSION')) { 1051 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 1052 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { 1053 $patternselect = 'pcre8'; 1054 } else { 1055 $patternselect = 'pcre'; 1056 } 1057 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { 1058 //Fall back to older PCRE 1059 $patternselect = 'pcre'; 1060 } else { 1061 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension 1062 if (version_compare(PHP_VERSION, '5.2.0') >= 0) { 1063 $patternselect = 'php'; 1064 } else { 1065 $patternselect = 'noregex'; 1066 } 1067 } 1068 } 1069 switch ($patternselect) { 1070 case 'pcre8': 1071 /** 1072 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. 1073 * @link http://squiloople.com/2009/12/20/email-address-validation/ 1074 * @copyright 2009-2010 Michael Rushton 1075 * Feel free to use and redistribute this code. But please keep this copyright notice. 1076 */ 1077 return (boolean)preg_match( 1078 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . 1079 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . 1080 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . 1081 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . 1082 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . 1083 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . 1084 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . 1085 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . 1086 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', 1087 $address 1088 ); 1089 case 'pcre': 1090 //An older regex that doesn't need a recent PCRE 1091 return (boolean)preg_match( 1092 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . 1093 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . 1094 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . 1095 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . 1096 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . 1097 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . 1098 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . 1099 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . 1100 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . 1101 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', 1102 $address 1103 ); 1104 case 'html5': 1105 /** 1106 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. 1107 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) 1108 */ 1109 return (boolean)preg_match( 1110 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . 1111 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', 1112 $address 1113 ); 1114 case 'noregex': 1115 //No PCRE! Do something _very_ approximate! 1116 //Check the address is 3 chars or longer and contains an @ that's not the first or last char 1117 return (strlen($address) >= 3 1118 and strpos($address, '@') >= 1 1119 and strpos($address, '@') != strlen($address) - 1); 1120 case 'php': 1121 default: 1122 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); 1123 } 1124 } 1125 1126 /** 1127 * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the 1128 * "intl" and "mbstring" PHP extensions. 1129 * @return bool "true" if required functions for IDN support are present 1130 */ 1131 public function idnSupported() 1132 { 1133 // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. 1134 return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); 1135 } 1136 1137 /** 1138 * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. 1139 * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. 1140 * This function silently returns unmodified address if: 1141 * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) 1142 * - Conversion to punycode is impossible (e.g. required PHP functions are not available) 1143 * or fails for any reason (e.g. domain has characters not allowed in an IDN) 1144 * @see PHPMailer::$CharSet 1145 * @param string $address The email address to convert 1146 * @return string The encoded address in ASCII form 1147 */ 1148 public function punyencodeAddress($address) 1149 { 1150 // Verify we have required functions, CharSet, and at-sign. 1151 if ($this->idnSupported() and 1152 !empty($this->CharSet) and 1153 ($pos = strrpos($address, '@')) !== false) { 1154 $domain = substr($address, ++$pos); 1155 // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. 1156 if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { 1157 $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); 1158 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? 1159 idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : 1160 idn_to_ascii($domain)) !== false) { 1161 return substr($address, 0, $pos) . $punycode; 1162 } 1163 } 1164 } 1165 return $address; 1166 } 1167 1168 /** 1169 * Create a message and send it. 1170 * Uses the sending method specified by $Mailer. 1171 * @throws phpmailerException 1172 * @return boolean false on error - See the ErrorInfo property for details of the error. 1173 */ 1174 public function send() 1175 { 1176 try { 1177 if (!$this->preSend()) { 1178 return false; 1179 } 1180 return $this->postSend(); 1181 } catch (phpmailerException $exc) { 1182 $this->mailHeader = ''; 1183 $this->setError($exc->getMessage()); 1184 if ($this->exceptions) { 1185 throw $exc; 1186 } 1187 return false; 1188 } 1189 } 1190 1191 /** 1192 * Prepare a message for sending. 1193 * @throws phpmailerException 1194 * @return boolean 1195 */ 1196 public function preSend() 1197 { 1198 try { 1199 $this->error_count = 0; // Reset errors 1200 $this->mailHeader = ''; 1201 1202 // Dequeue recipient and Reply-To addresses with IDN 1203 foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { 1204 $params[1] = $this->punyencodeAddress($params[1]); 1205 call_user_func_array(array($this, 'addAnAddress'), $params); 1206 } 1207 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { 1208 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); 1209 } 1210 1211 // Validate From, Sender, and ConfirmReadingTo addresses 1212 foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { 1213 $this->$address_kind = trim($this->$address_kind); 1214 if (empty($this->$address_kind)) { 1215 continue; 1216 } 1217 $this->$address_kind = $this->punyencodeAddress($this->$address_kind); 1218 if (!$this->validateAddress($this->$address_kind)) { 1219 $error_message = $this->lang('invalid_address') . $this->$address_kind; 1220 $this->setError($error_message); 1221 $this->edebug($error_message); 1222 if ($this->exceptions) { 1223 throw new phpmailerException($error_message); 1224 } 1225 return false; 1226 } 1227 } 1228 1229 // Set whether the message is multipart/alternative 1230 if (!empty($this->AltBody)) { 1231 $this->ContentType = 'multipart/alternative'; 1232 } 1233 1234 $this->setMessageType(); 1235 // Refuse to send an empty message unless we are specifically allowing it 1236 if (!$this->AllowEmpty and empty($this->Body)) { 1237 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); 1238 } 1239 1240 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) 1241 $this->MIMEHeader = ''; 1242 $this->MIMEBody = $this->createBody(); 1243 // createBody may have added some headers, so retain them 1244 $tempheaders = $this->MIMEHeader; 1245 $this->MIMEHeader = $this->createHeader(); 1246 $this->MIMEHeader .= $tempheaders; 1247 1248 // To capture the complete message when using mail(), create 1249 // an extra header list which createHeader() doesn't fold in 1250 if ($this->Mailer == 'mail') { 1251 if (count($this->to) > 0) { 1252 $this->mailHeader .= $this->addrAppend('To', $this->to); 1253 } else { 1254 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); 1255 } 1256 $this->mailHeader .= $this->headerLine( 1257 'Subject', 1258 $this->encodeHeader($this->secureHeader(trim($this->Subject))) 1259 ); 1260 } 1261 1262 // Sign with DKIM if enabled 1263 if (!empty($this->DKIM_domain) 1264 && !empty($this->DKIM_private) 1265 && !empty($this->DKIM_selector) 1266 && file_exists($this->DKIM_private)) { 1267 $header_dkim = $this->DKIM_Add( 1268 $this->MIMEHeader . $this->mailHeader, 1269 $this->encodeHeader($this->secureHeader($this->Subject)), 1270 $this->MIMEBody 1271 ); 1272 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . 1273 str_replace("\r\n", "\n", $header_dkim) . self::CRLF; 1274 } 1275 return true; 1276 } catch (phpmailerException $exc) { 1277 $this->setError($exc->getMessage()); 1278 if ($this->exceptions) { 1279 throw $exc; 1280 } 1281 return false; 1282 } 1283 } 1284 1285 /** 1286 * Actually send a message. 1287 * Send the email via the selected mechanism 1288 * @throws phpmailerException 1289 * @return boolean 1290 */ 1291 public function postSend() 1292 { 1293 try { 1294 // Choose the mailer and send through it 1295 switch ($this->Mailer) { 1296 case 'sendmail': 1297 case 'qmail': 1298 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); 1299 case 'smtp': 1300 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); 1301 case 'mail': 1302 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1303 default: 1304 $sendMethod = $this->Mailer.'Send'; 1305 if (method_exists($this, $sendMethod)) { 1306 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); 1307 } 1308 1309 return $this->mailSend($this->MIMEHeader, $this->MIMEBody); 1310 } 1311 } catch (phpmailerException $exc) { 1312 $this->setError($exc->getMessage()); 1313 $this->edebug($exc->getMessage()); 1314 if ($this->exceptions) { 1315 throw $exc; 1316 } 1317 } 1318 return false; 1319 } 1320 1321 /** 1322 * Send mail using the $Sendmail program. 1323 * @param string $header The message headers 1324 * @param string $body The message body 1325 * @see PHPMailer::$Sendmail 1326 * @throws phpmailerException 1327 * @access protected 1328 * @return boolean 1329 */ 1330 protected function sendmailSend($header, $body) 1331 { 1332 if ($this->Sender != '') { 1333 if ($this->Mailer == 'qmail') { 1334 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1335 } else { 1336 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1337 } 1338 } else { 1339 if ($this->Mailer == 'qmail') { 1340 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); 1341 } else { 1342 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); 1343 } 1344 } 1345 if ($this->SingleTo) { 1346 foreach ($this->SingleToArray as $toAddr) { 1347 if (!@$mail = popen($sendmail, 'w')) { 1348 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1349 } 1350 fputs($mail, 'To: ' . $toAddr . "\n"); 1351 fputs($mail, $header); 1352 fputs($mail, $body); 1353 $result = pclose($mail); 1354 $this->doCallback( 1355 ($result == 0), 1356 array($toAddr), 1357 $this->cc, 1358 $this->bcc, 1359 $this->Subject, 1360 $body, 1361 $this->From 1362 ); 1363 if ($result != 0) { 1364 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1365 } 1366 } 1367 } else { 1368 if (!@$mail = popen($sendmail, 'w')) { 1369 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1370 } 1371 fputs($mail, $header); 1372 fputs($mail, $body); 1373 $result = pclose($mail); 1374 $this->doCallback( 1375 ($result == 0), 1376 $this->to, 1377 $this->cc, 1378 $this->bcc, 1379 $this->Subject, 1380 $body, 1381 $this->From 1382 ); 1383 if ($result != 0) { 1384 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); 1385 } 1386 } 1387 return true; 1388 } 1389 1390 /** 1391 * Send mail using the PHP mail() function. 1392 * @param string $header The message headers 1393 * @param string $body The message body 1394 * @link http://www.php.net/manual/en/book.mail.php 1395 * @throws phpmailerException 1396 * @access protected 1397 * @return boolean 1398 */ 1399 protected function mailSend($header, $body) 1400 { 1401 $toArr = array(); 1402 foreach ($this->to as $toaddr) { 1403 $toArr[] = $this->addrFormat($toaddr); 1404 } 1405 $to = implode(', ', $toArr); 1406 1407 if (empty($this->Sender)) { 1408 $params = ' '; 1409 } else { 1410 $params = sprintf('-f%s', $this->Sender); 1411 } 1412 if ($this->Sender != '' and !ini_get('safe_mode')) { 1413 $old_from = ini_get('sendmail_from'); 1414 ini_set('sendmail_from', $this->Sender); 1415 } 1416 $result = false; 1417 if ($this->SingleTo && count($toArr) > 1) { 1418 foreach ($toArr as $toAddr) { 1419 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 1420 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1421 } 1422 } else { 1423 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); 1424 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); 1425 } 1426 if (isset($old_from)) { 1427 ini_set('sendmail_from', $old_from); 1428 } 1429 if (!$result) { 1430 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); 1431 } 1432 return true; 1433 } 1434 1435 /** 1436 * Get an instance to use for SMTP operations. 1437 * Override this function to load your own SMTP implementation 1438 * @return SMTP 1439 */ 1440 public function getSMTPInstance() 1441 { 1442 if (!is_object($this->smtp)) { 1443 $this->smtp = new SMTP; 1444 } 1445 return $this->smtp; 1446 } 1447 1448 /** 1449 * Send mail via SMTP. 1450 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. 1451 * Uses the PHPMailerSMTP class by default. 1452 * @see PHPMailer::getSMTPInstance() to use a different class. 1453 * @param string $header The message headers 1454 * @param string $body The message body 1455 * @throws phpmailerException 1456 * @uses SMTP 1457 * @access protected 1458 * @return boolean 1459 */ 1460 protected function smtpSend($header, $body) 1461 { 1462 $bad_rcpt = array(); 1463 if (!$this->smtpConnect($this->SMTPOptions)) { 1464 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1465 } 1466 if ('' == $this->Sender) { 1467 $smtp_from = $this->From; 1468 } else { 1469 $smtp_from = $this->Sender; 1470 } 1471 if (!$this->smtp->mail($smtp_from)) { 1472 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); 1473 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); 1474 } 1475 1476 // Attempt to send to all recipients 1477 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { 1478 foreach ($togroup as $to) { 1479 if (!$this->smtp->recipient($to[0])) { 1480 $error = $this->smtp->getError(); 1481 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); 1482 $isSent = false; 1483 } else { 1484 $isSent = true; 1485 } 1486 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); 1487 } 1488 } 1489 1490 // Only send the DATA command if we have viable recipients 1491 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { 1492 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); 1493 } 1494 if ($this->SMTPKeepAlive) { 1495 $this->smtp->reset(); 1496 } else { 1497 $this->smtp->quit(); 1498 $this->smtp->close(); 1499 } 1500 //Create error message for any bad addresses 1501 if (count($bad_rcpt) > 0) { 1502 $errstr = ''; 1503 foreach ($bad_rcpt as $bad) { 1504 $errstr .= $bad['to'] . ': ' . $bad['error']; 1505 } 1506 throw new phpmailerException( 1507 $this->lang('recipients_failed') . $errstr, 1508 self::STOP_CONTINUE 1509 ); 1510 } 1511 return true; 1512 } 1513 1514 /** 1515 * Initiate a connection to an SMTP server. 1516 * Returns false if the operation failed. 1517 * @param array $options An array of options compatible with stream_context_create() 1518 * @uses SMTP 1519 * @access public 1520 * @throws phpmailerException 1521 * @return boolean 1522 */ 1523 public function smtpConnect($options = array()) 1524 { 1525 if (is_null($this->smtp)) { 1526 $this->smtp = $this->getSMTPInstance(); 1527 } 1528 1529 // Already connected? 1530 if ($this->smtp->connected()) { 1531 return true; 1532 } 1533 1534 $this->smtp->setTimeout($this->Timeout); 1535 $this->smtp->setDebugLevel($this->SMTPDebug); 1536 $this->smtp->setDebugOutput($this->Debugoutput); 1537 $this->smtp->setVerp($this->do_verp); 1538 $hosts = explode(';', $this->Host); 1539 $lastexception = null; 1540 1541 foreach ($hosts as $hostentry) { 1542 $hostinfo = array(); 1543 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { 1544 // Not a valid host entry 1545 continue; 1546 } 1547 // $hostinfo[2]: optional ssl or tls prefix 1548 // $hostinfo[3]: the hostname 1549 // $hostinfo[4]: optional port number 1550 // The host string prefix can temporarily override the current setting for SMTPSecure 1551 // If it's not specified, the default value is used 1552 $prefix = ''; 1553 $secure = $this->SMTPSecure; 1554 $tls = ($this->SMTPSecure == 'tls'); 1555 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { 1556 $prefix = 'ssl://'; 1557 $tls = false; // Can't have SSL and TLS at the same time 1558 $secure = 'ssl'; 1559 } elseif ($hostinfo[2] == 'tls') { 1560 $tls = true; 1561 // tls doesn't use a prefix 1562 $secure = 'tls'; 1563 } 1564 //Do we need the OpenSSL extension? 1565 $sslext = defined('OPENSSL_ALGO_SHA1'); 1566 if ('tls' === $secure or 'ssl' === $secure) { 1567 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled 1568 if (!$sslext) { 1569 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); 1570 } 1571 } 1572 $host = $hostinfo[3]; 1573 $port = $this->Port; 1574 $tport = (integer)$hostinfo[4]; 1575 if ($tport > 0 and $tport < 65536) { 1576 $port = $tport; 1577 } 1578 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { 1579 try { 1580 if ($this->Helo) { 1581 $hello = $this->Helo; 1582 } else { 1583 $hello = $this->serverHostname(); 1584 } 1585 $this->smtp->hello($hello); 1586 //Automatically enable TLS encryption if: 1587 // * it's not disabled 1588 // * we have openssl extension 1589 // * we are not already using SSL 1590 // * the server offers STARTTLS 1591 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { 1592 $tls = true; 1593 } 1594 if ($tls) { 1595 if (!$this->smtp->startTLS()) { 1596 throw new phpmailerException($this->lang('connect_host')); 1597 } 1598 // We must resend HELO after tls negotiation 1599 $this->smtp->hello($hello); 1600 } 1601 if ($this->SMTPAuth) { 1602 if (!$this->smtp->authenticate( 1603 $this->Username, 1604 $this->Password, 1605 $this->AuthType, 1606 $this->Realm, 1607 $this->Workstation 1608 ) 1609 ) { 1610 throw new phpmailerException($this->lang('authenticate')); 1611 } 1612 } 1613 return true; 1614 } catch (phpmailerException $exc) { 1615 $lastexception = $exc; 1616 $this->edebug($exc->getMessage()); 1617 // We must have connected, but then failed TLS or Auth, so close connection nicely 1618 $this->smtp->quit(); 1619 } 1620 } 1621 } 1622 // If we get here, all connection attempts have failed, so close connection hard 1623 $this->smtp->close(); 1624 // As we've caught all exceptions, just report whatever the last one was 1625 if ($this->exceptions and !is_null($lastexception)) { 1626 throw $lastexception; 1627 } 1628 return false; 1629 } 1630 1631 /** 1632 * Close the active SMTP session if one exists. 1633 * @return void 1634 */ 1635 public function smtpClose() 1636 { 1637 if ($this->smtp !== null) { 1638 if ($this->smtp->connected()) { 1639 $this->smtp->quit(); 1640 $this->smtp->close(); 1641 } 1642 } 1643 } 1644 1645 /** 1646 * Set the language for error messages. 1647 * Returns false if it cannot load the language file. 1648 * The default language is English. 1649 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") 1650 * @param string $lang_path Path to the language file directory, with trailing separator (slash) 1651 * @return boolean 1652 * @access public 1653 */ 1654 public function setLanguage($langcode = 'en', $lang_path = '') 1655 { 1656 // Define full set of translatable strings in English 1657 $PHPMAILER_LANG = array( 1658 'authenticate' => 'SMTP Error: Could not authenticate.', 1659 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 1660 'data_not_accepted' => 'SMTP Error: data not accepted.', 1661 'empty_message' => 'Message body empty', 1662 'encoding' => 'Unknown encoding: ', 1663 'execute' => 'Could not execute: ', 1664 'file_access' => 'Could not access file: ', 1665 'file_open' => 'File Error: Could not open file: ', 1666 'from_failed' => 'The following From address failed: ', 1667 'instantiate' => 'Could not instantiate mail function.', 1668 'invalid_address' => 'Invalid address: ', 1669 'mailer_not_supported' => ' mailer is not supported.', 1670 'provide_address' => 'You must provide at least one recipient email address.', 1671 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 1672 'signing' => 'Signing Error: ', 1673 'smtp_connect_failed' => 'SMTP connect() failed.', 1674 'smtp_error' => 'SMTP server error: ', 1675 'variable_set' => 'Cannot set or reset variable: ', 1676 'extension_missing' => 'Extension missing: ' 1677 ); 1678 if (empty($lang_path)) { 1679 // Calculate an absolute path so it can work if CWD is not here 1680 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 1681 } 1682 $foundlang = true; 1683 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1684 // There is no English translation file 1685 if ($langcode != 'en') { 1686 // Make sure language file path is readable 1687 if (!is_readable($lang_file)) { 1688 $foundlang = false; 1689 } else { 1690 // Overwrite language-specific strings. 1691 // This way we'll never have missing translation keys. 1692 $foundlang = include $lang_file; 1693 } 1694 } 1695 $this->language = $PHPMAILER_LANG; 1696 return (boolean)$foundlang; // Returns false if language not found 1697 } 1698 1699 /** 1700 * Get the array of strings for the current language. 1701 * @return array 1702 */ 1703 public function getTranslations() 1704 { 1705 return $this->language; 1706 } 1707 1708 /** 1709 * Create recipient headers. 1710 * @access public 1711 * @param string $type 1712 * @param array $addr An array of recipient, 1713 * where each recipient is a 2-element indexed array with element 0 containing an address 1714 * and element 1 containing a name, like: 1715 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) 1716 * @return string 1717 */ 1718 public function addrAppend($type, $addr) 1719 { 1720 $addresses = array(); 1721 foreach ($addr as $address) { 1722 $addresses[] = $this->addrFormat($address); 1723 } 1724 return $type . ': ' . implode(', ', $addresses) . $this->LE; 1725 } 1726 1727 /** 1728 * Format an address for use in a message header. 1729 * @access public 1730 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name 1731 * like array('joe@example.com', 'Joe User') 1732 * @return string 1733 */ 1734 public function addrFormat($addr) 1735 { 1736 if (empty($addr[1])) { // No name provided 1737 return $this->secureHeader($addr[0]); 1738 } else { 1739 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 1740 $addr[0] 1741 ) . '>'; 1742 } 1743 } 1744 1745 /** 1746 * Word-wrap message. 1747 * For use with mailers that do not automatically perform wrapping 1748 * and for quoted-printable encoded messages. 1749 * Original written by philippe. 1750 * @param string $message The message to wrap 1751 * @param integer $length The line length to wrap to 1752 * @param boolean $qp_mode Whether to run in Quoted-Printable mode 1753 * @access public 1754 * @return string 1755 */ 1756 public function wrapText($message, $length, $qp_mode = false) 1757 { 1758 if ($qp_mode) { 1759 $soft_break = sprintf(' =%s', $this->LE); 1760 } else { 1761 $soft_break = $this->LE; 1762 } 1763 // If utf-8 encoding is used, we will need to make sure we don't 1764 // split multibyte characters when we wrap 1765 $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); 1766 $lelen = strlen($this->LE); 1767 $crlflen = strlen(self::CRLF); 1768 1769 $message = $this->fixEOL($message); 1770 //Remove a trailing line break 1771 if (substr($message, -$lelen) == $this->LE) { 1772 $message = substr($message, 0, -$lelen); 1773 } 1774 1775 //Split message into lines 1776 $lines = explode($this->LE, $message); 1777 //Message will be rebuilt in here 1778 $message = ''; 1779 foreach ($lines as $line) { 1780 $words = explode(' ', $line); 1781 $buf = ''; 1782 $firstword = true; 1783 foreach ($words as $word) { 1784 if ($qp_mode and (strlen($word) > $length)) { 1785 $space_left = $length - strlen($buf) - $crlflen; 1786 if (!$firstword) { 1787 if ($space_left > 20) { 1788 $len = $space_left; 1789 if ($is_utf8) { 1790 $len = $this->utf8CharBoundary($word, $len); 1791 } elseif (substr($word, $len - 1, 1) == '=') { 1792 $len--; 1793 } elseif (substr($word, $len - 2, 1) == '=') { 1794 $len -= 2; 1795 } 1796 $part = substr($word, 0, $len); 1797 $word = substr($word, $len); 1798 $buf .= ' ' . $part; 1799 $message .= $buf . sprintf('=%s', self::CRLF); 1800 } else { 1801 $message .= $buf . $soft_break; 1802 } 1803 $buf = ''; 1804 } 1805 while (strlen($word) > 0) { 1806 if ($length <= 0) { 1807 break; 1808 } 1809 $len = $length; 1810 if ($is_utf8) { 1811 $len = $this->utf8CharBoundary($word, $len); 1812 } elseif (substr($word, $len - 1, 1) == '=') { 1813 $len--; 1814 } elseif (substr($word, $len - 2, 1) == '=') { 1815 $len -= 2; 1816 } 1817 $part = substr($word, 0, $len); 1818 $word = substr($word, $len); 1819 1820 if (strlen($word) > 0) { 1821 $message .= $part . sprintf('=%s', self::CRLF); 1822 } else { 1823 $buf = $part; 1824 } 1825 } 1826 } else { 1827 $buf_o = $buf; 1828 if (!$firstword) { 1829 $buf .= ' '; 1830 } 1831 $buf .= $word; 1832 1833 if (strlen($buf) > $length and $buf_o != '') { 1834 $message .= $buf_o . $soft_break; 1835 $buf = $word; 1836 } 1837 } 1838 $firstword = false; 1839 } 1840 $message .= $buf . self::CRLF; 1841 } 1842 1843 return $message; 1844 } 1845 1846 /** 1847 * Find the last character boundary prior to $maxLength in a utf-8 1848 * quoted-printable encoded string. 1849 * Original written by Colin Brown. 1850 * @access public 1851 * @param string $encodedText utf-8 QP text 1852 * @param integer $maxLength Find the last character boundary prior to this length 1853 * @return integer 1854 */ 1855 public function utf8CharBoundary($encodedText, $maxLength) 1856 { 1857 $foundSplitPos = false; 1858 $lookBack = 3; 1859 while (!$foundSplitPos) { 1860 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 1861 $encodedCharPos = strpos($lastChunk, '='); 1862 if (false !== $encodedCharPos) { 1863 // Found start of encoded character byte within $lookBack block. 1864 // Check the encoded byte value (the 2 chars after the '=') 1865 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 1866 $dec = hexdec($hex); 1867 if ($dec < 128) { 1868 // Single byte character. 1869 // If the encoded char was found at pos 0, it will fit 1870 // otherwise reduce maxLength to start of the encoded char 1871 if ($encodedCharPos > 0) { 1872 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1873 } 1874 $foundSplitPos = true; 1875 } elseif ($dec >= 192) { 1876 // First byte of a multi byte character 1877 // Reduce maxLength to split at start of character 1878 $maxLength = $maxLength - ($lookBack - $encodedCharPos); 1879 $foundSplitPos = true; 1880 } elseif ($dec < 192) { 1881 // Middle byte of a multi byte character, look further back 1882 $lookBack += 3; 1883 } 1884 } else { 1885 // No encoded character found 1886 $foundSplitPos = true; 1887 } 1888 } 1889 return $maxLength; 1890 } 1891 1892 /** 1893 * Apply word wrapping to the message body. 1894 * Wraps the message body to the number of chars set in the WordWrap property. 1895 * You should only do this to plain-text bodies as wrapping HTML tags may break them. 1896 * This is called automatically by createBody(), so you don't need to call it yourself. 1897 * @access public 1898 * @return void 1899 */ 1900 public function setWordWrap() 1901 { 1902 if ($this->WordWrap < 1) { 1903 return; 1904 } 1905 1906 switch ($this->message_type) { 1907 case 'alt': 1908 case 'alt_inline': 1909 case 'alt_attach': 1910 case 'alt_inline_attach': 1911 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); 1912 break; 1913 default: 1914 $this->Body = $this->wrapText($this->Body, $this->WordWrap); 1915 break; 1916 } 1917 } 1918 1919 /** 1920 * Assemble message headers. 1921 * @access public 1922 * @return string The assembled headers 1923 */ 1924 public function createHeader() 1925 { 1926 $result = ''; 1927 1928 if ($this->MessageDate == '') { 1929 $this->MessageDate = self::rfcDate(); 1930 } 1931 $result .= $this->headerLine('Date', $this->MessageDate); 1932 1933 // To be created automatically by mail() 1934 if ($this->SingleTo) { 1935 if ($this->Mailer != 'mail') { 1936 foreach ($this->to as $toaddr) { 1937 $this->SingleToArray[] = $this->addrFormat($toaddr); 1938 } 1939 } 1940 } else { 1941 if (count($this->to) > 0) { 1942 if ($this->Mailer != 'mail') { 1943 $result .= $this->addrAppend('To', $this->to); 1944 } 1945 } elseif (count($this->cc) == 0) { 1946 $result .= $this->headerLine('To', 'undisclosed-recipients:;'); 1947 } 1948 } 1949 1950 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); 1951 1952 // sendmail and mail() extract Cc from the header before sending 1953 if (count($this->cc) > 0) { 1954 $result .= $this->addrAppend('Cc', $this->cc); 1955 } 1956 1957 // sendmail and mail() extract Bcc from the header before sending 1958 if (( 1959 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' 1960 ) 1961 and count($this->bcc) > 0 1962 ) { 1963 $result .= $this->addrAppend('Bcc', $this->bcc); 1964 } 1965 1966 if (count($this->ReplyTo) > 0) { 1967 $result .= $this->addrAppend('Reply-To', $this->ReplyTo); 1968 } 1969 1970 // mail() sets the subject itself 1971 if ($this->Mailer != 'mail') { 1972 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 1973 } 1974 1975 if ($this->MessageID != '') { 1976 $this->lastMessageID = $this->MessageID; 1977 } else { 1978 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); 1979 } 1980 $result .= $this->headerLine('Message-ID', $this->lastMessageID); 1981 if (!is_null($this->Priority)) { 1982 $result .= $this->headerLine('X-Priority', $this->Priority); 1983 } 1984 if ($this->XMailer == '') { 1985 $result .= $this->headerLine( 1986 'X-Mailer', 1987 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' 1988 ); 1989 } else { 1990 $myXmailer = trim($this->XMailer); 1991 if ($myXmailer) { 1992 $result .= $this->headerLine('X-Mailer', $myXmailer); 1993 } 1994 } 1995 1996 if ($this->ConfirmReadingTo != '') { 1997 $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); 1998 } 1999 2000 // Add custom headers 2001 foreach ($this->CustomHeader as $header) { 2002 $result .= $this->headerLine( 2003 trim($header[0]), 2004 $this->encodeHeader(trim($header[1])) 2005 ); 2006 } 2007 if (!$this->sign_key_file) { 2008 $result .= $this->headerLine('MIME-Version', '1.0'); 2009 $result .= $this->getMailMIME(); 2010 } 2011 2012 return $result; 2013 } 2014 2015 /** 2016 * Get the message MIME type headers. 2017 * @access public 2018 * @return string 2019 */ 2020 public function getMailMIME() 2021 { 2022 $result = ''; 2023 $ismultipart = true; 2024 switch ($this->message_type) { 2025 case 'inline': 2026 $result .= $this->headerLine('Content-Type', 'multipart/related;'); 2027 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2028 break; 2029 case 'attach': 2030 case 'inline_attach': 2031 case 'alt_attach': 2032 case 'alt_inline_attach': 2033 $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); 2034 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2035 break; 2036 case 'alt': 2037 case 'alt_inline': 2038 $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); 2039 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); 2040 break; 2041 default: 2042 // Catches case 'plain': and case '': 2043 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); 2044 $ismultipart = false; 2045 break; 2046 } 2047 // RFC1341 part 5 says 7bit is assumed if not specified 2048 if ($this->Encoding != '7bit') { 2049 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE 2050 if ($ismultipart) { 2051 if ($this->Encoding == '8bit') { 2052 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); 2053 } 2054 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible 2055 } else { 2056 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); 2057 } 2058 } 2059 2060 if ($this->Mailer != 'mail') { 2061 $result .= $this->LE; 2062 } 2063 2064 return $result; 2065 } 2066 2067 /** 2068 * Returns the whole MIME message. 2069 * Includes complete headers and body. 2070 * Only valid post preSend(). 2071 * @see PHPMailer::preSend() 2072 * @access public 2073 * @return string 2074 */ 2075 public function getSentMIMEMessage() 2076 { 2077 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 2078 } 2079 2080 /** 2081 * Assemble the message body. 2082 * Returns an empty string on failure. 2083 * @access public 2084 * @throws phpmailerException 2085 * @return string The assembled message body 2086 */ 2087 public function createBody() 2088 { 2089 $body = ''; 2090 //Create unique IDs and preset boundaries 2091 $this->uniqueid = md5(uniqid(time())); 2092 $this->boundary[1] = 'b1_' . $this->uniqueid; 2093 $this->boundary[2] = 'b2_' . $this->uniqueid; 2094 $this->boundary[3] = 'b3_' . $this->uniqueid; 2095 2096 if ($this->sign_key_file) { 2097 $body .= $this->getMailMIME() . $this->LE; 2098 } 2099 2100 $this->setWordWrap(); 2101 2102 $bodyEncoding = $this->Encoding; 2103 $bodyCharSet = $this->CharSet; 2104 //Can we do a 7-bit downgrade? 2105 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 2106 $bodyEncoding = '7bit'; 2107 $bodyCharSet = 'us-ascii'; 2108 } 2109 //If lines are too long, and we're not already using an encoding that will shorten them, 2110 //change to quoted-printable transfer encoding 2111 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { 2112 $this->Encoding = 'quoted-printable'; 2113 $bodyEncoding = 'quoted-printable'; 2114 } 2115 2116 $altBodyEncoding = $this->Encoding; 2117 $altBodyCharSet = $this->CharSet; 2118 //Can we do a 7-bit downgrade? 2119 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 2120 $altBodyEncoding = '7bit'; 2121 $altBodyCharSet = 'us-ascii'; 2122 } 2123 //If lines are too long, change to quoted-printable transfer encoding 2124 if (self::hasLineLongerThanMax($this->AltBody)) { 2125 $altBodyEncoding = 'quoted-printable'; 2126 } 2127 //Use this as a preamble in all multipart message types 2128 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; 2129 switch ($this->message_type) { 2130 case 'inline': 2131 $body .= $mimepre; 2132 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 2133 $body .= $this->encodeString($this->Body, $bodyEncoding); 2134 $body .= $this->LE . $this->LE; 2135 $body .= $this->attachAll('inline', $this->boundary[1]); 2136 break; 2137 case 'attach': 2138 $body .= $mimepre; 2139 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); 2140 $body .= $this->encodeString($this->Body, $bodyEncoding); 2141 $body .= $this->LE . $this->LE; 2142 $body .= $this->attachAll('attachment', $this->boundary[1]); 2143 break; 2144 case 'inline_attach': 2145 $body .= $mimepre; 2146 $body .= $this->textLine('--' . $this->boundary[1]); 2147 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 2148 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2149 $body .= $this->LE; 2150 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); 2151 $body .= $this->encodeString($this->Body, $bodyEncoding); 2152 $body .= $this->LE . $this->LE; 2153 $body .= $this->attachAll('inline', $this->boundary[2]); 2154 $body .= $this->LE; 2155 $body .= $this->attachAll('attachment', $this->boundary[1]); 2156 break; 2157 case 'alt': 2158 $body .= $mimepre; 2159 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2160 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2161 $body .= $this->LE . $this->LE; 2162 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); 2163 $body .= $this->encodeString($this->Body, $bodyEncoding); 2164 $body .= $this->LE . $this->LE; 2165 if (!empty($this->Ical)) { 2166 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); 2167 $body .= $this->encodeString($this->Ical, $this->Encoding); 2168 $body .= $this->LE . $this->LE; 2169 } 2170 $body .= $this->endBoundary($this->boundary[1]); 2171 break; 2172 case 'alt_inline': 2173 $body .= $mimepre; 2174 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2175 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2176 $body .= $this->LE . $this->LE; 2177 $body .= $this->textLine('--' . $this->boundary[1]); 2178 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 2179 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2180 $body .= $this->LE; 2181 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 2182 $body .= $this->encodeString($this->Body, $bodyEncoding); 2183 $body .= $this->LE . $this->LE; 2184 $body .= $this->attachAll('inline', $this->boundary[2]); 2185 $body .= $this->LE; 2186 $body .= $this->endBoundary($this->boundary[1]); 2187 break; 2188 case 'alt_attach': 2189 $body .= $mimepre; 2190 $body .= $this->textLine('--' . $this->boundary[1]); 2191 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 2192 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2193 $body .= $this->LE; 2194 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2195 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2196 $body .= $this->LE . $this->LE; 2197 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); 2198 $body .= $this->encodeString($this->Body, $bodyEncoding); 2199 $body .= $this->LE . $this->LE; 2200 $body .= $this->endBoundary($this->boundary[2]); 2201 $body .= $this->LE; 2202 $body .= $this->attachAll('attachment', $this->boundary[1]); 2203 break; 2204 case 'alt_inline_attach': 2205 $body .= $mimepre; 2206 $body .= $this->textLine('--' . $this->boundary[1]); 2207 $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); 2208 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); 2209 $body .= $this->LE; 2210 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); 2211 $body .= $this->encodeString($this->AltBody, $altBodyEncoding); 2212 $body .= $this->LE . $this->LE; 2213 $body .= $this->textLine('--' . $this->boundary[2]); 2214 $body .= $this->headerLine('Content-Type', 'multipart/related;'); 2215 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); 2216 $body .= $this->LE; 2217 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); 2218 $body .= $this->encodeString($this->Body, $bodyEncoding); 2219 $body .= $this->LE . $this->LE; 2220 $body .= $this->attachAll('inline', $this->boundary[3]); 2221 $body .= $this->LE; 2222 $body .= $this->endBoundary($this->boundary[2]); 2223 $body .= $this->LE; 2224 $body .= $this->attachAll('attachment', $this->boundary[1]); 2225 break; 2226 default: 2227 // catch case 'plain' and case '' 2228 $body .= $this->encodeString($this->Body, $bodyEncoding); 2229 break; 2230 } 2231 2232 if ($this->isError()) { 2233 $body = ''; 2234 } elseif ($this->sign_key_file) { 2235 try { 2236 if (!defined('PKCS7_TEXT')) { 2237 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 2238 } 2239 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 2240 $file = tempnam(sys_get_temp_dir(), 'mail'); 2241 if (false === file_put_contents($file, $body)) { 2242 throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); 2243 } 2244 $signed = tempnam(sys_get_temp_dir(), 'signed'); 2245 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 2246 if (empty($this->sign_extracerts_file)) { 2247 $sign = @openssl_pkcs7_sign( 2248 $file, 2249 $signed, 2250 'file://' . realpath($this->sign_cert_file), 2251 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2252 null 2253 ); 2254 } else { 2255 $sign = @openssl_pkcs7_sign( 2256 $file, 2257 $signed, 2258 'file://' . realpath($this->sign_cert_file), 2259 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), 2260 null, 2261 PKCS7_DETACHED, 2262 $this->sign_extracerts_file 2263 ); 2264 } 2265 if ($sign) { 2266 @unlink($file); 2267 $body = file_get_contents($signed); 2268 @unlink($signed); 2269 //The message returned by openssl contains both headers and body, so need to split them up 2270 $parts = explode("\n\n", $body, 2); 2271 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; 2272 $body = $parts[1]; 2273 } else { 2274 @unlink($file); 2275 @unlink($signed); 2276 throw new phpmailerException($this->lang('signing') . openssl_error_string()); 2277 } 2278 } catch (phpmailerException $exc) { 2279 $body = ''; 2280 if ($this->exceptions) { 2281 throw $exc; 2282 } 2283 } 2284 } 2285 return $body; 2286 } 2287 2288 /** 2289 * Return the start of a message boundary. 2290 * @access protected 2291 * @param string $boundary 2292 * @param string $charSet 2293 * @param string $contentType 2294 * @param string $encoding 2295 * @return string 2296 */ 2297 protected function getBoundary($boundary, $charSet, $contentType, $encoding) 2298 { 2299 $result = ''; 2300 if ($charSet == '') { 2301 $charSet = $this->CharSet; 2302 } 2303 if ($contentType == '') { 2304 $contentType = $this->ContentType; 2305 } 2306 if ($encoding == '') { 2307 $encoding = $this->Encoding; 2308 } 2309 $result .= $this->textLine('--' . $boundary); 2310 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); 2311 $result .= $this->LE; 2312 // RFC1341 part 5 says 7bit is assumed if not specified 2313 if ($encoding != '7bit') { 2314 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); 2315 } 2316 $result .= $this->LE; 2317 2318 return $result; 2319 } 2320 2321 /** 2322 * Return the end of a message boundary. 2323 * @access protected 2324 * @param string $boundary 2325 * @return string 2326 */ 2327 protected function endBoundary($boundary) 2328 { 2329 return $this->LE . '--' . $boundary . '--' . $this->LE; 2330 } 2331 2332 /** 2333 * Set the message type. 2334 * PHPMailer only supports some preset message types, 2335 * not arbitrary MIME structures. 2336 * @access protected 2337 * @return void 2338 */ 2339 protected function setMessageType() 2340 { 2341 $type = array(); 2342 if ($this->alternativeExists()) { 2343 $type[] = 'alt'; 2344 } 2345 if ($this->inlineImageExists()) { 2346 $type[] = 'inline'; 2347 } 2348 if ($this->attachmentExists()) { 2349 $type[] = 'attach'; 2350 } 2351 $this->message_type = implode('_', $type); 2352 if ($this->message_type == '') { 2353 $this->message_type = 'plain'; 2354 } 2355 } 2356 2357 /** 2358 * Format a header line. 2359 * @access public 2360 * @param string $name 2361 * @param string $value 2362 * @return string 2363 */ 2364 public function headerLine($name, $value) 2365 { 2366 return $name . ': ' . $value . $this->LE; 2367 } 2368 2369 /** 2370 * Return a formatted mail line. 2371 * @access public 2372 * @param string $value 2373 * @return string 2374 */ 2375 public function textLine($value) 2376 { 2377 return $value . $this->LE; 2378 } 2379 2380 /** 2381 * Add an attachment from a path on the filesystem. 2382 * Returns false if the file could not be found or read. 2383 * @param string $path Path to the attachment. 2384 * @param string $name Overrides the attachment name. 2385 * @param string $encoding File encoding (see $Encoding). 2386 * @param string $type File extension (MIME) type. 2387 * @param string $disposition Disposition to use 2388 * @throws phpmailerException 2389 * @return boolean 2390 */ 2391 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') 2392 { 2393 try { 2394 if (!@is_file($path)) { 2395 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); 2396 } 2397 2398 // If a MIME type is not specified, try to work it out from the file name 2399 if ($type == '') { 2400 $type = self::filenameToType($path); 2401 } 2402 2403 $filename = basename($path); 2404 if ($name == '') { 2405 $name = $filename; 2406 } 2407 2408 $this->attachment[] = array( 2409 0 => $path, 2410 1 => $filename, 2411 2 => $name, 2412 3 => $encoding, 2413 4 => $type, 2414 5 => false, // isStringAttachment 2415 6 => $disposition, 2416 7 => 0 2417 ); 2418 2419 } catch (phpmailerException $exc) { 2420 $this->setError($exc->getMessage()); 2421 $this->edebug($exc->getMessage()); 2422 if ($this->exceptions) { 2423 throw $exc; 2424 } 2425 return false; 2426 } 2427 return true; 2428 } 2429 2430 /** 2431 * Return the array of attachments. 2432 * @return array 2433 */ 2434 public function getAttachments() 2435 { 2436 return $this->attachment; 2437 } 2438 2439 /** 2440 * Attach all file, string, and binary attachments to the message. 2441 * Returns an empty string on failure. 2442 * @access protected 2443 * @param string $disposition_type 2444 * @param string $boundary 2445 * @return string 2446 */ 2447 protected function attachAll($disposition_type, $boundary) 2448 { 2449 // Return text of body 2450 $mime = array(); 2451 $cidUniq = array(); 2452 $incl = array(); 2453 2454 // Add all attachments 2455 foreach ($this->attachment as $attachment) { 2456 // Check if it is a valid disposition_filter 2457 if ($attachment[6] == $disposition_type) { 2458 // Check for string attachment 2459 $string = ''; 2460 $path = ''; 2461 $bString = $attachment[5]; 2462 if ($bString) { 2463 $string = $attachment[0]; 2464 } else { 2465 $path = $attachment[0]; 2466 } 2467 2468 $inclhash = md5(serialize($attachment)); 2469 if (in_array($inclhash, $incl)) { 2470 continue; 2471 } 2472 $incl[] = $inclhash; 2473 $name = $attachment[2]; 2474 $encoding = $attachment[3]; 2475 $type = $attachment[4]; 2476 $disposition = $attachment[6]; 2477 $cid = $attachment[7]; 2478 if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { 2479 continue; 2480 } 2481 $cidUniq[$cid] = true; 2482 2483 $mime[] = sprintf('--%s%s', $boundary, $this->LE); 2484 //Only include a filename property if we have one 2485 if (!empty($name)) { 2486 $mime[] = sprintf( 2487 'Content-Type: %s; name="%s"%s', 2488 $type, 2489 $this->encodeHeader($this->secureHeader($name)), 2490 $this->LE 2491 ); 2492 } else { 2493 $mime[] = sprintf( 2494 'Content-Type: %s%s', 2495 $type, 2496 $this->LE 2497 ); 2498 } 2499 // RFC1341 part 5 says 7bit is assumed if not specified 2500 if ($encoding != '7bit') { 2501 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); 2502 } 2503 2504 if ($disposition == 'inline') { 2505 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); 2506 } 2507 2508 // If a filename contains any of these chars, it should be quoted, 2509 // but not otherwise: RFC2183 & RFC2045 5.1 2510 // Fixes a warning in IETF's msglint MIME checker 2511 // Allow for bypassing the Content-Disposition header totally 2512 if (!(empty($disposition))) { 2513 $encoded_name = $this->encodeHeader($this->secureHeader($name)); 2514 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { 2515 $mime[] = sprintf( 2516 'Content-Disposition: %s; filename="%s"%s', 2517 $disposition, 2518 $encoded_name, 2519 $this->LE . $this->LE 2520 ); 2521 } else { 2522 if (!empty($encoded_name)) { 2523 $mime[] = sprintf( 2524 'Content-Disposition: %s; filename=%s%s', 2525 $disposition, 2526 $encoded_name, 2527 $this->LE . $this->LE 2528 ); 2529 } else { 2530 $mime[] = sprintf( 2531 'Content-Disposition: %s%s', 2532 $disposition, 2533 $this->LE . $this->LE 2534 ); 2535 } 2536 } 2537 } else { 2538 $mime[] = $this->LE; 2539 } 2540 2541 // Encode as string attachment 2542 if ($bString) { 2543 $mime[] = $this->encodeString($string, $encoding); 2544 if ($this->isError()) { 2545 return ''; 2546 } 2547 $mime[] = $this->LE . $this->LE; 2548 } else { 2549 $mime[] = $this->encodeFile($path, $encoding); 2550 if ($this->isError()) { 2551 return ''; 2552 } 2553 $mime[] = $this->LE . $this->LE; 2554 } 2555 } 2556 } 2557 2558 $mime[] = sprintf('--%s--%s', $boundary, $this->LE); 2559 2560 return implode('', $mime); 2561 } 2562 2563 /** 2564 * Encode a file attachment in requested format. 2565 * Returns an empty string on failure. 2566 * @param string $path The full path to the file 2567 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2568 * @throws phpmailerException 2569 * @access protected 2570 * @return string 2571 */ 2572 protected function encodeFile($path, $encoding = 'base64') 2573 { 2574 try { 2575 if (!is_readable($path)) { 2576 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); 2577 } 2578 $magic_quotes = get_magic_quotes_runtime(); 2579 if ($magic_quotes) { 2580 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2581 set_magic_quotes_runtime(false); 2582 } else { 2583 //Doesn't exist in PHP 5.4, but we don't need to check because 2584 //get_magic_quotes_runtime always returns false in 5.4+ 2585 //so it will never get here 2586 ini_set('magic_quotes_runtime', false); 2587 } 2588 } 2589 $file_buffer = file_get_contents($path); 2590 $file_buffer = $this->encodeString($file_buffer, $encoding); 2591 if ($magic_quotes) { 2592 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 2593 set_magic_quotes_runtime($magic_quotes); 2594 } else { 2595 ini_set('magic_quotes_runtime', $magic_quotes); 2596 } 2597 } 2598 return $file_buffer; 2599 } catch (Exception $exc) { 2600 $this->setError($exc->getMessage()); 2601 return ''; 2602 } 2603 } 2604 2605 /** 2606 * Encode a string in requested format. 2607 * Returns an empty string on failure. 2608 * @param string $str The text to encode 2609 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 2610 * @access public 2611 * @return string 2612 */ 2613 public function encodeString($str, $encoding = 'base64') 2614 { 2615 $encoded = ''; 2616 switch (strtolower($encoding)) { 2617 case 'base64': 2618 $encoded = chunk_split(base64_encode($str), 76, $this->LE); 2619 break; 2620 case '7bit': 2621 case '8bit': 2622 $encoded = $this->fixEOL($str); 2623 // Make sure it ends with a line break 2624 if (substr($encoded, -(strlen($this->LE))) != $this->LE) { 2625 $encoded .= $this->LE; 2626 } 2627 break; 2628 case 'binary': 2629 $encoded = $str; 2630 break; 2631 case 'quoted-printable': 2632 $encoded = $this->encodeQP($str); 2633 break; 2634 default: 2635 $this->setError($this->lang('encoding') . $encoding); 2636 break; 2637 } 2638 return $encoded; 2639 } 2640 2641 /** 2642 * Encode a header string optimally. 2643 * Picks shortest of Q, B, quoted-printable or none. 2644 * @access public 2645 * @param string $str 2646 * @param string $position 2647 * @return string 2648 */ 2649 public function encodeHeader($str, $position = 'text') 2650 { 2651 $matchcount = 0; 2652 switch (strtolower($position)) { 2653 case 'phrase': 2654 if (!preg_match('/[\200-\377]/', $str)) { 2655 // Can't use addslashes as we don't know the value of magic_quotes_sybase 2656 $encoded = addcslashes($str, "\0..\37\177\\\""); 2657 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 2658 return ($encoded); 2659 } else { 2660 return ("\"$encoded\""); 2661 } 2662 } 2663 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 2664 break; 2665 /** @noinspection PhpMissingBreakStatementInspection */ 2666 case 'comment': 2667 $matchcount = preg_match_all('/[()"]/', $str, $matches); 2668 // Intentional fall-through 2669 case 'text': 2670 default: 2671 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 2672 break; 2673 } 2674 2675 //There are no chars that need encoding 2676 if ($matchcount == 0) { 2677 return ($str); 2678 } 2679 2680 $maxlen = 75 - 7 - strlen($this->CharSet); 2681 // Try to select the encoding which should produce the shortest output 2682 if ($matchcount > strlen($str) / 3) { 2683 // More than a third of the content will need encoding, so B encoding will be most efficient 2684 $encoding = 'B'; 2685 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { 2686 // Use a custom function which correctly encodes and wraps long 2687 // multibyte strings without breaking lines within a character 2688 $encoded = $this->base64EncodeWrapMB($str, "\n"); 2689 } else { 2690 $encoded = base64_encode($str); 2691 $maxlen -= $maxlen % 4; 2692 $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 2693 } 2694 } else { 2695 $encoding = 'Q'; 2696 $encoded = $this->encodeQ($str, $position); 2697 $encoded = $this->wrapText($encoded, $maxlen, true); 2698 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); 2699 } 2700 2701 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); 2702 $encoded = trim(str_replace("\n", $this->LE, $encoded)); 2703 2704 return $encoded; 2705 } 2706 2707 /** 2708 * Check if a string contains multi-byte characters. 2709 * @access public 2710 * @param string $str multi-byte text to wrap encode 2711 * @return boolean 2712 */ 2713 public function hasMultiBytes($str) 2714 { 2715 if (function_exists('mb_strlen')) { 2716 return (strlen($str) > mb_strlen($str, $this->CharSet)); 2717 } else { // Assume no multibytes (we can't handle without mbstring functions anyway) 2718 return false; 2719 } 2720 } 2721 2722 /** 2723 * Does a string contain any 8-bit chars (in any charset)? 2724 * @param string $text 2725 * @return boolean 2726 */ 2727 public function has8bitChars($text) 2728 { 2729 return (boolean)preg_match('/[\x80-\xFF]/', $text); 2730 } 2731 2732 /** 2733 * Encode and wrap long multibyte strings for mail headers 2734 * without breaking lines within a character. 2735 * Adapted from a function by paravoid 2736 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 2737 * @access public 2738 * @param string $str multi-byte text to wrap encode 2739 * @param string $linebreak string to use as linefeed/end-of-line 2740 * @return string 2741 */ 2742 public function base64EncodeWrapMB($str, $linebreak = null) 2743 { 2744 $start = '=?' . $this->CharSet . '?B?'; 2745 $end = '?='; 2746 $encoded = ''; 2747 if ($linebreak === null) { 2748 $linebreak = $this->LE; 2749 } 2750 2751 $mb_length = mb_strlen($str, $this->CharSet); 2752 // Each line must have length <= 75, including $start and $end 2753 $length = 75 - strlen($start) - strlen($end); 2754 // Average multi-byte ratio 2755 $ratio = $mb_length / strlen($str); 2756 // Base64 has a 4:3 ratio 2757 $avgLength = floor($length * $ratio * .75); 2758 2759 for ($i = 0; $i < $mb_length; $i += $offset) { 2760 $lookBack = 0; 2761 do { 2762 $offset = $avgLength - $lookBack; 2763 $chunk = mb_substr($str, $i, $offset, $this->CharSet); 2764 $chunk = base64_encode($chunk); 2765 $lookBack++; 2766 } while (strlen($chunk) > $length); 2767 $encoded .= $chunk . $linebreak; 2768 } 2769 2770 // Chomp the last linefeed 2771 $encoded = substr($encoded, 0, -strlen($linebreak)); 2772 return $encoded; 2773 } 2774 2775 /** 2776 * Encode a string in quoted-printable format. 2777 * According to RFC2045 section 6.7. 2778 * @access public 2779 * @param string $string The text to encode 2780 * @param integer $line_max Number of chars allowed on a line before wrapping 2781 * @return string 2782 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment 2783 */ 2784 public function encodeQP($string, $line_max = 76) 2785 { 2786 // Use native function if it's available (>= PHP5.3) 2787 if (function_exists('quoted_printable_encode')) { 2788 return quoted_printable_encode($string); 2789 } 2790 // Fall back to a pure PHP implementation 2791 $string = str_replace( 2792 array('%20', '%0D%0A.', '%0D%0A', '%'), 2793 array(' ', "\r\n=2E", "\r\n", '='), 2794 rawurlencode($string) 2795 ); 2796 return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); 2797 } 2798 2799 /** 2800 * Backward compatibility wrapper for an old QP encoding function that was removed. 2801 * @see PHPMailer::encodeQP() 2802 * @access public 2803 * @param string $string 2804 * @param integer $line_max 2805 * @param boolean $space_conv 2806 * @return string 2807 * @deprecated Use encodeQP instead. 2808 */ 2809 public function encodeQPphp( 2810 $string, 2811 $line_max = 76, 2812 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false 2813 ) { 2814 return $this->encodeQP($string, $line_max); 2815 } 2816 2817 /** 2818 * Encode a string using Q encoding. 2819 * @link http://tools.ietf.org/html/rfc2047 2820 * @param string $str the text to encode 2821 * @param string $position Where the text is going to be used, see the RFC for what that means 2822 * @access public 2823 * @return string 2824 */ 2825 public function encodeQ($str, $position = 'text') 2826 { 2827 // There should not be any EOL in the string 2828 $pattern = ''; 2829 $encoded = str_replace(array("\r", "\n"), '', $str); 2830 switch (strtolower($position)) { 2831 case 'phrase': 2832 // RFC 2047 section 5.3 2833 $pattern = '^A-Za-z0-9!*+\/ -'; 2834 break; 2835 /** @noinspection PhpMissingBreakStatementInspection */ 2836 case 'comment': 2837 // RFC 2047 section 5.2 2838 $pattern = '\(\)"'; 2839 // intentional fall-through 2840 // for this reason we build the $pattern without including delimiters and [] 2841 case 'text': 2842 default: 2843 // RFC 2047 section 5.1 2844 // Replace every high ascii, control, =, ? and _ characters 2845 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; 2846 break; 2847 } 2848 $matches = array(); 2849 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { 2850 // If the string contains an '=', make sure it's the first thing we replace 2851 // so as to avoid double-encoding 2852 $eqkey = array_search('=', $matches[0]); 2853 if (false !== $eqkey) { 2854 unset($matches[0][$eqkey]); 2855 array_unshift($matches[0], '='); 2856 } 2857 foreach (array_unique($matches[0]) as $char) { 2858 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); 2859 } 2860 } 2861 // Replace every spaces to _ (more readable than =20) 2862 return str_replace(' ', '_', $encoded); 2863 } 2864 2865 /** 2866 * Add a string or binary attachment (non-filesystem). 2867 * This method can be used to attach ascii or binary data, 2868 * such as a BLOB record from a database. 2869 * @param string $string String attachment data. 2870 * @param string $filename Name of the attachment. 2871 * @param string $encoding File encoding (see $Encoding). 2872 * @param string $type File extension (MIME) type. 2873 * @param string $disposition Disposition to use 2874 * @return void 2875 */ 2876 public function addStringAttachment( 2877 $string, 2878 $filename, 2879 $encoding = 'base64', 2880 $type = '', 2881 $disposition = 'attachment' 2882 ) { 2883 // If a MIME type is not specified, try to work it out from the file name 2884 if ($type == '') { 2885 $type = self::filenameToType($filename); 2886 } 2887 // Append to $attachment array 2888 $this->attachment[] = array( 2889 0 => $string, 2890 1 => $filename, 2891 2 => basename($filename), 2892 3 => $encoding, 2893 4 => $type, 2894 5 => true, // isStringAttachment 2895 6 => $disposition, 2896 7 => 0 2897 ); 2898 } 2899 2900 /** 2901 * Add an embedded (inline) attachment from a file. 2902 * This can include images, sounds, and just about any other document type. 2903 * These differ from 'regular' attachments in that they are intended to be 2904 * displayed inline with the message, not just attached for download. 2905 * This is used in HTML messages that embed the images 2906 * the HTML refers to using the $cid value. 2907 * @param string $path Path to the attachment. 2908 * @param string $cid Content ID of the attachment; Use this to reference 2909 * the content when using an embedded image in HTML. 2910 * @param string $name Overrides the attachment name. 2911 * @param string $encoding File encoding (see $Encoding). 2912 * @param string $type File MIME type. 2913 * @param string $disposition Disposition to use 2914 * @return boolean True on successfully adding an attachment 2915 */ 2916 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') 2917 { 2918 if (!@is_file($path)) { 2919 $this->setError($this->lang('file_access') . $path); 2920 return false; 2921 } 2922 2923 // If a MIME type is not specified, try to work it out from the file name 2924 if ($type == '') { 2925 $type = self::filenameToType($path); 2926 } 2927 2928 $filename = basename($path); 2929 if ($name == '') { 2930 $name = $filename; 2931 } 2932 2933 // Append to $attachment array 2934 $this->attachment[] = array( 2935 0 => $path, 2936 1 => $filename, 2937 2 => $name, 2938 3 => $encoding, 2939 4 => $type, 2940 5 => false, // isStringAttachment 2941 6 => $disposition, 2942 7 => $cid 2943 ); 2944 return true; 2945 } 2946 2947 /** 2948 * Add an embedded stringified attachment. 2949 * This can include images, sounds, and just about any other document type. 2950 * Be sure to set the $type to an image type for images: 2951 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. 2952 * @param string $string The attachment binary data. 2953 * @param string $cid Content ID of the attachment; Use this to reference 2954 * the content when using an embedded image in HTML. 2955 * @param string $name 2956 * @param string $encoding File encoding (see $Encoding). 2957 * @param string $type MIME type. 2958 * @param string $disposition Disposition to use 2959 * @return boolean True on successfully adding an attachment 2960 */ 2961 public function addStringEmbeddedImage( 2962 $string, 2963 $cid, 2964 $name = '', 2965 $encoding = 'base64', 2966 $type = '', 2967 $disposition = 'inline' 2968 ) { 2969 // If a MIME type is not specified, try to work it out from the name 2970 if ($type == '' and !empty($name)) { 2971 $type = self::filenameToType($name); 2972 } 2973 2974 // Append to $attachment array 2975 $this->attachment[] = array( 2976 0 => $string, 2977 1 => $name, 2978 2 => $name, 2979 3 => $encoding, 2980 4 => $type, 2981 5 => true, // isStringAttachment 2982 6 => $disposition, 2983 7 => $cid 2984 ); 2985 return true; 2986 } 2987 2988 /** 2989 * Check if an inline attachment is present. 2990 * @access public 2991 * @return boolean 2992 */ 2993 public function inlineImageExists() 2994 { 2995 foreach ($this->attachment as $attachment) { 2996 if ($attachment[6] == 'inline') { 2997 return true; 2998 } 2999 } 3000 return false; 3001 } 3002 3003 /** 3004 * Check if an attachment (non-inline) is present. 3005 * @return boolean 3006 */ 3007 public function attachmentExists() 3008 { 3009 foreach ($this->attachment as $attachment) { 3010 if ($attachment[6] == 'attachment') { 3011 return true; 3012 } 3013 } 3014 return false; 3015 } 3016 3017 /** 3018 * Check if this message has an alternative body set. 3019 * @return boolean 3020 */ 3021 public function alternativeExists() 3022 { 3023 return !empty($this->AltBody); 3024 } 3025 3026 /** 3027 * Clear queued addresses of given kind. 3028 * @access protected 3029 * @param string $kind 'to', 'cc', or 'bcc' 3030 * @return void 3031 */ 3032 public function clearQueuedAddresses($kind) 3033 { 3034 $RecipientsQueue = $this->RecipientsQueue; 3035 foreach ($RecipientsQueue as $address => $params) { 3036 if ($params[0] == $kind) { 3037 unset($this->RecipientsQueue[$address]); 3038 } 3039 } 3040 } 3041 3042 /** 3043 * Clear all To recipients. 3044 * @return void 3045 */ 3046 public function clearAddresses() 3047 { 3048 foreach ($this->to as $to) { 3049 unset($this->all_recipients[strtolower($to[0])]); 3050 } 3051 $this->to = array(); 3052 $this->clearQueuedAddresses('to'); 3053 } 3054 3055 /** 3056 * Clear all CC recipients. 3057 * @return void 3058 */ 3059 public function clearCCs() 3060 { 3061 foreach ($this->cc as $cc) { 3062 unset($this->all_recipients[strtolower($cc[0])]); 3063 } 3064 $this->cc = array(); 3065 $this->clearQueuedAddresses('cc'); 3066 } 3067 3068 /** 3069 * Clear all BCC recipients. 3070 * @return void 3071 */ 3072 public function clearBCCs() 3073 { 3074 foreach ($this->bcc as $bcc) { 3075 unset($this->all_recipients[strtolower($bcc[0])]); 3076 } 3077 $this->bcc = array(); 3078 $this->clearQueuedAddresses('bcc'); 3079 } 3080 3081 /** 3082 * Clear all ReplyTo recipients. 3083 * @return void 3084 */ 3085 public function clearReplyTos() 3086 { 3087 $this->ReplyTo = array(); 3088 $this->ReplyToQueue = array(); 3089 } 3090 3091 /** 3092 * Clear all recipient types. 3093 * @return void 3094 */ 3095 public function clearAllRecipients() 3096 { 3097 $this->to = array(); 3098 $this->cc = array(); 3099 $this->bcc = array(); 3100 $this->all_recipients = array(); 3101 $this->RecipientsQueue = array(); 3102 } 3103 3104 /** 3105 * Clear all filesystem, string, and binary attachments. 3106 * @return void 3107 */ 3108 public function clearAttachments() 3109 { 3110 $this->attachment = array(); 3111 } 3112 3113 /** 3114 * Clear all custom headers. 3115 * @return void 3116 */ 3117 public function clearCustomHeaders() 3118 { 3119 $this->CustomHeader = array(); 3120 } 3121 3122 /** 3123 * Add an error message to the error container. 3124 * @access protected 3125 * @param string $msg 3126 * @return void 3127 */ 3128 protected function setError($msg) 3129 { 3130 $this->error_count++; 3131 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { 3132 $lasterror = $this->smtp->getError(); 3133 if (!empty($lasterror['error'])) { 3134 $msg .= $this->lang('smtp_error') . $lasterror['error']; 3135 if (!empty($lasterror['detail'])) { 3136 $msg .= ' Detail: '. $lasterror['detail']; 3137 } 3138 if (!empty($lasterror['smtp_code'])) { 3139 $msg .= ' SMTP code: ' . $lasterror['smtp_code']; 3140 } 3141 if (!empty($lasterror['smtp_code_ex'])) { 3142 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; 3143 } 3144 } 3145 } 3146 $this->ErrorInfo = $msg; 3147 } 3148 3149 /** 3150 * Return an RFC 822 formatted date. 3151 * @access public 3152 * @return string 3153 * @static 3154 */ 3155 public static function rfcDate() 3156 { 3157 // Set the time zone to whatever the default is to avoid 500 errors 3158 // Will default to UTC if it's not set properly in php.ini 3159 date_default_timezone_set(@date_default_timezone_get()); 3160 return date('D, j M Y H:i:s O'); 3161 } 3162 3163 /** 3164 * Get the server hostname. 3165 * Returns 'localhost.localdomain' if unknown. 3166 * @access protected 3167 * @return string 3168 */ 3169 protected function serverHostname() 3170 { 3171 $result = 'localhost.localdomain'; 3172 if (!empty($this->Hostname)) { 3173 $result = $this->Hostname; 3174 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { 3175 $result = $_SERVER['SERVER_NAME']; 3176 } elseif (function_exists('gethostname') && gethostname() !== false) { 3177 $result = gethostname(); 3178 } elseif (php_uname('n') !== false) { 3179 $result = php_uname('n'); 3180 } 3181 return $result; 3182 } 3183 3184 /** 3185 * Get an error message in the current language. 3186 * @access protected 3187 * @param string $key 3188 * @return string 3189 */ 3190 protected function lang($key) 3191 { 3192 if (count($this->language) < 1) { 3193 $this->setLanguage('en'); // set the default language 3194 } 3195 3196 if (array_key_exists($key, $this->language)) { 3197 if ($key == 'smtp_connect_failed') { 3198 //Include a link to troubleshooting docs on SMTP connection failure 3199 //this is by far the biggest cause of support questions 3200 //but it's usually not PHPMailer's fault. 3201 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; 3202 } 3203 return $this->language[$key]; 3204 } else { 3205 //Return the key as a fallback 3206 return $key; 3207 } 3208 } 3209 3210 /** 3211 * Check if an error occurred. 3212 * @access public 3213 * @return boolean True if an error did occur. 3214 */ 3215 public function isError() 3216 { 3217 return ($this->error_count > 0); 3218 } 3219 3220 /** 3221 * Ensure consistent line endings in a string. 3222 * Changes every end of line from CRLF, CR or LF to $this->LE. 3223 * @access public 3224 * @param string $str String to fixEOL 3225 * @return string 3226 */ 3227 public function fixEOL($str) 3228 { 3229 // Normalise to \n 3230 $nstr = str_replace(array("\r\n", "\r"), "\n", $str); 3231 // Now convert LE as needed 3232 if ($this->LE !== "\n") { 3233 $nstr = str_replace("\n", $this->LE, $nstr); 3234 } 3235 return $nstr; 3236 } 3237 3238 /** 3239 * Add a custom header. 3240 * $name value can be overloaded to contain 3241 * both header name and value (name:value) 3242 * @access public 3243 * @param string $name Custom header name 3244 * @param string $value Header value 3245 * @return void 3246 */ 3247 public function addCustomHeader($name, $value = null) 3248 { 3249 if ($value === null) { 3250 // Value passed in as name:value 3251 $this->CustomHeader[] = explode(':', $name, 2); 3252 } else { 3253 $this->CustomHeader[] = array($name, $value); 3254 } 3255 } 3256 3257 /** 3258 * Returns all custom headers. 3259 * @return array 3260 */ 3261 public function getCustomHeaders() 3262 { 3263 return $this->CustomHeader; 3264 } 3265 3266 /** 3267 * Create a message from an HTML string. 3268 * Automatically makes modifications for inline images and backgrounds 3269 * and creates a plain-text version by converting the HTML. 3270 * Overwrites any existing values in $this->Body and $this->AltBody 3271 * @access public 3272 * @param string $message HTML message string 3273 * @param string $basedir baseline directory for path 3274 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3275 * or your own custom converter @see PHPMailer::html2text() 3276 * @return string $message 3277 */ 3278 public function msgHTML($message, $basedir = '', $advanced = false) 3279 { 3280 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); 3281 if (array_key_exists(2, $images)) { 3282 foreach ($images[2] as $imgindex => $url) { 3283 // Convert data URIs into embedded images 3284 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { 3285 $data = substr($url, strpos($url, ',')); 3286 if ($match[2]) { 3287 $data = base64_decode($data); 3288 } else { 3289 $data = rawurldecode($data); 3290 } 3291 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3292 if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { 3293 $message = str_replace( 3294 $images[0][$imgindex], 3295 $images[1][$imgindex] . '="cid:' . $cid . '"', 3296 $message 3297 ); 3298 } 3299 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) { 3300 // Do not change urls for absolute images (thanks to corvuscorax) 3301 // Do not change urls that are already inline images 3302 $filename = basename($url); 3303 $directory = dirname($url); 3304 if ($directory == '.') { 3305 $directory = ''; 3306 } 3307 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 3308 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { 3309 $basedir .= '/'; 3310 } 3311 if (strlen($directory) > 1 && substr($directory, -1) != '/') { 3312 $directory .= '/'; 3313 } 3314 if ($this->addEmbeddedImage( 3315 $basedir . $directory . $filename, 3316 $cid, 3317 $filename, 3318 'base64', 3319 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) 3320 ) 3321 ) { 3322 $message = preg_replace( 3323 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', 3324 $images[1][$imgindex] . '="cid:' . $cid . '"', 3325 $message 3326 ); 3327 } 3328 } 3329 } 3330 } 3331 $this->isHTML(true); 3332 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 3333 $this->Body = $this->normalizeBreaks($message); 3334 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 3335 if (empty($this->AltBody)) { 3336 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 3337 self::CRLF . self::CRLF; 3338 } 3339 return $this->Body; 3340 } 3341 3342 /** 3343 * Convert an HTML string into plain text. 3344 * This is used by msgHTML(). 3345 * Note - older versions of this function used a bundled advanced converter 3346 * which was been removed for license reasons in #232 3347 * Example usage: 3348 * <code> 3349 * // Use default conversion 3350 * $plain = $mail->html2text($html); 3351 * // Use your own custom converter 3352 * $plain = $mail->html2text($html, function($html) { 3353 * $converter = new MyHtml2text($html); 3354 * return $converter->get_text(); 3355 * }); 3356 * </code> 3357 * @param string $html The HTML text to convert 3358 * @param boolean|callable $advanced Any boolean value to use the internal converter, 3359 * or provide your own callable for custom conversion. 3360 * @return string 3361 */ 3362 public function html2text($html, $advanced = false) 3363 { 3364 if (is_callable($advanced)) { 3365 return call_user_func($advanced, $html); 3366 } 3367 return html_entity_decode( 3368 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), 3369 ENT_QUOTES, 3370 $this->CharSet 3371 ); 3372 } 3373 3374 /** 3375 * Get the MIME type for a file extension. 3376 * @param string $ext File extension 3377 * @access public 3378 * @return string MIME type of file. 3379 * @static 3380 */ 3381 public static function _mime_types($ext = '') 3382 { 3383 $mimes = array( 3384 'xl' => 'application/excel', 3385 'js' => 'application/javascript', 3386 'hqx' => 'application/mac-binhex40', 3387 'cpt' => 'application/mac-compactpro', 3388 'bin' => 'application/macbinary', 3389 'doc' => 'application/msword', 3390 'word' => 'application/msword', 3391 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 3392 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 3393 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 3394 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 3395 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 3396 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 3397 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 3398 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 3399 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 3400 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 3401 'class' => 'application/octet-stream', 3402 'dll' => 'application/octet-stream', 3403 'dms' => 'application/octet-stream', 3404 'exe' => 'application/octet-stream', 3405 'lha' => 'application/octet-stream', 3406 'lzh' => 'application/octet-stream', 3407 'psd' => 'application/octet-stream', 3408 'sea' => 'application/octet-stream', 3409 'so' => 'application/octet-stream', 3410 'oda' => 'application/oda', 3411 'pdf' => 'application/pdf', 3412 'ai' => 'application/postscript', 3413 'eps' => 'application/postscript', 3414 'ps' => 'application/postscript', 3415 'smi' => 'application/smil', 3416 'smil' => 'application/smil', 3417 'mif' => 'application/vnd.mif', 3418 'xls' => 'application/vnd.ms-excel', 3419 'ppt' => 'application/vnd.ms-powerpoint', 3420 'wbxml' => 'application/vnd.wap.wbxml', 3421 'wmlc' => 'application/vnd.wap.wmlc', 3422 'dcr' => 'application/x-director', 3423 'dir' => 'application/x-director', 3424 'dxr' => 'application/x-director', 3425 'dvi' => 'application/x-dvi', 3426 'gtar' => 'application/x-gtar', 3427 'php3' => 'application/x-httpd-php', 3428 'php4' => 'application/x-httpd-php', 3429 'php' => 'application/x-httpd-php', 3430 'phtml' => 'application/x-httpd-php', 3431 'phps' => 'application/x-httpd-php-source', 3432 'swf' => 'application/x-shockwave-flash', 3433 'sit' => 'application/x-stuffit', 3434 'tar' => 'application/x-tar', 3435 'tgz' => 'application/x-tar', 3436 'xht' => 'application/xhtml+xml', 3437 'xhtml' => 'application/xhtml+xml', 3438 'zip' => 'application/zip', 3439 'mid' => 'audio/midi', 3440 'midi' => 'audio/midi', 3441 'mp2' => 'audio/mpeg', 3442 'mp3' => 'audio/mpeg', 3443 'mpga' => 'audio/mpeg', 3444 'aif' => 'audio/x-aiff', 3445 'aifc' => 'audio/x-aiff', 3446 'aiff' => 'audio/x-aiff', 3447 'ram' => 'audio/x-pn-realaudio', 3448 'rm' => 'audio/x-pn-realaudio', 3449 'rpm' => 'audio/x-pn-realaudio-plugin', 3450 'ra' => 'audio/x-realaudio', 3451 'wav' => 'audio/x-wav', 3452 'bmp' => 'image/bmp', 3453 'gif' => 'image/gif', 3454 'jpeg' => 'image/jpeg', 3455 'jpe' => 'image/jpeg', 3456 'jpg' => 'image/jpeg', 3457 'png' => 'image/png', 3458 'tiff' => 'image/tiff', 3459 'tif' => 'image/tiff', 3460 'eml' => 'message/rfc822', 3461 'css' => 'text/css', 3462 'html' => 'text/html', 3463 'htm' => 'text/html', 3464 'shtml' => 'text/html', 3465 'log' => 'text/plain', 3466 'text' => 'text/plain', 3467 'txt' => 'text/plain', 3468 'rtx' => 'text/richtext', 3469 'rtf' => 'text/rtf', 3470 'vcf' => 'text/vcard', 3471 'vcard' => 'text/vcard', 3472 'xml' => 'text/xml', 3473 'xsl' => 'text/xml', 3474 'mpeg' => 'video/mpeg', 3475 'mpe' => 'video/mpeg', 3476 'mpg' => 'video/mpeg', 3477 'mov' => 'video/quicktime', 3478 'qt' => 'video/quicktime', 3479 'rv' => 'video/vnd.rn-realvideo', 3480 'avi' => 'video/x-msvideo', 3481 'movie' => 'video/x-sgi-movie' 3482 ); 3483 if (array_key_exists(strtolower($ext), $mimes)) { 3484 return $mimes[strtolower($ext)]; 3485 } 3486 return 'application/octet-stream'; 3487 } 3488 3489 /** 3490 * Map a file name to a MIME type. 3491 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. 3492 * @param string $filename A file name or full path, does not need to exist as a file 3493 * @return string 3494 * @static 3495 */ 3496 public static function filenameToType($filename) 3497 { 3498 // In case the path is a URL, strip any query string before getting extension 3499 $qpos = strpos($filename, '?'); 3500 if (false !== $qpos) { 3501 $filename = substr($filename, 0, $qpos); 3502 } 3503 $pathinfo = self::mb_pathinfo($filename); 3504 return self::_mime_types($pathinfo['extension']); 3505 } 3506 3507 /** 3508 * Multi-byte-safe pathinfo replacement. 3509 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. 3510 * Works similarly to the one in PHP >= 5.2.0 3511 * @link http://www.php.net/manual/en/function.pathinfo.php#107461 3512 * @param string $path A filename or path, does not need to exist as a file 3513 * @param integer|string $options Either a PATHINFO_* constant, 3514 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 3515 * @return string|array 3516 * @static 3517 */ 3518 public static function mb_pathinfo($path, $options = null) 3519 { 3520 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); 3521 $pathinfo = array(); 3522 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { 3523 if (array_key_exists(1, $pathinfo)) { 3524 $ret['dirname'] = $pathinfo[1]; 3525 } 3526 if (array_key_exists(2, $pathinfo)) { 3527 $ret['basename'] = $pathinfo[2]; 3528 } 3529 if (array_key_exists(5, $pathinfo)) { 3530 $ret['extension'] = $pathinfo[5]; 3531 } 3532 if (array_key_exists(3, $pathinfo)) { 3533 $ret['filename'] = $pathinfo[3]; 3534 } 3535 } 3536 switch ($options) { 3537 case PATHINFO_DIRNAME: 3538 case 'dirname': 3539 return $ret['dirname']; 3540 case PATHINFO_BASENAME: 3541 case 'basename': 3542 return $ret['basename']; 3543 case PATHINFO_EXTENSION: 3544 case 'extension': 3545 return $ret['extension']; 3546 case PATHINFO_FILENAME: 3547 case 'filename': 3548 return $ret['filename']; 3549 default: 3550 return $ret; 3551 } 3552 } 3553 3554 /** 3555 * Set or reset instance properties. 3556 * You should avoid this function - it's more verbose, less efficient, more error-prone and 3557 * harder to debug than setting properties directly. 3558 * Usage Example: 3559 * `$mail->set('SMTPSecure', 'tls');` 3560 * is the same as: 3561 * `$mail->SMTPSecure = 'tls';` 3562 * @access public 3563 * @param string $name The property name to set 3564 * @param mixed $value The value to set the property to 3565 * @return boolean 3566 * @TODO Should this not be using the __set() magic function? 3567 */ 3568 public function set($name, $value = '') 3569 { 3570 if (property_exists($this, $name)) { 3571 $this->$name = $value; 3572 return true; 3573 } else { 3574 $this->setError($this->lang('variable_set') . $name); 3575 return false; 3576 } 3577 } 3578 3579 /** 3580 * Strip newlines to prevent header injection. 3581 * @access public 3582 * @param string $str 3583 * @return string 3584 */ 3585 public function secureHeader($str) 3586 { 3587 return trim(str_replace(array("\r", "\n"), '', $str)); 3588 } 3589 3590 /** 3591 * Normalize line breaks in a string. 3592 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. 3593 * Defaults to CRLF (for message bodies) and preserves consecutive breaks. 3594 * @param string $text 3595 * @param string $breaktype What kind of line break to use, defaults to CRLF 3596 * @return string 3597 * @access public 3598 * @static 3599 */ 3600 public static function normalizeBreaks($text, $breaktype = "\r\n") 3601 { 3602 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); 3603 } 3604 3605 /** 3606 * Set the public and private key files and password for S/MIME signing. 3607 * @access public 3608 * @param string $cert_filename 3609 * @param string $key_filename 3610 * @param string $key_pass Password for private key 3611 * @param string $extracerts_filename Optional path to chain certificate 3612 */ 3613 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') 3614 { 3615 $this->sign_cert_file = $cert_filename; 3616 $this->sign_key_file = $key_filename; 3617 $this->sign_key_pass = $key_pass; 3618 $this->sign_extracerts_file = $extracerts_filename; 3619 } 3620 3621 /** 3622 * Quoted-Printable-encode a DKIM header. 3623 * @access public 3624 * @param string $txt 3625 * @return string 3626 */ 3627 public function DKIM_QP($txt) 3628 { 3629 $line = ''; 3630 for ($i = 0; $i < strlen($txt); $i++) { 3631 $ord = ord($txt[$i]); 3632 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { 3633 $line .= $txt[$i]; 3634 } else { 3635 $line .= '=' . sprintf('%02X', $ord); 3636 } 3637 } 3638 return $line; 3639 } 3640 3641 /** 3642 * Generate a DKIM signature. 3643 * @access public 3644 * @param string $signHeader 3645 * @throws phpmailerException 3646 * @return string 3647 */ 3648 public function DKIM_Sign($signHeader) 3649 { 3650 if (!defined('PKCS7_TEXT')) { 3651 if ($this->exceptions) { 3652 throw new phpmailerException($this->lang('extension_missing') . 'openssl'); 3653 } 3654 return ''; 3655 } 3656 $privKeyStr = file_get_contents($this->DKIM_private); 3657 if ($this->DKIM_passphrase != '') { 3658 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3659 } else { 3660 $privKey = $privKeyStr; 3661 } 3662 if (openssl_sign($signHeader, $signature, $privKey)) { 3663 return base64_encode($signature); 3664 } 3665 return ''; 3666 } 3667 3668 /** 3669 * Generate a DKIM canonicalization header. 3670 * @access public 3671 * @param string $signHeader Header 3672 * @return string 3673 */ 3674 public function DKIM_HeaderC($signHeader) 3675 { 3676 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); 3677 $lines = explode("\r\n", $signHeader); 3678 foreach ($lines as $key => $line) { 3679 list($heading, $value) = explode(':', $line, 2); 3680 $heading = strtolower($heading); 3681 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces 3682 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 3683 } 3684 $signHeader = implode("\r\n", $lines); 3685 return $signHeader; 3686 } 3687 3688 /** 3689 * Generate a DKIM canonicalization body. 3690 * @access public 3691 * @param string $body Message Body 3692 * @return string 3693 */ 3694 public function DKIM_BodyC($body) 3695 { 3696 if ($body == '') { 3697 return "\r\n"; 3698 } 3699 // stabilize line endings 3700 $body = str_replace("\r\n", "\n", $body); 3701 $body = str_replace("\n", "\r\n", $body); 3702 // END stabilize line endings 3703 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { 3704 $body = substr($body, 0, strlen($body) - 2); 3705 } 3706 return $body; 3707 } 3708 3709 /** 3710 * Create the DKIM header and body in a new message header. 3711 * @access public 3712 * @param string $headers_line Header lines 3713 * @param string $subject Subject 3714 * @param string $body Body 3715 * @return string 3716 */ 3717 public function DKIM_Add($headers_line, $subject, $body) 3718 { 3719 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 3720 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 3721 $DKIMquery = 'dns/txt'; // Query method 3722 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 3723 $subject_header = "Subject: $subject"; 3724 $headers = explode($this->LE, $headers_line); 3725 $from_header = ''; 3726 $to_header = ''; 3727 $current = ''; 3728 foreach ($headers as $header) { 3729 if (strpos($header, 'From:') === 0) { 3730 $from_header = $header; 3731 $current = 'from_header'; 3732 } elseif (strpos($header, 'To:') === 0) { 3733 $to_header = $header; 3734 $current = 'to_header'; 3735 } else { 3736 if (!empty($$current) && strpos($header, ' =?') === 0) { 3737 $$current .= $header; 3738 } else { 3739 $current = ''; 3740 } 3741 } 3742 } 3743 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3744 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3745 $subject = str_replace( 3746 '|', 3747 '=7C', 3748 $this->DKIM_QP($subject_header) 3749 ); // Copied header fields (dkim-quoted-printable) 3750 $body = $this->DKIM_BodyC($body); 3751 $DKIMlen = strlen($body); // Length of body 3752 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body 3753 if ('' == $this->DKIM_identity) { 3754 $ident = ''; 3755 } else { 3756 $ident = ' i=' . $this->DKIM_identity . ';'; 3757 } 3758 $dkimhdrs = 'DKIM-Signature: v=1; a=' . 3759 $DKIMsignatureType . '; q=' . 3760 $DKIMquery . '; l=' . 3761 $DKIMlen . '; s=' . 3762 $this->DKIM_selector . 3763 ";\r\n" . 3764 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3765 "\th=From:To:Subject;\r\n" . 3766 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3767 "\tz=$from\r\n" . 3768 "\t|$to\r\n" . 3769 "\t|$subject;\r\n" . 3770 "\tbh=" . $DKIMb64 . ";\r\n" . 3771 "\tb="; 3772 $toSign = $this->DKIM_HeaderC( 3773 $from_header . "\r\n" . 3774 $to_header . "\r\n" . 3775 $subject_header . "\r\n" . 3776 $dkimhdrs 3777 ); 3778 $signed = $this->DKIM_Sign($toSign); 3779 return $dkimhdrs . $signed . "\r\n"; 3780 } 3781 3782 /** 3783 * Detect if a string contains a line longer than the maximum line length allowed. 3784 * @param string $str 3785 * @return boolean 3786 * @static 3787 */ 3788 public static function hasLineLongerThanMax($str) 3789 { 3790 //+2 to include CRLF line break for a 1000 total 3791 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); 3792 } 3793 3794 /** 3795 * Allows for public read access to 'to' property. 3796 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. 3797 * @access public 3798 * @return array 3799 */ 3800 public function getToAddresses() 3801 { 3802 return $this->to; 3803 } 3804 3805 /** 3806 * Allows for public read access to 'cc' property. 3807 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. 3808 * @access public 3809 * @return array 3810 */ 3811 public function getCcAddresses() 3812 { 3813 return $this->cc; 3814 } 3815 3816 /** 3817 * Allows for public read access to 'bcc' property. 3818 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. 3819 * @access public 3820 * @return array 3821 */ 3822 public function getBccAddresses() 3823 { 3824 return $this->bcc; 3825 } 3826 3827 /** 3828 * Allows for public read access to 'ReplyTo' property. 3829 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. 3830 * @access public 3831 * @return array 3832 */ 3833 public function getReplyToAddresses() 3834 { 3835 return $this->ReplyTo; 3836 } 3837 3838 /** 3839 * Allows for public read access to 'all_recipients' property. 3840 * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. 3841 * @access public 3842 * @return array 3843 */ 3844 public function getAllRecipientAddresses() 3845 { 3846 return $this->all_recipients; 3847 } 3848 3849 /** 3850 * Perform a callback. 3851 * @param boolean $isSent 3852 * @param array $to 3853 * @param array $cc 3854 * @param array $bcc 3855 * @param string $subject 3856 * @param string $body 3857 * @param string $from 3858 */ 3859 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) 3860 { 3861 if (!empty($this->action_function) && is_callable($this->action_function)) { 3862 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); 3863 call_user_func_array($this->action_function, $params); 3864 } 3865 } 3866 } 3867 3868 /** 3869 * PHPMailer exception handler 3870 * @package PHPMailer 3871 */ 3872 class phpmailerException extends Exception 3873 { 3874 /** 3875 * Prettify error message output 3876 * @return string 3877 */ 3878 public function errorMessage() 3879 { 3880 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; 3881 return $errorMsg; 3882 } 3883 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |