[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7