source-class-PHPMailer

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.
Overview

Classes

Interfaces

Exceptions

Functions

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

© 2004-2018 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy