[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/phpmailer/ -> class.phpmailer.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1