Make WordPress Core

Ticket #25560: ticket-25560-phpmailer.5.patch

File ticket-25560-phpmailer.5.patch, 263.0 KB (added by bpetty, 11 years ago)
  • src/wp-includes/class-phpmailer.php

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