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