WordPress.org

Make WordPress Core

Ticket #25014: 25014.3.patch

File 25014.3.patch, 263.1 KB (added by MattyRob, 6 years ago)
  • wp-includes/class-phpmailer.php

     
    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 {
     37class PHPMailer
     38{
     39    /**
     40     * The PHPMailer Version number.
     41     * @type string
     42     */
     43    public $Version = '5.2.7';
    4444
    45   /////////////////////////////////////////////////
    46   // PROPERTIES, PUBLIC
    47   /////////////////////////////////////////////////
     45    /**
     46     * Email priority.
     47     * Options: 1 = High, 3 = Normal, 5 = low.
     48     * @type int
     49     */
     50    public $Priority = 3;
    4851
    49   /**
    50    * Email priority (1 = High, 3 = Normal, 5 = low).
    51    * @var int
    52    */
    53   public $Priority          = 3;
     52    /**
     53     * The character set of the message.
     54     * @type string
     55     */
     56    public $CharSet = 'iso-8859-1';
    5457
    55   /**
    56    * Sets the CharSet of the message.
    57    * @var string
    58    */
    59   public $CharSet           = 'iso-8859-1';
     58    /**
     59     * The MIME Content-type of the message.
     60     * @type string
     61     */
     62    public $ContentType = 'text/plain';
    6063
    61   /**
    62    * Sets the Content-type of the message.
    63    * @var string
    64    */
    65   public $ContentType       = 'text/plain';
     64    /**
     65     * The message encoding.
     66     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
     67     * @type string
     68     */
     69    public $Encoding = '8bit';
    6670
    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';
     71    /**
     72     * Holds the most recent mailer error message.
     73     * @type string
     74     */
     75    public $ErrorInfo = '';
    7376
    74   /**
    75    * Holds the most recent mailer error message.
    76    * @var string
    77    */
    78   public $ErrorInfo         = '';
     77    /**
     78     * The From email address for the message.
     79     * @type string
     80     */
     81    public $From = 'root@localhost';
    7982
    80   /**
    81    * Sets the From email address for the message.
    82    * @var string
    83    */
    84   public $From              = 'root@localhost';
     83    /**
     84     * The From name of the message.
     85     * @type string
     86     */
     87    public $FromName = 'Root User';
    8588
    86   /**
    87    * Sets the From name of the message.
    88    * @var string
    89    */
    90   public $FromName          = 'Root User';
     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 = '';
    9195
    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            = '';
     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 = '';
    98102
    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        = '';
     103    /**
     104     * The Subject of the message.
     105     * @type string
     106     */
     107    public $Subject = '';
    105108
    106   /**
    107    * Sets the Subject of the message.
    108    * @var string
    109    */
    110   public $Subject           = '';
     109    /**
     110     * An HTML or plain text message body.
     111     * If HTML then call isHTML(true).
     112     * @type string
     113     */
     114    public $Body = '';
    111115
    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              = '';
     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 = '';
    118124
    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           = '';
     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 = '';
    127134
    128   /**
    129    * Stores the complete compiled MIME message body.
    130    * @var string
    131    * @access protected
    132    */
    133   protected $MIMEBody      = '';
     135    /**
     136     * The complete compiled MIME message body.
     137     * @access protected
     138     * @type string
     139     */
     140    protected $MIMEBody = '';
    134141
    135   /**
    136    * Stores the complete compiled MIME message headers.
    137    * @var string
    138    * @access protected
    139    */
    140   protected $MIMEHeader    = '';
     142    /**
     143     * The complete compiled MIME message headers.
     144     * @type string
     145     * @access protected
     146     */
     147    protected $MIMEHeader = '';
    141148
    142   /**
    143    * Stores the extra header list which CreateHeader() doesn't fold in
    144    * @var string
    145    * @access protected
    146   */
    147   protected $mailHeader    = '';
     149    /**
     150     * Extra headers that createHeader() doesn't fold in.
     151     * @type string
     152     * @access protected
     153     */
     154    protected $mailHeader = '';
    148155
    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;
     156    /**
     157     * Word-wrap the message body to this number of chars.
     158     * @type int
     159     */
     160    public $WordWrap = 0;
    155161
    156   /**
    157    * Method to send mail: ("mail", "sendmail", or "smtp").
    158    * @var string
    159    */
    160   public $Mailer            = 'mail';
     162    /**
     163     * Which method to use to send mail.
     164     * Options: "mail", "sendmail", or "smtp".
     165     * @type string
     166     */
     167    public $Mailer = 'mail';
    161168
    162   /**
    163    * Sets the path of the sendmail program.
    164    * @var string
    165    */
    166   public $Sendmail          = '/usr/sbin/sendmail';
     169    /**
     170     * The path to the sendmail program.
     171     * @type string
     172     */
     173    public $Sendmail = '/usr/sbin/sendmail';
    167174
    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         = '';
     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;
    181181
    182   /**
    183    * Sets the email address that a reading confirmation will be sent.
    184    * @var string
    185    */
    186   public $ConfirmReadingTo  = '';
     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 = '';
    187189
    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          = '';
     190    /**
     191     * The email address that a reading confirmation should be sent to.
     192     * @type string
     193     */
     194    public $ConfirmReadingTo = '';
    195195
    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         = '';
     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 = '';
    202204
    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      = '';
     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 = '';
    209211
    210   /////////////////////////////////////////////////
    211   // PROPERTIES FOR SMTP
    212   /////////////////////////////////////////////////
     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 = '';
    213218
    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';
     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';
    225229
    226   /**
    227    * Sets the default SMTP server port.
    228    * @var int
    229    */
    230   public $Port          = 25;
     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;
    231236
    232   /**
    233    * Sets the SMTP HELO of the message (Default is $Hostname).
    234    * @var string
    235    */
    236   public $Helo          = '';
     237    /**
     238     * The SMTP HELO of the message.
     239     * Default is $Hostname.
     240     * @type string
     241     * @see PHPMailer::$Hostname
     242     */
     243    public $Helo = '';
    237244
    238   /**
    239    * Sets connection prefix. Options are "", "ssl" or "tls"
    240    * @var string
    241    */
    242   public $SMTPSecure    = '';
     245    /**
     246     * The secure connection prefix.
     247     * Options: "", "ssl" or "tls"
     248     * @type string
     249     */
     250    public $SMTPSecure = '';
    243251
    244   /**
    245    * Sets SMTP authentication. Utilizes the Username and Password variables.
    246    * @var bool
    247    */
    248   public $SMTPAuth      = false;
     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;
    249260
    250   /**
    251    * Sets SMTP username.
    252    * @var string
    253    */
    254   public $Username      = '';
     261    /**
     262     * SMTP username.
     263     * @type string
     264     */
     265    public $Username = '';
    255266
    256   /**
    257    * Sets SMTP password.
    258    * @var string
    259    */
    260   public $Password      = '';
     267    /**
     268     * SMTP password.
     269     * @type string
     270     */
     271    public $Password = '';
    261272
    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     * SMTP auth type.
     275     * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
     276     * @type string
     277     */
     278    public $AuthType = '';
    273279
    274   /**
    275    *  Sets SMTP workstation.
    276    *  @var string
    277    */
    278   public $Workstation   = '';
     280    /**
     281     * SMTP realm.
     282     * Used for NTLM auth
     283     * @type string
     284     */
     285    public $Realm = '';
    279286
    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;
     287    /**
     288     * SMTP workstation.
     289     * Used for NTLM auth
     290     * @type string
     291     */
     292    public $Workstation = '';
    286293
    287   /**
    288    * Sets SMTP class debugging on or off.
    289    * @var bool
    290    */
    291   public $SMTPDebug     = false;
     294    /**
     295     * The SMTP server timeout in seconds.
     296     * @type int
     297     */
     298    public $Timeout = 10;
    292299
    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";
     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;
    299307
    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;
     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";
    307315
    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;
     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;
    314323
    315    /**
    316    * If SingleTo is true, this provides the array to hold the email addresses
    317    * @var bool
    318    */
    319   public $SingleToArray = array();
     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;
    320330
    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";
     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();
    328337
    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   = '';
     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;
    337345
    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   = '';
     346    /**
     347     * Whether to allow sending messages with an empty body.
     348     * @type bool
     349     */
     350    public $AllowEmpty = false;
    344351
    345   /**
    346    * Used with DKIM Signing
    347    * optional parameter if your private key requires a passphras
    348    * @var string
    349    */
    350   public $DKIM_passphrase   = '';
     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";
    351359
    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     = '';
     360    /**
     361     * DKIM selector.
     362     * @type string
     363     */
     364    public $DKIM_selector = '';
    358365
    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    = '';
     366    /**
     367     * DKIM Identity.
     368     * Usually the email address used as the source of the email
     369     * @type string
     370     */
     371    public $DKIM_identity = '';
    365372
    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';
     373    /**
     374     * DKIM passphrase.
     375     * Used if your key is encrypted.
     376     * @type string
     377     */
     378    public $DKIM_passphrase = '';
    388379
    389   /**
    390    * Sets the PHPMailer Version number
    391    * @var string
    392    */
    393   public $Version         = '5.2.4';
     380    /**
     381     * DKIM signing domain name.
     382     * @example 'example.com'
     383     * @type string
     384     */
     385    public $DKIM_domain = '';
    394386
    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        = '';
     387    /**
     388     * DKIM private key file path.
     389     * @type string
     390     */
     391    public $DKIM_private = '';
    400392
    401   /////////////////////////////////////////////////
    402   // PROPERTIES, PRIVATE AND PROTECTED
    403   /////////////////////////////////////////////////
     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 = '';
    404417
    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;
     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 = '';
    485424
    486   /////////////////////////////////////////////////
    487   // CONSTANTS
    488   /////////////////////////////////////////////////
     425    /**
     426     * An instance of the SMTP sender class.
     427     * @type SMTP
     428     * @access protected
     429     */
     430    protected $smtp = null;
    489431
    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   /////////////////////////////////////////////////
     432    /**
     433     * The array of 'to' addresses.
     434     * @type array
     435     * @access protected
     436     */
     437    protected $to = array();
    498438
    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   }
     439    /**
     440     * The array of 'cc' addresses.
     441     * @type array
     442     * @access protected
     443     */
     444    protected $cc = array();
    520445
    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   }
     446    /**
     447     * The array of 'bcc' addresses.
     448     * @type array
     449     * @access protected
     450     */
     451    protected $bcc = array();
    532452
    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   }
     453    /**
     454     * The array of reply-to names and addresses.
     455     * @type array
     456     * @access protected
     457     */
     458    protected $ReplyTo = array();
    540459
    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   }
     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();
    553467
    554   /**
    555    * Sets Mailer to send message using SMTP.
    556    * @return void
    557    */
    558   public function IsSMTP() {
    559     $this->Mailer = 'smtp';
    560   }
     468    /**
     469     * The array of attachments.
     470     * @type array
     471     * @access protected
     472     */
     473    protected $attachment = array();
    561474
    562   /**
    563    * Sets Mailer to send message using PHP mail() function.
    564    * @return void
    565    */
    566   public function IsMail() {
    567     $this->Mailer = 'mail';
    568   }
     475    /**
     476     * The array of custom headers.
     477     * @type array
     478     * @access protected
     479     */
     480    protected $CustomHeader = array();
    569481
    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   }
     482    /**
     483     * The most recent Message-ID (including angular brackets).
     484     * @type string
     485     * @access protected
     486     */
     487    protected $lastMessageID = '';
    580488
    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   }
     489    /**
     490     * The message's MIME type.
     491     * @type string
     492     * @access protected
     493     */
     494    protected $message_type = '';
    591495
    592   /////////////////////////////////////////////////
    593   // METHODS, RECIPIENTS
    594   /////////////////////////////////////////////////
     496    /**
     497     * The array of MIME boundary strings.
     498     * @type array
     499     * @access protected
     500     */
     501    protected $boundary = array();
    595502
    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   }
     503    /**
     504     * The array of available languages.
     505     * @type array
     506     * @access protected
     507     */
     508    protected $language = array();
    605509
    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   }
     510    /**
     511     * The number of errors encountered.
     512     * @type integer
     513     * @access protected
     514     */
     515    protected $error_count = 0;
    616516
    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   }
     517    /**
     518     * The S/MIME certificate file path.
     519     * @type string
     520     * @access protected
     521     */
     522    protected $sign_cert_file = '';
    627523
    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   }
     524    /**
     525     * The S/MIME key file path.
     526     * @type string
     527     * @access protected
     528     */
     529    protected $sign_key_file = '';
    637530
    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 }
     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 = '';
    685538
    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   }
     539    /**
     540     * Whether to throw exceptions for errors.
     541     * @type bool
     542     * @access protected
     543     */
     544    protected $exceptions = false;
    719545
    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;
    741         } 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   }
     546    /**
     547     * Error severity: message only, continue processing
     548     */
     549    const STOP_MESSAGE = 0;
    748550
    749   /////////////////////////////////////////////////
    750   // METHODS, MAIL SENDING
    751   /////////////////////////////////////////////////
     551    /**
     552     * Error severity: message, likely ok to continue processing
     553     */
     554    const STOP_CONTINUE = 1;
    752555
    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   }
     556    /**
     557     * Error severity: message, plus full stop, critical error reached
     558     */
     559    const STOP_CRITICAL = 2;
    773560
    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       }
     561    /**
     562     * SMTP RFC standard line ending
     563     */
     564    const CRLF = "\r\n";
    785565
    786       // Set whether the message is multipart/alternative
    787       if(!empty($this->AltBody)) {
    788         $this->ContentType = 'multipart/alternative';
    789       }
     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        //Make sure our autoloader is loaded
     574        if (!in_array('PHPMailerAutoload', spl_autoload_functions())) {
     575            require 'PHPMailerAutoload.php';
     576        }
     577    }
    790578
    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       }
     579    /**
     580     * Destructor.
     581     */
     582    public function __destruct()
     583    {
     584        if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
     585            $this->smtpClose();
     586        }
     587    }
    797588
    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);
     589    /**
     590     * Call mail() in a safe_mode-aware fashion.
     591     * Also, unless sendmail_path points to sendmail (or something that
     592     * claims to be sendmail), don't pass params (not a perfect fix,
     593     * but it will do)
     594     * @param string $to To
     595     * @param string $subject Subject
     596     * @param string $body Message Body
     597     * @param string $header Additional Header(s)
     598     * @param string $params Params
     599     * @access private
     600     * @return bool
     601     */
     602    private function mailPassthru($to, $subject, $body, $header, $params)
     603    {
     604        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
     605            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);
    806606        } else {
    807           $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;");
     607            $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);
    808608        }
    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       }
     609        return $rt;
     610    }
    814611
    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       }
     612    /**
     613     * Output debugging info via user-defined method.
     614     * Only if debug output is enabled.
     615     * @see PHPMailer::$Debugoutput
     616     * @see PHPMailer::$SMTPDebug
     617     * @param string $str
     618     */
     619    protected function edebug($str)
     620    {
     621        if (!$this->SMTPDebug) {
     622            return;
     623        }
     624        switch ($this->Debugoutput) {
     625            case 'error_log':
     626                error_log($str);
     627                break;
     628            case 'html':
     629                //Cleans up output a bit for a better looking display that's HTML-safe
     630                echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n";
     631                break;
     632            case 'echo':
     633            default:
     634                //Just echoes exactly what was received
     635                echo $str;
     636        }
     637    }
    820638
    821       return true;
     639    /**
     640     * Sets message type to HTML or plain.
     641     * @param bool $ishtml True for HTML mode.
     642     * @return void
     643     */
     644    public function isHTML($ishtml = true)
     645    {
     646        if ($ishtml) {
     647            $this->ContentType = 'text/html';
     648        } else {
     649            $this->ContentType = 'text/plain';
     650        }
     651    }
    822652
    823     } catch (phpmailerException $e) {
    824       $this->SetError($e->getMessage());
    825       if ($this->exceptions) {
    826         throw $e;
    827       }
    828       return false;
     653    /**
     654     * Send messages using SMTP.
     655     * @return void
     656     */
     657    public function isSMTP()
     658    {
     659        $this->Mailer = 'smtp';
    829660    }
    830   }
    831661
    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       }
     662    /**
     663     * Send messages using PHP's mail() function.
     664     * @return void
     665     */
     666    public function isMail()
     667    {
     668        $this->Mailer = 'mail';
    859669    }
    860     return false;
    861   }
    862670
    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));
     671    /**
     672     * Send messages using $Sendmail.
     673     * @return void
     674     */
     675    public function isSendmail()
     676    {
     677        if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
     678            $this->Sendmail = '/var/qmail/bin/sendmail';
     679        }
     680        $this->Mailer = 'sendmail';
    876681    }
    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);
     682
     683    /**
     684     * Send messages using qmail.
     685     * @return void
     686     */
     687    public function isQmail()
     688    {
     689        if (stristr(ini_get('sendmail_path'), 'qmail')) {
     690            $this->Sendmail = '/var/qmail/bin/sendmail';
    881691        }
    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       }
     692        $this->Mailer = 'sendmail';
    906693    }
    907     return true;
    908   }
    909694
    910   /**
    911    * Sends mail using the PHP mail() function.
    912    * @param string $header The message headers
    913    * @param string $body The message body
    914      * @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);
     695    /**
     696     * Add a "To" address.
     697     * @param string $address
     698     * @param string $name
     699     * @return bool true on success, false if address already used
     700     */
     701    public function addAddress($address, $name = '')
     702    {
     703        return $this->addAnAddress('to', $address, $name);
    922704    }
    923     $to = implode(', ', $toArr);
    924705
    925     if (empty($this->Sender)) {
    926       $params = "-oi ";
    927     } else {
    928       $params = sprintf("-oi -f%s", $this->Sender);
     706    /**
     707     * Add a "CC" address.
     708     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
     709     * @param string $address
     710     * @param string $name
     711     * @return bool true on success, false if address already used
     712     */
     713    public function addCC($address, $name = '')
     714    {
     715        return $this->addAnAddress('cc', $address, $name);
    929716    }
    930     if ($this->Sender != '' and !ini_get('safe_mode')) {
    931       $old_from = ini_get('sendmail_from');
    932       ini_set('sendmail_from', $this->Sender);
     717
     718    /**
     719     * Add a "BCC" address.
     720     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
     721     * @param string $address
     722     * @param string $name
     723     * @return bool true on success, false if address already used
     724     */
     725    public function addBCC($address, $name = '')
     726    {
     727        return $this->addAnAddress('bcc', $address, $name);
    933728    }
    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);
     729
     730    /**
     731     * Add a "Reply-to" address.
     732     * @param string $address
     733     * @param string $name
     734     * @return bool
     735     */
     736    public function addReplyTo($address, $name = '')
     737    {
     738        return $this->addAnAddress('Reply-To', $address, $name);
    947739    }
    948     if (isset($old_from)) {
    949       ini_set('sendmail_from', $old_from);
     740
     741    /**
     742     * Add an address to one of the recipient arrays.
     743     * Addresses that have been added already return false, but do not throw exceptions
     744     * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
     745     * @param string $address The email address to send to
     746     * @param string $name
     747     * @throws phpmailerException
     748     * @return bool true on success, false if address already used or invalid in some way
     749     * @access protected
     750     */
     751    protected function addAnAddress($kind, $address, $name = '')
     752    {
     753        if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
     754            $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
     755            if ($this->exceptions) {
     756                throw new phpmailerException('Invalid recipient array: ' . $kind);
     757            }
     758            $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
     759            return false;
     760        }
     761        $address = trim($address);
     762        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
     763        if (!$this->validateAddress($address)) {
     764            $this->setError($this->lang('invalid_address') . ': ' . $address);
     765            if ($this->exceptions) {
     766                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
     767            }
     768            $this->edebug($this->lang('invalid_address') . ': ' . $address);
     769            return false;
     770        }
     771        if ($kind != 'Reply-To') {
     772            if (!isset($this->all_recipients[strtolower($address)])) {
     773                array_push($this->$kind, array($address, $name));
     774                $this->all_recipients[strtolower($address)] = true;
     775                return true;
     776            }
     777        } else {
     778            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
     779                $this->ReplyTo[strtolower($address)] = array($address, $name);
     780                return true;
     781            }
     782        }
     783        return false;
    950784    }
    951     if(!$rt) {
    952       throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);
    953     }
    954     return true;
    955   }
    956785
    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);
     786    /**
     787     * Set the From and FromName properties.
     788     * @param string $address
     789     * @param string $name
     790     * @param bool $auto Whether to also set the Sender address, defaults to true
     791     * @throws phpmailerException
     792     * @return bool
     793     */
     794    public function setFrom($address, $name = '', $auto = true)
     795    {
     796        $address = trim($address);
     797        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
     798        if (!$this->validateAddress($address)) {
     799            $this->setError($this->lang('invalid_address') . ': ' . $address);
     800            if ($this->exceptions) {
     801                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
     802            }
     803            $this->edebug($this->lang('invalid_address') . ': ' . $address);
     804            return false;
     805        }
     806        $this->From = $address;
     807        $this->FromName = $name;
     808        if ($auto) {
     809            if (empty($this->Sender)) {
     810                $this->Sender = $address;
     811            }
     812        }
     813        return true;
    973814    }
    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     }
    979815
    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       }
     816    /**
     817     * Return the Message-ID header of the last email.
     818     * Technically this is the value from the last time the headers were created,
     819     * but it's also the message ID of the last sent message except in
     820     * pathological cases.
     821     * @return string
     822     */
     823    public function getLastMessageID()
     824    {
     825        return $this->lastMessageID;
    992826    }
    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     }
    1017827
    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);
     828    /**
     829     * Check that a string looks like an email address.
     830     * @param string $address The email address to check
     831     * @param string $patternselect A selector for the validation pattern to use :
     832     *   'auto' - pick best one automatically;
     833     *   'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
     834     *   'pcre' - use old PCRE implementation;
     835     *   'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough;
     836     *   'noregex' - super fast, really dumb.
     837     * @return bool
     838     * @static
     839     * @access public
     840     */
     841    public static function validateAddress($address, $patternselect = 'auto')
     842    {
     843        if ($patternselect == 'auto') {
     844            if (defined(
     845                'PCRE_VERSION'
     846            )
     847            ) { //Check this instead of extension_loaded so it works when that function is disabled
     848                if (version_compare(PCRE_VERSION, '8.0') >= 0) {
     849                    $patternselect = 'pcre8';
     850                } else {
     851                    $patternselect = 'pcre';
     852                }
     853            } else {
     854                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
     855                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
     856                    $patternselect = 'php';
     857                } else {
     858                    $patternselect = 'noregex';
     859                }
     860            }
     861        }
     862        switch ($patternselect) {
     863            case 'pcre8':
     864                /**
     865                 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
     866                 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
     867                 * not allow a@b type valid addresses :(
     868                 * @link http://squiloople.com/2009/12/20/email-address-validation/
     869                 * @copyright 2009-2010 Michael Rushton
     870                 * Feel free to use and redistribute this code. But please keep this copyright notice.
     871                 */
     872                return (bool)preg_match(
     873                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
     874                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
     875                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
     876                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
     877                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
     878                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
     879                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
     880                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
     881                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
     882                    $address
     883                );
     884                break;
     885            case 'pcre':
     886                //An older regex that doesn't need a recent PCRE
     887                return (bool)preg_match(
     888                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
     889                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
     890                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
     891                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
     892                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
     893                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
     894                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
     895                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
     896                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
     897                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
     898                    $address
     899                );
     900                break;
     901            case 'php':
     902            default:
     903                return (bool)filter_var($address, FILTER_VALIDATE_EMAIL);
     904                break;
     905            case 'noregex':
     906                //No PCRE! Do something _very_ approximate!
     907                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
     908                return (strlen($address) >= 3
     909                    and strpos($address, '@') >= 1
     910                    and strpos($address, '@') != strlen($address) - 1);
     911                break;
     912        }
    1022913    }
    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   }
    1034914
    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;
     915    /**
     916     * Create a message and send it.
     917     * Uses the sending method specified by $Mailer.
     918     * Returns false on error - Use the ErrorInfo variable to view description of the error.
     919     * @throws phpmailerException
     920     * @return bool
     921     */
     922    public function send()
     923    {
     924        try {
     925            if (!$this->preSend()) {
     926                return false;
     927            }
     928            return $this->postSend();
     929        } catch (phpmailerException $e) {
     930            $this->mailHeader = '';
     931            $this->setError($e->getMessage());
     932            if ($this->exceptions) {
     933                throw $e;
     934            }
     935            return false;
     936        }
    1046937    }
    1047938
    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();
     939    /**
     940     * Prepare a message for sending.
     941     * @throws phpmailerException
     942     * @return bool
     943     */
     944    public function preSend()
     945    {
     946        try {
     947            $this->mailHeader = "";
     948            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
     949                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
     950            }
    1053951
    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];
    1061         } else {
    1062           $host = $hosts[$index];
    1063           $port = $this->Port;
    1064         }
     952            // Set whether the message is multipart/alternative
     953            if (!empty($this->AltBody)) {
     954                $this->ContentType = 'multipart/alternative';
     955            }
    1065956
    1066         $tls = ($this->SMTPSecure == 'tls');
    1067         $ssl = ($this->SMTPSecure == 'ssl');
     957            $this->error_count = 0; // reset errors
     958            $this->setMessageType();
     959            // Refuse to send an empty message unless we are specifically allowing it
     960            if (!$this->AllowEmpty and empty($this->Body)) {
     961                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
     962            }
    1068963
    1069         if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
     964            $this->MIMEHeader = $this->createHeader();
     965            $this->MIMEBody = $this->createBody();
    1070966
    1071           $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
    1072           $this->smtp->Hello($hello);
     967            // To capture the complete message when using mail(), create
     968            // an extra header list which createHeader() doesn't fold in
     969            if ($this->Mailer == 'mail') {
     970                if (count($this->to) > 0) {
     971                    $this->mailHeader .= $this->addrAppend("To", $this->to);
     972                } else {
     973                    $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;");
     974                }
     975                $this->mailHeader .= $this->headerLine(
     976                    'Subject',
     977                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
     978                );
     979            }
    1073980
    1074           if ($tls) {
    1075             if (!$this->smtp->StartTLS()) {
    1076               throw new phpmailerException($this->Lang('connect_host'));
     981            // Sign with DKIM if enabled
     982            if (!empty($this->DKIM_domain)
     983                && !empty($this->DKIM_private)
     984                && !empty($this->DKIM_selector)
     985                && !empty($this->DKIM_domain)
     986                && file_exists($this->DKIM_private)) {
     987                $header_dkim = $this->DKIM_Add(
     988                    $this->MIMEHeader . $this->mailHeader,
     989                    $this->encodeHeader($this->secureHeader($this->Subject)),
     990                    $this->MIMEBody
     991                );
     992                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
     993                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
    1077994            }
     995            return true;
    1078996
    1079             //We must resend HELO after tls negotiation
    1080             $this->smtp->Hello($hello);
    1081           }
    1082 
    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'));
     997        } catch (phpmailerException $e) {
     998            $this->setError($e->getMessage());
     999            if ($this->exceptions) {
     1000                throw $e;
    10881001            }
    1089           }
     1002            return false;
    10901003        }
    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       }
    11011004    }
    1102     return true;
    1103   }
    11041005
    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       }
     1006    /**
     1007     * Actually send a message.
     1008     * Send the email via the selected mechanism
     1009     * @throws phpmailerException
     1010     * @return bool
     1011     */
     1012    public function postSend()
     1013    {
     1014        try {
     1015            // Choose the mailer and send through it
     1016            switch ($this->Mailer) {
     1017                case 'sendmail':
     1018                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
     1019                case 'smtp':
     1020                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
     1021                case 'mail':
     1022                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
     1023                default:
     1024                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
     1025            }
     1026        } catch (phpmailerException $e) {
     1027            $this->setError($e->getMessage());
     1028            if ($this->exceptions) {
     1029                throw $e;
     1030            }
     1031            $this->edebug($e->getMessage() . "\n");
     1032        }
     1033        return false;
    11151034    }
    1116   }
    11171035
    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';
     1036    /**
     1037     * Send mail using the $Sendmail program.
     1038     * @param string $header The message headers
     1039     * @param string $body The message body
     1040     * @see PHPMailer::$Sendmail
     1041     * @throws phpmailerException
     1042     * @access protected
     1043     * @return bool
     1044     */
     1045    protected function sendmailSend($header, $body)
     1046    {
     1047        if ($this->Sender != '') {
     1048            $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
     1049        } else {
     1050            $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
     1051        }
     1052        if ($this->SingleTo === true) {
     1053            foreach ($this->SingleToArray as $val) {
     1054                if (!@$mail = popen($sendmail, 'w')) {
     1055                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1056                }
     1057                fputs($mail, "To: " . $val . "\n");
     1058                fputs($mail, $header);
     1059                fputs($mail, $body);
     1060                $result = pclose($mail);
     1061                // implement call back function if it exists
     1062                $isSent = ($result == 0) ? 1 : 0;
     1063                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1064                if ($result != 0) {
     1065                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1066                }
     1067            }
     1068        } else {
     1069            if (!@$mail = popen($sendmail, 'w')) {
     1070                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1071            }
     1072            fputs($mail, $header);
     1073            fputs($mail, $body);
     1074            $result = pclose($mail);
     1075            // implement call back function if it exists
     1076            $isSent = ($result == 0) ? 1 : 0;
     1077            $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1078            if ($result != 0) {
     1079                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1080            }
     1081        }
     1082        return true;
    11521083    }
    1153     $this->language = $PHPMAILER_LANG;
    1154     return ($l == true); //Returns false if language not found
    1155   }
    11561084
    1157   /**
    1158   * Return the current array of language strings
    1159   * @return array
    1160   */
    1161   public function GetTranslations() {
    1162     return $this->language;
    1163   }
     1085    /**
     1086     * Send mail using the PHP mail() function.
     1087     * @param string $header The message headers
     1088     * @param string $body The message body
     1089     * @link http://www.php.net/manual/en/book.mail.php
     1090     * @throws phpmailerException
     1091     * @access protected
     1092     * @return bool
     1093     */
     1094    protected function mailSend($header, $body)
     1095    {
     1096        $toArr = array();
     1097        foreach ($this->to as $t) {
     1098            $toArr[] = $this->addrFormat($t);
     1099        }
     1100        $to = implode(', ', $toArr);
    11641101
    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);
     1102        if (empty($this->Sender)) {
     1103            $params = " ";
     1104        } else {
     1105            $params = sprintf("-f%s", $this->Sender);
     1106        }
     1107        if ($this->Sender != '' and !ini_get('safe_mode')) {
     1108            $old_from = ini_get('sendmail_from');
     1109            ini_set('sendmail_from', $this->Sender);
     1110        }
     1111        $rt = false;
     1112        if ($this->SingleTo === true && count($toArr) > 1) {
     1113            foreach ($toArr as $val) {
     1114                $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
     1115                // implement call back function if it exists
     1116                $isSent = ($rt == 1) ? 1 : 0;
     1117                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1118            }
     1119        } else {
     1120            $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
     1121            // implement call back function if it exists
     1122            $isSent = ($rt == 1) ? 1 : 0;
     1123            $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1124        }
     1125        if (isset($old_from)) {
     1126            ini_set('sendmail_from', $old_from);
     1127        }
     1128        if (!$rt) {
     1129            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
     1130        }
     1131        return true;
    11811132    }
    1182     $addr_str .= implode(', ', $addresses);
    1183     $addr_str .= $this->LE;
    11841133
    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]) . ">";
     1134    /**
     1135     * Get an instance to use for SMTP operations.
     1136     * Override this function to load your own SMTP implementation
     1137     * @return SMTP
     1138     */
     1139    public function getSMTPInstance()
     1140    {
     1141        if (!is_object($this->smtp)) {
     1142            $this->smtp = new SMTP;
     1143        }
     1144        return $this->smtp;
    11991145    }
    1200   }
    12011146
    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);
     1147    /**
     1148     * Send mail via SMTP.
     1149     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
     1150     * Uses the PHPMailerSMTP class by default.
     1151     * @see PHPMailer::getSMTPInstance() to use a different class.
     1152     * @param string $header The message headers
     1153     * @param string $body The message body
     1154     * @throws phpmailerException
     1155     * @uses SMTP
     1156     * @access protected
     1157     * @return bool
     1158     */
     1159    protected function smtpSend($header, $body)
     1160    {
     1161        $bad_rcpt = array();
    12191162
    1220     $message = $this->FixEOL($message);
    1221     if (substr($message, -$lelen) == $this->LE) {
    1222       $message = substr($message, 0, -$lelen);
    1223     }
     1163        if (!$this->smtpConnect()) {
     1164            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
     1165        }
     1166        $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
     1167        if (!$this->smtp->mail($smtp_from)) {
     1168            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
     1169            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
     1170        }
    12241171
    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);
     1172        // Attempt to send attach all recipients
     1173        foreach ($this->to as $to) {
     1174            if (!$this->smtp->recipient($to[0])) {
     1175                $bad_rcpt[] = $to[0];
     1176                $isSent = 0;
    12481177            } else {
    1249               $message .= $buf . $soft_break;
     1178                $isSent = 1;
    12501179            }
    1251             $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;
     1180            $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);
     1181        }
     1182        foreach ($this->cc as $cc) {
     1183            if (!$this->smtp->recipient($cc[0])) {
     1184                $bad_rcpt[] = $cc[0];
     1185                $isSent = 0;
     1186            } else {
     1187                $isSent = 1;
    12611188            }
    1262             $part = substr($word, 0, $len);
    1263             $word = substr($word, $len);
    1264 
    1265             if (strlen($word) > 0) {
    1266               $message .= $part . sprintf("=%s", self::CRLF);
     1189            $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From);
     1190        }
     1191        foreach ($this->bcc as $bcc) {
     1192            if (!$this->smtp->recipient($bcc[0])) {
     1193                $bad_rcpt[] = $bcc[0];
     1194                $isSent = 0;
    12671195            } else {
    1268               $buf = $part;
     1196                $isSent = 1;
    12691197            }
    1270           }
    1271         } else {
    1272           $buf_o = $buf;
    1273           $buf .= ($e == 0) ? $word : (' ' . $word);
     1198            $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
     1199        }
    12741200
    1275           if (strlen($buf) > $length and $buf_o != '') {
    1276             $message .= $buf_o . $soft_break;
    1277             $buf = $word;
    1278           }
     1201        if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
     1202            throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt));
    12791203        }
    1280       }
    1281       $message .= $buf . self::CRLF;
     1204        if (!$this->smtp->data($header . $body)) {
     1205            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
     1206        }
     1207        if ($this->SMTPKeepAlive == true) {
     1208            $this->smtp->reset();
     1209        } else {
     1210            $this->smtp->quit();
     1211            $this->smtp->close();
     1212        }
     1213        return true;
    12821214    }
    12831215
    1284     return $message;
    1285   }
     1216    /**
     1217     * Initiate a connection to an SMTP server.
     1218     * Returns false if the operation failed.
     1219     * @param array $options An array of options compatible with stream_context_create()
     1220     * @uses SMTP
     1221     * @access public
     1222     * @throws phpmailerException
     1223     * @return bool
     1224     */
     1225    public function smtpConnect($options = array())
     1226    {
     1227        if (is_null($this->smtp)) {
     1228            $this->smtp = $this->getSMTPInstance();
     1229        }
    12861230
    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;
     1231        //Already connected?
     1232        if ($this->smtp->connected()) {
     1233            return true;
    13191234        }
    1320       } else {
    1321         // No encoded character found
    1322         $foundSplitPos = true;
    1323       }
    1324     }
    1325     return $maxLength;
    1326   }
    13271235
     1236        $this->smtp->setTimeout($this->Timeout);
     1237        $this->smtp->setDebugLevel($this->SMTPDebug);
     1238        $this->smtp->setDebugOutput($this->Debugoutput);
     1239        $this->smtp->setVerp($this->do_verp);
     1240        $tls = ($this->SMTPSecure == 'tls');
     1241        $ssl = ($this->SMTPSecure == 'ssl');
     1242        $hosts = explode(';', $this->Host);
     1243        $lastexception = null;
    13281244
    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     }
     1245        foreach ($hosts as $hostentry) {
     1246            $hostinfo = array();
     1247            $host = $hostentry;
     1248            $port = $this->Port;
     1249            if (preg_match(
     1250                '/^(.+):([0-9]+)$/',
     1251                $hostentry,
     1252                $hostinfo
     1253            )
     1254            ) { //If $hostentry contains 'address:port', override default
     1255                $host = $hostinfo[1];
     1256                $port = $hostinfo[2];
     1257            }
     1258            if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) {
     1259                try {
     1260                    if ($this->Helo) {
     1261                        $hello = $this->Helo;
     1262                    } else {
     1263                        $hello = $this->serverHostname();
     1264                    }
     1265                    $this->smtp->hello($hello);
    13381266
    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;
     1267                    if ($tls) {
     1268                        if (!$this->smtp->startTLS()) {
     1269                            throw new phpmailerException($this->lang('connect_host'));
     1270                        }
     1271                        //We must resend HELO after tls negotiation
     1272                        $this->smtp->hello($hello);
     1273                    }
     1274                    if ($this->SMTPAuth) {
     1275                        if (!$this->smtp->authenticate(
     1276                            $this->Username,
     1277                            $this->Password,
     1278                            $this->AuthType,
     1279                            $this->Realm,
     1280                            $this->Workstation
     1281                        )
     1282                        ) {
     1283                            throw new phpmailerException($this->lang('authenticate'));
     1284                        }
     1285                    }
     1286                    return true;
     1287                } catch (phpmailerException $e) {
     1288                    $lastexception = $e;
     1289                    //We must have connected, but then failed TLS or Auth, so close connection nicely
     1290                    $this->smtp->quit();
     1291                }
     1292            }
     1293        }
     1294        //If we get here, all connection attempts have failed, so close connection hard
     1295        $this->smtp->close();
     1296        //As we've caught all exceptions, just report whatever the last one was
     1297        if ($this->exceptions and !is_null($lastexception)) {
     1298            throw $lastexception;
     1299        }
     1300        return false;
    13491301    }
    1350   }
    13511302
    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);
     1303    /**
     1304     * Close the active SMTP session if one exists.
     1305     * @return void
     1306     */
     1307    public function smtpClose()
     1308    {
     1309        if ($this->smtp !== null) {
     1310            if ($this->smtp->connected()) {
     1311                $this->smtp->quit();
     1312                $this->smtp->close();
     1313            }
     1314        }
    13701315    }
    13711316
    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);
     1317    /**
     1318     * Set the language for error messages.
     1319     * Returns false if it cannot load the language file.
     1320     * The default language is English.
     1321     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
     1322     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
     1323     * @return bool
     1324     * @access public
     1325     */
     1326    public function setLanguage($langcode = 'en', $lang_path = 'language/')
     1327    {
     1328        //Define full set of translatable strings
     1329        $PHPMAILER_LANG = array(
     1330            'authenticate' => 'SMTP Error: Could not authenticate.',
     1331            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
     1332            'data_not_accepted' => 'SMTP Error: data not accepted.',
     1333            'empty_message' => 'Message body empty',
     1334            'encoding' => 'Unknown encoding: ',
     1335            'execute' => 'Could not execute: ',
     1336            'file_access' => 'Could not access file: ',
     1337            'file_open' => 'File Error: Could not open file: ',
     1338            'from_failed' => 'The following From address failed: ',
     1339            'instantiate' => 'Could not instantiate mail function.',
     1340            'invalid_address' => 'Invalid address',
     1341            'mailer_not_supported' => ' mailer is not supported.',
     1342            'provide_address' => 'You must provide at least one recipient email address.',
     1343            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
     1344            'signing' => 'Signing Error: ',
     1345            'smtp_connect_failed' => 'SMTP connect() failed.',
     1346            'smtp_error' => 'SMTP server error: ',
     1347            'variable_set' => 'Cannot set or reset variable: '
     1348        );
     1349        //Overwrite language-specific strings.
     1350        //This way we'll never have missing translations - no more "language string failed to load"!
     1351        $l = true;
     1352        if ($langcode != 'en') { //There is no English translation file
     1353            $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php';
    13851354        }
    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       }
     1355        $this->language = $PHPMAILER_LANG;
     1356        return ($l == true); //Returns false if language not found
    13931357    }
    13941358
    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);
     1359    /**
     1360     * Get the array of strings for the current language.
     1361     * @return array
     1362     */
     1363    public function getTranslations()
     1364    {
     1365        return $this->language;
    14031366    }
    14041367
    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);
     1368    /**
     1369     * Create recipient headers.
     1370     * @access public
     1371     * @param string $type
     1372     * @param array $addr An array of recipient,
     1373     * where each recipient is a 2-element indexed array with element 0 containing an address
     1374     * and element 1 containing a name, like:
     1375     * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
     1376     * @return string
     1377     */
     1378    public function addrAppend($type, $addr)
     1379    {
     1380        $addresses = array();
     1381        foreach ($addr as $a) {
     1382            $addresses[] = $this->addrFormat($a);
     1383        }
     1384        return $type . ': ' . implode(', ', $addresses) . $this->LE;
    14081385    }
    14091386
    1410     if(count($this->ReplyTo) > 0) {
    1411       $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);
     1387    /**
     1388     * Format an address for use in a message header.
     1389     * @access public
     1390     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
     1391     *      like array('joe@example.com', 'Joe User')
     1392     * @return string
     1393     */
     1394    public function addrFormat($addr)
     1395    {
     1396        if (empty($addr[1])) { // No name provided
     1397            return $this->secureHeader($addr[0]);
     1398        } else {
     1399            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader(
     1400                $addr[0]
     1401            ) . ">";
     1402        }
    14121403    }
    14131404
    1414     // mail() sets the subject itself
    1415     if($this->Mailer != 'mail') {
    1416       $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
    1417     }
     1405    /**
     1406     * Word-wrap message.
     1407     * For use with mailers that do not automatically perform wrapping
     1408     * and for quoted-printable encoded messages.
     1409     * Original written by philippe.
     1410     * @param string $message The message to wrap
     1411     * @param integer $length The line length to wrap to
     1412     * @param bool $qp_mode Whether to run in Quoted-Printable mode
     1413     * @access public
     1414     * @return string
     1415     */
     1416    public function wrapText($message, $length, $qp_mode = false)
     1417    {
     1418        $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
     1419        // If utf-8 encoding is used, we will need to make sure we don't
     1420        // split multibyte characters when we wrap
     1421        $is_utf8 = (strtolower($this->CharSet) == "utf-8");
     1422        $lelen = strlen($this->LE);
     1423        $crlflen = strlen(self::CRLF);
    14181424
    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     }
     1425        $message = $this->fixEOL($message);
     1426        if (substr($message, -$lelen) == $this->LE) {
     1427            $message = substr($message, 0, -$lelen);
     1428        }
    14331429
    1434     if($this->ConfirmReadingTo != '') {
    1435       $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
    1436     }
     1430        $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
     1431        $message = '';
     1432        for ($i = 0; $i < count($line); $i++) {
     1433            $line_part = explode(' ', $line[$i]);
     1434            $buf = '';
     1435            for ($e = 0; $e < count($line_part); $e++) {
     1436                $word = $line_part[$e];
     1437                if ($qp_mode and (strlen($word) > $length)) {
     1438                    $space_left = $length - strlen($buf) - $crlflen;
     1439                    if ($e != 0) {
     1440                        if ($space_left > 20) {
     1441                            $len = $space_left;
     1442                            if ($is_utf8) {
     1443                                $len = $this->utf8CharBoundary($word, $len);
     1444                            } elseif (substr($word, $len - 1, 1) == "=") {
     1445                                $len--;
     1446                            } elseif (substr($word, $len - 2, 1) == "=") {
     1447                                $len -= 2;
     1448                            }
     1449                            $part = substr($word, 0, $len);
     1450                            $word = substr($word, $len);
     1451                            $buf .= ' ' . $part;
     1452                            $message .= $buf . sprintf("=%s", self::CRLF);
     1453                        } else {
     1454                            $message .= $buf . $soft_break;
     1455                        }
     1456                        $buf = '';
     1457                    }
     1458                    while (strlen($word) > 0) {
     1459                        if ($length <= 0) {
     1460                            break;
     1461                        }
     1462                        $len = $length;
     1463                        if ($is_utf8) {
     1464                            $len = $this->utf8CharBoundary($word, $len);
     1465                        } elseif (substr($word, $len - 1, 1) == "=") {
     1466                            $len--;
     1467                        } elseif (substr($word, $len - 2, 1) == "=") {
     1468                            $len -= 2;
     1469                        }
     1470                        $part = substr($word, 0, $len);
     1471                        $word = substr($word, $len);
    14371472
    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     }
     1473                        if (strlen($word) > 0) {
     1474                            $message .= $part . sprintf("=%s", self::CRLF);
     1475                        } else {
     1476                            $buf = $part;
     1477                        }
     1478                    }
     1479                } else {
     1480                    $buf_o = $buf;
     1481                    $buf .= ($e == 0) ? $word : (' ' . $word);
    14461482
    1447     return $result;
    1448   }
     1483                    if (strlen($buf) > $length and $buf_o != '') {
     1484                        $message .= $buf_o . $soft_break;
     1485                        $buf = $word;
     1486                    }
     1487                }
     1488            }
     1489            $message .= $buf . self::CRLF;
     1490        }
    14491491
    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;
     1492        return $message;
    14791493    }
    14801494
    1481     if($this->Mailer != 'mail') {
    1482       $result .= $this->LE;
     1495    /**
     1496     * Find the last character boundary prior to $maxLength in a utf-8
     1497     * quoted (printable) encoded string.
     1498     * Original written by Colin Brown.
     1499     * @access public
     1500     * @param string $encodedText utf-8 QP text
     1501     * @param int $maxLength   find last character boundary prior to this length
     1502     * @return int
     1503     */
     1504    public function utf8CharBoundary($encodedText, $maxLength)
     1505    {
     1506        $foundSplitPos = false;
     1507        $lookBack = 3;
     1508        while (!$foundSplitPos) {
     1509            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
     1510            $encodedCharPos = strpos($lastChunk, "=");
     1511            if ($encodedCharPos !== false) {
     1512                // Found start of encoded character byte within $lookBack block.
     1513                // Check the encoded byte value (the 2 chars after the '=')
     1514                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
     1515                $dec = hexdec($hex);
     1516                if ($dec < 128) { // Single byte character.
     1517                    // If the encoded char was found at pos 0, it will fit
     1518                    // otherwise reduce maxLength to start of the encoded char
     1519                    $maxLength = ($encodedCharPos == 0) ? $maxLength :
     1520                        $maxLength - ($lookBack - $encodedCharPos);
     1521                    $foundSplitPos = true;
     1522                } elseif ($dec >= 192) { // First byte of a multi byte character
     1523                    // Reduce maxLength to split at start of character
     1524                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
     1525                    $foundSplitPos = true;
     1526                } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
     1527                    $lookBack += 3;
     1528                }
     1529            } else {
     1530                // No encoded character found
     1531                $foundSplitPos = true;
     1532            }
     1533        }
     1534        return $maxLength;
    14831535    }
    14841536
    1485     return $result;
    1486   }
    14871537
    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   }
     1538    /**
     1539     * Set the body wrapping.
     1540     * @access public
     1541     * @return void
     1542     */
     1543    public function setWordWrap()
     1544    {
     1545        if ($this->WordWrap < 1) {
     1546            return;
     1547        }
    14961548
    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;
     1549        switch ($this->message_type) {
     1550            case 'alt':
     1551            case 'alt_inline':
     1552            case 'alt_attach':
     1553            case 'alt_inline_attach':
     1554                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
     1555                break;
     1556            default:
     1557                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
     1558                break;
     1559        }
    15091560    }
    15101561
    1511     $this->SetWordWrap();
     1562    /**
     1563     * Assemble message headers.
     1564     * @access public
     1565     * @return string The assembled headers
     1566     */
     1567    public function createHeader()
     1568    {
     1569        $result = '';
    15121570
    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     }
     1571        // Set the boundaries
     1572        $uniq_id = md5(uniqid(time()));
     1573        $this->boundary[1] = 'b1_' . $uniq_id;
     1574        $this->boundary[2] = 'b2_' . $uniq_id;
     1575        $this->boundary[3] = 'b3_' . $uniq_id;
    16031576
    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);
     1577        if ($this->MessageDate == '') {
     1578            $result .= $this->headerLine('Date', self::rfcDate());
    16151579        } else {
    1616           @unlink($file);
    1617           @unlink($signed);
    1618           throw new phpmailerException($this->Lang("signing").openssl_error_string());
     1580            $result .= $this->headerLine('Date', $this->MessageDate);
    16191581        }
    1620       } catch (phpmailerException $e) {
    1621         $body = '';
    1622         if ($this->exceptions) {
    1623           throw $e;
     1582
     1583        if ($this->ReturnPath) {
     1584            $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>');
     1585        } elseif ($this->Sender == '') {
     1586            $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>');
     1587        } else {
     1588            $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>');
    16241589        }
    1625       }
    1626     }
    16271590
    1628     return $body;
    1629   }
     1591        // To be created automatically by mail()
     1592        if ($this->Mailer != 'mail') {
     1593            if ($this->SingleTo === true) {
     1594                foreach ($this->to as $t) {
     1595                    $this->SingleToArray[] = $this->addrFormat($t);
     1596                }
     1597            } else {
     1598                if (count($this->to) > 0) {
     1599                    $result .= $this->addrAppend('To', $this->to);
     1600                } elseif (count($this->cc) == 0) {
     1601                    $result .= $this->headerLine('To', 'undisclosed-recipients:;');
     1602                }
     1603            }
     1604        }
    16301605
    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;
     1606        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
    16561607
    1657     return $result;
    1658   }
     1608        // sendmail and mail() extract Cc from the header before sending
     1609        if (count($this->cc) > 0) {
     1610            $result .= $this->addrAppend('Cc', $this->cc);
     1611        }
    16591612
    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   }
     1613        // sendmail and mail() extract Bcc from the header before sending
     1614        if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
     1615            $result .= $this->addrAppend('Bcc', $this->bcc);
     1616        }
    16691617
    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   }
     1618        if (count($this->ReplyTo) > 0) {
     1619            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
     1620        }
    16831621
    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   }
     1622        // mail() sets the subject itself
     1623        if ($this->Mailer != 'mail') {
     1624            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
     1625        }
    16941626
    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   }
     1627        if ($this->MessageID != '') {
     1628            $this->lastMessageID = $this->MessageID;
     1629        } else {
     1630            $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname());
     1631        }
     1632        $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
     1633        $result .= $this->headerLine('X-Priority', $this->Priority);
     1634        if ($this->XMailer == '') {
     1635            $result .= $this->headerLine(
     1636                'X-Mailer',
     1637                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
     1638            );
     1639        } else {
     1640            $myXmailer = trim($this->XMailer);
     1641            if ($myXmailer) {
     1642                $result .= $this->headerLine('X-Mailer', $myXmailer);
     1643            }
     1644        }
    17041645
    1705   /////////////////////////////////////////////////
    1706   // CLASS METHODS, ATTACHMENTS
    1707   /////////////////////////////////////////////////
     1646        if ($this->ConfirmReadingTo != '') {
     1647            $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
     1648        }
    17081649
    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       }
     1650        // Add custom headers
     1651        for ($index = 0; $index < count($this->CustomHeader); $index++) {
     1652            $result .= $this->headerLine(
     1653                trim($this->CustomHeader[$index][0]),
     1654                $this->encodeHeader(trim($this->CustomHeader[$index][1]))
     1655            );
     1656        }
     1657        if (!$this->sign_key_file) {
     1658            $result .= $this->headerLine('MIME-Version', '1.0');
     1659            $result .= $this->getMailMIME();
     1660        }
    17291661
    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       }
     1662        return $result;
    17521663    }
    1753     return true;
    1754   }
    17551664
    1756   /**
    1757   * Return the current array of attachments
    1758   * @return array
    1759   */
    1760   public function GetAttachments() {
    1761     return $this->attachment;
    1762   }
     1665    /**
     1666     * Get the message MIME type headers.
     1667     * @access public
     1668     * @return string
     1669     */
     1670    public function getMailMIME()
     1671    {
     1672        $result = '';
     1673        switch ($this->message_type) {
     1674            case 'inline':
     1675                $result .= $this->headerLine('Content-Type', 'multipart/related;');
     1676                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
     1677                break;
     1678            case 'attach':
     1679            case 'inline_attach':
     1680            case 'alt_attach':
     1681            case 'alt_inline_attach':
     1682                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
     1683                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
     1684                break;
     1685            case 'alt':
     1686            case 'alt_inline':
     1687                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
     1688                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
     1689                break;
     1690            default:
     1691                // Catches case 'plain': and case '':
     1692                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
     1693                break;
     1694        }
     1695        //RFC1341 part 5 says 7bit is assumed if not specified
     1696        if ($this->Encoding != '7bit') {
     1697            $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
     1698        }
    17631699
    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];
    1788         } else {
    1789           $path = $attachment[0];
     1700        if ($this->Mailer != 'mail') {
     1701            $result .= $this->LE;
    17901702        }
    17911703
    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;
     1704        return $result;
     1705    }
    18031706
    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);
     1707    /**
     1708     * Returns the whole MIME message.
     1709     * Includes complete headers and body.
     1710     * Only valid post PreSend().
     1711     * @see PHPMailer::PreSend()
     1712     * @access public
     1713     * @return string
     1714     */
     1715    public function getSentMIMEMessage()
     1716    {
     1717        return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
     1718    }
    18071719
    1808         if($disposition == 'inline') {
    1809           $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
    1810         }
    18111720
    1812         $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
     1721    /**
     1722     * Assemble the message body.
     1723     * Returns an empty string on failure.
     1724     * @access public
     1725     * @throws phpmailerException
     1726     * @return string The assembled message body
     1727     */
     1728    public function createBody()
     1729    {
     1730        $body = '';
    18131731
    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;
    1821         } else {
    1822           $mime[] = $this->EncodeFile($path, $encoding);
    1823           if($this->IsError()) {
    1824             return '';
    1825           }
    1826           $mime[] = $this->LE.$this->LE;
     1732        if ($this->sign_key_file) {
     1733            $body .= $this->getMailMIME() . $this->LE;
    18271734        }
    1828       }
    1829     }
    18301735
    1831     $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
     1736        $this->setWordWrap();
    18321737
    1833     return implode("", $mime);
    1834   }
     1738        switch ($this->message_type) {
     1739            case 'inline':
     1740                $body .= $this->getBoundary($this->boundary[1], '', '', '');
     1741                $body .= $this->encodeString($this->Body, $this->Encoding);
     1742                $body .= $this->LE . $this->LE;
     1743                $body .= $this->attachAll('inline', $this->boundary[1]);
     1744                break;
     1745            case 'attach':
     1746                $body .= $this->getBoundary($this->boundary[1], '', '', '');
     1747                $body .= $this->encodeString($this->Body, $this->Encoding);
     1748                $body .= $this->LE . $this->LE;
     1749                $body .= $this->attachAll('attachment', $this->boundary[1]);
     1750                break;
     1751            case 'inline_attach':
     1752                $body .= $this->textLine('--' . $this->boundary[1]);
     1753                $body .= $this->headerLine('Content-Type', 'multipart/related;');
     1754                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
     1755                $body .= $this->LE;
     1756                $body .= $this->getBoundary($this->boundary[2], '', '', '');
     1757                $body .= $this->encodeString($this->Body, $this->Encoding);
     1758                $body .= $this->LE . $this->LE;
     1759                $body .= $this->attachAll('inline', $this->boundary[2]);
     1760                $body .= $this->LE;
     1761                $body .= $this->attachAll('attachment', $this->boundary[1]);
     1762                break;
     1763            case 'alt':
     1764                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
     1765                $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1766                $body .= $this->LE . $this->LE;
     1767                $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');
     1768                $body .= $this->encodeString($this->Body, $this->Encoding);
     1769                $body .= $this->LE . $this->LE;
     1770                if (!empty($this->Ical)) {
     1771                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
     1772                    $body .= $this->encodeString($this->Ical, $this->Encoding);
     1773                    $body .= $this->LE . $this->LE;
     1774                }
     1775                $body .= $this->endBoundary($this->boundary[1]);
     1776                break;
     1777            case 'alt_inline':
     1778                $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
     1779                $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1780                $body .= $this->LE . $this->LE;
     1781                $body .= $this->textLine('--' . $this->boundary[1]);
     1782                $body .= $this->headerLine('Content-Type', 'multipart/related;');
     1783                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
     1784                $body .= $this->LE;
     1785                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
     1786                $body .= $this->encodeString($this->Body, $this->Encoding);
     1787                $body .= $this->LE . $this->LE;
     1788                $body .= $this->attachAll('inline', $this->boundary[2]);
     1789                $body .= $this->LE;
     1790                $body .= $this->endBoundary($this->boundary[1]);
     1791                break;
     1792            case 'alt_attach':
     1793                $body .= $this->textLine('--' . $this->boundary[1]);
     1794                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
     1795                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
     1796                $body .= $this->LE;
     1797                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
     1798                $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1799                $body .= $this->LE . $this->LE;
     1800                $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
     1801                $body .= $this->encodeString($this->Body, $this->Encoding);
     1802                $body .= $this->LE . $this->LE;
     1803                $body .= $this->endBoundary($this->boundary[2]);
     1804                $body .= $this->LE;
     1805                $body .= $this->attachAll('attachment', $this->boundary[1]);
     1806                break;
     1807            case 'alt_inline_attach':
     1808                $body .= $this->textLine('--' . $this->boundary[1]);
     1809                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
     1810                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
     1811                $body .= $this->LE;
     1812                $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
     1813                $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1814                $body .= $this->LE . $this->LE;
     1815                $body .= $this->textLine('--' . $this->boundary[2]);
     1816                $body .= $this->headerLine('Content-Type', 'multipart/related;');
     1817                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
     1818                $body .= $this->LE;
     1819                $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');
     1820                $body .= $this->encodeString($this->Body, $this->Encoding);
     1821                $body .= $this->LE . $this->LE;
     1822                $body .= $this->attachAll('inline', $this->boundary[3]);
     1823                $body .= $this->LE;
     1824                $body .= $this->endBoundary($this->boundary[2]);
     1825                $body .= $this->LE;
     1826                $body .= $this->attachAll('attachment', $this->boundary[1]);
     1827                break;
     1828            default:
     1829                // catch case 'plain' and case ''
     1830                $body .= $this->encodeString($this->Body, $this->Encoding);
     1831                break;
     1832        }
    18351833
    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);
    1860         } else {
    1861           ini_set('magic_quotes_runtime', 0);
     1834        if ($this->isError()) {
     1835            $body = '';
     1836        } elseif ($this->sign_key_file) {
     1837            try {
     1838                if (!defined('PKCS7_TEXT')) {
     1839                    throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
     1840                }
     1841                $file = tempnam(sys_get_temp_dir(), 'mail');
     1842                file_put_contents($file, $body); //TODO check this worked
     1843                $signed = tempnam(sys_get_temp_dir(), 'signed');
     1844                if (@openssl_pkcs7_sign(
     1845                    $file,
     1846                    $signed,
     1847                    'file://' . realpath($this->sign_cert_file),
     1848                    array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
     1849                    null
     1850                )
     1851                ) {
     1852                    @unlink($file);
     1853                    $body = file_get_contents($signed);
     1854                    @unlink($signed);
     1855                } else {
     1856                    @unlink($file);
     1857                    @unlink($signed);
     1858                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
     1859                }
     1860            } catch (phpmailerException $e) {
     1861                $body = '';
     1862                if ($this->exceptions) {
     1863                    throw $e;
     1864                }
     1865            }
    18621866        }
    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);
    1869         } else {
    1870           ini_set('magic_quotes_runtime', $magic_quotes);
     1867        return $body;
     1868    }
     1869
     1870    /**
     1871     * Return the start of a message boundary.
     1872     * @access protected
     1873     * @param string $boundary
     1874     * @param string $charSet
     1875     * @param string $contentType
     1876     * @param string $encoding
     1877     * @return string
     1878     */
     1879    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
     1880    {
     1881        $result = '';
     1882        if ($charSet == '') {
     1883            $charSet = $this->CharSet;
    18711884        }
    1872       }
    1873       return $file_buffer;
    1874     } catch (Exception $e) {
    1875       $this->SetError($e->getMessage());
    1876       return '';
     1885        if ($contentType == '') {
     1886            $contentType = $this->ContentType;
     1887        }
     1888        if ($encoding == '') {
     1889            $encoding = $this->Encoding;
     1890        }
     1891        $result .= $this->textLine('--' . $boundary);
     1892        $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);
     1893        $result .= $this->LE;
     1894        $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
     1895        $result .= $this->LE;
     1896
     1897        return $result;
    18771898    }
    1878   }
    18791899
    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;
     1900    /**
     1901     * Return the end of a message boundary.
     1902     * @access protected
     1903     * @param string $boundary
     1904     * @return string
     1905     */
     1906    protected function endBoundary($boundary)
     1907    {
     1908        return $this->LE . '--' . $boundary . '--' . $this->LE;
    19101909    }
    1911     return $encoded;
    1912   }
    19131910
    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           }
     1911    /**
     1912     * Set the message type.
     1913     * PHPMailer only supports some preset message types,
     1914     * not arbitrary MIME structures.
     1915     * @access protected
     1916     * @return void
     1917     */
     1918    protected function setMessageType()
     1919    {
     1920        $this->message_type = array();
     1921        if ($this->alternativeExists()) {
     1922            $this->message_type[] = "alt";
    19341923        }
    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;
     1924        if ($this->inlineImageExists()) {
     1925            $this->message_type[] = "inline";
     1926        }
     1927        if ($this->attachmentExists()) {
     1928            $this->message_type[] = "attach";
     1929        }
     1930        $this->message_type = implode("_", $this->message_type);
     1931        if ($this->message_type == "") {
     1932            $this->message_type = "plain";
     1933        }
    19441934    }
    19451935
    1946     if ($x == 0) {
    1947       return ($str);
     1936    /**
     1937     * Format a header line.
     1938     * @access public
     1939     * @param string $name
     1940     * @param string $value
     1941     * @return string
     1942     */
     1943    public function headerLine($name, $value)
     1944    {
     1945        return $name . ': ' . $value . $this->LE;
    19481946    }
    19491947
    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));
     1948    /**
     1949     * Return a formatted mail line.
     1950     * @access public
     1951     * @param string $value
     1952     * @return string
     1953     */
     1954    public function textLine($value)
     1955    {
     1956        return $value . $this->LE;
    19681957    }
    19691958
    1970     $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
    1971     $encoded = trim(str_replace("\n", $this->LE, $encoded));
     1959    /**
     1960     * Add an attachment from a path on the filesystem.
     1961     * Returns false if the file could not be found or read.
     1962     * @param string $path Path to the attachment.
     1963     * @param string $name Overrides the attachment name.
     1964     * @param string $encoding File encoding (see $Encoding).
     1965     * @param string $type File extension (MIME) type.
     1966     * @param string $disposition Disposition to use
     1967     * @throws phpmailerException
     1968     * @return bool
     1969     */
     1970    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
     1971    {
     1972        try {
     1973            if (!@is_file($path)) {
     1974                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
     1975            }
    19721976
    1973     return $encoded;
    1974   }
     1977            //If a MIME type is not specified, try to work it out from the file name
     1978            if ($type == '') {
     1979                $type = self::filenameToType($path);
     1980            }
    19751981
    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;
     1982            $filename = basename($path);
     1983            if ($name == '') {
     1984                $name = $filename;
     1985            }
     1986
     1987            $this->attachment[] = array(
     1988                0 => $path,
     1989                1 => $filename,
     1990                2 => $name,
     1991                3 => $encoding,
     1992                4 => $type,
     1993                5 => false, // isStringAttachment
     1994                6 => $disposition,
     1995                7 => 0
     1996            );
     1997
     1998        } catch (phpmailerException $e) {
     1999            $this->setError($e->getMessage());
     2000            if ($this->exceptions) {
     2001                throw $e;
     2002            }
     2003            $this->edebug($e->getMessage() . "\n");
     2004            return false;
     2005        }
     2006        return true;
    19872007    }
    1988   }
    19892008
    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;
     2009    /**
     2010     * Return the array of attachments.
     2011     * @return array
     2012     */
     2013    public function getAttachments()
     2014    {
     2015        return $this->attachment;
    20052016    }
    20062017
    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);
     2018    /**
     2019     * Attach all file, string, and binary attachments to the message.
     2020     * Returns an empty string on failure.
     2021     * @access protected
     2022     * @param string $disposition_type
     2023     * @param string $boundary
     2024     * @return string
     2025     */
     2026    protected function attachAll($disposition_type, $boundary)
     2027    {
     2028        // Return text of body
     2029        $mime = array();
     2030        $cidUniq = array();
     2031        $incl = array();
    20142032
    2015     for ($i = 0; $i < $mb_length; $i += $offset) {
    2016       $lookBack = 0;
     2033        // Add all attachments
     2034        foreach ($this->attachment as $attachment) {
     2035            // Check if it is a valid disposition_filter
     2036            if ($attachment[6] == $disposition_type) {
     2037                // Check for string attachment
     2038                $string = '';
     2039                $path = '';
     2040                $bString = $attachment[5];
     2041                if ($bString) {
     2042                    $string = $attachment[0];
     2043                } else {
     2044                    $path = $attachment[0];
     2045                }
    20172046
    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);
     2047                $inclhash = md5(serialize($attachment));
     2048                if (in_array($inclhash, $incl)) {
     2049                    continue;
     2050                }
     2051                $incl[] = $inclhash;
     2052                $name = $attachment[2];
     2053                $encoding = $attachment[3];
     2054                $type = $attachment[4];
     2055                $disposition = $attachment[6];
     2056                $cid = $attachment[7];
     2057                if ($disposition == 'inline' && isset($cidUniq[$cid])) {
     2058                    continue;
     2059                }
     2060                $cidUniq[$cid] = true;
    20252061
    2026       $encoded .= $chunk . $lf;
     2062                $mime[] = sprintf("--%s%s", $boundary, $this->LE);
     2063                $mime[] = sprintf(
     2064                    "Content-Type: %s; name=\"%s\"%s",
     2065                    $type,
     2066                    $this->encodeHeader($this->secureHeader($name)),
     2067                    $this->LE
     2068                );
     2069                $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
     2070
     2071                if ($disposition == 'inline') {
     2072                    $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
     2073                }
     2074
     2075                // If a filename contains any of these chars, it should be quoted,
     2076                // but not otherwise: RFC2183 & RFC2045 5.1
     2077                // Fixes a warning in IETF's msglint MIME checker
     2078                // Allow for bypassing the Content-Disposition header totally
     2079                if (!(empty($disposition))) {
     2080                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
     2081                        $mime[] = sprintf(
     2082                            "Content-Disposition: %s; filename=\"%s\"%s",
     2083                            $disposition,
     2084                            $this->encodeHeader($this->secureHeader($name)),
     2085                            $this->LE . $this->LE
     2086                        );
     2087                    } else {
     2088                        $mime[] = sprintf(
     2089                            "Content-Disposition: %s; filename=%s%s",
     2090                            $disposition,
     2091                            $this->encodeHeader($this->secureHeader($name)),
     2092                            $this->LE . $this->LE
     2093                        );
     2094                    }
     2095                } else {
     2096                    $mime[] = $this->LE;
     2097                }
     2098
     2099                // Encode as string attachment
     2100                if ($bString) {
     2101                    $mime[] = $this->encodeString($string, $encoding);
     2102                    if ($this->isError()) {
     2103                        return '';
     2104                    }
     2105                    $mime[] = $this->LE . $this->LE;
     2106                } else {
     2107                    $mime[] = $this->encodeFile($path, $encoding);
     2108                    if ($this->isError()) {
     2109                        return '';
     2110                    }
     2111                    $mime[] = $this->LE . $this->LE;
     2112                }
     2113            }
     2114        }
     2115
     2116        $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
     2117
     2118        return implode("", $mime);
    20272119    }
    20282120
    2029     // Chomp the last linefeed
    2030     $encoded = substr($encoded, 0, -strlen($lf));
    2031     return $encoded;
    2032   }
     2121    /**
     2122     * Encode a file attachment in requested format.
     2123     * Returns an empty string on failure.
     2124     * @param string $path The full path to the file
     2125     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
     2126     * @throws phpmailerException
     2127     * @see EncodeFile(encodeFile
     2128     * @access protected
     2129     * @return string
     2130     */
     2131    protected function encodeFile($path, $encoding = 'base64')
     2132    {
     2133        try {
     2134            if (!is_readable($path)) {
     2135                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
     2136            }
     2137            $magic_quotes = get_magic_quotes_runtime();
     2138            if ($magic_quotes) {
     2139                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
     2140                    set_magic_quotes_runtime(0);
     2141                } else {
     2142                    ini_set('magic_quotes_runtime', 0);
     2143                }
     2144            }
     2145            $file_buffer = file_get_contents($path);
     2146            $file_buffer = $this->encodeString($file_buffer, $encoding);
     2147            if ($magic_quotes) {
     2148                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
     2149                    set_magic_quotes_runtime($magic_quotes);
     2150                } else {
     2151                    ini_set('magic_quotes_runtime', $magic_quotes);
     2152                }
     2153            }
     2154            return $file_buffer;
     2155        } catch (Exception $e) {
     2156            $this->setError($e->getMessage());
     2157            return '';
     2158        }
     2159    }
    20332160
    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';
     2161    /**
     2162     * Encode a string in requested format.
     2163     * Returns an empty string on failure.
     2164     * @param string $str The text to encode
     2165     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
     2166     * @access public
     2167     * @return string
     2168     */
     2169    public function encodeString($str, $encoding = 'base64')
     2170    {
     2171        $encoded = '';
     2172        switch (strtolower($encoding)) {
     2173            case 'base64':
     2174                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
     2175                break;
     2176            case '7bit':
     2177            case '8bit':
     2178                $encoded = $this->fixEOL($str);
     2179                //Make sure it ends with a line break
     2180                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
     2181                    $encoded .= $this->LE;
     2182                }
     2183                break;
     2184            case 'binary':
     2185                $encoded = $str;
     2186                break;
     2187            case 'quoted-printable':
     2188                $encoded = $this->encodeQP($str);
     2189                break;
     2190            default:
     2191                $this->setError($this->lang('encoding') . $encoding);
     2192                break;
    20582193        }
    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];
     2194        return $encoded;
     2195    }
     2196
     2197    /**
     2198     * Encode a header string optimally.
     2199     * Picks shortest of Q, B, quoted-printable or none.
     2200     * @access public
     2201     * @param string $str
     2202     * @param string $position
     2203     * @return string
     2204     */
     2205    public function encodeHeader($str, $position = 'text')
     2206    {
     2207        $x = 0;
     2208        switch (strtolower($position)) {
     2209            case 'phrase':
     2210                if (!preg_match('/[\200-\377]/', $str)) {
     2211                    // Can't use addslashes as we don't know what value has magic_quotes_sybase
     2212                    $encoded = addcslashes($str, "\0..\37\177\\\"");
     2213                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
     2214                        return ($encoded);
     2215                    } else {
     2216                        return ("\"$encoded\"");
     2217                    }
     2218                }
     2219                $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
     2220                break;
     2221            /** @noinspection PhpMissingBreakStatementInspection */
     2222            case 'comment':
     2223                $x = preg_match_all('/[()"]/', $str, $matches);
     2224                // Intentional fall-through
     2225            case 'text':
     2226            default:
     2227                $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
     2228                break;
    20692229        }
    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           }
     2230
     2231        if ($x == 0) { //There are no chars that need encoding
     2232            return ($str);
    20772233        }
    2078         $newline .= $c;
    2079       } // end of for
    2080       $output .= $newline.$eol;
    2081     } // end of while
    2082     return $output;
    2083   }
    20842234
    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);
     2235        $maxlen = 75 - 7 - strlen($this->CharSet);
     2236        // Try to select the encoding which should produce the shortest output
     2237        if ($x > strlen($str) / 3) {
     2238            //More than a third of the content will need encoding, so B encoding will be most efficient
     2239            $encoding = 'B';
     2240            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
     2241                // Use a custom function which correctly encodes and wraps long
     2242                // multibyte strings without breaking lines within a character
     2243                $encoded = $this->base64EncodeWrapMB($str, "\n");
     2244            } else {
     2245                $encoded = base64_encode($str);
     2246                $maxlen -= $maxlen % 4;
     2247                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
     2248            }
     2249        } else {
     2250            $encoding = 'Q';
     2251            $encoded = $this->encodeQ($str, $position);
     2252            $encoded = $this->wrapText($encoded, $maxlen, true);
     2253            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
     2254        }
     2255
     2256        $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded);
     2257        $encoded = trim(str_replace("\n", $this->LE, $encoded));
     2258
     2259        return $encoded;
    21002260    }
    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
     2261
     2262    /**
     2263     * Check if a string contains multi-byte characters.
     2264     * @access public
     2265     * @param string $str multi-byte text to wrap encode
     2266     * @return bool
     2267     */
     2268    public function hasMultiBytes($str)
     2269    {
     2270        if (function_exists('mb_strlen')) {
     2271            return (strlen($str) > mb_strlen($str, $this->CharSet));
     2272        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
     2273            return false;
     2274        }
    21042275    }
    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   }
    21172276
    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;
     2277    /**
     2278     * Encode and wrap long multibyte strings for mail headers
     2279     * without breaking lines within a character.
     2280     * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
     2281     * @access public
     2282     * @param string $str multi-byte text to wrap encode
     2283     * @param string $lf string to use as linefeed/end-of-line
     2284     * @return string
     2285     */
     2286    public function base64EncodeWrapMB($str, $lf = null)
     2287    {
     2288        $start = "=?" . $this->CharSet . "?B?";
     2289        $end = "?=";
     2290        $encoded = "";
     2291        if ($lf === null) {
     2292            $lf = $this->LE;
     2293        }
    21342294
    2135       case 'comment':
    2136         $pattern = '\(\)"';
    2137         //note that we dont break here!
    2138         //for this reason we build the $pattern withoud including delimiters and []
     2295        $mb_length = mb_strlen($str, $this->CharSet);
     2296        // Each line must have length <= 75, including $start and $end
     2297        $length = 75 - strlen($start) - strlen($end);
     2298        // Average multi-byte ratio
     2299        $ratio = $mb_length / strlen($str);
     2300        // Base64 has a 4:3 ratio
     2301        $avgLength = floor($length * $ratio * .75);
    21392302
    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;
     2303        for ($i = 0; $i < $mb_length; $i += $offset) {
     2304            $lookBack = 0;
     2305            do {
     2306                $offset = $avgLength - $lookBack;
     2307                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
     2308                $chunk = base64_encode($chunk);
     2309                $lookBack++;
     2310            } while (strlen($chunk) > $length);
     2311            $encoded .= $chunk . $lf;
     2312        }
     2313
     2314        // Chomp the last linefeed
     2315        $encoded = substr($encoded, 0, -strlen($lf));
     2316        return $encoded;
    21462317    }
    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       }
     2318
     2319    /**
     2320     * Encode a string in quoted-printable format.
     2321     * According to RFC2045 section 6.7.
     2322     * @access public
     2323     * @param string $string The text to encode
     2324     * @param integer $line_max Number of chars allowed on a line before wrapping
     2325     * @return string
     2326     * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
     2327     */
     2328    public function encodeQP($string, $line_max = 76)
     2329    {
     2330        if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
     2331            return quoted_printable_encode($string);
     2332        }
     2333        //Fall back to a pure PHP implementation
     2334        $string = str_replace(
     2335            array('%20', '%0D%0A.', '%0D%0A', '%'),
     2336            array(' ', "\r\n=2E", "\r\n", '='),
     2337            rawurlencode($string)
     2338        );
     2339        $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
     2340        return $string;
    21522341    }
    2153    
    2154     //Replace every spaces to _ (more readable than =20)
    2155     return str_replace(' ', '_', $encoded);
    2156 }
    21572342
     2343    /**
     2344     * Backward compatibility wrapper for an old QP encoding function that was removed.
     2345     * @see PHPMailer::encodeQP()
     2346     * @access public
     2347     * @param string $string
     2348     * @param integer $line_max
     2349     * @param bool $space_conv
     2350     * @return string
     2351     * @deprecated Use encodeQP instead.
     2352     */
     2353    public function encodeQPphp(
     2354        $string,
     2355        $line_max = 76,
     2356        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
     2357    ) {
     2358        return $this->encodeQP($string, $line_max);
     2359    }
    21582360
    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   }
     2361    /**
     2362     * Encode a string using Q encoding.
     2363     * @link http://tools.ietf.org/html/rfc2047
     2364     * @param string $str the text to encode
     2365     * @param string $position Where the text is going to be used, see the RFC for what that means
     2366     * @access public
     2367     * @return string
     2368     */
     2369    public function encodeQ($str, $position = 'text')
     2370    {
     2371        //There should not be any EOL in the string
     2372        $pattern = '';
     2373        $encoded = str_replace(array("\r", "\n"), '', $str);
     2374        switch (strtolower($position)) {
     2375            case 'phrase':
     2376                //RFC 2047 section 5.3
     2377                $pattern = '^A-Za-z0-9!*+\/ -';
     2378                break;
     2379            /** @noinspection PhpMissingBreakStatementInspection */
     2380            case 'comment':
     2381                //RFC 2047 section 5.2
     2382                $pattern = '\(\)"';
     2383                //intentional fall-through
     2384                //for this reason we build the $pattern without including delimiters and []
     2385            case 'text':
     2386            default:
     2387                //RFC 2047 section 5.1
     2388                //Replace every high ascii, control, =, ? and _ characters
     2389                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
     2390                break;
     2391        }
     2392        $matches = array();
     2393        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
     2394            //If the string contains an '=', make sure it's the first thing we replace
     2395            //so as to avoid double-encoding
     2396            $s = array_search('=', $matches[0]);
     2397            if ($s !== false) {
     2398                unset($matches[0][$s]);
     2399                array_unshift($matches[0], '=');
     2400            }
     2401            foreach (array_unique($matches[0]) as $char) {
     2402                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
     2403            }
     2404        }
     2405        //Replace every spaces to _ (more readable than =20)
     2406        return str_replace(' ', '_', $encoded);
     2407    }
    21822408
    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') {
    21972409
    2198     if ( !@is_file($path) ) {
    2199       $this->SetError($this->Lang('file_access') . $path);
    2200       return false;
     2410    /**
     2411     * Add a string or binary attachment (non-filesystem).
     2412     * This method can be used to attach ascii or binary data,
     2413     * such as a BLOB record from a database.
     2414     * @param string $string String attachment data.
     2415     * @param string $filename Name of the attachment.
     2416     * @param string $encoding File encoding (see $Encoding).
     2417     * @param string $type File extension (MIME) type.
     2418     * @param string $disposition Disposition to use
     2419     * @return void
     2420     */
     2421    public function addStringAttachment(
     2422        $string,
     2423        $filename,
     2424        $encoding = 'base64',
     2425        $type = '',
     2426        $disposition = 'attachment'
     2427    ) {
     2428        //If a MIME type is not specified, try to work it out from the file name
     2429        if ($type == '') {
     2430            $type = self::filenameToType($filename);
     2431        }
     2432        // Append to $attachment array
     2433        $this->attachment[] = array(
     2434            0 => $string,
     2435            1 => $filename,
     2436            2 => basename($filename),
     2437            3 => $encoding,
     2438            4 => $type,
     2439            5 => true, // isStringAttachment
     2440            6 => $disposition,
     2441            7 => 0
     2442        );
    22012443    }
    22022444
    2203     $filename = basename($path);
    2204     if ( $name == '' ) {
    2205       $name = $filename;
    2206     }
     2445    /**
     2446     * Add an embedded (inline) attachment from a file.
     2447     * This can include images, sounds, and just about any other document type.
     2448     * These differ from 'regular' attachmants in that they are intended to be
     2449     * displayed inline with the message, not just attached for download.
     2450     * This is used in HTML messages that embed the images
     2451     * the HTML refers to using the $cid value.
     2452     * @param string $path Path to the attachment.
     2453     * @param string $cid Content ID of the attachment; Use this to reference
     2454     *        the content when using an embedded image in HTML.
     2455     * @param string $name Overrides the attachment name.
     2456     * @param string $encoding File encoding (see $Encoding).
     2457     * @param string $type File MIME type.
     2458     * @param string $disposition Disposition to use
     2459     * @return bool True on successfully adding an attachment
     2460     */
     2461    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
     2462    {
     2463        if (!@is_file($path)) {
     2464            $this->setError($this->lang('file_access') . $path);
     2465            return false;
     2466        }
    22072467
    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     );
     2468        //If a MIME type is not specified, try to work it out from the file name
     2469        if ($type == '') {
     2470            $type = self::filenameToType($path);
     2471        }
    22192472
    2220     return true;
    2221   }
     2473        $filename = basename($path);
     2474        if ($name == '') {
     2475            $name = $filename;
     2476        }
    22222477
    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') {
     2478        // Append to $attachment array
     2479        $this->attachment[] = array(
     2480            0 => $path,
     2481            1 => $filename,
     2482            2 => $name,
     2483            3 => $encoding,
     2484            4 => $type,
     2485            5 => false, // isStringAttachment
     2486            6 => $disposition,
     2487            7 => $cid
     2488        );
    22582489        return true;
    2259       }
    22602490    }
    2261     return false;
    2262   }
    22632491
    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') {
     2492    /**
     2493     * Add an embedded stringified attachment.
     2494     * This can include images, sounds, and just about any other document type.
     2495     * Be sure to set the $type to an image type for images:
     2496     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
     2497     * @param string $string The attachment binary data.
     2498     * @param string $cid Content ID of the attachment; Use this to reference
     2499     *        the content when using an embedded image in HTML.
     2500     * @param string $name
     2501     * @param string $encoding File encoding (see $Encoding).
     2502     * @param string $type MIME type.
     2503     * @param string $disposition Disposition to use
     2504     * @return bool True on successfully adding an attachment
     2505     */
     2506    public function addStringEmbeddedImage(
     2507        $string,
     2508        $cid,
     2509        $name = '',
     2510        $encoding = 'base64',
     2511        $type = '',
     2512        $disposition = 'inline'
     2513    ) {
     2514        //If a MIME type is not specified, try to work it out from the name
     2515        if ($type == '') {
     2516            $type = self::filenameToType($name);
     2517        }
     2518
     2519        // Append to $attachment array
     2520        $this->attachment[] = array(
     2521            0 => $string,
     2522            1 => $name,
     2523            2 => $name,
     2524            3 => $encoding,
     2525            4 => $type,
     2526            5 => true, // isStringAttachment
     2527            6 => $disposition,
     2528            7 => $cid
     2529        );
    22712530        return true;
    2272       }
    22732531    }
    2274     return false;
    2275   }
    22762532
    2277   /**
    2278    * Does this message have an alternative body set?
    2279    * @return bool
    2280    */
    2281   public function AlternativeExists() {
    2282     return !empty($this->AltBody);
    2283   }
     2533    /**
     2534     * Check if an inline attachment is present.
     2535     * @access public
     2536     * @return bool
     2537     */
     2538    public function inlineImageExists()
     2539    {
     2540        foreach ($this->attachment as $attachment) {
     2541            if ($attachment[6] == 'inline') {
     2542                return true;
     2543            }
     2544        }
     2545        return false;
     2546    }
    22842547
    2285   /////////////////////////////////////////////////
    2286   // CLASS METHODS, MESSAGE RESET
    2287   /////////////////////////////////////////////////
     2548    /**
     2549     * Check if an attachment (non-inline) is present.
     2550     * @return bool
     2551     */
     2552    public function attachmentExists()
     2553    {
     2554        foreach ($this->attachment as $attachment) {
     2555            if ($attachment[6] == 'attachment') {
     2556                return true;
     2557            }
     2558        }
     2559        return false;
     2560    }
    22882561
    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])]);
     2562    /**
     2563     * Check if this message has an alternative body set.
     2564     * @return bool
     2565     */
     2566    public function alternativeExists()
     2567    {
     2568        return !empty($this->AltBody);
    22962569    }
    2297     $this->to = array();
    2298   }
    22992570
    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])]);
     2571    /**
     2572     * Clear all To recipients.
     2573     * @return void
     2574     */
     2575    public function clearAddresses()
     2576    {
     2577        foreach ($this->to as $to) {
     2578            unset($this->all_recipients[strtolower($to[0])]);
     2579        }
     2580        $this->to = array();
    23072581    }
    2308     $this->cc = array();
    2309   }
    23102582
    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])]);
     2583    /**
     2584     * Clear all CC recipients.
     2585     * @return void
     2586     */
     2587    public function clearCCs()
     2588    {
     2589        foreach ($this->cc as $cc) {
     2590            unset($this->all_recipients[strtolower($cc[0])]);
     2591        }
     2592        $this->cc = array();
    23182593    }
    2319     $this->bcc = array();
    2320   }
    23212594
    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   }
     2595    /**
     2596     * Clear all BCC recipients.
     2597     * @return void
     2598     */
     2599    public function clearBCCs()
     2600    {
     2601        foreach ($this->bcc as $bcc) {
     2602            unset($this->all_recipients[strtolower($bcc[0])]);
     2603        }
     2604        $this->bcc = array();
     2605    }
    23292606
    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   }
     2607    /**
     2608     * Clear all ReplyTo recipients.
     2609     * @return void
     2610     */
     2611    public function clearReplyTos()
     2612    {
     2613        $this->ReplyTo = array();
     2614    }
    23412615
    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   }
     2616    /**
     2617     * Clear all recipient types.
     2618     * @return void
     2619     */
     2620    public function clearAllRecipients()
     2621    {
     2622        $this->to = array();
     2623        $this->cc = array();
     2624        $this->bcc = array();
     2625        $this->all_recipients = array();
     2626    }
    23502627
    2351   /**
    2352    * Clears all custom headers.  Returns void.
    2353    * @return void
    2354    */
    2355   public function ClearCustomHeaders() {
    2356     $this->CustomHeader = array();
    2357   }
     2628    /**
     2629     * Clear all filesystem, string, and binary attachments.
     2630     * @return void
     2631     */
     2632    public function clearAttachments()
     2633    {
     2634        $this->attachment = array();
     2635    }
    23582636
    2359   /////////////////////////////////////////////////
    2360   // CLASS METHODS, MISCELLANEOUS
    2361   /////////////////////////////////////////////////
     2637    /**
     2638     * Clear all custom headers.
     2639     * @return void
     2640     */
     2641    public function clearCustomHeaders()
     2642    {
     2643        $this->CustomHeader = array();
     2644    }
    23622645
    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       }
     2646    /**
     2647     * Add an error message to the error container.
     2648     * @access protected
     2649     * @param string $msg
     2650     * @return void
     2651     */
     2652    protected function setError($msg)
     2653    {
     2654        $this->error_count++;
     2655        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
     2656            $lasterror = $this->smtp->getError();
     2657            if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
     2658                $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
     2659            }
     2660        }
     2661        $this->ErrorInfo = $msg;
    23762662    }
    2377     $this->ErrorInfo = $msg;
    2378   }
    23792663
    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);
     2664    /**
     2665     * Return an RFC 822 formatted date.
     2666     * @access public
     2667     * @return string
     2668     * @static
     2669     */
     2670    public static function rfcDate()
     2671    {
     2672        //Set the time zone to whatever the default is to avoid 500 errors
     2673        //Will default to UTC if it's not set properly in php.ini
     2674        date_default_timezone_set(@date_default_timezone_get());
     2675        return date('D, j M Y H:i:s O');
     2676    }
    23922677
    2393     return $result;
    2394   }
     2678    /**
     2679     * Get the server hostname.
     2680     * Returns 'localhost.localdomain' if unknown.
     2681     * @access protected
     2682     * @return string
     2683     */
     2684    protected function serverHostname()
     2685    {
     2686        if (!empty($this->Hostname)) {
     2687            $result = $this->Hostname;
     2688        } elseif (isset($_SERVER['SERVER_NAME'])) {
     2689            $result = $_SERVER['SERVER_NAME'];
     2690        } else {
     2691            $result = 'localhost.localdomain';
     2692        }
    23952693
    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';
     2694        return $result;
    24082695    }
    24092696
    2410     return $result;
    2411   }
     2697    /**
     2698     * Get an error message in the current language.
     2699     * @access protected
     2700     * @param string $key
     2701     * @return string
     2702     */
     2703    protected function lang($key)
     2704    {
     2705        if (count($this->language) < 1) {
     2706            $this->setLanguage('en'); // set the default language
     2707        }
    24122708
    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
     2709        if (isset($this->language[$key])) {
     2710            return $this->language[$key];
     2711        } else {
     2712            return 'Language string failed to load: ' . $key;
     2713        }
    24222714    }
    24232715
    2424     if(isset($this->language[$key])) {
    2425       return $this->language[$key];
    2426     } else {
    2427       return 'Language string failed to load: ' . $key;
     2716    /**
     2717     * Check if an error occurred.
     2718     * @access public
     2719     * @return bool True if an error did occur.
     2720     */
     2721    public function isError()
     2722    {
     2723        return ($this->error_count > 0);
    24282724    }
    2429   }
    24302725
    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   }
     2726    /**
     2727     * Ensure consistent line endings in a string.
     2728     * Changes every end of line from CRLF, CR or LF to $this->LE.
     2729     * @access public
     2730     * @param string $str String to fixEOL
     2731     * @return string
     2732     */
     2733    public function fixEOL($str)
     2734    {
     2735        // Normalise to \n
     2736        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
     2737        // Now convert LE as needed
     2738        if ($this->LE !== "\n") {
     2739            $nstr = str_replace("\n", $this->LE, $nstr);
     2740        }
     2741        return $nstr;
     2742    }
    24392743
    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   }
     2744    /**
     2745     * Add a custom header.
     2746     * $name value can be overloaded to contain
     2747     * both header name and value (name:value)
     2748     * @access public
     2749     * @param string $name Custom header name
     2750     * @param string $value Header value
     2751     * @return void
     2752     */
     2753    public function addCustomHeader($name, $value = null)
     2754    {
     2755        if ($value === null) {
     2756            // Value passed in as name:value
     2757            $this->CustomHeader[] = explode(':', $name, 2);
     2758        } else {
     2759            $this->CustomHeader[] = array($name, $value);
     2760        }
     2761    }
    24552762
    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   }
     2763    /**
     2764     * Create a message from an HTML string.
     2765     * Automatically makes modifications for inline images and backgrounds
     2766     * and creates a plain-text version by converting the HTML.
     2767     * Overwrites any existing values in $this->Body and $this->AltBody
     2768     * @access public
     2769     * @param string $message HTML message string
     2770     * @param string $basedir baseline directory for path
     2771     * @param bool $advanced Whether to use the advanced HTML to text converter
     2772     * @return string $message
     2773     */
     2774    public function msgHTML($message, $basedir = '', $advanced = false)
     2775    {
     2776        preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
     2777        if (isset($images[2])) {
     2778            foreach ($images[2] as $i => $url) {
     2779                // do not change urls for absolute images (thanks to corvuscorax)
     2780                if (!preg_match('#^[A-z]+://#', $url)) {
     2781                    $filename = basename($url);
     2782                    $directory = dirname($url);
     2783                    if ($directory == '.') {
     2784                        $directory = '';
     2785                    }
     2786                    $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2
     2787                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
     2788                        $basedir .= '/';
     2789                    }
     2790                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
     2791                        $directory .= '/';
     2792                    }
     2793                    if ($this->addEmbeddedImage(
     2794                        $basedir . $directory . $filename,
     2795                        $cid,
     2796                        $filename,
     2797                        'base64',
     2798                        self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
     2799                    )
     2800                    ) {
     2801                        $message = preg_replace(
     2802                            "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",
     2803                            $images[1][$i] . "=\"cid:" . $cid . "\"",
     2804                            $message
     2805                        );
     2806                    }
     2807                }
     2808            }
     2809        }
     2810        $this->isHTML(true);
     2811        if (empty($this->AltBody)) {
     2812            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
     2813        }
     2814        //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
     2815        $this->Body = $this->normalizeBreaks($message);
     2816        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
     2817        return $this->Body;
     2818    }
    24722819
    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           }
     2820    /**
     2821     * Convert an HTML string into plain text.
     2822     * @param string $html The HTML text to convert
     2823     * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
     2824     * @return string
     2825     */
     2826    public function html2text($html, $advanced = false)
     2827    {
     2828        if ($advanced) {
     2829            require_once 'extras/class.html2text.php';
     2830            $h = new html2text($html);
     2831            return $h->get_text();
    24992832        }
    2500       }
     2833        return html_entity_decode(
     2834            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
     2835            ENT_QUOTES,
     2836            $this->CharSet
     2837        );
    25012838    }
    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);
     2839
     2840    /**
     2841     * Get the MIME type for a file extension.
     2842     * @param string $ext File extension
     2843     * @access public
     2844     * @return string MIME type of file.
     2845     * @static
     2846     */
     2847    public static function _mime_types($ext = '')
     2848    {
     2849        $mimes = array(
     2850            'xl' => 'application/excel',
     2851            'hqx' => 'application/mac-binhex40',
     2852            'cpt' => 'application/mac-compactpro',
     2853            'bin' => 'application/macbinary',
     2854            'doc' => 'application/msword',
     2855            'word' => 'application/msword',
     2856            'class' => 'application/octet-stream',
     2857            'dll' => 'application/octet-stream',
     2858            'dms' => 'application/octet-stream',
     2859            'exe' => 'application/octet-stream',
     2860            'lha' => 'application/octet-stream',
     2861            'lzh' => 'application/octet-stream',
     2862            'psd' => 'application/octet-stream',
     2863            'sea' => 'application/octet-stream',
     2864            'so' => 'application/octet-stream',
     2865            'oda' => 'application/oda',
     2866            'pdf' => 'application/pdf',
     2867            'ai' => 'application/postscript',
     2868            'eps' => 'application/postscript',
     2869            'ps' => 'application/postscript',
     2870            'smi' => 'application/smil',
     2871            'smil' => 'application/smil',
     2872            'mif' => 'application/vnd.mif',
     2873            'xls' => 'application/vnd.ms-excel',
     2874            'ppt' => 'application/vnd.ms-powerpoint',
     2875            'wbxml' => 'application/vnd.wap.wbxml',
     2876            'wmlc' => 'application/vnd.wap.wmlc',
     2877            'dcr' => 'application/x-director',
     2878            'dir' => 'application/x-director',
     2879            'dxr' => 'application/x-director',
     2880            'dvi' => 'application/x-dvi',
     2881            'gtar' => 'application/x-gtar',
     2882            'php3' => 'application/x-httpd-php',
     2883            'php4' => 'application/x-httpd-php',
     2884            'php' => 'application/x-httpd-php',
     2885            'phtml' => 'application/x-httpd-php',
     2886            'phps' => 'application/x-httpd-php-source',
     2887            'js' => 'application/x-javascript',
     2888            'swf' => 'application/x-shockwave-flash',
     2889            'sit' => 'application/x-stuffit',
     2890            'tar' => 'application/x-tar',
     2891            'tgz' => 'application/x-tar',
     2892            'xht' => 'application/xhtml+xml',
     2893            'xhtml' => 'application/xhtml+xml',
     2894            'zip' => 'application/zip',
     2895            'mid' => 'audio/midi',
     2896            'midi' => 'audio/midi',
     2897            'mp2' => 'audio/mpeg',
     2898            'mp3' => 'audio/mpeg',
     2899            'mpga' => 'audio/mpeg',
     2900            'aif' => 'audio/x-aiff',
     2901            'aifc' => 'audio/x-aiff',
     2902            'aiff' => 'audio/x-aiff',
     2903            'ram' => 'audio/x-pn-realaudio',
     2904            'rm' => 'audio/x-pn-realaudio',
     2905            'rpm' => 'audio/x-pn-realaudio-plugin',
     2906            'ra' => 'audio/x-realaudio',
     2907            'wav' => 'audio/x-wav',
     2908            'bmp' => 'image/bmp',
     2909            'gif' => 'image/gif',
     2910            'jpeg' => 'image/jpeg',
     2911            'jpe' => 'image/jpeg',
     2912            'jpg' => 'image/jpeg',
     2913            'png' => 'image/png',
     2914            'tiff' => 'image/tiff',
     2915            'tif' => 'image/tiff',
     2916            'eml' => 'message/rfc822',
     2917            'css' => 'text/css',
     2918            'html' => 'text/html',
     2919            'htm' => 'text/html',
     2920            'shtml' => 'text/html',
     2921            'log' => 'text/plain',
     2922            'text' => 'text/plain',
     2923            'txt' => 'text/plain',
     2924            'rtx' => 'text/richtext',
     2925            'rtf' => 'text/rtf',
     2926            'xml' => 'text/xml',
     2927            'xsl' => 'text/xml',
     2928            'mpeg' => 'video/mpeg',
     2929            'mpe' => 'video/mpeg',
     2930            'mpg' => 'video/mpeg',
     2931            'mov' => 'video/quicktime',
     2932            'qt' => 'video/quicktime',
     2933            'rv' => 'video/vnd.rn-realvideo',
     2934            'avi' => 'video/x-msvideo',
     2935            'movie' => 'video/x-sgi-movie'
     2936        );
     2937        return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
     2938    }
     2939
     2940    /**
     2941     * Map a file name to a MIME type.
     2942     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
     2943     * @param string $filename A file name or full path, does not need to exist as a file
     2944     * @return string
     2945     * @static
     2946     */
     2947    public static function filenameToType($filename)
     2948    {
     2949        //In case the path is a URL, strip any query string before getting extension
     2950        $qpos = strpos($filename, '?');
     2951        if ($qpos !== false) {
     2952            $filename = substr($filename, 0, $qpos);
    25082953        }
     2954        $pathinfo = self::mb_pathinfo($filename);
     2955        return self::_mime_types($pathinfo['extension']);
    25092956    }
    2510     if (empty($this->AltBody)) {
    2511       $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
     2957
     2958    /**
     2959     * Multi-byte-safe pathinfo replacement.
     2960     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
     2961     * Works similarly to the one in PHP >= 5.2.0
     2962     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
     2963     * @param string $path A filename or path, does not need to exist as a file
     2964     * @param integer|string $options Either a PATHINFO_* constant,
     2965     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
     2966     * @return string|array
     2967     * @static
     2968     */
     2969    public static function mb_pathinfo($path, $options = null)
     2970    {
     2971        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
     2972        $m = array();
     2973        preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
     2974        if (array_key_exists(1, $m)) {
     2975            $ret['dirname'] = $m[1];
     2976        }
     2977        if (array_key_exists(2, $m)) {
     2978            $ret['basename'] = $m[2];
     2979        }
     2980        if (array_key_exists(5, $m)) {
     2981            $ret['extension'] = $m[5];
     2982        }
     2983        if (array_key_exists(3, $m)) {
     2984            $ret['filename'] = $m[3];
     2985        }
     2986        switch ($options) {
     2987            case PATHINFO_DIRNAME:
     2988            case 'dirname':
     2989                return $ret['dirname'];
     2990                break;
     2991            case PATHINFO_BASENAME:
     2992            case 'basename':
     2993                return $ret['basename'];
     2994                break;
     2995            case PATHINFO_EXTENSION:
     2996            case 'extension':
     2997                return $ret['extension'];
     2998                break;
     2999            case PATHINFO_FILENAME:
     3000            case 'filename':
     3001                return $ret['filename'];
     3002                break;
     3003            default:
     3004                return $ret;
     3005        }
    25123006    }
    2513     return $message;
    2514   }
    25153007
    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   }
     3008    /**
     3009     * Set or reset instance properties.
     3010     *
     3011     * Usage Example:
     3012     * $page->set('X-Priority', '3');
     3013     *
     3014     * @access public
     3015     * @param string $name
     3016     * @param mixed $value
     3017     * NOTE: will not work with arrays, there are no arrays to set/reset
     3018     * @throws phpmailerException
     3019     * @return bool
     3020     * @todo Should this not be using __set() magic function?
     3021     */
     3022    public function set($name, $value = '')
     3023    {
     3024        try {
     3025            if (isset($this->$name)) {
     3026                $this->$name = $value;
     3027            } else {
     3028                throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
     3029            }
     3030        } catch (Exception $e) {
     3031            $this->setError($e->getMessage());
     3032            if ($e->getCode() == self::STOP_CRITICAL) {
     3033                return false;
     3034            }
     3035        }
     3036        return true;
     3037    }
    26143038
    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) {
    2639         return false;
    2640       }
     3039    /**
     3040     * Strip newlines to prevent header injection.
     3041     * @access public
     3042     * @param string $str
     3043     * @return string
     3044     */
     3045    public function secureHeader($str)
     3046    {
     3047        return trim(str_replace(array("\r", "\n"), '', $str));
    26413048    }
    2642     return true;
    2643   }
    26443049
    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   }
     3050    /**
     3051     * Normalize line breaks in a string.
     3052     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
     3053     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
     3054     * @param string $text
     3055     * @param string $breaktype What kind of line break to use, defaults to CRLF
     3056     * @return string
     3057     * @access public
     3058     * @static
     3059     */
     3060    public static function normalizeBreaks($text, $breaktype = "\r\n")
     3061    {
     3062        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
     3063    }
    26543064
    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   }
    26683065
    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       }
     3066    /**
     3067     * Set the private key file and password for S/MIME signing.
     3068     * @access public
     3069     * @param string $cert_filename
     3070     * @param string $key_filename
     3071     * @param string $key_pass Password for private key
     3072     */
     3073    public function sign($cert_filename, $key_filename, $key_pass)
     3074    {
     3075        $this->sign_cert_file = $cert_filename;
     3076        $this->sign_key_file = $key_filename;
     3077        $this->sign_key_pass = $key_pass;
    26853078    }
    2686     return $line;
    2687   }
    26883079
    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;
     3080    /**
     3081     * Quoted-Printable-encode a DKIM header.
     3082     * @access public
     3083     * @param string $txt
     3084     * @return string
     3085     */
     3086    public function DKIM_QP($txt)
     3087    {
     3088        $line = '';
     3089        for ($i = 0; $i < strlen($txt); $i++) {
     3090            $ord = ord($txt[$i]);
     3091            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
     3092                $line .= $txt[$i];
     3093            } else {
     3094                $line .= "=" . sprintf("%02X", $ord);
     3095            }
     3096        }
     3097        return $line;
    27023098    }
    2703     if (openssl_sign($s, $signature, $privKey)) {
    2704       return base64_encode($signature);
     3099
     3100    /**
     3101     * Generate a DKIM signature.
     3102     * @access public
     3103     * @param string $s Header
     3104     * @throws phpmailerException
     3105     * @return string
     3106     */
     3107    public function DKIM_Sign($s)
     3108    {
     3109        if (!defined('PKCS7_TEXT')) {
     3110            if ($this->exceptions) {
     3111                throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.');
     3112            }
     3113            return '';
     3114        }
     3115        $privKeyStr = file_get_contents($this->DKIM_private);
     3116        if ($this->DKIM_passphrase != '') {
     3117            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
     3118        } else {
     3119            $privKey = $privKeyStr;
     3120        }
     3121        if (openssl_sign($s, $signature, $privKey)) {
     3122            return base64_encode($signature);
     3123        }
     3124        return '';
    27053125    }
    2706     return '';
    2707   }
    27083126
    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
     3127    /**
     3128     * Generate a DKIM canonicalization header.
     3129     * @access public
     3130     * @param string $s Header
     3131     * @return string
     3132     */
     3133    public function DKIM_HeaderC($s)
     3134    {
     3135        $s = preg_replace("/\r\n\s+/", " ", $s);
     3136        $lines = explode("\r\n", $s);
     3137        foreach ($lines as $key => $line) {
     3138            list($heading, $value) = explode(":", $line, 2);
     3139            $heading = strtolower($heading);
     3140            $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces
     3141            $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value
     3142        }
     3143        $s = implode("\r\n", $lines);
     3144        return $s;
    27243145    }
    2725     $s = implode("\r\n", $lines);
    2726     return $s;
    2727   }
    27283146
    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);
     3147    /**
     3148     * Generate a DKIM canonicalization body.
     3149     * @access public
     3150     * @param string $body Message Body
     3151     * @return string
     3152     */
     3153    public function DKIM_BodyC($body)
     3154    {
     3155        if ($body == '') {
     3156            return "\r\n";
     3157        }
     3158        // stabilize line endings
     3159        $body = str_replace("\r\n", "\n", $body);
     3160        $body = str_replace("\n", "\r\n", $body);
     3161        // END stabilize line endings
     3162        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
     3163            $body = substr($body, 0, strlen($body) - 2);
     3164        }
     3165        return $body;
    27443166    }
    2745     return $body;
    2746   }
    27473167
    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       }
     3168    /**
     3169     * Create the DKIM header and body in a new message header.
     3170     * @access public
     3171     * @param string $headers_line Header lines
     3172     * @param string $subject Subject
     3173     * @param string $body Body
     3174     * @return string
     3175     */
     3176    public function DKIM_Add($headers_line, $subject, $body)
     3177    {
     3178        $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
     3179        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
     3180        $DKIMquery = 'dns/txt'; // Query method
     3181        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
     3182        $subject_header = "Subject: $subject";
     3183        $headers = explode($this->LE, $headers_line);
     3184        $from_header = '';
     3185        $to_header = '';
     3186        $current = '';
     3187        foreach ($headers as $header) {
     3188            if (strpos($header, 'From:') === 0) {
     3189                $from_header = $header;
     3190                $current = 'from_header';
     3191            } elseif (strpos($header, 'To:') === 0) {
     3192                $to_header = $header;
     3193                $current = 'to_header';
     3194            } else {
     3195                if ($current && strpos($header, ' =?') === 0) {
     3196                    $current .= $header;
     3197                } else {
     3198                    $current = '';
     3199                }
     3200            }
     3201        }
     3202        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
     3203        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
     3204        $subject = str_replace(
     3205            '|',
     3206            '=7C',
     3207            $this->DKIM_QP($subject_header)
     3208        ); // Copied header fields (dkim-quoted-printable)
     3209        $body = $this->DKIM_BodyC($body);
     3210        $DKIMlen = strlen($body); // Length of body
     3211        $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body
     3212        $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";";
     3213        $dkimhdrs = "DKIM-Signature: v=1; a=" .
     3214            $DKIMsignatureType . "; q=" .
     3215            $DKIMquery . "; l=" .
     3216            $DKIMlen . "; s=" .
     3217            $this->DKIM_selector .
     3218            ";\r\n" .
     3219            "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" .
     3220            "\th=From:To:Subject;\r\n" .
     3221            "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" .
     3222            "\tz=$from\r\n" .
     3223            "\t|$to\r\n" .
     3224            "\t|$subject;\r\n" .
     3225            "\tbh=" . $DKIMb64 . ";\r\n" .
     3226            "\tb=";
     3227        $toSign = $this->DKIM_HeaderC(
     3228            $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
     3229        );
     3230        $signed = $this->DKIM_Sign($toSign);
     3231        return $dkimhdrs . $signed . "\r\n";
    27723232    }
    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   }
    27933233
    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);
     3234    /**
     3235     * Perform a callback.
     3236     * @param bool $isSent
     3237     * @param string $to
     3238     * @param string $cc
     3239     * @param string $bcc
     3240     * @param string $subject
     3241     * @param string $body
     3242     * @param string $from
     3243     */
     3244    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)
     3245    {
     3246        if (!empty($this->action_function) && is_callable($this->action_function)) {
     3247            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
     3248            call_user_func_array($this->action_function, $params);
     3249        }
    28083250    }
    2809   }
    28103251}
    28113252
    28123253/**
    2813  * Exception handler for PHPMailer
     3254 * PHPMailer exception handler
    28143255 * @package PHPMailer
    28153256 */
    2816 class phpmailerException extends Exception {
    2817   /**
    2818    * Prettify error message output
    2819    * @return string
    2820    */
    2821   public function errorMessage() {
    2822     $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
    2823     return $errorMsg;
    2824   }
     3257class phpmailerException extends Exception
     3258{
     3259    /**
     3260     * Prettify error message output
     3261     * @return string
     3262     */
     3263    public function errorMessage()
     3264    {
     3265        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
     3266        return $errorMsg;
     3267    }
    28253268}
    2826 ?>
  • wp-includes/class-smtp.php

     
    11<?php
    2 /*~ class.smtp.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 SMTP email transport class
    27  * NOTE: Designed for use with PHP version 5 and up
    28  * @package PHPMailer
    29  * @author Andy Prevost
    30  * @author Marcus Bointon
     3 * PHPMailer RFC821 SMTP email transport class.
     4 * Version 5.2.7
     5 * PHP version 5.0.0
     6 * @category  PHP
     7 * @package   PHPMailer
     8 * @link      https://github.com/PHPMailer/PHPMailer/
     9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     12 * @copyright 2013 Marcus Bointon
    3113 * @copyright 2004 - 2008 Andy Prevost
    32  * @author Jim Jagielski
    3314 * @copyright 2010 - 2012 Jim Jagielski
    34  * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     15 * @license   http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
    3516 */
    3617
    3718/**
    38  * PHP RFC821 SMTP client
     19 * PHPMailer RFC821 SMTP email transport class.
    3920 *
    40  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
    41  * SMTP also provides some utility methods for sending mail to an SMTP server.
    42  * @author Chris Ryan
    43  * @package PHPMailer
     21 * Implements RFC 821 SMTP commands
     22 * and provides some utility methods for sending mail to an SMTP server.
     23 *
     24 * PHP Version 5.0.0
     25 *
     26 * @category PHP
     27 * @package  PHPMailer
     28 * @link     https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php
     29 * @author   Chris Ryan <unknown@example.com>
     30 * @author   Marcus Bointon <phpmailer@synchromedia.co.uk>
     31 * @license  http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
    4432 */
    4533
    46 class SMTP {
    47   /**
    48    *  SMTP server port
    49    *  @var int
    50    */
    51   public $SMTP_PORT = 25;
     34class SMTP
     35{
     36    /**
     37     * The PHPMailer SMTP Version number.
     38     */
     39    const VERSION = '5.2.7';
    5240
    53   /**
    54    *  SMTP reply line ending (don't change)
    55    *  @var string
    56    */
    57   public $CRLF = "\r\n";
     41    /**
     42     * SMTP line break constant.
     43     */
     44    const CRLF = "\r\n";
    5845
    59   /**
    60    *  Sets whether debugging is turned on
    61    *  @var bool
    62    */
    63   public $do_debug;       // the level of debug to perform
     46    /**
     47     * The SMTP port to use if one is not specified.
     48     */
     49    const DEFAULT_SMTP_PORT = 25;
    6450
    65   /**
    66    * Sets the function/method to use for debugging output.
    67    * Right now we only honor "echo" or "error_log"
    68    * @var string
    69    */
    70   public $Debugoutput     = "echo";
     51    /**
     52     * The PHPMailer SMTP Version number.
     53     * @type string
     54     * @deprecated This should be a constant
     55     * @see SMTP::VERSION
     56     */
     57    public $Version = '5.2.7';
    7158
    72   /**
    73    *  Sets VERP use on/off (default is off)
    74    *  @var bool
    75    */
    76   public $do_verp = false;
     59    /**
     60     * SMTP server port number.
     61     * @type int
     62     * @deprecated This is only ever ued as default value, so should be a constant
     63     * @see SMTP::DEFAULT_SMTP_PORT
     64     */
     65    public $SMTP_PORT = 25;
    7766
    78   /**
    79    * Sets the SMTP timeout value for reads, in seconds
    80    * @var int
    81    */
    82   public $Timeout         = 15;
     67    /**
     68     * SMTP reply line ending
     69     * @type string
     70     * @deprecated Use the class constant instead
     71     * @see SMTP::CRLF
     72     */
     73    public $CRLF = "\r\n";
    8374
    84   /**
    85    * Sets the SMTP timelimit value for reads, in seconds
    86    * @var int
    87    */
    88   public $Timelimit       = 30;
     75    /**
     76     * Debug output level.
     77     * Options: 0 for no output,