[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/modules/Emails/ -> class.phpmailer.php (source)

   1  <?php
   2  /*~ class.phpmailer.php
   3  .---------------------------------------------------------------------------.
   4  |  Software: PHPMailer - PHP email class                                    |
   5  |   Version: 5.2.6                                                          |
   6  |      Site: https://github.com/PHPMailer/PHPMailer/                        |
   7  | ------------------------------------------------------------------------- |
   8  |    Admins: Marcus Bointon                                                 |
   9  |    Admins: Jim Jagielski                                                  |
  10  |   Authors: Andy Prevost (codeworxtech) [email protected] |
  11  |          : Marcus Bointon (coolbru) [email protected]          |
  12  |          : Jim Jagielski (jimjag) [email protected]                        |
  13  |   Founder: Brent R. Matzelle (original founder)                           |
  14  | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
  15  | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
  16  | Copyright (c) 2001-2003, Brent R. Matzelle                                |
  17  | ------------------------------------------------------------------------- |
  18  |   License: Distributed under the Lesser General Public License (LGPL)     |
  19  |            http://www.gnu.org/copyleft/lesser.html                        |
  20  | This program is distributed in the hope that it will be useful - WITHOUT  |
  21  | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
  22  | FITNESS FOR A PARTICULAR PURPOSE.                                         |
  23  '---------------------------------------------------------------------------'
  24  */
  25  
  26  /**
  27   * PHPMailer - PHP email creation and transport class
  28   * NOTE: Requires PHP version 5 or later
  29   * @package PHPMailer
  30   * @author Andy Prevost
  31   * @author Marcus Bointon
  32   * @author Jim Jagielski
  33   * @copyright 2010 - 2012 Jim Jagielski
  34   * @copyright 2004 - 2009 Andy Prevost
  35   * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  36   */
  37  
  38  if (version_compare(PHP_VERSION, '5.0.0', '<') ) {
  39    exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
  40  }
  41  
  42  /**
  43   * PHP email creation and transport class
  44   * @package PHPMailer
  45   */
  46  class PHPMailer {
  47  
  48    /////////////////////////////////////////////////
  49    // PROPERTIES, PUBLIC
  50    /////////////////////////////////////////////////
  51  
  52    /**
  53     * Email priority (1 = High, 3 = Normal, 5 = low).
  54     * @var int
  55     */
  56    public $Priority          = 3;
  57  
  58    /**
  59     * Sets the CharSet of the message.
  60     * @var string
  61     */
  62    public $CharSet           = 'UTF-8';
  63  
  64    /**
  65     * Sets the Content-type of the message.
  66     * @var string
  67     */
  68    public $ContentType       = 'text/plain';
  69  
  70    /**
  71     * Sets the Encoding of the message. Options for this are
  72     *  "8bit", "7bit", "binary", "base64", and "quoted-printable".
  73     * @var string
  74     */
  75    public $Encoding          = '8bit';
  76  
  77    /**
  78     * Holds the most recent mailer error message.
  79     * @var string
  80     */
  81    public $ErrorInfo         = '';
  82  
  83    /**
  84     * Sets the From email address for the message.
  85     * @var string
  86     */
  87    public $From              = 'root@localhost';
  88  
  89    /**
  90     * Sets the From name of the message.
  91     * @var string
  92     */
  93    public $FromName          = 'Root User';
  94  
  95    /**
  96     * Sets the Sender email (Return-Path) of the message.
  97     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
  98     * @var string
  99     */
 100    public $Sender            = '';
 101  
 102    /**
 103     * Sets the Return-Path of the message.  If empty, it will
 104     * be set to either From or Sender.
 105     * @var string
 106     */
 107    public $ReturnPath        = '';
 108  
 109    /**
 110     * Sets the Subject of the message.
 111     * @var string
 112     */
 113    public $Subject           = '';
 114  
 115    /**
 116     * An HTML or plain text message body.
 117     * If HTML then call IsHTML(true).
 118     * @var string
 119     */
 120    public $Body              = '';
 121  
 122    /**
 123     * The plain-text message body.
 124     * This body can be read by mail clients that do not have HTML email
 125     * capability such as mutt & Eudora.
 126     * Clients that can read HTML will view the normal Body.
 127     * @var string
 128     */
 129    public $AltBody           = '';
 130  
 131    /**
 132     * An iCal message part body
 133     * Only supported in simple alt or alt_inline message types
 134     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
 135     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
 136     * @link http://kigkonsult.se/iCalcreator/
 137     * @var string
 138     */
 139    public $Ical              = '';
 140  
 141    /**
 142     * Stores the complete compiled MIME message body.
 143     * @var string
 144     * @access protected
 145     */
 146    protected $MIMEBody       = '';
 147  
 148    /**
 149     * Stores the complete compiled MIME message headers.
 150     * @var string
 151     * @access protected
 152     */
 153    protected $MIMEHeader     = '';
 154  
 155    /**
 156     * Stores the extra header list which CreateHeader() doesn't fold in
 157     * @var string
 158     * @access protected
 159     */
 160    protected $mailHeader     = '';
 161  
 162    /**
 163     * Sets word wrapping on the body of the message to a given number of
 164     * characters.
 165     * @var int
 166     */
 167    public $WordWrap          = 0;
 168  
 169    /**
 170     * Method to send mail: ("mail", "sendmail", or "smtp").
 171     * @var string
 172     */
 173    public $Mailer            = 'mail';
 174  
 175    /**
 176     * Sets the path of the sendmail program.
 177     * @var string
 178     */
 179    public $Sendmail          = '/usr/sbin/sendmail';
 180  
 181    /**
 182     * Determine if mail() uses a fully sendmail compatible MTA that
 183     * supports sendmail's "-oi -f" options
 184     * @var boolean
 185     */
 186    public $UseSendmailOptions    = true;
 187  
 188    /**
 189     * Path to PHPMailer plugins.  Useful if the SMTP class
 190     * is in a different directory than the PHP include path.
 191     * @var string
 192     */
 193    public $PluginDir         = '';
 194  
 195    /**
 196     * Sets the email address that a reading confirmation will be sent.
 197     * @var string
 198     */
 199    public $ConfirmReadingTo  = '';
 200  
 201    /**
 202     * Sets the hostname to use in Message-Id and Received headers
 203     * and as default HELO string. If empty, the value returned
 204     * by SERVER_NAME is used or 'localhost.localdomain'.
 205     * @var string
 206     */
 207    public $Hostname          = '';
 208  
 209    /**
 210     * Sets the message ID to be used in the Message-Id header.
 211     * If empty, a unique id will be generated.
 212     * @var string
 213     */
 214    public $MessageID         = '';
 215  
 216    /**
 217     * Sets the message Date to be used in the Date header.
 218     * If empty, the current date will be added.
 219     * @var string
 220     */
 221    public $MessageDate       = '';
 222  
 223    /////////////////////////////////////////////////
 224    // PROPERTIES FOR SMTP
 225    /////////////////////////////////////////////////
 226  
 227    /**
 228     * Sets the SMTP hosts.
 229     *
 230     * All hosts must be separated by a
 231     * semicolon.  You can also specify a different port
 232     * for each host by using this format: [hostname:port]
 233     * (e.g. "smtp1.example.com:25;smtp2.example.com").
 234     * Hosts will be tried in order.
 235     * @var string
 236     */
 237    public $Host          = 'localhost';
 238  
 239    /**
 240     * Sets the default SMTP server port.
 241     * @var int
 242     */
 243    public $Port          = 25;
 244  
 245    /**
 246     * Sets the SMTP HELO of the message (Default is $Hostname).
 247     * @var string
 248     */
 249    public $Helo          = '';
 250  
 251    /**
 252     * Sets connection prefix. Options are "", "ssl" or "tls"
 253     * @var string
 254     */
 255    public $SMTPSecure    = '';
 256  
 257    /**
 258     * Sets SMTP authentication. Utilizes the Username and Password variables.
 259     * @var bool
 260     */
 261    public $SMTPAuth      = false;
 262  
 263    /**
 264     * Sets SMTP username.
 265     * @var string
 266     */
 267    public $Username      = '';
 268  
 269    /**
 270     * Sets SMTP password.
 271     * @var string
 272     */
 273    public $Password      = '';
 274  
 275    /**
 276     *  Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM | CRAM-MD5 (default LOGIN)
 277     *  @var string
 278     */
 279    public $AuthType      = '';
 280  
 281    /**
 282     *  Sets SMTP realm.
 283     *  @var string
 284     */
 285    public $Realm         = '';
 286  
 287    /**
 288     *  Sets SMTP workstation.
 289     *  @var string
 290     */
 291    public $Workstation   = '';
 292  
 293    /**
 294     * Sets the SMTP server timeout in seconds.
 295     * This function will not work with the win32 version.
 296     * @var int
 297     */
 298    public $Timeout       = 10;
 299  
 300    /**
 301     * Sets SMTP class debugging on or off.
 302     * @var bool
 303     */
 304    public $SMTPDebug     = false;
 305  
 306    /**
 307     * Sets the function/method to use for debugging output.
 308     * Right now we only honor "echo" or "error_log"
 309     * @var string
 310     */
 311    public $Debugoutput     = "echo";
 312  
 313    /**
 314     * Prevents the SMTP connection from being closed after each mail
 315     * sending.  If this is set to true then to close the connection
 316     * requires an explicit call to SmtpClose().
 317     * @var bool
 318     */
 319    public $SMTPKeepAlive = false;
 320  
 321    /**
 322     * Provides the ability to have the TO field process individual
 323     * emails, instead of sending to entire TO addresses
 324     * @var bool
 325     */
 326    public $SingleTo      = false;
 327  
 328    /**
 329     * Should we generate VERP addresses when sending via SMTP?
 330     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
 331     * @var bool
 332     */
 333    public $do_verp      = false;
 334  
 335    /**
 336     * If SingleTo is true, this provides the array to hold the email addresses
 337     * @var bool
 338     */
 339    public $SingleToArray = array();
 340  
 341    /**
 342     * Should we allow sending messages with empty body?
 343     * @var bool
 344     */
 345    public $AllowEmpty = false;
 346  
 347      /**
 348     * Provides the ability to change the generic line ending
 349     * NOTE: The default remains '\n'. We force CRLF where we KNOW
 350     *        it must be used via self::CRLF
 351     * @var string
 352     */
 353    public $LE              = "\n";
 354  
 355     /**
 356     * Used with DKIM Signing
 357     * required parameter if DKIM is enabled
 358     *
 359     * domain selector example domainkey
 360     * @var string
 361     */
 362    public $DKIM_selector   = '';
 363  
 364    /**
 365     * Used with DKIM Signing
 366     * required if DKIM is enabled, in format of email address '[email protected]' typically used as the source of the email
 367     * @var string
 368     */
 369    public $DKIM_identity   = '';
 370  
 371    /**
 372     * Used with DKIM Signing
 373     * optional parameter if your private key requires a passphras
 374     * @var string
 375     */
 376    public $DKIM_passphrase   = '';
 377  
 378    /**
 379     * Used with DKIM Singing
 380     * required if DKIM is enabled, in format of email address 'domain.com'
 381     * @var string
 382     */
 383    public $DKIM_domain     = '';
 384  
 385    /**
 386     * Used with DKIM Signing
 387     * required if DKIM is enabled, path to private key file
 388     * @var string
 389     */
 390    public $DKIM_private    = '';
 391  
 392    /**
 393     * Callback Action function name.
 394     * The function that handles the result of the send email action.
 395     * It is called out by Send() for each email sent.
 396     *
 397     * Value can be:
 398     * - 'function_name' for function names
 399     * - 'Class::Method' for static method calls
 400     * - array($object, 'Method') for calling methods on $object
 401     * See http://php.net/is_callable manual page for more details.
 402     *
 403     * Parameters:
 404     *   bool    $result        result of the send action
 405     *   string  $to            email address of the recipient
 406     *   string  $cc            cc email addresses
 407     *   string  $bcc           bcc email addresses
 408     *   string  $subject       the subject
 409     *   string  $body          the email body
 410     *   string  $from          email address of sender
 411     * @var string
 412     */
 413    public $action_function = ''; //'callbackAction';
 414  
 415    /**
 416     * Sets the PHPMailer Version number
 417     * @var string
 418     */
 419    public $Version         = '5.2.6';
 420  
 421    /**
 422     * What to use in the X-Mailer header
 423     * @var string NULL for default, whitespace for None, or actual string to use
 424     */
 425    public $XMailer         = '';
 426  
 427    /////////////////////////////////////////////////
 428    // PROPERTIES, PRIVATE AND PROTECTED
 429    /////////////////////////////////////////////////
 430  
 431    /**
 432     * @var SMTP An instance of the SMTP sender class
 433     * @access protected
 434     */
 435    public   $smtp           = null;
 436    /**
 437     * @var array An array of 'to' addresses
 438     * @access protected
 439     */
 440    public   $to             = array();
 441    /**
 442     * @var array An array of 'cc' addresses
 443     * @access protected
 444     */
 445    public   $cc             = array();
 446    /**
 447     * @var array An array of 'bcc' addresses
 448     * @access protected
 449     */
 450    public   $bcc            = array();
 451    /**
 452     * @var array An array of reply-to name and address
 453     * @access protected
 454     */
 455    public   $ReplyTo        = array();
 456    /**
 457     * @var array An array of all kinds of addresses: to, cc, bcc, replyto
 458     * @access protected
 459     */
 460    protected   $all_recipients = array();
 461    /**
 462     * @var array An array of attachments
 463     * @access protected
 464     */
 465    public   $attachment     = array();
 466    /**
 467     * @var array An array of custom headers
 468     * @access protected
 469     */
 470    protected   $CustomHeader   = array();
 471    /**
 472     * @var string The message's MIME type
 473     * @access protected
 474     */
 475    protected   $message_type   = '';
 476    /**
 477     * @var array An array of MIME boundary strings
 478     * @access protected
 479     */
 480    protected   $boundary       = array();
 481    /**
 482     * @var array An array of available languages
 483     * @access protected
 484     */
 485    public   $language       = array();
 486    /**
 487     * @var integer The number of errors encountered
 488     * @access protected
 489     */
 490    protected   $error_count    = 0;
 491    /**
 492     * @var string The filename of a DKIM certificate file
 493     * @access protected
 494     */
 495    protected   $sign_cert_file = '';
 496    /**
 497     * @var string The filename of a DKIM key file
 498     * @access protected
 499     */
 500    protected   $sign_key_file  = '';
 501    /**
 502     * @var string The password of a DKIM key
 503     * @access protected
 504     */
 505    protected   $sign_key_pass  = '';
 506    /**
 507     * @var boolean Whether to throw exceptions for errors
 508     * @access protected
 509     */
 510    protected   $exceptions     = false;
 511  
 512    /////////////////////////////////////////////////
 513    // CONSTANTS
 514    /////////////////////////////////////////////////
 515  
 516    const STOP_MESSAGE  = 0; // message only, continue processing
 517    const STOP_CONTINUE = 1; // message?, likely ok to continue processing
 518    const STOP_CRITICAL = 2; // message, plus full stop, critical error reached
 519    const CRLF = "\r\n";     // SMTP RFC specified EOL
 520  
 521    /////////////////////////////////////////////////
 522    // METHODS, VARIABLES
 523    /////////////////////////////////////////////////
 524  
 525    /**
 526     * Calls actual mail() function, but in a safe_mode aware fashion
 527     * Also, unless sendmail_path points to sendmail (or something that
 528     * claims to be sendmail), don't pass params (not a perfect fix,
 529     * but it will do)
 530     * @param string $to To
 531     * @param string $subject Subject
 532     * @param string $body Message Body
 533     * @param string $header Additional Header(s)
 534     * @param string $params Params
 535     * @access private
 536     * @return bool
 537     */
 538    private function mail_passthru($to, $subject, $body, $header, $params) {
 539      if ( ini_get('safe_mode') || !($this->UseSendmailOptions) ) {
 540          $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header);
 541      } else {
 542          $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params);
 543      }
 544      return $rt;
 545    }
 546  
 547    /**
 548     * Outputs debugging info via user-defined method
 549     * @param string $str
 550     */
 551    protected function edebug($str) {
 552      switch ($this->Debugoutput) {
 553        case 'error_log':
 554          error_log($str);
 555          break;
 556        case 'html':
 557          //Cleans up output a bit for a better looking display that's HTML-safe
 558          echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."<br>\n";
 559          break;
 560        case 'echo':
 561        default:
 562          //Just echoes exactly what was received
 563          echo $str;
 564      }
 565    }
 566  
 567    /**
 568     * Constructor
 569     * @param boolean $exceptions Should we throw external exceptions?
 570     */
 571    public function __construct($exceptions = false) {
 572      $this->exceptions = ($exceptions == true);
 573    }
 574  
 575    /**
 576     * Destructor
 577     */
 578    public function __destruct() {
 579        if ($this->Mailer == 'smtp') { //Close any open SMTP connection nicely
 580            $this->SmtpClose();
 581        }
 582    }
 583  
 584    /**
 585     * Sets message type to HTML.
 586     * @param bool $ishtml
 587     * @return void
 588     */
 589    public function IsHTML($ishtml = true) {
 590      if ($ishtml) {
 591        $this->ContentType = 'text/html';
 592      } else {
 593        $this->ContentType = 'text/plain';
 594      }
 595    }
 596  
 597    /**
 598     * Sets Mailer to send message using SMTP.
 599     * @return void
 600     */
 601    public function IsSMTP() {
 602      $this->Mailer = 'smtp';
 603    }
 604  
 605    /**
 606     * Sets Mailer to send message using PHP mail() function.
 607     * @return void
 608     */
 609    public function IsMail() {
 610      $this->Mailer = 'mail';
 611    }
 612  
 613    /**
 614     * Sets Mailer to send message using the $Sendmail program.
 615     * @return void
 616     */
 617    public function IsSendmail() {
 618      if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
 619        $this->Sendmail = '/var/qmail/bin/sendmail';
 620      }
 621      $this->Mailer = 'sendmail';
 622    }
 623  
 624    /**
 625     * Sets Mailer to send message using the qmail MTA.
 626     * @return void
 627     */
 628    public function IsQmail() {
 629      if (stristr(ini_get('sendmail_path'), 'qmail')) {
 630        $this->Sendmail = '/var/qmail/bin/sendmail';
 631      }
 632      $this->Mailer = 'sendmail';
 633    }
 634  
 635    /////////////////////////////////////////////////
 636    // METHODS, RECIPIENTS
 637    /////////////////////////////////////////////////
 638  
 639    /**
 640     * Adds a "To" address.
 641     * @param string $address
 642     * @param string $name
 643     * @return boolean true on success, false if address already used
 644     */
 645    public function AddAddress($address, $name = '') {
 646      return $this->AddAnAddress('to', $address, $name);
 647    }
 648  
 649    /**
 650     * Adds a "Cc" address.
 651     * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.
 652     * @param string $address
 653     * @param string $name
 654     * @return boolean true on success, false if address already used
 655     */
 656    public function AddCC($address, $name = '') {
 657      return $this->AddAnAddress('cc', $address, $name);
 658    }
 659  
 660    /**
 661     * Adds a "Bcc" address.
 662     * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.
 663     * @param string $address
 664     * @param string $name
 665     * @return boolean true on success, false if address already used
 666     */
 667    public function AddBCC($address, $name = '') {
 668      return $this->AddAnAddress('bcc', $address, $name);
 669    }
 670  
 671    /**
 672     * Adds a "Reply-to" address.
 673     * @param string $address
 674     * @param string $name
 675     * @return boolean
 676     */
 677    public function AddReplyTo($address, $name = '') {
 678      return $this->AddAnAddress('Reply-To', $address, $name);
 679    }
 680  
 681    /**
 682     * Adds an address to one of the recipient arrays
 683     * Addresses that have been added already return false, but do not throw exceptions
 684     * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
 685     * @param string $address The email address to send to
 686     * @param string $name
 687     * @throws phpmailerException
 688     * @return boolean true on success, false if address already used or invalid in some way
 689     * @access protected
 690     */
 691    protected function AddAnAddress($kind, $address, $name = '') {
 692      if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
 693        $this->SetError($this->Lang('Invalid recipient array').': '.$kind);
 694        if ($this->exceptions) {
 695          throw new phpmailerException('Invalid recipient array: ' . $kind);
 696        }
 697        if ($this->SMTPDebug) {
 698          $this->edebug($this->Lang('Invalid recipient array').': '.$kind);
 699        }
 700        return false;
 701      }
 702      $address = trim($address);
 703      
 704      $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
 705      if (!$this->ValidateAddress($address)) {
 706        $this->SetError($this->Lang('invalid_address').': '. $address);
 707        if ($this->exceptions) {
 708          throw new phpmailerException($this->Lang('invalid_address').': '.$address);
 709        }
 710        if ($this->SMTPDebug) {
 711          $this->edebug($this->Lang('invalid_address').': '.$address);
 712        }
 713        return false;
 714      }
 715      if ($kind != 'Reply-To') {
 716        if (!isset($this->all_recipients[strtolower($address)])) {
 717           array_push($this->$kind, array($address, $name));
 718          $this->all_recipients[strtolower($address)] = true;
 719          return true;
 720        }
 721      } else {
 722        if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
 723          $this->ReplyTo[strtolower($address)] = array($address, $name);
 724        return true;
 725      }
 726    }
 727    return false;
 728  }
 729  
 730    /**
 731     * Set the From and FromName properties
 732     * @param string $address
 733     * @param string $name
 734     * @param boolean $auto Whether to also set the Sender address, defaults to true
 735     * @throws phpmailerException
 736     * @return boolean
 737     */
 738    public function SetFrom($address, $name = '', $auto = true) {
 739      $address = trim($address);
 740      $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
 741      if (!$this->ValidateAddress($address)) {
 742        $this->SetError($this->Lang('invalid_address').': '. $address);
 743        if ($this->exceptions) {
 744          throw new phpmailerException($this->Lang('invalid_address').': '.$address);
 745        }
 746        if ($this->SMTPDebug) {
 747          $this->edebug($this->Lang('invalid_address').': '.$address);
 748        }
 749        return false;
 750      }
 751      $this->From = $address;
 752      $this->FromName = $name;
 753      if ($auto) {
 754        if (empty($this->Sender)) {
 755          $this->Sender = $address;
 756        }
 757      }
 758      return true;
 759    }
 760  
 761    /**
 762     * Check that a string looks roughly like an email address should
 763     * Static so it can be used without instantiation, public so people can overload
 764     * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
 765     * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
 766     * not allow a@b type valid addresses :(
 767     * @link http://squiloople.com/2009/12/20/email-address-validation/
 768     * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice.
 769     * @param string $address The email address to check
 770     * @return boolean
 771     * @static
 772     * @access public
 773     */
 774    public static function ValidateAddress($address) {
 775        if (defined('PCRE_VERSION')) { //Check this instead of extension_loaded so it works when that function is disabled
 776            if (version_compare(PCRE_VERSION, '8.0') >= 0) {
 777                return (boolean)preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address);
 778            } else {
 779                //Fall back to an older regex that doesn't need a recent PCRE
 780                return (boolean)preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address);
 781            }
 782        } else {
 783            //No PCRE! Do something _very_ approximate!
 784            //Check the address is 3 chars or longer and contains an @ that's not the first or last char
 785            return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1);
 786        }
 787    }
 788  
 789    /////////////////////////////////////////////////
 790    // METHODS, MAIL SENDING
 791    /////////////////////////////////////////////////
 792  
 793    /**
 794     * Creates message and assigns Mailer. If the message is
 795     * not sent successfully then it returns false.  Use the ErrorInfo
 796     * variable to view description of the error.
 797     * @throws phpmailerException
 798     * @return bool
 799     */
 800    public function Send() {
 801      try {
 802        if(!$this->PreSend()) return false;
 803        return $this->PostSend();
 804      } catch (phpmailerException $e) {
 805        $this->mailHeader = '';
 806        $this->SetError($e->getMessage());
 807        if ($this->exceptions) {
 808          throw $e;
 809        }
 810        return false;
 811      }
 812    }
 813  
 814    /**
 815     * Prep mail by constructing all message entities
 816     * @throws phpmailerException
 817     * @return bool
 818     */
 819    public function PreSend() {
 820      try {
 821        $this->mailHeader = "";
 822        if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
 823          throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);
 824        }
 825        // Set whether the message is multipart/alternative
 826        if(!empty($this->AltBody)) {
 827          $this->ContentType = 'multipart/alternative';
 828        }
 829  
 830        $this->error_count = 0; // reset errors
 831        $this->SetMessageType();
 832        //Refuse to send an empty message unless we are specifically allowing it
 833        if (!$this->AllowEmpty and empty($this->Body)) {
 834          throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);
 835        }
 836  
 837        $this->MIMEHeader = $this->CreateHeader();
 838        $this->MIMEBody = $this->CreateBody();
 839        // an extra header list which CreateHeader() doesn't fold in
 840        if ($this->Mailer == 'mail') {
 841          if (count($this->to) > 0) {
 842            $this->mailHeader .= $this->AddrAppend("To", $this->to);
 843          } else {
 844            $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;");
 845          }
 846          $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject))));
 847        }
 848  
 849        // digitally sign with DKIM if enabled
 850        if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) {
 851          $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody);
 852          $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader;
 853        }
 854  
 855        return true;
 856  
 857      } catch (phpmailerException $e) {
 858        $this->SetError($e->getMessage());
 859        if ($this->exceptions) {
 860          throw $e;
 861        }
 862        return false;
 863      }
 864    }
 865  
 866    /**
 867     * Actual Email transport function
 868     * Send the email via the selected mechanism
 869     * @throws phpmailerException
 870     * @return bool
 871     */
 872    public function PostSend() {
 873      try {
 874        // Choose the mailer and send through it
 875        switch($this->Mailer) {
 876          case 'sendmail':
 877            return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody);
 878          case 'smtp':
 879            return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody);
 880          case 'mail':
 881            return $this->MailSend($this->MIMEHeader, $this->MIMEBody);
 882          default:
 883            return $this->MailSend($this->MIMEHeader, $this->MIMEBody);
 884        }
 885      } catch (phpmailerException $e) {
 886        $this->SetError($e->getMessage());
 887        if ($this->exceptions) {
 888          throw $e;
 889        }
 890        if ($this->SMTPDebug) {
 891          $this->edebug($e->getMessage()."\n");
 892        }
 893      }
 894      return false;
 895    }
 896  
 897    /**
 898     * Sends mail using the $Sendmail program.
 899     * @param string $header The message headers
 900     * @param string $body The message body
 901     * @throws phpmailerException
 902     * @access protected
 903     * @return bool
 904     */
 905    protected function SendmailSend($header, $body) {
 906      if ($this->Sender != '') {
 907        $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
 908      } else {
 909        $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
 910      }
 911      if ($this->SingleTo === true) {
 912        foreach ($this->SingleToArray as $val) {
 913          if(!@$mail = popen($sendmail, 'w')) {
 914            throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
 915          }
 916          fputs($mail, "To: " . $val . "\n");
 917          fputs($mail, $header);
 918          fputs($mail, $body);
 919          $result = pclose($mail);
 920          // implement call back function if it exists
 921          $isSent = ($result == 0) ? 1 : 0;
 922          $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body);
 923          if($result != 0) {
 924            throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
 925          }
 926        }
 927      } else {
 928        if(!@$mail = popen($sendmail, 'w')) {
 929          throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
 930        }
 931        fputs($mail, $header);
 932        fputs($mail, $body);
 933        $result = pclose($mail);
 934        // implement call back function if it exists
 935        $isSent = ($result == 0) ? 1 : 0;
 936        $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body);
 937        if($result != 0) {
 938          throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
 939        }
 940      }
 941      return true;
 942    }
 943  
 944    /**
 945     * Sends mail using the PHP mail() function.
 946     * @param string $header The message headers
 947     * @param string $body The message body
 948     * @throws phpmailerException
 949     * @access protected
 950     * @return bool
 951     */
 952    protected function MailSend($header, $body) {
 953      $toArr = array();
 954      foreach($this->to as $t) {
 955        $toArr[] = $this->AddrFormat($t);
 956      }
 957      $to = implode(', ', $toArr);
 958  
 959      if (empty($this->Sender)) {
 960        $params = " ";
 961      } else {
 962        $params = sprintf("-f%s", $this->Sender);
 963      }
 964      if ($this->Sender != '' and !ini_get('safe_mode')) {
 965        $old_from = ini_get('sendmail_from');
 966        ini_set('sendmail_from', $this->Sender);
 967      }
 968        $rt = false;
 969      if ($this->SingleTo === true && count($toArr) > 1) {
 970        foreach ($toArr as $val) {
 971          $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params);
 972          // implement call back function if it exists
 973          $isSent = ($rt == 1) ? 1 : 0;
 974          $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body);
 975        }
 976      } else {
 977        $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params);
 978        // implement call back function if it exists
 979        $isSent = ($rt == 1) ? 1 : 0;
 980        $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body);
 981      }
 982      if (isset($old_from)) {
 983        ini_set('sendmail_from', $old_from);
 984      }
 985      if(!$rt) {
 986        throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);
 987      }
 988      return true;
 989    }
 990  
 991    /**
 992     * Sends mail via SMTP using PhpSMTP
 993     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
 994     * @param string $header The message headers
 995     * @param string $body The message body
 996     * @throws phpmailerException
 997     * @uses SMTP
 998     * @access protected
 999     * @return bool
1000     */
1001    protected function SmtpSend($header, $body) {
1002      require_once $this->PluginDir . 'class.smtp.php';
1003      $bad_rcpt = array();
1004  
1005      if(!$this->SmtpConnect()) {
1006        throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);
1007      }
1008      $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1009      if(!$this->smtp->Mail($smtp_from)) {
1010        $this->SetError($this->Lang('from_failed') . $smtp_from . ' : ' .implode(',', $this->smtp->getError()));
1011        throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1012      }
1013  
1014      // Attempt to send attach all recipients
1015      foreach($this->to as $to) {
1016        if (!$this->smtp->Recipient($to[0])) {
1017          $bad_rcpt[] = $to[0];
1018          // implement call back function if it exists
1019          $isSent = 0;
1020          $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body);
1021        } else {
1022          // implement call back function if it exists
1023          $isSent = 1;
1024          $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body);
1025        }
1026      }
1027      foreach($this->cc as $cc) {
1028        if (!$this->smtp->Recipient($cc[0])) {
1029          $bad_rcpt[] = $cc[0];
1030          // implement call back function if it exists
1031          $isSent = 0;
1032          $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body);
1033        } else {
1034          // implement call back function if it exists
1035          $isSent = 1;
1036          $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body);
1037        }
1038      }
1039      foreach($this->bcc as $bcc) {
1040        if (!$this->smtp->Recipient($bcc[0])) {
1041          $bad_rcpt[] = $bcc[0];
1042          // implement call back function if it exists
1043          $isSent = 0;
1044          $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body);
1045        } else {
1046          // implement call back function if it exists
1047          $isSent = 1;
1048          $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body);
1049        }
1050      }
1051  
1052  
1053      if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses
1054        $badaddresses = implode(', ', $bad_rcpt);
1055        throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);
1056      }
1057      if(!$this->smtp->Data($header . $body)) {
1058        throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);
1059      }
1060      if($this->SMTPKeepAlive == true) {
1061        $this->smtp->Reset();
1062      } else {
1063          $this->smtp->Quit();
1064          $this->smtp->Close();
1065      }
1066      return true;
1067    }
1068  
1069    /**
1070     * Initiates a connection to an SMTP server.
1071     * Returns false if the operation failed.
1072     * @param array $options An array of options compatible with stream_context_create()
1073     * @uses SMTP
1074     * @access public
1075     * @throws phpmailerException
1076     * @return bool
1077     */
1078    public function SmtpConnect($options = array()) {
1079      if(is_null($this->smtp)) {
1080        $this->smtp = new SMTP;
1081      }
1082  
1083      //Already connected?
1084      if ($this->smtp->Connected()) {
1085        return true;
1086      }
1087  
1088      $this->smtp->Timeout = $this->Timeout;
1089      $this->smtp->do_debug = $this->SMTPDebug;
1090      $this->smtp->Debugoutput = $this->Debugoutput;
1091      $this->smtp->do_verp = $this->do_verp;
1092      $index = 0;
1093      $tls = ($this->SMTPSecure == 'tls');
1094      $ssl = ($this->SMTPSecure == 'ssl');
1095      $hosts = explode(';', $this->Host);
1096      $lastexception = null;
1097  
1098      foreach ($hosts as $hostentry) {
1099        $hostinfo = array();
1100        $host = $hostentry;
1101        $port = $this->Port;
1102        if (preg_match('/^(.+):([0-9]+)$/', $hostentry, $hostinfo)) { //If $hostentry contains 'address:port', override default
1103          $host = $hostinfo[1];
1104          $port = $hostinfo[2];
1105        }
1106        if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $options)) {
1107          try {
1108            if ($this->Helo) {
1109              $hello = $this->Helo;
1110            } else {
1111              $hello = $this->ServerHostname();
1112            }
1113            $this->smtp->Hello($hello);
1114  
1115            if ($tls) {
1116              if (!$this->smtp->StartTLS()) {
1117                throw new phpmailerException($this->Lang('connect_host'));
1118              }
1119              //We must resend HELO after tls negotiation
1120              $this->smtp->Hello($hello);
1121            }
1122            if ($this->SMTPAuth) {
1123              if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation)) {
1124                throw new phpmailerException($this->Lang('authenticate'));
1125              }
1126            }
1127            return true;
1128          } catch (phpmailerException $e) {
1129            $lastexception = $e;
1130            //We must have connected, but then failed TLS or Auth, so close connection nicely
1131            $this->smtp->Quit();
1132          }
1133        }
1134      }
1135      //If we get here, all connection attempts have failed, so close connection hard
1136      $this->smtp->Close();
1137      //As we've caught all exceptions, just report whatever the last one was
1138      if ($this->exceptions and !is_null($lastexception)) {
1139        throw $lastexception;
1140      }
1141      return false;
1142    }
1143  
1144    /**
1145     * Closes the active SMTP session if one exists.
1146     * @return void
1147     */
1148    public function SmtpClose() {
1149      if ($this->smtp !== null) {
1150        if($this->smtp->Connected()) {
1151          $this->smtp->Quit();
1152          $this->smtp->Close();
1153        }
1154      }
1155    }
1156  
1157    /**
1158     * Sets the language for all class error messages.
1159     * Returns false if it cannot load the language file.  The default language is English.
1160     * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")
1161     * @param string $lang_path Path to the language file directory
1162     * @return bool
1163     * @access public
1164     */
1165    function SetLanguage($langcode = 'en', $lang_path = 'language/') {
1166      //Define full set of translatable strings
1167      $PHPMAILER_LANG = array(
1168        'authenticate'         => 'SMTP Error: Could not authenticate.',
1169        'connect_host'         => 'SMTP Error: Could not connect to SMTP host.',
1170        'data_not_accepted'    => 'SMTP Error: Data not accepted.',
1171        'empty_message'        => 'Message body empty',
1172        'encoding'             => 'Unknown encoding: ',
1173        'execute'              => 'Could not execute: ',
1174        'file_access'          => 'Could not access file: ',
1175        'file_open'            => 'File Error: Could not open file: ',
1176        'from_failed'          => 'The following From address failed: ',
1177        'instantiate'          => 'Could not instantiate mail function.',
1178        'invalid_address'      => 'Invalid address',
1179        'mailer_not_supported' => ' mailer is not supported.',
1180        'provide_address'      => 'You must provide at least one recipient email address.',
1181        'recipients_failed'    => 'SMTP Error: The following recipients failed: ',
1182        'signing'              => 'Signing Error: ',
1183        'smtp_connect_failed'  => 'SMTP Connect() failed.',
1184        'smtp_error'           => 'SMTP server error: ',
1185        'variable_set'         => 'Cannot set or reset variable: '
1186      );
1187      //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!
1188      $l = true;
1189      if ($langcode != 'en') { //There is no English translation file
1190        $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';
1191      }
1192      $this->language = $PHPMAILER_LANG;
1193      return ($l == true); //Returns false if language not found
1194    }
1195  
1196    /**
1197    * Return the current array of language strings
1198    * @return array
1199    */
1200    public function GetTranslations() {
1201      return $this->language;
1202    }
1203  
1204    /////////////////////////////////////////////////
1205    // METHODS, MESSAGE CREATION
1206    /////////////////////////////////////////////////
1207  
1208    /**
1209     * Creates recipient headers.
1210     * @access public
1211     * @param string $type
1212     * @param array $addr
1213     * @return string
1214     */
1215    public function AddrAppend($type, $addr) {
1216      $addr_str = $type . ': ';
1217      $addresses = array();
1218      foreach ($addr as $a) {
1219        $addresses[] = $this->AddrFormat($a);
1220      }
1221      $addr_str .= implode(', ', $addresses);
1222      $addr_str .= $this->LE;
1223  
1224      return $addr_str;
1225    }
1226  
1227    /**
1228     * Formats an address correctly.
1229     * @access public
1230     * @param string $addr
1231     * @return string
1232     */
1233    public function AddrFormat($addr) {
1234      if (empty($addr[1])) {
1235        return $this->SecureHeader($addr[0]);
1236      } else {
1237        return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
1238      }
1239    }
1240  
1241    /**
1242     * Wraps message for use with mailers that do not
1243     * automatically perform wrapping and for quoted-printable.
1244     * Original written by philippe.
1245     * @param string $message The message to wrap
1246     * @param integer $length The line length to wrap to
1247     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1248     * @access public
1249     * @return string
1250     */
1251    public function WrapText($message, $length, $qp_mode = false) {
1252      $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
1253      // If utf-8 encoding is used, we will need to make sure we don't
1254      // split multibyte characters when we wrap
1255      $is_utf8 = (strtolower($this->CharSet) == "utf-8");
1256      $lelen = strlen($this->LE);
1257      $crlflen = strlen(self::CRLF);
1258  
1259      $message = $this->FixEOL($message);
1260      if (substr($message, -$lelen) == $this->LE) {
1261        $message = substr($message, 0, -$lelen);
1262      }
1263  
1264      $line = explode($this->LE, $message);   // Magic. We know FixEOL uses $LE
1265      $message = '';
1266      for ($i = 0 ;$i < count($line); $i++) {
1267        $line_part = explode(' ', $line[$i]);
1268        $buf = '';
1269        for ($e = 0; $e<count($line_part); $e++) {
1270          $word = $line_part[$e];
1271          if ($qp_mode and (strlen($word) > $length)) {
1272            $space_left = $length - strlen($buf) - $crlflen;
1273            if ($e != 0) {
1274              if ($space_left > 20) {
1275                $len = $space_left;
1276                if ($is_utf8) {
1277                  $len = $this->UTF8CharBoundary($word, $len);
1278                } elseif (substr($word, $len - 1, 1) == "=") {
1279                  $len--;
1280                } elseif (substr($word, $len - 2, 1) == "=") {
1281                  $len -= 2;
1282                }
1283                $part = substr($word, 0, $len);
1284                $word = substr($word, $len);
1285                $buf .= ' ' . $part;
1286                $message .= $buf . sprintf("=%s", self::CRLF);
1287              } else {
1288                $message .= $buf . $soft_break;
1289              }
1290              $buf = '';
1291            }
1292            while (strlen($word) > 0) {
1293              if ($length <= 0) {
1294                  break;
1295              }
1296              $len = $length;
1297              if ($is_utf8) {
1298                $len = $this->UTF8CharBoundary($word, $len);
1299              } elseif (substr($word, $len - 1, 1) == "=") {
1300                $len--;
1301              } elseif (substr($word, $len - 2, 1) == "=") {
1302                $len -= 2;
1303              }
1304              $part = substr($word, 0, $len);
1305              $word = substr($word, $len);
1306  
1307              if (strlen($word) > 0) {
1308                $message .= $part . sprintf("=%s", self::CRLF);
1309              } else {
1310                $buf = $part;
1311              }
1312            }
1313          } else {
1314            $buf_o = $buf;
1315            $buf .= ($e == 0) ? $word : (' ' . $word);
1316  
1317            if (strlen($buf) > $length and $buf_o != '') {
1318              $message .= $buf_o . $soft_break;
1319              $buf = $word;
1320            }
1321          }
1322        }
1323        $message .= $buf . self::CRLF;
1324      }
1325  
1326      return $message;
1327    }
1328  
1329    /**
1330     * Finds last character boundary prior to maxLength in a utf-8
1331     * quoted (printable) encoded string.
1332     * Original written by Colin Brown.
1333     * @access public
1334     * @param string $encodedText utf-8 QP text
1335     * @param int    $maxLength   find last character boundary prior to this length
1336     * @return int
1337     */
1338    public function UTF8CharBoundary($encodedText, $maxLength) {
1339      $foundSplitPos = false;
1340      $lookBack = 3;
1341      while (!$foundSplitPos) {
1342        $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1343        $encodedCharPos = strpos($lastChunk, "=");
1344        if ($encodedCharPos !== false) {
1345          // Found start of encoded character byte within $lookBack block.
1346          // Check the encoded byte value (the 2 chars after the '=')
1347          $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1348          $dec = hexdec($hex);
1349          if ($dec < 128) { // Single byte character.
1350            // If the encoded char was found at pos 0, it will fit
1351            // otherwise reduce maxLength to start of the encoded char
1352            $maxLength = ($encodedCharPos == 0) ? $maxLength :
1353            $maxLength - ($lookBack - $encodedCharPos);
1354            $foundSplitPos = true;
1355          } elseif ($dec >= 192) { // First byte of a multi byte character
1356            // Reduce maxLength to split at start of character
1357            $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1358            $foundSplitPos = true;
1359          } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1360            $lookBack += 3;
1361          }
1362        } else {
1363          // No encoded character found
1364          $foundSplitPos = true;
1365        }
1366      }
1367      return $maxLength;
1368    }
1369  
1370  
1371    /**
1372     * Set the body wrapping.
1373     * @access public
1374     * @return void
1375     */
1376    public function SetWordWrap() {
1377      if($this->WordWrap < 1) {
1378        return;
1379      }
1380  
1381      switch($this->message_type) {
1382        case 'alt':
1383        case 'alt_inline':
1384        case 'alt_attach':
1385        case 'alt_inline_attach':
1386          $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
1387          break;
1388        default:
1389          $this->Body = $this->WrapText($this->Body, $this->WordWrap);
1390          break;
1391      }
1392    }
1393  
1394    /**
1395     * Assembles message header.
1396     * @access public
1397     * @return string The assembled header
1398     */
1399    public function CreateHeader() {
1400      $result = '';
1401  
1402      // Set the boundaries
1403      $uniq_id = md5(uniqid(time()));
1404      $this->boundary[1] = 'b1_' . $uniq_id;
1405      $this->boundary[2] = 'b2_' . $uniq_id;
1406      $this->boundary[3] = 'b3_' . $uniq_id;
1407  
1408      if ($this->MessageDate == '') {
1409        $result .= $this->HeaderLine('Date', self::RFCDate());
1410      } else {
1411        $result .= $this->HeaderLine('Date', $this->MessageDate);
1412      }
1413  
1414      if ($this->ReturnPath) {
1415        $result .= $this->HeaderLine('Return-Path', '<'.trim($this->ReturnPath).'>');
1416      } elseif ($this->Sender == '') {
1417        $result .= $this->HeaderLine('Return-Path', '<'.trim($this->From).'>');
1418      } else {
1419        $result .= $this->HeaderLine('Return-Path', '<'.trim($this->Sender).'>');
1420      }
1421  
1422      // To be created automatically by mail()
1423      if($this->Mailer != 'mail') {
1424        if ($this->SingleTo === true) {
1425          foreach($this->to as $t) {
1426            $this->SingleToArray[] = $this->AddrFormat($t);
1427          }
1428        } else {
1429          if(count($this->to) > 0) {
1430            $result .= $this->AddrAppend('To', $this->to);
1431          } elseif (count($this->cc) == 0) {
1432            $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
1433          }
1434        }
1435      }
1436  
1437      $from = array();
1438      $from[0][0] = trim($this->From);
1439      $from[0][1] = $this->FromName;
1440      $result .= $this->AddrAppend('From', $from);
1441  
1442      // sendmail and mail() extract Cc from the header before sending
1443      if(count($this->cc) > 0) {
1444        $result .= $this->AddrAppend('Cc', $this->cc);
1445      }
1446  
1447      // sendmail and mail() extract Bcc from the header before sending
1448      if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
1449        $result .= $this->AddrAppend('Bcc', $this->bcc);
1450      }
1451  
1452      if(count($this->ReplyTo) > 0) {
1453        $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);
1454      }
1455  
1456      // mail() sets the subject itself
1457      if($this->Mailer != 'mail') {
1458        $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
1459      }
1460  
1461      if($this->MessageID != '') {
1462        $result .= $this->HeaderLine('Message-ID', $this->MessageID);
1463      } else {
1464        $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
1465      }
1466      $result .= $this->HeaderLine('X-Priority', $this->Priority);
1467      if ($this->XMailer == '') {
1468          $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)');
1469      } else {
1470        $myXmailer = trim($this->XMailer);
1471        if ($myXmailer) {
1472          $result .= $this->HeaderLine('X-Mailer', $myXmailer);
1473        }
1474      }
1475  
1476      if($this->ConfirmReadingTo != '') {
1477        $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1478      }
1479  
1480      // Add custom headers
1481      for($index = 0; $index < count($this->CustomHeader); $index++) {
1482        $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
1483      }
1484      if (!$this->sign_key_file) {
1485        $result .= $this->HeaderLine('MIME-Version', '1.0');
1486        $result .= $this->GetMailMIME();
1487      }
1488  
1489      return $result;
1490    }
1491  
1492    /**
1493     * Returns the message MIME.
1494     * @access public
1495     * @return string
1496     */
1497    public function GetMailMIME() {
1498      $result = '';
1499      switch($this->message_type) {
1500        case 'inline':
1501          $result .= $this->HeaderLine('Content-Type', 'multipart/related;');
1502          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
1503          break;
1504        case 'attach':
1505        case 'inline_attach':
1506        case 'alt_attach':
1507        case 'alt_inline_attach':
1508          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
1509          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
1510          break;
1511        case 'alt':
1512        case 'alt_inline':
1513          $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
1514          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
1515          break;
1516        default:
1517          // Catches case 'plain': and case '':
1518          $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet);
1519          break;
1520      }
1521      //RFC1341 part 5 says 7bit is assumed if not specified
1522      if ($this->Encoding != '7bit') {
1523        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
1524      }
1525  
1526      if($this->Mailer != 'mail') {
1527        $result .= $this->LE;
1528      }
1529  
1530      return $result;
1531    }
1532  
1533    /**
1534     * Returns the MIME message (headers and body). Only really valid post PreSend().
1535     * @access public
1536     * @return string
1537     */
1538    public function GetSentMIMEMessage() {
1539      return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1540    }
1541  
1542  
1543    /**
1544     * Assembles the message body.  Returns an empty string on failure.
1545     * @access public
1546     * @throws phpmailerException
1547     * @return string The assembled message body
1548     */
1549    public function CreateBody() {
1550      $body = '';
1551  
1552      if ($this->sign_key_file) {
1553        $body .= $this->GetMailMIME().$this->LE;
1554      }
1555  
1556      $this->SetWordWrap();
1557  
1558      switch($this->message_type) {
1559        case 'inline':
1560          $body .= $this->GetBoundary($this->boundary[1], '', '', '');
1561          $body .= $this->EncodeString($this->Body, $this->Encoding);
1562          $body .= $this->LE.$this->LE;
1563          $body .= $this->AttachAll('inline', $this->boundary[1]);
1564          break;
1565        case 'attach':
1566          $body .= $this->GetBoundary($this->boundary[1], '', '', '');
1567          $body .= $this->EncodeString($this->Body, $this->Encoding);
1568          $body .= $this->LE.$this->LE;
1569          $body .= $this->AttachAll('attachment', $this->boundary[1]);
1570          break;
1571        case 'inline_attach':
1572          $body .= $this->TextLine('--' . $this->boundary[1]);
1573          $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
1574          $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
1575          $body .= $this->LE;
1576          $body .= $this->GetBoundary($this->boundary[2], '', '', '');
1577          $body .= $this->EncodeString($this->Body, $this->Encoding);
1578          $body .= $this->LE.$this->LE;
1579          $body .= $this->AttachAll('inline', $this->boundary[2]);
1580          $body .= $this->LE;
1581          $body .= $this->AttachAll('attachment', $this->boundary[1]);
1582          break;
1583        case 'alt':
1584          $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
1585          $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1586          $body .= $this->LE.$this->LE;
1587          $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
1588          $body .= $this->EncodeString($this->Body, $this->Encoding);
1589          $body .= $this->LE.$this->LE;
1590          if(!empty($this->Ical)) {
1591            $body .= $this->GetBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1592            $body .= $this->EncodeString($this->Ical, $this->Encoding);
1593            $body .= $this->LE.$this->LE;
1594          }
1595          $body .= $this->EndBoundary($this->boundary[1]);
1596          break;
1597        case 'alt_inline':
1598          $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
1599          $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1600          $body .= $this->LE.$this->LE;
1601          $body .= $this->TextLine('--' . $this->boundary[1]);
1602          $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
1603          $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
1604          $body .= $this->LE;
1605          $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '');
1606          $body .= $this->EncodeString($this->Body, $this->Encoding);
1607          $body .= $this->LE.$this->LE;
1608          $body .= $this->AttachAll('inline', $this->boundary[2]);
1609          $body .= $this->LE;
1610          $body .= $this->EndBoundary($this->boundary[1]);
1611          break;
1612        case 'alt_attach':
1613          $body .= $this->TextLine('--' . $this->boundary[1]);
1614          $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
1615          $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
1616          $body .= $this->LE;
1617          $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');
1618          $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1619          $body .= $this->LE.$this->LE;
1620          $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '');
1621          $body .= $this->EncodeString($this->Body, $this->Encoding);
1622          $body .= $this->LE.$this->LE;
1623          $body .= $this->EndBoundary($this->boundary[2]);
1624          $body .= $this->LE;
1625          $body .= $this->AttachAll('attachment', $this->boundary[1]);
1626          break;
1627        case 'alt_inline_attach':
1628          $body .= $this->TextLine('--' . $this->boundary[1]);
1629          $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
1630          $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
1631          $body .= $this->LE;
1632          $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');
1633          $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1634          $body .= $this->LE.$this->LE;
1635          $body .= $this->TextLine('--' . $this->boundary[2]);
1636          $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
1637          $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3].'"');
1638          $body .= $this->LE;
1639          $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', '');
1640          $body .= $this->EncodeString($this->Body, $this->Encoding);
1641          $body .= $this->LE.$this->LE;
1642          $body .= $this->AttachAll('inline', $this->boundary[3]);
1643          $body .= $this->LE;
1644          $body .= $this->EndBoundary($this->boundary[2]);
1645          $body .= $this->LE;
1646          $body .= $this->AttachAll('attachment', $this->boundary[1]);
1647          break;
1648        default:
1649          // catch case 'plain' and case ''
1650          $body .= $this->EncodeString($this->Body, $this->Encoding);
1651          break;
1652      }
1653  
1654      if ($this->IsError()) {
1655        $body = '';
1656      } elseif ($this->sign_key_file) {
1657        try {
1658          if (!defined('PKCS7_TEXT')) {
1659              throw new phpmailerException($this->Lang('signing').' OpenSSL extension missing.');
1660          }
1661          $file = tempnam(sys_get_temp_dir(), 'mail');
1662          file_put_contents($file, $body); //TODO check this worked
1663          $signed = tempnam(sys_get_temp_dir(), 'signed');
1664          if (@openssl_pkcs7_sign($file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null)) {
1665            @unlink($file);
1666            $body = file_get_contents($signed);
1667            @unlink($signed);
1668          } else {
1669            @unlink($file);
1670            @unlink($signed);
1671            throw new phpmailerException($this->Lang('signing').openssl_error_string());
1672          }
1673        } catch (phpmailerException $e) {
1674          $body = '';
1675          if ($this->exceptions) {
1676            throw $e;
1677          }
1678        }
1679      }
1680      return $body;
1681    }
1682  
1683    /**
1684     * Returns the start of a message boundary.
1685     * @access protected
1686     * @param string $boundary
1687     * @param string $charSet
1688     * @param string $contentType
1689     * @param string $encoding
1690     * @return string
1691     */
1692    protected function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1693      $result = '';
1694      if($charSet == '') {
1695        $charSet = $this->CharSet;
1696      }
1697      if($contentType == '') {
1698        $contentType = $this->ContentType;
1699      }
1700      if($encoding == '') {
1701        $encoding = $this->Encoding;
1702      }
1703      $result .= $this->TextLine('--' . $boundary);
1704      $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);
1705      $result .= $this->LE;
1706      $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1707      $result .= $this->LE;
1708  
1709      return $result;
1710    }
1711  
1712    /**
1713     * Returns the end of a message boundary.
1714     * @access protected
1715     * @param string $boundary
1716     * @return string
1717     */
1718    protected function EndBoundary($boundary) {
1719      return $this->LE . '--' . $boundary . '--' . $this->LE;
1720    }
1721  
1722    /**
1723     * Sets the message type.
1724     * @access protected
1725     * @return void
1726     */
1727    protected function SetMessageType() {
1728      $this->message_type = array();
1729      if($this->AlternativeExists()) $this->message_type[] = "alt";
1730      if($this->InlineImageExists()) $this->message_type[] = "inline";
1731      if($this->AttachmentExists()) $this->message_type[] = "attach";
1732      $this->message_type = implode("_", $this->message_type);
1733      if($this->message_type == "") $this->message_type = "plain";
1734    }
1735  
1736    /**
1737     * Returns a formatted header line.
1738     * @access public
1739     * @param string $name
1740     * @param string $value
1741     * @return string
1742     */
1743    public function HeaderLine($name, $value) {
1744      return $name . ': ' . $value . $this->LE;
1745    }
1746  
1747    /**
1748     * Returns a formatted mail line.
1749     * @access public
1750     * @param string $value
1751     * @return string
1752     */
1753    public function TextLine($value) {
1754      return $value . $this->LE;
1755    }
1756  
1757    /////////////////////////////////////////////////
1758    // CLASS METHODS, ATTACHMENTS
1759    /////////////////////////////////////////////////
1760  
1761    /**
1762     * Adds an attachment from a path on the filesystem.
1763     * Returns false if the file could not be found
1764     * or accessed.
1765     * @param string $path Path to the attachment.
1766     * @param string $name Overrides the attachment name.
1767     * @param string $encoding File encoding (see $Encoding).
1768     * @param string $type File extension (MIME) type.
1769     * @param string $disposition Disposition to use
1770     * @throws phpmailerException
1771     * @return bool
1772     */
1773    public function AddAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') {
1774      try {
1775        if ( !@is_file($path) ) {
1776          throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);
1777        }
1778  
1779        //If a MIME type is not specified, try to work it out from the file name
1780        if ($type == '') {
1781          $type = self::filenameToType($path);
1782        }
1783  
1784        $filename = basename($path);
1785        if ( $name == '' ) {
1786          $name = $filename;
1787        }
1788  
1789        $this->attachment[] = array(
1790          0 => $path,
1791          1 => $filename,
1792          2 => $name,
1793          3 => $encoding,
1794          4 => $type,
1795          5 => false,  // isStringAttachment
1796          6 => $disposition,
1797          7 => 0
1798        );
1799  
1800      } catch (phpmailerException $e) {
1801        $this->SetError($e->getMessage());
1802        if ($this->exceptions) {
1803          throw $e;
1804        }
1805        if ($this->SMTPDebug) {
1806          $this->edebug($e->getMessage()."\n");
1807        }
1808        return false;
1809      }
1810      return true;
1811    }
1812  
1813    /**
1814    * Return the current array of attachments
1815    * @return array
1816    */
1817    public function GetAttachments() {
1818      return $this->attachment;
1819    }
1820  
1821    /**
1822     * Attaches all fs, string, and binary attachments to the message.
1823     * Returns an empty string on failure.
1824     * @access protected
1825     * @param string $disposition_type
1826     * @param string $boundary
1827     * @return string
1828     */
1829    protected function AttachAll($disposition_type, $boundary) {
1830      // Return text of body
1831      $mime = array();
1832      $cidUniq = array();
1833      $incl = array();
1834  
1835      // Add all attachments
1836      foreach ($this->attachment as $attachment) {
1837        // CHECK IF IT IS A VALID DISPOSITION_FILTER
1838        if($attachment[6] == $disposition_type) {
1839          // Check for string attachment
1840          $string = '';
1841          $path = '';
1842          $bString = $attachment[5];
1843          if ($bString) {
1844            $string = $attachment[0];
1845          } else {
1846            $path = $attachment[0];
1847          }
1848  
1849          $inclhash = md5(serialize($attachment));
1850          if (in_array($inclhash, $incl)) { continue; }
1851          $incl[]      = $inclhash;
1852          $filename    = $attachment[1];
1853          $name        = $attachment[2];
1854          $encoding    = $attachment[3];
1855          $type        = $attachment[4];
1856          $disposition = $attachment[6];
1857          $cid         = $attachment[7];
1858          if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }
1859          $cidUniq[$cid] = true;
1860  
1861          $mime[] = sprintf("--%s%s", $boundary, $this->LE);
1862          $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1863          $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1864  
1865          if($disposition == 'inline') {
1866            $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1867          }
1868  
1869          //If a filename contains any of these chars, it should be quoted, but not otherwise: RFC2183 & RFC2045 5.1
1870          //Fixes a warning in IETF's msglint MIME checker
1871          //
1872          // Allow for bypassing the Content-Disposition header totally
1873          if (!(empty($disposition))) {
1874            if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
1875              $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1876            } else {
1877              $mime[] = sprintf("Content-Disposition: %s; filename=%s%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1878            }
1879          } else {
1880            $mime[] = $this->LE;
1881          }
1882  
1883          // Encode as string attachment
1884          if($bString) {
1885            $mime[] = $this->EncodeString($string, $encoding);
1886            if($this->IsError()) {
1887              return '';
1888            }
1889            $mime[] = $this->LE.$this->LE;
1890          } else {
1891            $mime[] = $this->EncodeFile($path, $encoding);
1892            if($this->IsError()) {
1893              return '';
1894            }
1895            $mime[] = $this->LE.$this->LE;
1896          }
1897        }
1898      }
1899  
1900      $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
1901  
1902      return implode("", $mime);
1903    }
1904  
1905    /**
1906     * Encodes attachment in requested format.
1907     * Returns an empty string on failure.
1908     * @param string $path The full path to the file
1909     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
1910     * @throws phpmailerException
1911     * @see EncodeFile()
1912     * @access protected
1913     * @return string
1914     */
1915    protected function EncodeFile($path, $encoding = 'base64') {
1916      try {
1917        if (!is_readable($path)) {
1918          throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);
1919        }
1920        $magic_quotes = get_magic_quotes_runtime();
1921        if ($magic_quotes) {
1922          if (version_compare(PHP_VERSION, '5.3.0', '<')) {
1923            set_magic_quotes_runtime(0);
1924          } else {
1925            ini_set('magic_quotes_runtime', 0);
1926          }
1927        }
1928        $file_buffer  = file_get_contents($path);
1929        $file_buffer  = $this->EncodeString($file_buffer, $encoding);
1930        if ($magic_quotes) {
1931          if (version_compare(PHP_VERSION, '5.3.0', '<')) {
1932            set_magic_quotes_runtime($magic_quotes);
1933          } else {
1934            ini_set('magic_quotes_runtime', $magic_quotes);
1935          }
1936        }
1937        return $file_buffer;
1938      } catch (Exception $e) {
1939        $this->SetError($e->getMessage());
1940        return '';
1941      }
1942    }
1943  
1944    /**
1945     * Encodes string to requested format.
1946     * Returns an empty string on failure.
1947     * @param string $str The text to encode
1948     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
1949     * @access public
1950     * @return string
1951     */
1952    public function EncodeString($str, $encoding = 'base64') {
1953      $encoded = '';
1954      switch(strtolower($encoding)) {
1955        case 'base64':
1956          $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1957          break;
1958        case '7bit':
1959        case '8bit':
1960          $encoded = $this->FixEOL($str);
1961          //Make sure it ends with a line break
1962          if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1963            $encoded .= $this->LE;
1964          break;
1965        case 'binary':
1966          $encoded = $str;
1967          break;
1968        case 'quoted-printable':
1969          $encoded = $this->EncodeQP($str);
1970          break;
1971        default:
1972          $this->SetError($this->Lang('encoding') . $encoding);
1973          break;
1974      }
1975      return $encoded;
1976    }
1977  
1978    /**
1979     * Encode a header string to best (shortest) of Q, B, quoted or none.
1980     * @access public
1981     * @param string $str
1982     * @param string $position
1983     * @return string
1984     */
1985    public function EncodeHeader($str, $position = 'text') {
1986      $x = 0;
1987  
1988      switch (strtolower($position)) {
1989        case 'phrase':
1990          if (!preg_match('/[\200-\377]/', $str)) {
1991            // Can't use addslashes as we don't know what value has magic_quotes_sybase
1992            $encoded = addcslashes($str, "\0..\37\177\\\"");
1993            if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1994              return ($encoded);
1995            } else {
1996              return ("\"$encoded\"");
1997            }
1998          }
1999          $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2000          break;
2001        case 'comment':
2002          $x = preg_match_all('/[()"]/', $str, $matches);
2003          // Fall-through
2004        case 'text':
2005        default:
2006          $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2007          break;
2008      }
2009  
2010      if ($x == 0) { //There are no chars that need encoding
2011        return ($str);
2012      }
2013  
2014      $maxlen = 75 - 7 - strlen($this->CharSet);
2015      // Try to select the encoding which should produce the shortest output
2016      if ($x > strlen($str)/3) { //More than a third of the content will need encoding, so B encoding will be most efficient
2017        $encoding = 'B';
2018        if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
2019          // Use a custom function which correctly encodes and wraps long
2020          // multibyte strings without breaking lines within a character
2021          $encoded = $this->Base64EncodeWrapMB($str, "\n");
2022        } else {
2023          $encoded = base64_encode($str);
2024          $maxlen -= $maxlen % 4;
2025          $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2026        }
2027      } else {
2028        $encoding = 'Q';
2029        $encoded = $this->EncodeQ($str, $position);
2030        $encoded = $this->WrapText($encoded, $maxlen, true);
2031        $encoded = str_replace('='.self::CRLF, "\n", trim($encoded));
2032      }
2033  
2034      $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
2035      $encoded = trim(str_replace("\n", $this->LE, $encoded));
2036  
2037      return $encoded;
2038    }
2039  
2040    /**
2041     * Checks if a string contains multibyte characters.
2042     * @access public
2043     * @param string $str multi-byte text to wrap encode
2044     * @return bool
2045     */
2046    public function HasMultiBytes($str) {
2047      if (function_exists('mb_strlen')) {
2048        return (strlen($str) > mb_strlen($str, $this->CharSet));
2049      } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2050        return false;
2051      }
2052    }
2053  
2054    /**
2055     * Correctly encodes and wraps long multibyte strings for mail headers
2056     * without breaking lines within a character.
2057     * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
2058     * @access public
2059     * @param string $str multi-byte text to wrap encode
2060     * @param string $lf string to use as linefeed/end-of-line
2061     * @return string
2062     */
2063    public function Base64EncodeWrapMB($str, $lf=null) {
2064      $start = "=?".$this->CharSet."?B?";
2065      $end = "?=";
2066      $encoded = "";
2067      if ($lf === null) {
2068        $lf = $this->LE;
2069      }
2070  
2071      $mb_length = mb_strlen($str, $this->CharSet);
2072      // Each line must have length <= 75, including $start and $end
2073      $length = 75 - strlen($start) - strlen($end);
2074      // Average multi-byte ratio
2075      $ratio = $mb_length / strlen($str);
2076      // Base64 has a 4:3 ratio
2077      $offset = $avgLength = floor($length * $ratio * .75);
2078  
2079      for ($i = 0; $i < $mb_length; $i += $offset) {
2080        $lookBack = 0;
2081  
2082        do {
2083          $offset = $avgLength - $lookBack;
2084          $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2085          $chunk = base64_encode($chunk);
2086          $lookBack++;
2087        }
2088        while (strlen($chunk) > $length);
2089  
2090        $encoded .= $chunk . $lf;
2091      }
2092  
2093      // Chomp the last linefeed
2094      $encoded = substr($encoded, 0, -strlen($lf));
2095      return $encoded;
2096    }
2097  
2098    /**
2099     * Encode string to RFC2045 (6.7) quoted-printable format
2100     * @access public
2101     * @param string $string The text to encode
2102     * @param integer $line_max Number of chars allowed on a line before wrapping
2103     * @return string
2104     * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
2105     */
2106    public function EncodeQP($string, $line_max = 76) {
2107      if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
2108        return quoted_printable_encode($string);
2109      }
2110      //Fall back to a pure PHP implementation
2111      $string = str_replace(array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string));
2112      $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string);
2113      return $string;
2114    }
2115  
2116    /**
2117     * Wrapper to preserve BC for old QP encoding function that was removed
2118     * @see EncodeQP()
2119     * @access public
2120     * @param string $string
2121     * @param integer $line_max
2122     * @param bool $space_conv
2123     * @return string
2124     */
2125    public function EncodeQPphp($string, $line_max = 76, $space_conv = false) {
2126      return $this->EncodeQP($string, $line_max);
2127    }
2128  
2129    /**
2130     * Encode string to q encoding.
2131     * @link http://tools.ietf.org/html/rfc2047
2132     * @param string $str the text to encode
2133     * @param string $position Where the text is going to be used, see the RFC for what that means
2134     * @access public
2135     * @return string
2136     */
2137    public function EncodeQ($str, $position = 'text') {
2138      //There should not be any EOL in the string
2139      $pattern = '';
2140      $encoded = str_replace(array("\r", "\n"), '', $str);
2141      switch (strtolower($position)) {
2142        case 'phrase':
2143          $pattern = '^A-Za-z0-9!*+\/ -';
2144          break;
2145  
2146        case 'comment':
2147          $pattern = '\(\)"';
2148          //note that we don't break here!
2149          //for this reason we build the $pattern without including delimiters and []
2150  
2151        case 'text':
2152        default:
2153          //Replace every high ascii, control =, ? and _ characters
2154          //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode
2155          $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern;
2156          break;
2157      }
2158  
2159      if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2160        foreach (array_unique($matches[0]) as $char) {
2161          $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2162        }
2163      }
2164  
2165      //Replace every spaces to _ (more readable than =20)
2166      return str_replace(' ', '_', $encoded);
2167  }
2168  
2169  
2170    /**
2171     * Adds a string or binary attachment (non-filesystem) to the list.
2172     * This method can be used to attach ascii or binary data,
2173     * such as a BLOB record from a database.
2174     * @param string $string String attachment data.
2175     * @param string $filename Name of the attachment.
2176     * @param string $encoding File encoding (see $Encoding).
2177     * @param string $type File extension (MIME) type.
2178     * @param string $disposition Disposition to use
2179     * @return void
2180     */
2181    public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = '', $disposition = 'attachment') {
2182      //If a MIME type is not specified, try to work it out from the file name
2183      if ($type == '') {
2184        $type = self::filenameToType($filename);
2185      }
2186      // Append to $attachment array
2187      $this->attachment[] = array(
2188        0 => $string,
2189        1 => $filename,
2190        2 => basename($filename),
2191        3 => $encoding,
2192        4 => $type,
2193        5 => true,  // isStringAttachment
2194        6 => $disposition,
2195        7 => 0
2196      );
2197    }
2198  
2199    /**
2200     * Add an embedded attachment from a file.
2201     * This can include images, sounds, and just about any other document type.
2202     * @param string $path Path to the attachment.
2203     * @param string $cid Content ID of the attachment; Use this to reference
2204     *        the content when using an embedded image in HTML.
2205     * @param string $name Overrides the attachment name.
2206     * @param string $encoding File encoding (see $Encoding).
2207     * @param string $type File MIME type.
2208     * @param string $disposition Disposition to use
2209     * @return bool True on successfully adding an attachment
2210     */
2211    public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') {
2212      if ( !@is_file($path) ) {
2213        $this->SetError($this->Lang('file_access') . $path);
2214        return false;
2215      }
2216  
2217      //If a MIME type is not specified, try to work it out from the file name
2218      if ($type == '') {
2219        $type = self::filenameToType($path);
2220      }
2221  
2222      $filename = basename($path);
2223      if ( $name == '' ) {
2224        $name = $filename;
2225      }
2226  
2227      // Append to $attachment array
2228      $this->attachment[] = array(
2229        0 => $path,
2230        1 => $filename,
2231        2 => $name,
2232        3 => $encoding,
2233        4 => $type,
2234        5 => false,  // isStringAttachment
2235        6 => $disposition,
2236        7 => $cid
2237      );
2238      return true;
2239    }
2240  
2241  
2242    /**
2243     * Add an embedded stringified attachment.
2244     * This can include images, sounds, and just about any other document type.
2245     * Be sure to set the $type to an image type for images:
2246     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2247     * @param string $string The attachment binary data.
2248     * @param string $cid Content ID of the attachment; Use this to reference
2249     *        the content when using an embedded image in HTML.
2250     * @param string $name
2251     * @param string $encoding File encoding (see $Encoding).
2252     * @param string $type MIME type.
2253     * @param string $disposition Disposition to use
2254     * @return bool True on successfully adding an attachment
2255     */
2256    public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') {
2257      //If a MIME type is not specified, try to work it out from the name
2258      if ($type == '') {
2259        $type = self::filenameToType($name);
2260      }
2261  
2262      // Append to $attachment array
2263      $this->attachment[] = array(
2264        0 => $string,
2265        1 => $name,
2266        2 => $name,
2267        3 => $encoding,
2268        4 => $type,
2269        5 => true,  // isStringAttachment
2270        6 => $disposition,
2271        7 => $cid
2272      );
2273      return true;
2274    }
2275  
2276    /**
2277     * Returns true if an inline attachment is present.
2278     * @access public
2279     * @return bool
2280     */
2281    public function InlineImageExists() {
2282      foreach($this->attachment as $attachment) {
2283        if ($attachment[6] == 'inline') {
2284          return true;
2285        }
2286      }
2287      return false;
2288    }
2289  
2290    /**
2291     * Returns true if an attachment (non-inline) is present.
2292     * @return bool
2293     */
2294    public function AttachmentExists() {
2295      foreach($this->attachment as $attachment) {
2296        if ($attachment[6] == 'attachment') {
2297          return true;
2298        }
2299      }
2300      return false;
2301    }
2302  
2303    /**
2304     * Does this message have an alternative body set?
2305     * @return bool
2306     */
2307    public function AlternativeExists() {
2308      return !empty($this->AltBody);
2309    }
2310  
2311    /////////////////////////////////////////////////
2312    // CLASS METHODS, MESSAGE RESET
2313    /////////////////////////////////////////////////
2314  
2315    /**
2316     * Clears all recipients assigned in the TO array.  Returns void.
2317     * @return void
2318     */
2319    public function ClearAddresses() {
2320      foreach($this->to as $to) {
2321        unset($this->all_recipients[strtolower($to[0])]);
2322      }
2323      $this->to = array();
2324    }
2325  
2326    /**
2327     * Clears all recipients assigned in the CC array.  Returns void.
2328     * @return void
2329     */
2330    public function ClearCCs() {
2331      foreach($this->cc as $cc) {
2332        unset($this->all_recipients[strtolower($cc[0])]);
2333      }
2334      $this->cc = array();
2335    }
2336  
2337    /**
2338     * Clears all recipients assigned in the BCC array.  Returns void.
2339     * @return void
2340     */
2341    public function ClearBCCs() {
2342      foreach($this->bcc as $bcc) {
2343        unset($this->all_recipients[strtolower($bcc[0])]);
2344      }
2345      $this->bcc = array();
2346    }
2347  
2348    /**
2349     * Clears all recipients assigned in the ReplyTo array.  Returns void.
2350     * @return void
2351     */
2352    public function ClearReplyTos() {
2353      $this->ReplyTo = array();
2354    }
2355  
2356    /**
2357     * Clears all recipients assigned in the TO, CC and BCC
2358     * array.  Returns void.
2359     * @return void
2360     */
2361    public function ClearAllRecipients() {
2362      $this->to = array();
2363      $this->cc = array();
2364      $this->bcc = array();
2365      $this->all_recipients = array();
2366    }
2367  
2368    /**
2369     * Clears all previously set filesystem, string, and binary
2370     * attachments.  Returns void.
2371     * @return void
2372     */
2373    public function ClearAttachments() {
2374      $this->attachment = array();
2375    }
2376  
2377    /**
2378     * Clears all custom headers.  Returns void.
2379     * @return void
2380     */
2381    public function ClearCustomHeaders() {
2382      $this->CustomHeader = array();
2383    }
2384  
2385    /////////////////////////////////////////////////
2386    // CLASS METHODS, MISCELLANEOUS
2387    /////////////////////////////////////////////////
2388  
2389    /**
2390     * Adds the error message to the error container.
2391     * @access protected
2392     * @param string $msg
2393     * @return void
2394     */
2395    protected function SetError($msg) {
2396      $this->error_count++;
2397      if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2398        $lasterror = $this->smtp->getError();
2399        if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2400          $msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2401        }
2402      }
2403      $this->ErrorInfo = $msg;
2404    }
2405  
2406    /**
2407     * Returns the proper RFC 822 formatted date.
2408     * @access public
2409     * @return string
2410     * @static
2411     */
2412    public static function RFCDate() {
2413      //Set the time zone to whatever the default is to avoid 500 errors
2414      //Will default to UTC if it's not set properly in php.ini
2415      date_default_timezone_set(@date_default_timezone_get());
2416      return date('D, j M Y H:i:s O');
2417    }
2418  
2419    /**
2420     * Returns the server hostname or 'localhost.localdomain' if unknown.
2421     * @access protected
2422     * @return string
2423     */
2424    protected function ServerHostname() {
2425      if (!empty($this->Hostname)) {
2426        $result = $this->Hostname;
2427      } elseif (isset($_SERVER['SERVER_NAME'])) {
2428        $result = $_SERVER['SERVER_NAME'];
2429      } else {
2430        $result = 'localhost.localdomain';
2431      }
2432  
2433      return $result;
2434    }
2435  
2436    /**
2437     * Returns a message in the appropriate language.
2438     * @access protected
2439     * @param string $key
2440     * @return string
2441     */
2442    protected function Lang($key) {
2443      if(count($this->language) < 1) {
2444        $this->SetLanguage('en'); // set the default language
2445      }
2446  
2447      if(isset($this->language[$key])) {
2448        return $this->language[$key];
2449      } else {
2450        return 'Language string failed to load: ' . $key;
2451      }
2452    }
2453  
2454    /**
2455     * Returns true if an error occurred.
2456     * @access public
2457     * @return bool
2458     */
2459    public function IsError() {
2460      return ($this->error_count > 0);
2461    }
2462  
2463    /**
2464     * Changes every end of line from CRLF, CR or LF to $this->LE.
2465     * @access public
2466     * @param string $str String to FixEOL
2467     * @return string
2468     */
2469    public function FixEOL($str) {
2470      // condense down to \n
2471      $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2472      // Now convert LE as needed
2473      if ($this->LE !== "\n") {
2474          $nstr = str_replace("\n", $this->LE, $nstr);
2475      }
2476      return  $nstr;
2477    }
2478  
2479    /**
2480     * Adds a custom header. $name value can be overloaded to contain
2481     * both header name and value (name:value)
2482     * @access public
2483     * @param string $name custom header name
2484     * @param string $value header value
2485     * @return void
2486     */
2487    public function AddCustomHeader($name, $value=null) {
2488      if ($value === null) {
2489          // Value passed in as name:value
2490          $this->CustomHeader[] = explode(':', $name, 2);
2491      } else {
2492          $this->CustomHeader[] = array($name, $value);
2493      }
2494    }
2495  
2496    /**
2497     * Creates a message from an HTML string, making modifications for inline images and backgrounds
2498     * and creates a plain-text version by converting the HTML
2499     * Overwrites any existing values in $this->Body and $this->AltBody
2500     * @access public
2501     * @param string $message HTML message string
2502     * @param string $basedir baseline directory for path
2503     * @param bool $advanced Whether to use the advanced HTML to text converter
2504     * @return string $message
2505     */
2506    public function MsgHTML($message, $basedir = '', $advanced = false) {
2507      preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
2508      if (isset($images[2])) {
2509        foreach ($images[2] as $i => $url) {
2510          // do not change urls for absolute images (thanks to corvuscorax)
2511          if (!preg_match('#^[A-z]+://#', $url)) {
2512            $filename = basename($url);
2513            $directory = dirname($url);
2514            if ($directory == '.') {
2515              $directory = '';
2516            }
2517            $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2
2518            if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2519              $basedir .= '/';
2520            }
2521            if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2522              $directory .= '/';
2523            }
2524            if ($this->AddEmbeddedImage($basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)))) {
2525              $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"cid:".$cid."\"", $message);
2526            }
2527          }
2528        }
2529      }
2530      $this->IsHTML(true);
2531      if (empty($this->AltBody)) {
2532        $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
2533      }
2534      //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2535      $this->Body = $this->NormalizeBreaks($message);
2536      $this->AltBody = $this->NormalizeBreaks($this->html2text($message, $advanced));
2537      return $this->Body;
2538    }
2539  
2540      /**
2541       * Convert an HTML string into a plain text version
2542       * @param string $html The HTML text to convert
2543       * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
2544       * @return string
2545       */
2546    public function html2text($html, $advanced = false) {
2547      if ($advanced) {
2548        require_once 'extras/class.html2text.php';
2549        $h = new html2text($html);
2550        return $h->get_text();
2551      }
2552      return html_entity_decode(trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet);
2553    }
2554  
2555    /**
2556     * Gets the MIME type of the embedded or inline image
2557     * @param string $ext File extension
2558     * @access public
2559     * @return string MIME type of ext
2560     * @static
2561     */
2562    public static function _mime_types($ext = '') {
2563      $mimes = array(
2564        'xl'    =>  'application/excel',
2565        'hqx'   =>  'application/mac-binhex40',
2566        'cpt'   =>  'application/mac-compactpro',
2567        'bin'   =>  'application/macbinary',
2568        'doc'   =>  'application/msword',
2569        'word'  =>  'application/msword',
2570        'class' =>  'application/octet-stream',
2571        'dll'   =>  'application/octet-stream',
2572        'dms'   =>  'application/octet-stream',
2573        'exe'   =>  'application/octet-stream',
2574        'lha'   =>  'application/octet-stream',
2575        'lzh'   =>  'application/octet-stream',
2576        'psd'   =>  'application/octet-stream',
2577        'sea'   =>  'application/octet-stream',
2578        'so'    =>  'application/octet-stream',
2579        'oda'   =>  'application/oda',
2580        'pdf'   =>  'application/pdf',
2581        'ai'    =>  'application/postscript',
2582        'eps'   =>  'application/postscript',
2583        'ps'    =>  'application/postscript',
2584        'smi'   =>  'application/smil',
2585        'smil'  =>  'application/smil',
2586        'mif'   =>  'application/vnd.mif',
2587        'xls'   =>  'application/vnd.ms-excel',
2588        'ppt'   =>  'application/vnd.ms-powerpoint',
2589        'wbxml' =>  'application/vnd.wap.wbxml',
2590        'wmlc'  =>  'application/vnd.wap.wmlc',
2591        'dcr'   =>  'application/x-director',
2592        'dir'   =>  'application/x-director',
2593        'dxr'   =>  'application/x-director',
2594        'dvi'   =>  'application/x-dvi',
2595        'gtar'  =>  'application/x-gtar',
2596        'php3'  =>  'application/x-httpd-php',
2597        'php4'  =>  'application/x-httpd-php',
2598        'php'   =>  'application/x-httpd-php',
2599        'phtml' =>  'application/x-httpd-php',
2600        'phps'  =>  'application/x-httpd-php-source',
2601        'js'    =>  'application/x-javascript',
2602        'swf'   =>  'application/x-shockwave-flash',
2603        'sit'   =>  'application/x-stuffit',
2604        'tar'   =>  'application/x-tar',
2605        'tgz'   =>  'application/x-tar',
2606        'xht'   =>  'application/xhtml+xml',
2607        'xhtml' =>  'application/xhtml+xml',
2608        'zip'   =>  'application/zip',
2609        'mid'   =>  'audio/midi',
2610        'midi'  =>  'audio/midi',
2611        'mp2'   =>  'audio/mpeg',
2612        'mp3'   =>  'audio/mpeg',
2613        'mpga'  =>  'audio/mpeg',
2614        'aif'   =>  'audio/x-aiff',
2615        'aifc'  =>  'audio/x-aiff',
2616        'aiff'  =>  'audio/x-aiff',
2617        'ram'   =>  'audio/x-pn-realaudio',
2618        'rm'    =>  'audio/x-pn-realaudio',
2619        'rpm'   =>  'audio/x-pn-realaudio-plugin',
2620        'ra'    =>  'audio/x-realaudio',
2621        'wav'   =>  'audio/x-wav',
2622        'bmp'   =>  'image/bmp',
2623        'gif'   =>  'image/gif',
2624        'jpeg'  =>  'image/jpeg',
2625        'jpe'   =>  'image/jpeg',
2626        'jpg'   =>  'image/jpeg',
2627        'png'   =>  'image/png',
2628        'tiff'  =>  'image/tiff',
2629        'tif'   =>  'image/tiff',
2630        'eml'   =>  'message/rfc822',
2631        'css'   =>  'text/css',
2632        'html'  =>  'text/html',
2633        'htm'   =>  'text/html',
2634        'shtml' =>  'text/html',
2635        'log'   =>  'text/plain',
2636        'text'  =>  'text/plain',
2637        'txt'   =>  'text/plain',
2638        'rtx'   =>  'text/richtext',
2639        'rtf'   =>  'text/rtf',
2640        'xml'   =>  'text/xml',
2641        'xsl'   =>  'text/xml',
2642        'mpeg'  =>  'video/mpeg',
2643        'mpe'   =>  'video/mpeg',
2644        'mpg'   =>  'video/mpeg',
2645        'mov'   =>  'video/quicktime',
2646        'qt'    =>  'video/quicktime',
2647        'rv'    =>  'video/vnd.rn-realvideo',
2648        'avi'   =>  'video/x-msvideo',
2649        'movie' =>  'video/x-sgi-movie'
2650      );
2651      return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
2652    }
2653  
2654    /**
2655     * Try to map a file name to a MIME type, default to application/octet-stream
2656     * @param string $filename A file name or full path, does not need to exist as a file
2657     * @return string
2658     * @static
2659     */
2660    public static function filenameToType($filename) {
2661      //In case the path is a URL, strip any query string before getting extension
2662      $qpos = strpos($filename, '?');
2663      if ($qpos !== false) {
2664        $filename = substr($filename, 0, $qpos);
2665      }
2666      $pathinfo = self::mb_pathinfo($filename);
2667      return self::_mime_types($pathinfo['extension']);
2668    }
2669  
2670    /**
2671     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
2672     * Works similarly to the one in PHP >= 5.2.0
2673     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
2674     * @param string $path A filename or path, does not need to exist as a file
2675     * @param integer|string $options Either a PATHINFO_* constant, or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
2676     * @return string|array
2677     * @static
2678     */
2679    public static function mb_pathinfo($path, $options = null) {
2680      $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
2681      $m = array();
2682      preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
2683      if(array_key_exists(1, $m)) {
2684        $ret['dirname'] = $m[1];
2685      }
2686      if(array_key_exists(2, $m)) {
2687        $ret['basename'] = $m[2];
2688      }
2689      if(array_key_exists(5, $m)) {
2690        $ret['extension'] = $m[5];
2691      }
2692      if(array_key_exists(3, $m)) {
2693        $ret['filename'] = $m[3];
2694      }
2695      switch($options) {
2696        case PATHINFO_DIRNAME:
2697        case 'dirname':
2698          return $ret['dirname'];
2699          break;
2700        case PATHINFO_BASENAME:
2701        case 'basename':
2702          return $ret['basename'];
2703          break;
2704        case PATHINFO_EXTENSION:
2705        case 'extension':
2706          return $ret['extension'];
2707          break;
2708        case PATHINFO_FILENAME:
2709        case 'filename':
2710          return $ret['filename'];
2711          break;
2712        default:
2713          return $ret;
2714      }
2715    }
2716  
2717    /**
2718     * Set (or reset) Class Objects (variables)
2719     *
2720     * Usage Example:
2721     * $page->set('X-Priority', '3');
2722     *
2723     * @access public
2724     * @param string $name
2725     * @param mixed $value
2726     * NOTE: will not work with arrays, there are no arrays to set/reset
2727     * @throws phpmailerException
2728     * @return bool
2729     * @todo Should this not be using __set() magic function?
2730     */
2731    public function set($name, $value = '') {
2732      try {
2733        if (isset($this->$name) ) {
2734          $this->$name = $value;
2735        } else {
2736          throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);
2737        }
2738      } catch (Exception $e) {
2739        $this->SetError($e->getMessage());
2740        if ($e->getCode() == self::STOP_CRITICAL) {
2741          return false;
2742        }
2743      }
2744      return true;
2745    }
2746  
2747    /**
2748     * Strips newlines to prevent header injection.
2749     * @access public
2750     * @param string $str
2751     * @return string
2752     */
2753    public function SecureHeader($str) {
2754      return trim(str_replace(array("\r", "\n"), '', $str));
2755    }
2756  
2757    /**
2758     * Normalize UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format
2759     * Defaults to CRLF (for message bodies) and preserves consecutive breaks
2760     * @param string $text
2761     * @param string $breaktype What kind of line break to use, defaults to CRLF
2762     * @return string
2763     * @access public
2764     * @static
2765     */
2766    public static function NormalizeBreaks($text, $breaktype = "\r\n") {
2767      return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
2768    }
2769  
2770  
2771      /**
2772     * Set the private key file and password to sign the message.
2773     *
2774     * @access public
2775     * @param string $cert_filename
2776     * @param string $key_filename
2777     * @param string $key_pass Password for private key
2778     */
2779    public function Sign($cert_filename, $key_filename, $key_pass) {
2780      $this->sign_cert_file = $cert_filename;
2781      $this->sign_key_file = $key_filename;
2782      $this->sign_key_pass = $key_pass;
2783    }
2784  
2785    /**
2786     * Set the private key file and password to sign the message.
2787     *
2788     * @access public
2789     * @param string $txt
2790     * @return string
2791     */
2792    public function DKIM_QP($txt) {
2793      $line = '';
2794      for ($i = 0; $i < strlen($txt); $i++) {
2795        $ord = ord($txt[$i]);
2796        if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {
2797          $line .= $txt[$i];
2798        } else {
2799          $line .= "=".sprintf("%02X", $ord);
2800        }
2801      }
2802      return $line;
2803    }
2804  
2805    /**
2806     * Generate DKIM signature
2807     *
2808     * @access public
2809     * @param string $s Header
2810     * @throws phpmailerException
2811     * @return string
2812     */
2813    public function DKIM_Sign($s) {
2814      if (!defined('PKCS7_TEXT')) {
2815          if ($this->exceptions) {
2816              throw new phpmailerException($this->Lang("signing").' OpenSSL extension missing.');
2817          }
2818          return '';
2819      }
2820      $privKeyStr = file_get_contents($this->DKIM_private);
2821      if ($this->DKIM_passphrase != '') {
2822        $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
2823      } else {
2824        $privKey = $privKeyStr;
2825      }
2826      if (openssl_sign($s, $signature, $privKey)) {
2827        return base64_encode($signature);
2828      }
2829      return '';
2830    }
2831  
2832    /**
2833     * Generate DKIM Canonicalization Header
2834     *
2835     * @access public
2836     * @param string $s Header
2837     * @return string
2838     */
2839    public function DKIM_HeaderC($s) {
2840      $s = preg_replace("/\r\n\s+/", " ", $s);
2841      $lines = explode("\r\n", $s);
2842      foreach ($lines as $key => $line) {
2843        list($heading, $value) = explode(":", $line, 2);
2844        $heading = strtolower($heading);
2845        $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces
2846        $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value
2847      }
2848      $s = implode("\r\n", $lines);
2849      return $s;
2850    }
2851  
2852    /**
2853     * Generate DKIM Canonicalization Body
2854     *
2855     * @access public
2856     * @param string $body Message Body
2857     * @return string
2858     */
2859    public function DKIM_BodyC($body) {
2860      if ($body == '') return "\r\n";
2861      // stabilize line endings
2862      $body = str_replace("\r\n", "\n", $body);
2863      $body = str_replace("\n", "\r\n", $body);
2864      // END stabilize line endings
2865      while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
2866        $body = substr($body, 0, strlen($body) - 2);
2867      }
2868      return $body;
2869    }
2870  
2871    /**
2872     * Create the DKIM header, body, as new header
2873     *
2874     * @access public
2875     * @param string $headers_line Header lines
2876     * @param string $subject Subject
2877     * @param string $body Body
2878     * @return string
2879     */
2880    public function DKIM_Add($headers_line, $subject, $body) {
2881      $DKIMsignatureType    = 'rsa-sha1'; // Signature & hash algorithms
2882      $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
2883      $DKIMquery            = 'dns/txt'; // Query method
2884      $DKIMtime             = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
2885      $subject_header       = "Subject: $subject";
2886      $headers              = explode($this->LE, $headers_line);
2887      $from_header          = '';
2888      $to_header            = '';
2889      $current = '';
2890      foreach($headers as $header) {
2891        if (strpos($header, 'From:') === 0) {
2892          $from_header = $header;
2893          $current = 'from_header';
2894        } elseif (strpos($header, 'To:') === 0) {
2895          $to_header = $header;
2896          $current = 'to_header';
2897        } else {
2898          if($current && strpos($header, ' =?') === 0){
2899            $current .= $header;
2900          } else {
2901            $current = '';
2902          }
2903        }
2904      }
2905      $from     = str_replace('|', '=7C', $this->DKIM_QP($from_header));
2906      $to       = str_replace('|', '=7C', $this->DKIM_QP($to_header));
2907      $subject  = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable
2908      $body     = $this->DKIM_BodyC($body);
2909      $DKIMlen  = strlen($body) ; // Length of body
2910      $DKIMb64  = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body
2911      $ident    = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";
2912      $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".
2913                  "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".
2914                  "\th=From:To:Subject;\r\n".
2915                  "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".
2916                  "\tz=$from\r\n".
2917                  "\t|$to\r\n".
2918                  "\t|$subject;\r\n".
2919                  "\tbh=" . $DKIMb64 . ";\r\n".
2920                  "\tb=";
2921      $toSign   = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);
2922      $signed   = $this->DKIM_Sign($toSign);
2923      return $dkimhdrs.$signed."\r\n";
2924    }
2925  
2926    /**
2927     * Perform callback
2928     * @param boolean $isSent
2929     * @param string $to
2930     * @param string $cc
2931     * @param string $bcc
2932     * @param string $subject
2933     * @param string $body
2934     * @param string $from
2935     */
2936    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) {
2937      if (!empty($this->action_function) && is_callable($this->action_function)) {
2938        $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
2939        call_user_func_array($this->action_function, $params);
2940      }
2941    }
2942  }
2943  
2944  /**
2945   * Exception handler for PHPMailer
2946   * @package PHPMailer
2947   */
2948  class phpmailerException extends Exception {
2949    /**
2950     * Prettify error message output
2951     * @return string
2952     */
2953    public function errorMessage() {
2954      $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
2955      return $errorMsg;
2956    }
2957  }


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1