Make WordPress Core

Ticket #41750: 41750.2.diff

File 41750.2.diff, 242.1 KB (added by desrosj, 5 years ago)
  • src/wp-includes/class-phpmailer.php

     
    11<?php
    22/**
    33 * PHPMailer - PHP email creation and transport class.
    4  * PHP Version 5
    5  * @package PHPMailer
    6  * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
    7  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    8  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    9  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    10  * @author Brent R. Matzelle (original founder)
    11  * @copyright 2012 - 2014 Marcus Bointon
     4 * PHP Version 5.5.
     5 *
     6 * @see       https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
     7 *
     8 * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
     9 * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
     10 * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     11 * @author    Brent R. Matzelle (original founder)
     12 * @copyright 2012 - 2017 Marcus Bointon
    1213 * @copyright 2010 - 2012 Jim Jagielski
    1314 * @copyright 2004 - 2009 Andy Prevost
    14  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
    15  * @note This program is distributed in the hope that it will be useful - WITHOUT
     15 * @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
    1617 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1718 * FITNESS FOR A PARTICULAR PURPOSE.
    1819 */
    1920
     21namespace PHPMailer\PHPMailer;
     22
    2023/**
    2124 * PHPMailer - PHP email creation and transport class.
    22  * @package PHPMailer
    23  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    24  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    25  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    26  * @author Brent R. Matzelle (original founder)
     25 *
     26 * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
     27 * @author  Jim Jagielski (jimjag) <jimjag@gmail.com>
     28 * @author  Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     29 * @author  Brent R. Matzelle (original founder)
    2730 */
    2831class PHPMailer
    2932{
    30     /**
    31      * The PHPMailer Version number.
    32      * @var string
    33      */
    34     public $Version = '5.2.27';
     33    const CHARSET_ISO88591 = 'iso-8859-1';
     34    const CHARSET_UTF8 = 'utf-8';
     35
     36    const CONTENT_TYPE_PLAINTEXT = 'text/plain';
     37    const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar';
     38    const CONTENT_TYPE_TEXT_HTML = 'text/html';
     39    const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative';
     40    const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed';
     41    const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related';
     42
     43    const ENCODING_7BIT = '7bit';
     44    const ENCODING_8BIT = '8bit';
     45    const ENCODING_BASE64 = 'base64';
     46    const ENCODING_BINARY = 'binary';
     47    const ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
    3548
    3649    /**
    3750     * Email priority.
    3851     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
    3952     * When null, the header is not set at all.
    40      * @var integer
     53     *
     54     * @var int
    4155     */
    42     public $Priority = null;
     56    public $Priority;
    4357
    4458    /**
    4559     * The character set of the message.
     60     *
    4661     * @var string
    4762     */
    48     public $CharSet = 'iso-8859-1';
     63    public $CharSet = self::CHARSET_ISO88591;
    4964
    5065    /**
    5166     * The MIME Content-type of the message.
     67     *
    5268     * @var string
    5369     */
    54     public $ContentType = 'text/plain';
     70    public $ContentType = self::CONTENT_TYPE_PLAINTEXT;
    5571
    5672    /**
    5773     * The message encoding.
    5874     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
     75     *
    5976     * @var string
    6077     */
    61     public $Encoding = '8bit';
     78    public $Encoding = self::ENCODING_8BIT;
    6279
    6380    /**
    6481     * Holds the most recent mailer error message.
     82     *
    6583     * @var string
    6684     */
    6785    public $ErrorInfo = '';
    6886
    6987    /**
    7088     * The From email address for the message.
     89     *
    7190     * @var string
    7291     */
    7392    public $From = 'root@localhost';
    7493
    7594    /**
    7695     * The From name of the message.
     96     *
    7797     * @var string
    7898     */
    7999    public $FromName = 'Root User';
    80100
    81101    /**
    82      * The Sender email (Return-Path) of the message.
    83      * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
     102     * The envelope sender of the message.
     103     * This will usually be turned into a Return-Path header by the receiver,
     104     * and is the address that bounces will be sent to.
     105     * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP.
     106     *
    84107     * @var string
    85108     */
    86109    public $Sender = '';
    87110
    88111    /**
    89      * The Return-Path of the message.
    90      * If empty, it will be set to either From or Sender.
    91      * @var string
    92      * @deprecated Email senders should never set a return-path header;
    93      * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
    94      * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
    95      */
    96     public $ReturnPath = '';
    97 
    98     /**
    99112     * The Subject of the message.
     113     *
    100114     * @var string
    101115     */
    102116    public $Subject = '';
     
    104118    /**
    105119     * An HTML or plain text message body.
    106120     * If HTML then call isHTML(true).
     121     *
    107122     * @var string
    108123     */
    109124    public $Body = '';
     
    113128     * This body can be read by mail clients that do not have HTML email
    114129     * capability such as mutt & Eudora.
    115130     * Clients that can read HTML will view the normal Body.
     131     *
    116132     * @var string
    117133     */
    118134    public $AltBody = '';
     
    120136    /**
    121137     * An iCal message part body.
    122138     * Only supported in simple alt or alt_inline message types
    123      * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
    124      * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
    125      * @link http://kigkonsult.se/iCalcreator/
     139     * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
     140     *
     141     * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
     142     * @see http://kigkonsult.se/iCalcreator/
     143     *
    126144     * @var string
    127145     */
    128146    public $Ical = '';
    129147
    130148    /**
    131149     * The complete compiled MIME message body.
    132      * @access protected
     150     *
    133151     * @var string
    134152     */
    135153    protected $MIMEBody = '';
    136154
    137155    /**
    138156     * The complete compiled MIME message headers.
     157     *
    139158     * @var string
    140      * @access protected
    141159     */
    142160    protected $MIMEHeader = '';
    143161
    144162    /**
    145163     * Extra headers that createHeader() doesn't fold in.
     164     *
    146165     * @var string
    147      * @access protected
    148166     */
    149167    protected $mailHeader = '';
    150168
    151169    /**
    152170     * Word-wrap the message body to this number of chars.
    153171     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
    154      * @var integer
     172     *
     173     * @see static::STD_LINE_LENGTH
     174     *
     175     * @var int
    155176     */
    156177    public $WordWrap = 0;
    157178
    158179    /**
    159180     * Which method to use to send mail.
    160181     * Options: "mail", "sendmail", or "smtp".
     182     *
    161183     * @var string
    162184     */
    163185    public $Mailer = 'mail';
    164186
    165187    /**
    166188     * The path to the sendmail program.
     189     *
    167190     * @var string
    168191     */
    169192    public $Sendmail = '/usr/sbin/sendmail';
     
    171194    /**
    172195     * Whether mail() uses a fully sendmail-compatible MTA.
    173196     * One which supports sendmail's "-oi -f" options.
    174      * @var boolean
     197     *
     198     * @var bool
    175199     */
    176200    public $UseSendmailOptions = true;
    177201
    178202    /**
    179      * Path to PHPMailer plugins.
    180      * Useful if the SMTP class is not in the PHP include path.
    181      * @var string
    182      * @deprecated Should not be needed now there is an autoloader.
    183      */
    184     public $PluginDir = '';
    185 
    186     /**
    187203     * The email address that a reading confirmation should be sent to, also known as read receipt.
     204     *
    188205     * @var string
    189206     */
    190207    public $ConfirmReadingTo = '';
     
    194211     * If empty, PHPMailer attempts to find one with, in order,
    195212     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
    196213     * 'localhost.localdomain'.
     214     *
    197215     * @var string
    198216     */
    199217    public $Hostname = '';
     
    203221     * If empty, a unique id will be generated.
    204222     * You can set your own, but it must be in the format "<id@domain>",
    205223     * as defined in RFC5322 section 3.6.4 or it will be ignored.
     224     *
    206225     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
     226     *
    207227     * @var string
    208228     */
    209229    public $MessageID = '';
     
    211231    /**
    212232     * The message Date to be used in the Date header.
    213233     * If empty, the current date will be added.
     234     *
    214235     * @var string
    215236     */
    216237    public $MessageDate = '';
     
    224245     * You can also specify encryption type, for example:
    225246     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
    226247     * Hosts will be tried in order.
     248     *
    227249     * @var string
    228250     */
    229251    public $Host = 'localhost';
    230252
    231253    /**
    232254     * The default SMTP server port.
    233      * @var integer
    234      * @TODO Why is this needed when the SMTP class takes care of it?
     255     *
     256     * @var int
    235257     */
    236258    public $Port = 25;
    237259
     
    239261     * The SMTP HELO of the message.
    240262     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
    241263     * one with the same method described above for $Hostname.
    242      * @var string
     264     *
    243265     * @see PHPMailer::$Hostname
     266     *
     267     * @var string
    244268     */
    245269    public $Helo = '';
    246270
    247271    /**
    248272     * What kind of encryption to use on the SMTP connection.
    249      * Options: '', 'ssl' or 'tls'
     273     * Options: '', 'ssl' or 'tls'.
     274     *
    250275     * @var string
    251276     */
    252277    public $SMTPSecure = '';
     
    255280     * Whether to enable TLS encryption automatically if a server supports it,
    256281     * even if `SMTPSecure` is not set to 'tls'.
    257282     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
    258      * @var boolean
     283     *
     284     * @var bool
    259285     */
    260286    public $SMTPAutoTLS = true;
    261287
    262288    /**
    263289     * Whether to use SMTP authentication.
    264290     * Uses the Username and Password properties.
    265      * @var boolean
     291     *
    266292     * @see PHPMailer::$Username
    267293     * @see PHPMailer::$Password
     294     *
     295     * @var bool
    268296     */
    269297    public $SMTPAuth = false;
    270298
    271299    /**
    272300     * Options array passed to stream_context_create when connecting via SMTP.
     301     *
    273302     * @var array
    274303     */
    275     public $SMTPOptions = array();
     304    public $SMTPOptions = [];
    276305
    277306    /**
    278307     * SMTP username.
     308     *
    279309     * @var string
    280310     */
    281311    public $Username = '';
    282312
    283313    /**
    284314     * SMTP password.
     315     *
    285316     * @var string
    286317     */
    287318    public $Password = '';
    288319
    289320    /**
    290321     * SMTP auth type.
    291      * Options are CRAM-MD5, LOGIN, PLAIN, attempted in that order if not specified
     322     * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified.
     323     *
    292324     * @var string
    293325     */
    294326    public $AuthType = '';
    295327
    296328    /**
    297      * SMTP realm.
    298      * Used for NTLM auth
    299      * @var string
    300      */
    301     public $Realm = '';
    302 
    303     /**
    304      * SMTP workstation.
    305      * Used for NTLM auth
    306      * @var string
     329     * An instance of the PHPMailer OAuth class.
     330     *
     331     * @var OAuth
    307332     */
    308     public $Workstation = '';
     333    protected $oauth;
    309334
    310335    /**
    311336     * The SMTP server timeout in seconds.
    312      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
    313      * @var integer
     337     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
     338     *
     339     * @var int
    314340     */
    315341    public $Timeout = 300;
    316342
     
    322348     * * `1` Commands
    323349     * * `2` Data and commands
    324350     * * `3` As 2 plus connection status
    325      * * `4` Low-level data output
    326      * @var integer
     351     * * `4` Low-level data output.
     352     *
    327353     * @see SMTP::$do_debug
     354     *
     355     * @var int
    328356     */
    329357    public $SMTPDebug = 0;
    330358
     
    334362     * * `echo` Output plain-text as-is, appropriate for CLI
    335363     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
    336364     * * `error_log` Output to error log as configured in php.ini
    337      *
     365     * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise.
    338366     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
    339      * <code>
     367     *
     368     * ```php
    340369     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
    341      * </code>
    342      * @var string|callable
     370     * ```
     371     *
     372     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
     373     * level output is used:
     374     *
     375     * ```php
     376     * $mail->Debugoutput = new myPsr3Logger;
     377     * ```
     378     *
    343379     * @see SMTP::$Debugoutput
     380     *
     381     * @var string|callable|\Psr\Log\LoggerInterface
    344382     */
    345383    public $Debugoutput = 'echo';
    346384
     
    348386     * Whether to keep SMTP connection open after each message.
    349387     * If this is set to true then to close the connection
    350388     * requires an explicit call to smtpClose().
    351      * @var boolean
     389     *
     390     * @var bool
    352391     */
    353392    public $SMTPKeepAlive = false;
    354393
     
    356395     * Whether to split multiple to addresses into multiple messages
    357396     * or send them all in one message.
    358397     * Only supported in `mail` and `sendmail` transports, not in SMTP.
    359      * @var boolean
     398     *
     399     * @var bool
    360400     */
    361401    public $SingleTo = false;
    362402
    363403    /**
    364404     * Storage for addresses when SingleTo is enabled.
     405     *
    365406     * @var array
    366      * @TODO This should really not be public
    367407     */
    368     public $SingleToArray = array();
     408    protected $SingleToArray = [];
    369409
    370410    /**
    371411     * Whether to generate VERP addresses on send.
    372412     * Only applicable when sending via SMTP.
    373      * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
    374      * @link http://www.postfix.org/VERP_README.html Postfix VERP info
    375      * @var boolean
     413     *
     414     * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
     415     * @see http://www.postfix.org/VERP_README.html Postfix VERP info
     416     *
     417     * @var bool
    376418     */
    377419    public $do_verp = false;
    378420
    379421    /**
    380422     * Whether to allow sending messages with an empty body.
    381      * @var boolean
     423     *
     424     * @var bool
    382425     */
    383426    public $AllowEmpty = false;
    384427
    385428    /**
    386      * The default line ending.
    387      * @note The default remains "\n". We force CRLF where we know
    388      *        it must be used via self::CRLF.
    389      * @var string
    390      */
    391     public $LE = "\n";
    392 
    393     /**
    394429     * DKIM selector.
     430     *
    395431     * @var string
    396432     */
    397433    public $DKIM_selector = '';
     
    399435    /**
    400436     * DKIM Identity.
    401437     * Usually the email address used as the source of the email.
     438     *
    402439     * @var string
    403440     */
    404441    public $DKIM_identity = '';
     
    406443    /**
    407444     * DKIM passphrase.
    408445     * Used if your key is encrypted.
     446     *
    409447     * @var string
    410448     */
    411449    public $DKIM_passphrase = '';
    412450
    413451    /**
    414452     * DKIM signing domain name.
     453     *
    415454     * @example 'example.com'
     455     *
    416456     * @var string
    417457     */
    418458    public $DKIM_domain = '';
    419459
    420460    /**
     461     * DKIM Copy header field values for diagnostic use.
     462     *
     463     * @var bool
     464     */
     465    public $DKIM_copyHeaderFields = true;
     466
     467    /**
     468     * DKIM Extra signing headers.
     469     *
     470     * @example ['List-Unsubscribe', 'List-Help']
     471     *
     472     * @var array
     473     */
     474    public $DKIM_extraHeaders = [];
     475
     476    /**
    421477     * DKIM private key file path.
     478     *
    422479     * @var string
    423480     */
    424481    public $DKIM_private = '';
    425482
    426483    /**
    427484     * DKIM private key string.
     485     *
    428486     * If set, takes precedence over `$DKIM_private`.
     487     *
    429488     * @var string
    430489     */
    431490    public $DKIM_private_string = '';
     
    439498     * Value can be any php callable: http://www.php.net/is_callable
    440499     *
    441500     * Parameters:
    442      *   boolean $result        result of the send action
     501     *   bool $result        result of the send action
    443502     *   array   $to            email addresses of the recipients
    444503     *   array   $cc            cc email addresses
    445504     *   array   $bcc           bcc email addresses
    446505     *   string  $subject       the subject
    447506     *   string  $body          the email body
    448507     *   string  $from          email address of sender
     508     *   string  $extra         extra information of possible use
     509     *                          "smtp_transaction_id' => last smtp transaction id
     510     *
    449511     * @var string
    450512     */
    451513    public $action_function = '';
    452514
    453515    /**
    454516     * What to put in the X-Mailer header.
    455      * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
     517     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use.
     518     *
    456519     * @var string
    457520     */
    458521    public $XMailer = '';
     
    460523    /**
    461524     * Which validator to use by default when validating email addresses.
    462525     * May be a callable to inject your own validator, but there are several built-in validators.
     526     * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
     527     *
    463528     * @see PHPMailer::validateAddress()
     529     *
    464530     * @var string|callable
    465      * @static
    466531     */
    467     public static $validator = 'auto';
     532    public static $validator = 'php';
    468533
    469534    /**
    470535     * An instance of the SMTP sender class.
     536     *
    471537     * @var SMTP
    472      * @access protected
    473538     */
    474     protected $smtp = null;
     539    protected $smtp;
    475540
    476541    /**
    477542     * The array of 'to' names and addresses.
     543     *
    478544     * @var array
    479      * @access protected
    480545     */
    481     protected $to = array();
     546    protected $to = [];
    482547
    483548    /**
    484549     * The array of 'cc' names and addresses.
     550     *
    485551     * @var array
    486      * @access protected
    487552     */
    488     protected $cc = array();
     553    protected $cc = [];
    489554
    490555    /**
    491556     * The array of 'bcc' names and addresses.
     557     *
    492558     * @var array
    493      * @access protected
    494559     */
    495     protected $bcc = array();
     560    protected $bcc = [];
    496561
    497562    /**
    498563     * The array of reply-to names and addresses.
     564     *
    499565     * @var array
    500      * @access protected
    501566     */
    502     protected $ReplyTo = array();
     567    protected $ReplyTo = [];
    503568
    504569    /**
    505570     * An array of all kinds of addresses.
    506      * Includes all of $to, $cc, $bcc
     571     * Includes all of $to, $cc, $bcc.
     572     *
     573     * @see PHPMailer::$to
     574     * @see PHPMailer::$cc
     575     * @see PHPMailer::$bcc
     576     *
    507577     * @var array
    508      * @access protected
    509      * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
    510578     */
    511     protected $all_recipients = array();
     579    protected $all_recipients = [];
    512580
    513581    /**
    514582     * An array of names and addresses queued for validation.
    515583     * In send(), valid and non duplicate entries are moved to $all_recipients
    516584     * and one of $to, $cc, or $bcc.
    517585     * 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
     586     *
     587     * @see PHPMailer::$to
     588     * @see PHPMailer::$cc
     589     * @see PHPMailer::$bcc
    521590     * @see PHPMailer::$all_recipients
     591     *
     592     * @var array
    522593     */
    523     protected $RecipientsQueue = array();
     594    protected $RecipientsQueue = [];
    524595
    525596    /**
    526597     * An array of reply-to names and addresses queued for validation.
    527598     * In send(), valid and non duplicate entries are moved to $ReplyTo.
    528599     * This array is used only for addresses with IDN.
    529      * @var array
    530      * @access protected
     600     *
    531601     * @see PHPMailer::$ReplyTo
     602     *
     603     * @var array
    532604     */
    533     protected $ReplyToQueue = array();
     605    protected $ReplyToQueue = [];
    534606
    535607    /**
    536608     * The array of attachments.
     609     *
    537610     * @var array
    538      * @access protected
    539611     */
    540     protected $attachment = array();
     612    protected $attachment = [];
    541613
    542614    /**
    543615     * The array of custom headers.
     616     *
    544617     * @var array
    545      * @access protected
    546618     */
    547     protected $CustomHeader = array();
     619    protected $CustomHeader = [];
    548620
    549621    /**
    550622     * The most recent Message-ID (including angular brackets).
     623     *
    551624     * @var string
    552      * @access protected
    553625     */
    554626    protected $lastMessageID = '';
    555627
    556628    /**
    557629     * The message's MIME type.
     630     *
    558631     * @var string
    559      * @access protected
    560632     */
    561633    protected $message_type = '';
    562634
    563635    /**
    564636     * The array of MIME boundary strings.
     637     *
    565638     * @var array
    566      * @access protected
    567639     */
    568     protected $boundary = array();
     640    protected $boundary = [];
    569641
    570642    /**
    571643     * The array of available languages.
     644     *
    572645     * @var array
    573      * @access protected
    574646     */
    575     protected $language = array();
     647    protected $language = [];
    576648
    577649    /**
    578650     * The number of errors encountered.
    579      * @var integer
    580      * @access protected
     651     *
     652     * @var int
    581653     */
    582654    protected $error_count = 0;
    583655
    584656    /**
    585657     * The S/MIME certificate file path.
     658     *
    586659     * @var string
    587      * @access protected
    588660     */
    589661    protected $sign_cert_file = '';
    590662
    591663    /**
    592664     * The S/MIME key file path.
     665     *
    593666     * @var string
    594      * @access protected
    595667     */
    596668    protected $sign_key_file = '';
    597669
    598670    /**
    599671     * The optional S/MIME extra certificates ("CA Chain") file path.
     672     *
    600673     * @var string
    601      * @access protected
    602674     */
    603675    protected $sign_extracerts_file = '';
    604676
    605677    /**
    606678     * The S/MIME password for the key.
    607679     * Used only if the key is encrypted.
     680     *
    608681     * @var string
    609      * @access protected
    610682     */
    611683    protected $sign_key_pass = '';
    612684
    613685    /**
    614686     * Whether to throw exceptions for errors.
    615      * @var boolean
    616      * @access protected
     687     *
     688     * @var bool
    617689     */
    618690    protected $exceptions = false;
    619691
    620692    /**
    621693     * Unique ID used for message ID and boundaries.
     694     *
    622695     * @var string
    623      * @access protected
    624696     */
    625697    protected $uniqueid = '';
    626698
    627699    /**
     700     * The PHPMailer Version number.
     701     *
     702     * @var string
     703     */
     704    const VERSION = '6.0.7';
     705
     706    /**
    628707     * Error severity: message only, continue processing.
     708     *
     709     * @var int
    629710     */
    630711    const STOP_MESSAGE = 0;
    631712
    632713    /**
    633714     * Error severity: message, likely ok to continue processing.
     715     *
     716     * @var int
    634717     */
    635718    const STOP_CONTINUE = 1;
    636719
    637720    /**
    638721     * Error severity: message, plus full stop, critical error reached.
     722     *
     723     * @var int
    639724     */
    640725    const STOP_CRITICAL = 2;
    641726
    642727    /**
    643728     * SMTP RFC standard line ending.
     729     *
     730     * @var string
    644731     */
    645     const CRLF = "\r\n";
     732    protected static $LE = "\r\n";
    646733
    647734    /**
    648      * The maximum line length allowed by RFC 2822 section 2.1.1
    649      * @var integer
     735     * The maximum line length allowed by RFC 2822 section 2.1.1.
     736     *
     737     * @var int
    650738     */
    651739    const MAX_LINE_LENGTH = 998;
    652740
    653741    /**
     742     * The lower maximum line length allowed by RFC 2822 section 2.1.1.
     743     * This length does NOT include the line break
     744     * 76 means that lines will be 77 or 78 chars depending on whether
     745     * the line break format is LF or CRLF; both are valid.
     746     *
     747     * @var int
     748     */
     749    const STD_LINE_LENGTH = 76;
     750
     751    /**
    654752     * Constructor.
    655      * @param boolean $exceptions Should we throw external exceptions?
     753     *
     754     * @param bool $exceptions Should we throw external exceptions?
    656755     */
    657756    public function __construct($exceptions = null)
    658757    {
    659         if ($exceptions !== null) {
    660             $this->exceptions = (boolean)$exceptions;
     758        if (null !== $exceptions) {
     759            $this->exceptions = (bool) $exceptions;
    661760        }
    662761        //Pick an appropriate debug output format automatically
    663762        $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
     
    676775     * Call mail() in a safe_mode-aware fashion.
    677776     * Also, unless sendmail_path points to sendmail (or something that
    678777     * claims to be sendmail), don't pass params (not a perfect fix,
    679      * but it will do)
    680      * @param string $to To
    681      * @param string $subject Subject
    682      * @param string $body Message Body
    683      * @param string $header Additional Header(s)
    684      * @param string $params Params
    685      * @access private
    686      * @return boolean
     778     * but it will do).
     779     *
     780     * @param string      $to      To
     781     * @param string      $subject Subject
     782     * @param string      $body    Message Body
     783     * @param string      $header  Additional Header(s)
     784     * @param string|null $params  Params
     785     *
     786     * @return bool
    687787     */
    688788    private function mailPassthru($to, $subject, $body, $header, $params)
    689789    {
     
    693793        } else {
    694794            $subject = $this->encodeHeader($this->secureHeader($subject));
    695795        }
    696 
    697         //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
    698         //@link http://php.net/manual/en/function.mail.php
    699         if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
     796        //Calling mail() with null params breaks
     797        if (!$this->UseSendmailOptions or null === $params) {
    700798            $result = @mail($to, $subject, $body, $header);
    701799        } else {
    702800            $result = @mail($to, $subject, $body, $header, $params);
    703801        }
     802
    704803        return $result;
    705804    }
     805
    706806    /**
    707807     * Output debugging info via user-defined method.
    708808     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
     809     *
    709810     * @see PHPMailer::$Debugoutput
    710811     * @see PHPMailer::$SMTPDebug
     812     *
    711813     * @param string $str
    712814     */
    713815    protected function edebug($str)
     
    715817        if ($this->SMTPDebug <= 0) {
    716818            return;
    717819        }
     820        //Is this a PSR-3 logger?
     821        if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
     822            $this->Debugoutput->debug($str);
     823
     824            return;
     825        }
    718826        //Avoid clash with built-in function names
    719         if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
     827        if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
    720828            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
     829
    721830            return;
    722831        }
    723832        switch ($this->Debugoutput) {
     
    731840                    preg_replace('/[\r\n]+/', '', $str),
    732841                    ENT_QUOTES,
    733842                    'UTF-8'
    734                 )
    735                 . "<br>\n";
     843                ), "<br>\n";
    736844                break;
    737845            case 'echo':
    738846            default:
    739847                //Normalize line breaks
    740                 $str = preg_replace('/\r\n?/ms', "\n", $str);
    741                 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
    742                     "\n",
    743                     "\n                   \t                  ",
    744                     trim($str)
    745                 ) . "\n";
     848                $str = preg_replace('/\r\n|\r/ms', "\n", $str);
     849                echo gmdate('Y-m-d H:i:s'),
     850                "\t",
     851                    //Trim trailing space
     852                trim(
     853                //Indent for readability, except for trailing break
     854                    str_replace(
     855                        "\n",
     856                        "\n                   \t                  ",
     857                        trim($str)
     858                    )
     859                ),
     860                "\n";
    746861        }
    747862    }
    748863
    749864    /**
    750865     * Sets message type to HTML or plain.
    751      * @param boolean $isHtml True for HTML mode.
    752      * @return void
     866     *
     867     * @param bool $isHtml True for HTML mode
    753868     */
    754869    public function isHTML($isHtml = true)
    755870    {
    756871        if ($isHtml) {
    757             $this->ContentType = 'text/html';
     872            $this->ContentType = static::CONTENT_TYPE_TEXT_HTML;
    758873        } else {
    759             $this->ContentType = 'text/plain';
     874            $this->ContentType = static::CONTENT_TYPE_PLAINTEXT;
    760875        }
    761876    }
    762877
    763878    /**
    764879     * Send messages using SMTP.
    765      * @return void
    766880     */
    767881    public function isSMTP()
    768882    {
     
    771885
    772886    /**
    773887     * Send messages using PHP's mail() function.
    774      * @return void
    775888     */
    776889    public function isMail()
    777890    {
     
    780893
    781894    /**
    782895     * Send messages using $Sendmail.
    783      * @return void
    784896     */
    785897    public function isSendmail()
    786898    {
    787899        $ini_sendmail_path = ini_get('sendmail_path');
    788900
    789         if (!stristr($ini_sendmail_path, 'sendmail')) {
     901        if (false === stripos($ini_sendmail_path, 'sendmail')) {
    790902            $this->Sendmail = '/usr/sbin/sendmail';
    791903        } else {
    792904            $this->Sendmail = $ini_sendmail_path;
     
    796908
    797909    /**
    798910     * Send messages using qmail.
    799      * @return void
    800911     */
    801912    public function isQmail()
    802913    {
    803914        $ini_sendmail_path = ini_get('sendmail_path');
    804915
    805         if (!stristr($ini_sendmail_path, 'qmail')) {
     916        if (false === stripos($ini_sendmail_path, 'qmail')) {
    806917            $this->Sendmail = '/var/qmail/bin/qmail-inject';
    807918        } else {
    808919            $this->Sendmail = $ini_sendmail_path;
     
    812923
    813924    /**
    814925     * Add a "To" address.
     926     *
    815927     * @param string $address The email address to send to
    816928     * @param string $name
    817      * @return boolean true on success, false if address already used or invalid in some way
     929     *
     930     * @return bool true on success, false if address already used or invalid in some way
    818931     */
    819932    public function addAddress($address, $name = '')
    820933    {
     
    823936
    824937    /**
    825938     * Add a "CC" address.
    826      * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
     939     *
    827940     * @param string $address The email address to send to
    828941     * @param string $name
    829      * @return boolean true on success, false if address already used or invalid in some way
     942     *
     943     * @return bool true on success, false if address already used or invalid in some way
    830944     */
    831945    public function addCC($address, $name = '')
    832946    {
     
    835949
    836950    /**
    837951     * Add a "BCC" address.
    838      * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
     952     *
    839953     * @param string $address The email address to send to
    840954     * @param string $name
    841      * @return boolean true on success, false if address already used or invalid in some way
     955     *
     956     * @return bool true on success, false if address already used or invalid in some way
    842957     */
    843958    public function addBCC($address, $name = '')
    844959    {
     
    847962
    848963    /**
    849964     * Add a "Reply-To" address.
     965     *
    850966     * @param string $address The email address to reply to
    851967     * @param string $name
    852      * @return boolean true on success, false if address already used or invalid in some way
     968     *
     969     * @return bool true on success, false if address already used or invalid in some way
    853970     */
    854971    public function addReplyTo($address, $name = '')
    855972    {
     
    861978     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
    862979     * be modified after calling this function), addition of such addresses is delayed until send().
    863980     * Addresses that have been added already return false, but do not throw exceptions.
    864      * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
     981     *
     982     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
    865983     * @param string $address The email address to send, resp. to reply to
    866984     * @param string $name
    867      * @throws phpmailerException
    868      * @return boolean true on success, false if address already used or invalid in some way
    869      * @access protected
     985     *
     986     * @throws \Exception
     987     *
     988     * @return bool true on success, false if address already used or invalid in some way
    870989     */
    871990    protected function addOrEnqueueAnAddress($kind, $address, $name)
    872991    {
    873992        $address = trim($address);
    874993        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    875         if (($pos = strrpos($address, '@')) === false) {
    876             // At-sign is misssing.
    877             $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
     994        $pos = strrpos($address, '@');
     995        if (false === $pos) {
     996            // At-sign is missing.
     997            $error_message = sprintf('%s (%s): %s',
     998                $this->lang('invalid_address'),
     999                $kind,
     1000                $address);
    8781001            $this->setError($error_message);
    8791002            $this->edebug($error_message);
    8801003            if ($this->exceptions) {
    881                 throw new phpmailerException($error_message);
     1004                throw new \Exception($error_message);
    8821005            }
     1006
    8831007            return false;
    8841008        }
    885         $params = array($kind, $address, $name);
     1009        $params = [$kind, $address, $name];
    8861010        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
    887         if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
    888             if ($kind != 'Reply-To') {
     1011        if ($this->has8bitChars(substr($address, ++$pos)) and static::idnSupported()) {
     1012            if ('Reply-To' != $kind) {
    8891013                if (!array_key_exists($address, $this->RecipientsQueue)) {
    8901014                    $this->RecipientsQueue[$address] = $params;
     1015
    8911016                    return true;
    8921017                }
    8931018            } else {
    8941019                if (!array_key_exists($address, $this->ReplyToQueue)) {
    8951020                    $this->ReplyToQueue[$address] = $params;
     1021
    8961022                    return true;
    8971023                }
    8981024            }
     1025
    8991026            return false;
    9001027        }
     1028
    9011029        // Immediately add standard addresses without IDN.
    902         return call_user_func_array(array($this, 'addAnAddress'), $params);
     1030        return call_user_func_array([$this, 'addAnAddress'], $params);
    9031031    }
    9041032
    9051033    /**
    9061034     * Add an address to one of the recipient arrays or to the ReplyTo array.
    9071035     * Addresses that have been added already return false, but do not throw exceptions.
    908      * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
     1036     *
     1037     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
    9091038     * @param string $address The email address to send, resp. to reply to
    9101039     * @param string $name
    911      * @throws phpmailerException
    912      * @return boolean true on success, false if address already used or invalid in some way
    913      * @access protected
     1040     *
     1041     * @throws \Exception
     1042     *
     1043     * @return bool true on success, false if address already used or invalid in some way
    9141044     */
    9151045    protected function addAnAddress($kind, $address, $name = '')
    9161046    {
    917         if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
    918             $error_message = $this->lang('Invalid recipient kind: ') . $kind;
     1047        if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
     1048            $error_message = sprintf('%s: %s',
     1049                $this->lang('Invalid recipient kind'),
     1050                $kind);
    9191051            $this->setError($error_message);
    9201052            $this->edebug($error_message);
    9211053            if ($this->exceptions) {
    922                 throw new phpmailerException($error_message);
     1054                throw new \Exception($error_message);
    9231055            }
     1056
    9241057            return false;
    9251058        }
    926         if (!$this->validateAddress($address)) {
    927             $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
     1059        if (!static::validateAddress($address)) {
     1060            $error_message = sprintf('%s (%s): %s',
     1061                $this->lang('invalid_address'),
     1062                $kind,
     1063                $address);
    9281064            $this->setError($error_message);
    9291065            $this->edebug($error_message);
    9301066            if ($this->exceptions) {
    931                 throw new phpmailerException($error_message);
     1067                throw new \Exception($error_message);
    9321068            }
     1069
    9331070            return false;
    9341071        }
    935         if ($kind != 'Reply-To') {
     1072        if ('Reply-To' != $kind) {
    9361073            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
    937                 array_push($this->$kind, array($address, $name));
     1074                $this->{$kind}[] = [$address, $name];
    9381075                $this->all_recipients[strtolower($address)] = true;
     1076
    9391077                return true;
    9401078            }
    9411079        } else {
    9421080            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
    943                 $this->ReplyTo[strtolower($address)] = array($address, $name);
     1081                $this->ReplyTo[strtolower($address)] = [$address, $name];
     1082
    9441083                return true;
    9451084            }
    9461085        }
     1086
    9471087        return false;
    9481088    }
    9491089
     
    9521092     * of the form "display name <address>" into an array of name/address pairs.
    9531093     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
    9541094     * Note that quotes in the name part are removed.
     1095     *
     1096     * @see    http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
     1097     *
    9551098     * @param string $addrstr The address list string
    956      * @param bool $useimap Whether to use the IMAP extension to parse the list
     1099     * @param bool   $useimap Whether to use the IMAP extension to parse the list
     1100     *
    9571101     * @return array
    958      * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
    9591102     */
    960     public function parseAddresses($addrstr, $useimap = true)
     1103    public static function parseAddresses($addrstr, $useimap = true)
    9611104    {
    962         $addresses = array();
     1105        $addresses = [];
    9631106        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
    9641107            //Use this built-in parser if it's available
    9651108            $list = imap_rfc822_parse_adrlist($addrstr, '');
    9661109            foreach ($list as $address) {
    967                 if ($address->host != '.SYNTAX-ERROR.') {
    968                     if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
    969                         $addresses[] = array(
     1110                if ('.SYNTAX-ERROR.' != $address->host) {
     1111                    if (static::validateAddress($address->mailbox . '@' . $address->host)) {
     1112                        $addresses[] = [
    9701113                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
    971                             'address' => $address->mailbox . '@' . $address->host
    972                         );
     1114                            'address' => $address->mailbox . '@' . $address->host,
     1115                        ];
    9731116                    }
    9741117                }
    9751118            }
     
    9811124                //Is there a separate name part?
    9821125                if (strpos($address, '<') === false) {
    9831126                    //No separate name, just use the whole thing
    984                     if ($this->validateAddress($address)) {
    985                         $addresses[] = array(
     1127                    if (static::validateAddress($address)) {
     1128                        $addresses[] = [
    9861129                            'name' => '',
    987                             'address' => $address
    988                         );
     1130                            'address' => $address,
     1131                        ];
    9891132                    }
    9901133                } else {
    9911134                    list($name, $email) = explode('<', $address);
    9921135                    $email = trim(str_replace('>', '', $email));
    993                     if ($this->validateAddress($email)) {
    994                         $addresses[] = array(
    995                             'name' => trim(str_replace(array('"', "'"), '', $name)),
    996                             'address' => $email
    997                         );
     1136                    if (static::validateAddress($email)) {
     1137                        $addresses[] = [
     1138                            'name' => trim(str_replace(['"', "'"], '', $name)),
     1139                            'address' => $email,
     1140                        ];
    9981141                    }
    9991142                }
    10001143            }
    10011144        }
     1145
    10021146        return $addresses;
    10031147    }
    10041148
    10051149    /**
    10061150     * Set the From and FromName properties.
     1151     *
    10071152     * @param string $address
    10081153     * @param string $name
    1009      * @param boolean $auto Whether to also set the Sender address, defaults to true
    1010      * @throws phpmailerException
    1011      * @return boolean
     1154     * @param bool   $auto    Whether to also set the Sender address, defaults to true
     1155     *
     1156     * @throws \Exception
     1157     *
     1158     * @return bool
    10121159     */
    10131160    public function setFrom($address, $name = '', $auto = true)
    10141161    {
    10151162        $address = trim($address);
    10161163        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    10171164        // Don't validate now addresses with IDN. Will be done in send().
    1018         if (($pos = strrpos($address, '@')) === false or
    1019             (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
    1020             !$this->validateAddress($address)) {
    1021             $error_message = $this->lang('invalid_address') . " (setFrom) $address";
     1165        $pos = strrpos($address, '@');
     1166        if (false === $pos or
     1167            (!$this->has8bitChars(substr($address, ++$pos)) or !static::idnSupported()) and
     1168            !static::validateAddress($address)) {
     1169            $error_message = sprintf('%s (From): %s',
     1170                $this->lang('invalid_address'),
     1171                $address);
    10221172            $this->setError($error_message);
    10231173            $this->edebug($error_message);
    10241174            if ($this->exceptions) {
    1025                 throw new phpmailerException($error_message);
     1175                throw new \Exception($error_message);
    10261176            }
     1177
    10271178            return false;
    10281179        }
    10291180        $this->From = $address;
     
    10331184                $this->Sender = $address;
    10341185            }
    10351186        }
     1187
    10361188        return true;
    10371189    }
    10381190
     
    10411193     * Technically this is the value from the last time the headers were created,
    10421194     * but it's also the message ID of the last sent message except in
    10431195     * pathological cases.
     1196     *
    10441197     * @return string
    10451198     */
    10461199    public function getLastMessageID()
     
    10501203
    10511204    /**
    10521205     * Check that a string looks like an email address.
    1053      * @param string $address The email address to check
    1054      * @param string|callable $patternselect A selector for the validation pattern to use :
     1206     * Validation patterns supported:
    10551207     * * `auto` Pick best pattern automatically;
    1056      * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
     1208     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0;
    10571209     * * `pcre` Use old PCRE implementation;
    10581210     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
    10591211     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
    10601212     * * `noregex` Don't use a regex: super fast, really dumb.
    10611213     * Alternatively you may pass in a callable to inject your own validator, for example:
     1214     *
     1215     * ```php
    10621216     * PHPMailer::validateAddress('user@example.com', function($address) {
    10631217     *     return (strpos($address, '@') !== false);
    10641218     * });
     1219     * ```
     1220     *
    10651221     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
    1066      * @return boolean
    1067      * @static
    1068      * @access public
     1222     *
     1223     * @param string          $address       The email address to check
     1224     * @param string|callable $patternselect Which pattern to use
     1225     *
     1226     * @return bool
    10691227     */
    10701228    public static function validateAddress($address, $patternselect = null)
    10711229    {
    1072         if (is_null($patternselect)) {
    1073             $patternselect = self::$validator;
     1230        if (null === $patternselect) {
     1231            $patternselect = static::$validator;
    10741232        }
    10751233        if (is_callable($patternselect)) {
    10761234            return call_user_func($patternselect, $address);
     
    10791237        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
    10801238            return false;
    10811239        }
    1082         if (!$patternselect or $patternselect == 'auto') {
    1083             //Check this constant first so it works when extension_loaded() is disabled by safe mode
    1084             //Constant was added in PHP 5.2.4
    1085             if (defined('PCRE_VERSION')) {
    1086                 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
    1087                 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
    1088                     $patternselect = 'pcre8';
    1089                 } else {
    1090                     $patternselect = 'pcre';
    1091                 }
    1092             } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
    1093                 //Fall back to older PCRE
    1094                 $patternselect = 'pcre';
    1095             } else {
    1096                 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
    1097                 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
    1098                     $patternselect = 'php';
    1099                 } else {
    1100                     $patternselect = 'noregex';
    1101                 }
    1102             }
    1103         }
    11041240        switch ($patternselect) {
     1241            case 'pcre': //Kept for BC
    11051242            case 'pcre8':
    1106                 /**
    1107                  * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
    1108                  * @link http://squiloople.com/2009/12/20/email-address-validation/
     1243                /*
     1244                 * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL
     1245                 * is based.
     1246                 * In addition to the addresses allowed by filter_var, also permits:
     1247                 *  * dotless domains: `a@b`
     1248                 *  * comments: `1234 @ local(blah) .machine .example`
     1249                 *  * quoted elements: `'"test blah"@example.org'`
     1250                 *  * numeric TLDs: `a@b.123`
     1251                 *  * unbracketed IPv4 literals: `a@192.168.0.1`
     1252                 *  * IPv6 literals: 'first.last@[IPv6:a1::]'
     1253                 * Not all of these will necessarily work for sending!
     1254                 *
     1255                 * @see       http://squiloople.com/2009/12/20/email-address-validation/
    11091256                 * @copyright 2009-2010 Michael Rushton
    11101257                 * Feel free to use and redistribute this code. But please keep this copyright notice.
    11111258                 */
    1112                 return (boolean)preg_match(
     1259                return (bool) preg_match(
    11131260                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
    11141261                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
    11151262                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
     
    11211268                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
    11221269                    $address
    11231270                );
    1124             case 'pcre':
    1125                 //An older regex that doesn't need a recent PCRE
    1126                 return (boolean)preg_match(
    1127                     '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
    1128                     '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
    1129                     '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
    1130                     '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
    1131                     '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
    1132                     '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
    1133                     '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
    1134                     '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
    1135                     '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
    1136                     '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
    1137                     $address
    1138                 );
    11391271            case 'html5':
    1140                 /**
     1272                /*
    11411273                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
    1142                  * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
     1274                 *
     1275                 * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
    11431276                 */
    1144                 return (boolean)preg_match(
     1277                return (bool) preg_match(
    11451278                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
    11461279                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
    11471280                    $address
    11481281                );
    1149             case 'noregex':
    1150                 //No PCRE! Do something _very_ approximate!
    1151                 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
    1152                 return (strlen($address) >= 3
    1153                     and strpos($address, '@') >= 1
    1154                     and strpos($address, '@') != strlen($address) - 1);
    11551282            case 'php':
    11561283            default:
    1157                 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
     1284                return (bool) filter_var($address, FILTER_VALIDATE_EMAIL);
    11581285        }
    11591286    }
    11601287
    11611288    /**
    11621289     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
    1163      * "intl" and "mbstring" PHP extensions.
    1164      * @return bool "true" if required functions for IDN support are present
     1290     * `intl` and `mbstring` PHP extensions.
     1291     *
     1292     * @return bool `true` if required functions for IDN support are present
    11651293     */
    1166     public function idnSupported()
     1294    public static function idnSupported()
    11671295    {
    1168         // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
    11691296        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
    11701297    }
    11711298
     
    11751302     * This function silently returns unmodified address if:
    11761303     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
    11771304     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
    1178      *   or fails for any reason (e.g. domain has characters not allowed in an IDN)
    1179      * @see PHPMailer::$CharSet
     1305     *   or fails for any reason (e.g. domain contains characters not allowed in an IDN).
     1306     *
     1307     * @see    PHPMailer::$CharSet
     1308     *
    11801309     * @param string $address The email address to convert
     1310     *
    11811311     * @return string The encoded address in ASCII form
    11821312     */
    11831313    public function punyencodeAddress($address)
    11841314    {
    11851315        // Verify we have required functions, CharSet, and at-sign.
    1186         if ($this->idnSupported() and
     1316        $pos = strrpos($address, '@');
     1317        if (static::idnSupported() and
    11871318            !empty($this->CharSet) and
    1188             ($pos = strrpos($address, '@')) !== false) {
     1319            false !== $pos
     1320        ) {
    11891321            $domain = substr($address, ++$pos);
    11901322            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
    11911323            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
    11921324                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
    1193                 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
    1194                     idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
    1195                     idn_to_ascii($domain)) !== false) {
     1325                //Ignore IDE complaints about this line - method signature changed in PHP 5.4
     1326                $errorcode = 0;
     1327                $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46);
     1328                if (false !== $punycode) {
    11961329                    return substr($address, 0, $pos) . $punycode;
    11971330                }
    11981331            }
    11991332        }
     1333
    12001334        return $address;
    12011335    }
    12021336
    12031337    /**
    12041338     * Create a message and send it.
    12051339     * Uses the sending method specified by $Mailer.
    1206      * @throws phpmailerException
    1207      * @return boolean false on error - See the ErrorInfo property for details of the error.
     1340     *
     1341     * @throws \Exception
     1342     *
     1343     * @return bool false on error - See the ErrorInfo property for details of the error
    12081344     */
    12091345    public function send()
    12101346    {
     
    12121348            if (!$this->preSend()) {
    12131349                return false;
    12141350            }
     1351
    12151352            return $this->postSend();
    1216         } catch (phpmailerException $exc) {
     1353        } catch (\Exception $exc) {
    12171354            $this->mailHeader = '';
    12181355            $this->setError($exc->getMessage());
    12191356            if ($this->exceptions) {
    12201357                throw $exc;
    12211358            }
     1359
    12221360            return false;
    12231361        }
    12241362    }
    12251363
    12261364    /**
    12271365     * Prepare a message for sending.
    1228      * @throws phpmailerException
    1229      * @return boolean
     1366     *
     1367     * @throws \Exception
     1368     *
     1369     * @return bool
    12301370     */
    12311371    public function preSend()
    12321372    {
     1373        if ('smtp' == $this->Mailer or
     1374            ('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0)
     1375        ) {
     1376            //SMTP mandates RFC-compliant line endings
     1377            //and it's also used with mail() on Windows
     1378            static::setLE("\r\n");
     1379        } else {
     1380            //Maintain backward compatibility with legacy Linux command line mailers
     1381            static::setLE(PHP_EOL);
     1382        }
     1383        //Check for buggy PHP versions that add a header with an incorrect line break
     1384        if (ini_get('mail.add_x_header') == 1
     1385            and 'mail' == $this->Mailer
     1386            and stripos(PHP_OS, 'WIN') === 0
     1387            and ((version_compare(PHP_VERSION, '7.0.0', '>=')
     1388                    and version_compare(PHP_VERSION, '7.0.17', '<'))
     1389                or (version_compare(PHP_VERSION, '7.1.0', '>=')
     1390                    and version_compare(PHP_VERSION, '7.1.3', '<')))
     1391        ) {
     1392            trigger_error(
     1393                'Your version of PHP is affected by a bug that may result in corrupted messages.' .
     1394                ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
     1395                ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
     1396                E_USER_WARNING
     1397            );
     1398        }
     1399
    12331400        try {
    12341401            $this->error_count = 0; // Reset errors
    12351402            $this->mailHeader = '';
     
    12371404            // Dequeue recipient and Reply-To addresses with IDN
    12381405            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
    12391406                $params[1] = $this->punyencodeAddress($params[1]);
    1240                 call_user_func_array(array($this, 'addAnAddress'), $params);
     1407                call_user_func_array([$this, 'addAnAddress'], $params);
    12411408            }
    1242             if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
    1243                 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
     1409            if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
     1410                throw new \Exception($this->lang('provide_address'), self::STOP_CRITICAL);
    12441411            }
    12451412
    12461413            // Validate From, Sender, and ConfirmReadingTo addresses
    1247             foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
     1414            foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
    12481415                $this->$address_kind = trim($this->$address_kind);
    12491416                if (empty($this->$address_kind)) {
    12501417                    continue;
    12511418                }
    12521419                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
    1253                 if (!$this->validateAddress($this->$address_kind)) {
    1254                     $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
     1420                if (!static::validateAddress($this->$address_kind)) {
     1421                    $error_message = sprintf('%s (%s): %s',
     1422                        $this->lang('invalid_address'),
     1423                        $address_kind,
     1424                        $this->$address_kind);
    12551425                    $this->setError($error_message);
    12561426                    $this->edebug($error_message);
    12571427                    if ($this->exceptions) {
    1258                         throw new phpmailerException($error_message);
     1428                        throw new \Exception($error_message);
    12591429                    }
     1430
    12601431                    return false;
    12611432                }
    12621433            }
    12631434
    12641435            // Set whether the message is multipart/alternative
    12651436            if ($this->alternativeExists()) {
    1266                 $this->ContentType = 'multipart/alternative';
     1437                $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE;
    12671438            }
    12681439
    12691440            $this->setMessageType();
    12701441            // Refuse to send an empty message unless we are specifically allowing it
    12711442            if (!$this->AllowEmpty and empty($this->Body)) {
    1272                 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
     1443                throw new \Exception($this->lang('empty_message'), self::STOP_CRITICAL);
    12731444            }
    12741445
     1446            //Trim subject consistently
     1447            $this->Subject = trim($this->Subject);
    12751448            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
    12761449            $this->MIMEHeader = '';
    12771450            $this->MIMEBody = $this->createBody();
     
    12821455
    12831456            // To capture the complete message when using mail(), create
    12841457            // an extra header list which createHeader() doesn't fold in
    1285             if ($this->Mailer == 'mail') {
     1458            if ('mail' == $this->Mailer) {
    12861459                if (count($this->to) > 0) {
    12871460                    $this->mailHeader .= $this->addrAppend('To', $this->to);
    12881461                } else {
     
    12901463                }
    12911464                $this->mailHeader .= $this->headerLine(
    12921465                    'Subject',
    1293                     $this->encodeHeader($this->secureHeader(trim($this->Subject)))
     1466                    $this->encodeHeader($this->secureHeader($this->Subject))
    12941467                );
    12951468            }
    12961469
     
    12991472                and !empty($this->DKIM_selector)
    13001473                and (!empty($this->DKIM_private_string)
    13011474                    or (!empty($this->DKIM_private)
    1302                         and self::isPermittedPath($this->DKIM_private)
     1475                        and static::isPermittedPath($this->DKIM_private)
    13031476                        and file_exists($this->DKIM_private)
    13041477                    )
    13051478                )
     
    13091482                    $this->encodeHeader($this->secureHeader($this->Subject)),
    13101483                    $this->MIMEBody
    13111484                );
    1312                 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
    1313                     str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
     1485                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE .
     1486                    static::normalizeBreaks($header_dkim) . static::$LE;
    13141487            }
     1488
    13151489            return true;
    1316         } catch (phpmailerException $exc) {
     1490        } catch (\Exception $exc) {
    13171491            $this->setError($exc->getMessage());
    13181492            if ($this->exceptions) {
    13191493                throw $exc;
    13201494            }
     1495
    13211496            return false;
    13221497        }
    13231498    }
    13241499
    13251500    /**
    1326      * Actually send a message.
    1327      * Send the email via the selected mechanism
    1328      * @throws phpmailerException
    1329      * @return boolean
     1501     * Actually send a message via the selected mechanism.
     1502     *
     1503     * @throws \Exception
     1504     *
     1505     * @return bool
    13301506     */
    13311507    public function postSend()
    13321508    {
     
    13411517                case 'mail':
    13421518                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
    13431519                default:
    1344                     $sendMethod = $this->Mailer.'Send';
     1520                    $sendMethod = $this->Mailer . 'Send';
    13451521                    if (method_exists($this, $sendMethod)) {
    13461522                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
    13471523                    }
    13481524
    13491525                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
    13501526            }
    1351         } catch (phpmailerException $exc) {
     1527        } catch (\Exception $exc) {
    13521528            $this->setError($exc->getMessage());
    13531529            $this->edebug($exc->getMessage());
    13541530            if ($this->exceptions) {
    13551531                throw $exc;
    13561532            }
    13571533        }
     1534
    13581535        return false;
    13591536    }
    13601537
    13611538    /**
    13621539     * Send mail using the $Sendmail program.
     1540     *
     1541     * @see    PHPMailer::$Sendmail
     1542     *
    13631543     * @param string $header The message headers
    1364      * @param string $body The message body
    1365      * @see PHPMailer::$Sendmail
    1366      * @throws phpmailerException
    1367      * @access protected
    1368      * @return boolean
     1544     * @param string $body   The message body
     1545     *
     1546     * @throws \Exception
     1547     *
     1548     * @return bool
    13691549     */
    13701550    protected function sendmailSend($header, $body)
    13711551    {
    13721552        // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
    13731553        if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
    1374             if ($this->Mailer == 'qmail') {
     1554            if ('qmail' == $this->Mailer) {
    13751555                $sendmailFmt = '%s -f%s';
    13761556            } else {
    13771557                $sendmailFmt = '%s -oi -f%s -t';
    13781558            }
    13791559        } else {
    1380             if ($this->Mailer == 'qmail') {
     1560            if ('qmail' == $this->Mailer) {
    13811561                $sendmailFmt = '%s';
    13821562            } else {
    13831563                $sendmailFmt = '%s -oi -t';
    13841564            }
    13851565        }
    13861566
    1387         // TODO: If possible, this should be changed to escapeshellarg.  Needs thorough testing.
    13881567        $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
    13891568
    13901569        if ($this->SingleTo) {
    13911570            foreach ($this->SingleToArray as $toAddr) {
    1392                 if (!@$mail = popen($sendmail, 'w')) {
    1393                     throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
    1394                 }
    1395                 fputs($mail, 'To: ' . $toAddr . "\n");
    1396                 fputs($mail, $header);
    1397                 fputs($mail, $body);
     1571                $mail = @popen($sendmail, 'w');
     1572                if (!$mail) {
     1573                    throw new \Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1574                }
     1575                fwrite($mail, 'To: ' . $toAddr . "\n");
     1576                fwrite($mail, $header);
     1577                fwrite($mail, $body);
    13981578                $result = pclose($mail);
    13991579                $this->doCallback(
    14001580                    ($result == 0),
    1401                     array($toAddr),
     1581                    [$toAddr],
    14021582                    $this->cc,
    14031583                    $this->bcc,
    14041584                    $this->Subject,
    14051585                    $body,
    1406                     $this->From
     1586                    $this->From,
     1587                    []
    14071588                );
    1408                 if ($result != 0) {
    1409                     throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1589                if (0 !== $result) {
     1590                    throw new \Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
    14101591                }
    14111592            }
    14121593        } else {
    1413             if (!@$mail = popen($sendmail, 'w')) {
    1414                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1594            $mail = @popen($sendmail, 'w');
     1595            if (!$mail) {
     1596                throw new \Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
    14151597            }
    1416             fputs($mail, $header);
    1417             fputs($mail, $body);
     1598            fwrite($mail, $header);
     1599            fwrite($mail, $body);
    14181600            $result = pclose($mail);
    14191601            $this->doCallback(
    14201602                ($result == 0),
     
    14231605                $this->bcc,
    14241606                $this->Subject,
    14251607                $body,
    1426                 $this->From
     1608                $this->From,
     1609                []
    14271610            );
    1428             if ($result != 0) {
    1429                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
     1611            if (0 !== $result) {
     1612                throw new \Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
    14301613            }
    14311614        }
     1615
    14321616        return true;
    14331617    }
    14341618
    14351619    /**
    14361620     * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
    1437      *
    14381621     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
    1439      * @param string $string The string to be validated
     1622     *
    14401623     * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
    1441      * @access protected
    1442      * @return boolean
     1624     *
     1625     * @param string $string The string to be validated
     1626     *
     1627     * @return bool
    14431628     */
    14441629    protected static function isShellSafe($string)
    14451630    {
    14461631        // Future-proof
    14471632        if (escapeshellcmd($string) !== $string
    1448             or !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))
     1633            or !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
    14491634        ) {
    14501635            return false;
    14511636        }
    14521637
    14531638        $length = strlen($string);
    14541639
    1455         for ($i = 0; $i < $length; $i++) {
     1640        for ($i = 0; $i < $length; ++$i) {
    14561641            $c = $string[$i];
    14571642
    14581643            // All other characters have a special meaning in at least one common shell, including = and +.
     
    14701655     * Check whether a file path is of a permitted type.
    14711656     * Used to reject URLs and phar files from functions that access local file paths,
    14721657     * such as addAttachment.
    1473      * @param string $path A relative or absolute path to a file.
     1658     *
     1659     * @param string $path A relative or absolute path to a file
     1660     *
    14741661     * @return bool
    14751662     */
    14761663    protected static function isPermittedPath($path)
     
    14801667
    14811668    /**
    14821669     * Send mail using the PHP mail() function.
     1670     *
     1671     * @see    http://www.php.net/manual/en/book.mail.php
     1672     *
    14831673     * @param string $header The message headers
    1484      * @param string $body The message body
    1485      * @link http://www.php.net/manual/en/book.mail.php
    1486      * @throws phpmailerException
    1487      * @access protected
    1488      * @return boolean
     1674     * @param string $body   The message body
     1675     *
     1676     * @throws \Exception
     1677     *
     1678     * @return bool
    14891679     */
    14901680    protected function mailSend($header, $body)
    14911681    {
    1492         $toArr = array();
     1682        $toArr = [];
    14931683        foreach ($this->to as $toaddr) {
    14941684            $toArr[] = $this->addrFormat($toaddr);
    14951685        }
     
    14971687
    14981688        $params = null;
    14991689        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
    1500         if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
     1690        if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
     1691            //A space after `-f` is optional, but there is a long history of its presence
     1692            //causing problems, so we don't use one
     1693            //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
     1694            //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
     1695            //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
     1696            //Example problem: https://www.drupal.org/node/1057954
    15011697            // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
    15021698            if (self::isShellSafe($this->Sender)) {
    15031699                $params = sprintf('-f%s', $this->Sender);
    15041700            }
    15051701        }
    1506         if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
     1702        if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
    15071703            $old_from = ini_get('sendmail_from');
    15081704            ini_set('sendmail_from', $this->Sender);
    15091705        }
     
    15111707        if ($this->SingleTo and count($toArr) > 1) {
    15121708            foreach ($toArr as $toAddr) {
    15131709                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
    1514                 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1710                $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
    15151711            }
    15161712        } else {
    15171713            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
    1518             $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
     1714            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
    15191715        }
    15201716        if (isset($old_from)) {
    15211717            ini_set('sendmail_from', $old_from);
    15221718        }
    15231719        if (!$result) {
    1524             throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
     1720            throw new \Exception($this->lang('instantiate'), self::STOP_CRITICAL);
    15251721        }
     1722
    15261723        return true;
    15271724    }
    15281725
    15291726    /**
    15301727     * Get an instance to use for SMTP operations.
    1531      * Override this function to load your own SMTP implementation
     1728     * Override this function to load your own SMTP implementation,
     1729     * or set one with setSMTPInstance.
     1730     *
    15321731     * @return SMTP
    15331732     */
    15341733    public function getSMTPInstance()
    15351734    {
    15361735        if (!is_object($this->smtp)) {
    1537                         require_once( 'class-smtp.php' );
    1538             $this->smtp = new SMTP;
     1736            $this->smtp = new SMTP();
    15391737        }
     1738
     1739        return $this->smtp;
     1740    }
     1741
     1742    /**
     1743     * Provide an instance to use for SMTP operations.
     1744     *
     1745     * @param SMTP $smtp
     1746     *
     1747     * @return SMTP
     1748     */
     1749    public function setSMTPInstance(SMTP $smtp)
     1750    {
     1751        $this->smtp = $smtp;
     1752
    15401753        return $this->smtp;
    15411754    }
    15421755
    15431756    /**
    15441757     * Send mail via SMTP.
    15451758     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
    1546      * Uses the PHPMailerSMTP class by default.
    1547      * @see PHPMailer::getSMTPInstance() to use a different class.
     1759     *
     1760     * @see PHPMailer::setSMTPInstance() to use a different class.
     1761     *
     1762     * @uses \PHPMailer\PHPMailer\SMTP
     1763     *
    15481764     * @param string $header The message headers
    1549      * @param string $body The message body
    1550      * @throws phpmailerException
    1551      * @uses SMTP
    1552      * @access protected
    1553      * @return boolean
     1765     * @param string $body   The message body
     1766     *
     1767     * @throws \Exception
     1768     *
     1769     * @return bool
    15541770     */
    15551771    protected function smtpSend($header, $body)
    15561772    {
    1557         $bad_rcpt = array();
     1773        $bad_rcpt = [];
    15581774        if (!$this->smtpConnect($this->SMTPOptions)) {
    1559             throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
     1775            throw new \Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
    15601776        }
    1561         if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
    1562             $smtp_from = $this->Sender;
    1563         } else {
     1777        //Sender already validated in preSend()
     1778        if ('' == $this->Sender) {
    15641779            $smtp_from = $this->From;
     1780        } else {
     1781            $smtp_from = $this->Sender;
    15651782        }
    15661783        if (!$this->smtp->mail($smtp_from)) {
    15671784            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
    1568             throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
     1785            throw new \Exception($this->ErrorInfo, self::STOP_CRITICAL);
    15691786        }
    15701787
     1788        $callbacks = [];
    15711789        // Attempt to send to all recipients
    1572         foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
     1790        foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
    15731791            foreach ($togroup as $to) {
    15741792                if (!$this->smtp->recipient($to[0])) {
    15751793                    $error = $this->smtp->getError();
    1576                     $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
     1794                    $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']];
    15771795                    $isSent = false;
    15781796                } else {
    15791797                    $isSent = true;
    15801798                }
    1581                 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
     1799
     1800                $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]];
    15821801            }
    15831802        }
    15841803
    15851804        // Only send the DATA command if we have viable recipients
    15861805        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
    1587             throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
     1806            throw new \Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
    15881807        }
     1808
     1809        $smtp_transaction_id = $this->smtp->getLastTransactionID();
     1810
    15891811        if ($this->SMTPKeepAlive) {
    15901812            $this->smtp->reset();
    15911813        } else {
    15921814            $this->smtp->quit();
    15931815            $this->smtp->close();
    15941816        }
     1817
     1818        foreach ($callbacks as $cb) {
     1819            $this->doCallback(
     1820                $cb['issent'],
     1821                [$cb['to']],
     1822                [],
     1823                [],
     1824                $this->Subject,
     1825                $body,
     1826                $this->From,
     1827                ['smtp_transaction_id' => $smtp_transaction_id]
     1828            );
     1829        }
     1830
    15951831        //Create error message for any bad addresses
    15961832        if (count($bad_rcpt) > 0) {
    15971833            $errstr = '';
    15981834            foreach ($bad_rcpt as $bad) {
    15991835                $errstr .= $bad['to'] . ': ' . $bad['error'];
    16001836            }
    1601             throw new phpmailerException(
     1837            throw new \Exception(
    16021838                $this->lang('recipients_failed') . $errstr,
    16031839                self::STOP_CONTINUE
    16041840            );
    16051841        }
     1842
    16061843        return true;
    16071844    }
    16081845
    16091846    /**
    16101847     * Initiate a connection to an SMTP server.
    16111848     * Returns false if the operation failed.
     1849     *
    16121850     * @param array $options An array of options compatible with stream_context_create()
    1613      * @uses SMTP
    1614      * @access public
    1615      * @throws phpmailerException
    1616      * @return boolean
     1851     *
     1852     * @throws \Exception
     1853     *
     1854     * @uses \PHPMailer\PHPMailer\SMTP
     1855     *
     1856     * @return bool
    16171857     */
    16181858    public function smtpConnect($options = null)
    16191859    {
    1620         if (is_null($this->smtp)) {
     1860        if (null === $this->smtp) {
    16211861            $this->smtp = $this->getSMTPInstance();
    16221862        }
    16231863
    16241864        //If no options are provided, use whatever is set in the instance
    1625         if (is_null($options)) {
     1865        if (null === $options) {
    16261866            $options = $this->SMTPOptions;
    16271867        }
    16281868
     
    16391879        $lastexception = null;
    16401880
    16411881        foreach ($hosts as $hostentry) {
    1642             $hostinfo = array();
     1882            $hostinfo = [];
    16431883            if (!preg_match(
    16441884                '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/',
    16451885                trim($hostentry),
    16461886                $hostinfo
    16471887            )) {
     1888                static::edebug($this->lang('connect_host') . ' ' . $hostentry);
    16481889                // Not a valid host entry
    1649                 $this->edebug('Ignoring invalid host: ' . $hostentry);
    16501890                continue;
    16511891            }
    16521892            // $hostinfo[2]: optional ssl or tls prefix
     
    16541894            // $hostinfo[4]: optional port number
    16551895            // The host string prefix can temporarily override the current setting for SMTPSecure
    16561896            // If it's not specified, the default value is used
     1897
     1898            //Check the host name is a valid name or IP address before trying to use it
     1899            if (!static::isValidHost($hostinfo[3])) {
     1900                static::edebug($this->lang('connect_host') . ' ' . $hostentry);
     1901                continue;
     1902            }
    16571903            $prefix = '';
    16581904            $secure = $this->SMTPSecure;
    1659             $tls = ($this->SMTPSecure == 'tls');
     1905            $tls = ('tls' == $this->SMTPSecure);
    16601906            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
    16611907                $prefix = 'ssl://';
    16621908                $tls = false; // Can't have SSL and TLS at the same time
    16631909                $secure = 'ssl';
    1664             } elseif ($hostinfo[2] == 'tls') {
     1910            } elseif ('tls' == $hostinfo[2]) {
    16651911                $tls = true;
    16661912                // tls doesn't use a prefix
    16671913                $secure = 'tls';
    16681914            }
    16691915            //Do we need the OpenSSL extension?
    1670             $sslext = defined('OPENSSL_ALGO_SHA1');
     1916            $sslext = defined('OPENSSL_ALGO_SHA256');
    16711917            if ('tls' === $secure or 'ssl' === $secure) {
    16721918                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
    16731919                if (!$sslext) {
    1674                     throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
     1920                    throw new \Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
    16751921                }
    16761922            }
    16771923            $host = $hostinfo[3];
    16781924            $port = $this->Port;
    1679             $tport = (integer)$hostinfo[4];
     1925            $tport = (int) $hostinfo[4];
    16801926            if ($tport > 0 and $tport < 65536) {
    16811927                $port = $tport;
    16821928            }
     
    16931939                    // * we have openssl extension
    16941940                    // * we are not already using SSL
    16951941                    // * the server offers STARTTLS
    1696                     if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
     1942                    if ($this->SMTPAutoTLS and $sslext and 'ssl' != $secure and $this->smtp->getServerExt('STARTTLS')) {
    16971943                        $tls = true;
    16981944                    }
    16991945                    if ($tls) {
    17001946                        if (!$this->smtp->startTLS()) {
    1701                             throw new phpmailerException($this->lang('connect_host'));
     1947                            throw new \Exception($this->lang('connect_host'));
    17021948                        }
    17031949                        // We must resend EHLO after TLS negotiation
    17041950                        $this->smtp->hello($hello);
     
    17081954                            $this->Username,
    17091955                            $this->Password,
    17101956                            $this->AuthType,
    1711                             $this->Realm,
    1712                             $this->Workstation
     1957                            $this->oauth
    17131958                        )
    17141959                        ) {
    1715                             throw new phpmailerException($this->lang('authenticate'));
     1960                            throw new \Exception($this->lang('authenticate'));
    17161961                        }
    17171962                    }
     1963
    17181964                    return true;
    1719                 } catch (phpmailerException $exc) {
     1965                } catch (\Exception $exc) {
    17201966                    $lastexception = $exc;
    17211967                    $this->edebug($exc->getMessage());
    17221968                    // We must have connected, but then failed TLS or Auth, so close connection nicely
     
    17271973        // If we get here, all connection attempts have failed, so close connection hard
    17281974        $this->smtp->close();
    17291975        // As we've caught all exceptions, just report whatever the last one was
    1730         if ($this->exceptions and !is_null($lastexception)) {
     1976        if ($this->exceptions and null !== $lastexception) {
    17311977            throw $lastexception;
    17321978        }
     1979
    17331980        return false;
    17341981    }
    17351982
    17361983    /**
    17371984     * Close the active SMTP session if one exists.
    1738      * @return void
    17391985     */
    17401986    public function smtpClose()
    17411987    {
    1742         if (is_a($this->smtp, 'SMTP')) {
     1988        if (null !== $this->smtp) {
    17431989            if ($this->smtp->connected()) {
    17441990                $this->smtp->quit();
    17451991                $this->smtp->close();
     
    17511997     * Set the language for error messages.
    17521998     * Returns false if it cannot load the language file.
    17531999     * The default language is English.
    1754      * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
     2000     *
     2001     * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
    17552002     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
    1756      * @return boolean
    1757      * @access public
     2003     *
     2004     * @return bool
    17582005     */
    17592006    public function setLanguage($langcode = 'en', $lang_path = '')
    17602007    {
    17612008        // Backwards compatibility for renamed language codes
    1762         $renamed_langcodes = array(
     2009        $renamed_langcodes = [
    17632010            'br' => 'pt_br',
    17642011            'cz' => 'cs',
    17652012            'dk' => 'da',
    17662013            'no' => 'nb',
    17672014            'se' => 'sv',
    1768             'sr' => 'rs'
    1769         );
     2015            'rs' => 'sr',
     2016            'tg' => 'tl',
     2017        ];
    17702018
    17712019        if (isset($renamed_langcodes[$langcode])) {
    17722020            $langcode = $renamed_langcodes[$langcode];
    17732021        }
    17742022
    17752023        // Define full set of translatable strings in English
    1776         $PHPMAILER_LANG = array(
     2024        $PHPMAILER_LANG = [
    17772025            'authenticate' => 'SMTP Error: Could not authenticate.',
    17782026            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
    17792027            'data_not_accepted' => 'SMTP Error: data not accepted.',
     
    17922040            'smtp_connect_failed' => 'SMTP connect() failed.',
    17932041            'smtp_error' => 'SMTP server error: ',
    17942042            'variable_set' => 'Cannot set or reset variable: ',
    1795             'extension_missing' => 'Extension missing: '
    1796         );
     2043            'extension_missing' => 'Extension missing: ',
     2044        ];
    17972045        if (empty($lang_path)) {
    17982046            // Calculate an absolute path so it can work if CWD is not here
    1799             $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
     2047            $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
    18002048        }
    18012049        //Validate $langcode
    18022050        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
     
    18052053        $foundlang = true;
    18062054        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
    18072055        // There is no English translation file
    1808         if ($langcode != 'en') {
     2056        if ('en' != $langcode) {
    18092057            // Make sure language file path is readable
    1810             if (!self::isPermittedPath($lang_file) or !is_readable($lang_file)) {
     2058            if (!static::isPermittedPath($lang_file) || !file_exists($lang_file)) {
    18112059                $foundlang = false;
    18122060            } else {
    18132061                // Overwrite language-specific strings.
     
    18162064            }
    18172065        }
    18182066        $this->language = $PHPMAILER_LANG;
    1819         return (boolean)$foundlang; // Returns false if language not found
     2067
     2068        return (bool) $foundlang; // Returns false if language not found
    18202069    }
    18212070
    18222071    /**
    18232072     * Get the array of strings for the current language.
     2073     *
    18242074     * @return array
    18252075     */
    18262076    public function getTranslations()
     
    18302080
    18312081    /**
    18322082     * Create recipient headers.
    1833      * @access public
     2083     *
    18342084     * @param string $type
    1835      * @param array $addr An array of recipient,
    1836      * where each recipient is a 2-element indexed array with element 0 containing an address
    1837      * and element 1 containing a name, like:
    1838      * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
     2085     * @param array  $addr An array of recipients,
     2086     *                     where each recipient is a 2-element indexed array with element 0 containing an address
     2087     *                     and element 1 containing a name, like:
     2088     *                     [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']]
     2089     *
    18392090     * @return string
    18402091     */
    18412092    public function addrAppend($type, $addr)
    18422093    {
    1843         $addresses = array();
     2094        $addresses = [];
    18442095        foreach ($addr as $address) {
    18452096            $addresses[] = $this->addrFormat($address);
    18462097        }
    1847         return $type . ': ' . implode(', ', $addresses) . $this->LE;
     2098
     2099        return $type . ': ' . implode(', ', $addresses) . static::$LE;
    18482100    }
    18492101
    18502102    /**
    18512103     * Format an address for use in a message header.
    1852      * @access public
    1853      * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
    1854      *      like array('joe@example.com', 'Joe User')
     2104     *
     2105     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like
     2106     *                    ['joe@example.com', 'Joe User']
     2107     *
    18552108     * @return string
    18562109     */
    18572110    public function addrFormat($addr)
    18582111    {
    18592112        if (empty($addr[1])) { // No name provided
    18602113            return $this->secureHeader($addr[0]);
    1861         } else {
    1862             return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
     2114        }
     2115
     2116        return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
    18632117                $addr[0]
    18642118            ) . '>';
    1865         }
    18662119    }
    18672120
    18682121    /**
     
    18702123     * For use with mailers that do not automatically perform wrapping
    18712124     * and for quoted-printable encoded messages.
    18722125     * Original written by philippe.
     2126     *
    18732127     * @param string $message The message to wrap
    1874      * @param integer $length The line length to wrap to
    1875      * @param boolean $qp_mode Whether to run in Quoted-Printable mode
    1876      * @access public
     2128     * @param int    $length The line length to wrap to
     2129     * @param bool  $qp_mode Whether to run in Quoted-Printable mode
     2130     *
    18772131     * @return string
    18782132     */
    18792133    public function wrapText($message, $length, $qp_mode = false)
    18802134    {
    18812135        if ($qp_mode) {
    1882             $soft_break = sprintf(' =%s', $this->LE);
     2136            $soft_break = sprintf(' =%s', static::$LE);
    18832137        } else {
    1884             $soft_break = $this->LE;
     2138            $soft_break = static::$LE;
    18852139        }
    18862140        // If utf-8 encoding is used, we will need to make sure we don't
    18872141        // split multibyte characters when we wrap
    1888         $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
    1889         $lelen = strlen($this->LE);
    1890         $crlflen = strlen(self::CRLF);
     2142        $is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet);
     2143        $lelen = strlen(static::$LE);
     2144        $crlflen = strlen(static::$LE);
    18912145
    1892         $message = $this->fixEOL($message);
     2146        $message = static::normalizeBreaks($message);
    18932147        //Remove a trailing line break
    1894         if (substr($message, -$lelen) == $this->LE) {
     2148        if (substr($message, -$lelen) == static::$LE) {
    18952149            $message = substr($message, 0, -$lelen);
    18962150        }
    18972151
    18982152        //Split message into lines
    1899         $lines = explode($this->LE, $message);
     2153        $lines = explode(static::$LE, $message);
    19002154        //Message will be rebuilt in here
    19012155        $message = '';
    19022156        foreach ($lines as $line) {
     
    19112165                            $len = $space_left;
    19122166                            if ($is_utf8) {
    19132167                                $len = $this->utf8CharBoundary($word, $len);
    1914                             } elseif (substr($word, $len - 1, 1) == '=') {
    1915                                 $len--;
    1916                             } elseif (substr($word, $len - 2, 1) == '=') {
     2168                            } elseif ('=' == substr($word, $len - 1, 1)) {
     2169                                --$len;
     2170                            } elseif ('=' == substr($word, $len - 2, 1)) {
    19172171                                $len -= 2;
    19182172                            }
    19192173                            $part = substr($word, 0, $len);
    19202174                            $word = substr($word, $len);
    19212175                            $buf .= ' ' . $part;
    1922                             $message .= $buf . sprintf('=%s', self::CRLF);
     2176                            $message .= $buf . sprintf('=%s', static::$LE);
    19232177                        } else {
    19242178                            $message .= $buf . $soft_break;
    19252179                        }
     
    19322186                        $len = $length;
    19332187                        if ($is_utf8) {
    19342188                            $len = $this->utf8CharBoundary($word, $len);
    1935                         } elseif (substr($word, $len - 1, 1) == '=') {
    1936                             $len--;
    1937                         } elseif (substr($word, $len - 2, 1) == '=') {
     2189                        } elseif ('=' == substr($word, $len - 1, 1)) {
     2190                            --$len;
     2191                        } elseif ('=' == substr($word, $len - 2, 1)) {
    19382192                            $len -= 2;
    19392193                        }
    19402194                        $part = substr($word, 0, $len);
    19412195                        $word = substr($word, $len);
    19422196
    19432197                        if (strlen($word) > 0) {
    1944                             $message .= $part . sprintf('=%s', self::CRLF);
     2198                            $message .= $part . sprintf('=%s', static::$LE);
    19452199                        } else {
    19462200                            $buf = $part;
    19472201                        }
     
    19532207                    }
    19542208                    $buf .= $word;
    19552209
    1956                     if (strlen($buf) > $length and $buf_o != '') {
     2210                    if (strlen($buf) > $length and '' != $buf_o) {
    19572211                        $message .= $buf_o . $soft_break;
    19582212                        $buf = $word;
    19592213                    }
    19602214                }
    19612215                $firstword = false;
    19622216            }
    1963             $message .= $buf . self::CRLF;
     2217            $message .= $buf . static::$LE;
    19642218        }
    19652219
    19662220        return $message;
     
    19702224     * Find the last character boundary prior to $maxLength in a utf-8
    19712225     * quoted-printable encoded string.
    19722226     * Original written by Colin Brown.
    1973      * @access public
     2227     *
    19742228     * @param string $encodedText utf-8 QP text
    1975      * @param integer $maxLength Find the last character boundary prior to this length
    1976      * @return integer
     2229     * @param int    $maxLength   Find the last character boundary prior to this length
     2230     *
     2231     * @return int
    19772232     */
    19782233    public function utf8CharBoundary($encodedText, $maxLength)
    19792234    {
     
    19922247                    // If the encoded char was found at pos 0, it will fit
    19932248                    // otherwise reduce maxLength to start of the encoded char
    19942249                    if ($encodedCharPos > 0) {
    1995                         $maxLength = $maxLength - ($lookBack - $encodedCharPos);
     2250                        $maxLength -= $lookBack - $encodedCharPos;
    19962251                    }
    19972252                    $foundSplitPos = true;
    19982253                } elseif ($dec >= 192) {
    19992254                    // First byte of a multi byte character
    20002255                    // Reduce maxLength to split at start of character
    2001                     $maxLength = $maxLength - ($lookBack - $encodedCharPos);
     2256                    $maxLength -= $lookBack - $encodedCharPos;
    20022257                    $foundSplitPos = true;
    20032258                } elseif ($dec < 192) {
    20042259                    // Middle byte of a multi byte character, look further back
     
    20092264                $foundSplitPos = true;
    20102265            }
    20112266        }
     2267
    20122268        return $maxLength;
    20132269    }
    20142270
     
    20172273     * Wraps the message body to the number of chars set in the WordWrap property.
    20182274     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
    20192275     * This is called automatically by createBody(), so you don't need to call it yourself.
    2020      * @access public
    2021      * @return void
    20222276     */
    20232277    public function setWordWrap()
    20242278    {
     
    20412295
    20422296    /**
    20432297     * Assemble message headers.
    2044      * @access public
     2298     *
    20452299     * @return string The assembled headers
    20462300     */
    20472301    public function createHeader()
    20482302    {
    20492303        $result = '';
    20502304
    2051         $result .= $this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate);
     2305        $result .= $this->headerLine('Date', '' == $this->MessageDate ? self::rfcDate() : $this->MessageDate);
    20522306
    20532307        // To be created automatically by mail()
    20542308        if ($this->SingleTo) {
    2055             if ($this->Mailer != 'mail') {
     2309            if ('mail' != $this->Mailer) {
    20562310                foreach ($this->to as $toaddr) {
    20572311                    $this->SingleToArray[] = $this->addrFormat($toaddr);
    20582312                }
    20592313            }
    20602314        } else {
    20612315            if (count($this->to) > 0) {
    2062                 if ($this->Mailer != 'mail') {
     2316                if ('mail' != $this->Mailer) {
    20632317                    $result .= $this->addrAppend('To', $this->to);
    20642318                }
    20652319            } elseif (count($this->cc) == 0) {
     
    20672321            }
    20682322        }
    20692323
    2070         $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
     2324        $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
    20712325
    20722326        // sendmail and mail() extract Cc from the header before sending
    20732327        if (count($this->cc) > 0) {
     
    20762330
    20772331        // sendmail and mail() extract Bcc from the header before sending
    20782332        if ((
    2079                 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
     2333                'sendmail' == $this->Mailer or 'qmail' == $this->Mailer or 'mail' == $this->Mailer
    20802334            )
    20812335            and count($this->bcc) > 0
    20822336        ) {
     
    20882342        }
    20892343
    20902344        // mail() sets the subject itself
    2091         if ($this->Mailer != 'mail') {
     2345        if ('mail' != $this->Mailer) {
    20922346            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
    20932347        }
    20942348
     
    21002354            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
    21012355        }
    21022356        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
    2103         if (!is_null($this->Priority)) {
     2357        if (null !== $this->Priority) {
    21042358            $result .= $this->headerLine('X-Priority', $this->Priority);
    21052359        }
    2106         if ($this->XMailer == '') {
     2360        if ('' == $this->XMailer) {
    21072361            $result .= $this->headerLine(
    21082362                'X-Mailer',
    2109                 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
     2363                'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
    21102364            );
    21112365        } else {
    21122366            $myXmailer = trim($this->XMailer);
     
    21152369            }
    21162370        }
    21172371
    2118         if ($this->ConfirmReadingTo != '') {
     2372        if ('' != $this->ConfirmReadingTo) {
    21192373            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
    21202374        }
    21212375
     
    21362390
    21372391    /**
    21382392     * Get the message MIME type headers.
    2139      * @access public
     2393     *
    21402394     * @return string
    21412395     */
    21422396    public function getMailMIME()
     
    21452399        $ismultipart = true;
    21462400        switch ($this->message_type) {
    21472401            case 'inline':
    2148                 $result .= $this->headerLine('Content-Type', 'multipart/related;');
     2402                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
    21492403                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
    21502404                break;
    21512405            case 'attach':
    21522406            case 'inline_attach':
    21532407            case 'alt_attach':
    21542408            case 'alt_inline_attach':
    2155                 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
     2409                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_MIXED . ';');
    21562410                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
    21572411                break;
    21582412            case 'alt':
    21592413            case 'alt_inline':
    2160                 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
     2414                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
    21612415                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
    21622416                break;
    21632417            default:
     
    21672421                break;
    21682422        }
    21692423        // RFC1341 part 5 says 7bit is assumed if not specified
    2170         if ($this->Encoding != '7bit') {
     2424        if (static::ENCODING_7BIT != $this->Encoding) {
    21712425            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
    21722426            if ($ismultipart) {
    2173                 if ($this->Encoding == '8bit') {
    2174                     $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
     2427                if (static::ENCODING_8BIT == $this->Encoding) {
     2428                    $result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT);
    21752429                }
    21762430                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
    21772431            } else {
     
    21792433            }
    21802434        }
    21812435
    2182         if ($this->Mailer != 'mail') {
    2183             $result .= $this->LE;
     2436        if ('mail' != $this->Mailer) {
     2437            $result .= static::$LE;
    21842438        }
    21852439
    21862440        return $result;
     
    21902444     * Returns the whole MIME message.
    21912445     * Includes complete headers and body.
    21922446     * Only valid post preSend().
     2447     *
    21932448     * @see PHPMailer::preSend()
    2194      * @access public
     2449     *
    21952450     * @return string
    21962451     */
    21972452    public function getSentMIMEMessage()
    21982453    {
    2199         return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
     2454        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . static::$LE . static::$LE . $this->MIMEBody;
    22002455    }
    22012456
    22022457    /**
    2203      * Create unique ID
     2458     * Create a unique ID to use for boundaries.
     2459     *
    22042460     * @return string
    22052461     */
    2206     protected function generateId() {
    2207         return md5(uniqid(time()));
     2462    protected function generateId()
     2463    {
     2464        $len = 32; //32 bytes = 256 bits
     2465        if (function_exists('random_bytes')) {
     2466            $bytes = random_bytes($len);
     2467        } elseif (function_exists('openssl_random_pseudo_bytes')) {
     2468            $bytes = openssl_random_pseudo_bytes($len);
     2469        } else {
     2470            //Use a hash to force the length to the same as the other methods
     2471            $bytes = hash('sha256', uniqid((string) mt_rand(), true), true);
     2472        }
     2473
     2474        //We don't care about messing up base64 format here, just want a random string
     2475        return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true)));
    22082476    }
    22092477
    22102478    /**
    22112479     * Assemble the message body.
    22122480     * Returns an empty string on failure.
    2213      * @access public
    2214      * @throws phpmailerException
     2481     *
     2482     * @throws \Exception
     2483     *
    22152484     * @return string The assembled message body
    22162485     */
    22172486    public function createBody()
     
    22242493        $this->boundary[3] = 'b3_' . $this->uniqueid;
    22252494
    22262495        if ($this->sign_key_file) {
    2227             $body .= $this->getMailMIME() . $this->LE;
     2496            $body .= $this->getMailMIME() . static::$LE;
    22282497        }
    22292498
    22302499        $this->setWordWrap();
     
    22322501        $bodyEncoding = $this->Encoding;
    22332502        $bodyCharSet = $this->CharSet;
    22342503        //Can we do a 7-bit downgrade?
    2235         if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
    2236             $bodyEncoding = '7bit';
     2504        if (static::ENCODING_8BIT == $bodyEncoding and !$this->has8bitChars($this->Body)) {
     2505            $bodyEncoding = static::ENCODING_7BIT;
    22372506            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
    22382507            $bodyCharSet = 'us-ascii';
    22392508        }
    22402509        //If lines are too long, and we're not already using an encoding that will shorten them,
    22412510        //change to quoted-printable transfer encoding for the body part only
    2242         if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
    2243             $bodyEncoding = 'quoted-printable';
     2511        if (static::ENCODING_BASE64 != $this->Encoding and static::hasLineLongerThanMax($this->Body)) {
     2512            $bodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
    22442513        }
    22452514
    22462515        $altBodyEncoding = $this->Encoding;
    22472516        $altBodyCharSet = $this->CharSet;
    22482517        //Can we do a 7-bit downgrade?
    2249         if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
    2250             $altBodyEncoding = '7bit';
     2518        if (static::ENCODING_8BIT == $altBodyEncoding and !$this->has8bitChars($this->AltBody)) {
     2519            $altBodyEncoding = static::ENCODING_7BIT;
    22512520            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
    22522521            $altBodyCharSet = 'us-ascii';
    22532522        }
    22542523        //If lines are too long, and we're not already using an encoding that will shorten them,
    22552524        //change to quoted-printable transfer encoding for the alt body part only
    2256         if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
    2257             $altBodyEncoding = 'quoted-printable';
     2525        if (static::ENCODING_BASE64 != $altBodyEncoding and static::hasLineLongerThanMax($this->AltBody)) {
     2526            $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
    22582527        }
    22592528        //Use this as a preamble in all multipart message types
    2260         $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
     2529        $mimepre = 'This is a multi-part message in MIME format.' . static::$LE;
    22612530        switch ($this->message_type) {
    22622531            case 'inline':
    22632532                $body .= $mimepre;
    22642533                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
    22652534                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2266                 $body .= $this->LE . $this->LE;
     2535                $body .= static::$LE;
    22672536                $body .= $this->attachAll('inline', $this->boundary[1]);
    22682537                break;
    22692538            case 'attach':
    22702539                $body .= $mimepre;
    22712540                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
    22722541                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2273                 $body .= $this->LE . $this->LE;
     2542                $body .= static::$LE;
    22742543                $body .= $this->attachAll('attachment', $this->boundary[1]);
    22752544                break;
    22762545            case 'inline_attach':
    22772546                $body .= $mimepre;
    22782547                $body .= $this->textLine('--' . $this->boundary[1]);
    2279                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
     2548                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
    22802549                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    2281                 $body .= $this->LE;
     2550                $body .= static::$LE;
    22822551                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
    22832552                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2284                 $body .= $this->LE . $this->LE;
     2553                $body .= static::$LE;
    22852554                $body .= $this->attachAll('inline', $this->boundary[2]);
    2286                 $body .= $this->LE;
     2555                $body .= static::$LE;
    22872556                $body .= $this->attachAll('attachment', $this->boundary[1]);
    22882557                break;
    22892558            case 'alt':
    22902559                $body .= $mimepre;
    2291                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     2560                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding);
    22922561                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    2293                 $body .= $this->LE . $this->LE;
    2294                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
     2562                $body .= static::$LE;
     2563                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding);
    22952564                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2296                 $body .= $this->LE . $this->LE;
     2565                $body .= static::$LE;
    22972566                if (!empty($this->Ical)) {
    2298                     $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
     2567                    $body .= $this->getBoundary($this->boundary[1], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', '');
    22992568                    $body .= $this->encodeString($this->Ical, $this->Encoding);
    2300                     $body .= $this->LE . $this->LE;
     2569                    $body .= static::$LE;
    23012570                }
    23022571                $body .= $this->endBoundary($this->boundary[1]);
    23032572                break;
    23042573            case 'alt_inline':
    23052574                $body .= $mimepre;
    2306                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     2575                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding);
    23072576                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    2308                 $body .= $this->LE . $this->LE;
     2577                $body .= static::$LE;
    23092578                $body .= $this->textLine('--' . $this->boundary[1]);
    2310                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
     2579                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
    23112580                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    2312                 $body .= $this->LE;
    2313                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
     2581                $body .= static::$LE;
     2582                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding);
    23142583                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2315                 $body .= $this->LE . $this->LE;
     2584                $body .= static::$LE;
    23162585                $body .= $this->attachAll('inline', $this->boundary[2]);
    2317                 $body .= $this->LE;
     2586                $body .= static::$LE;
    23182587                $body .= $this->endBoundary($this->boundary[1]);
    23192588                break;
    23202589            case 'alt_attach':
    23212590                $body .= $mimepre;
    23222591                $body .= $this->textLine('--' . $this->boundary[1]);
    2323                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
     2592                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
    23242593                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    2325                 $body .= $this->LE;
    2326                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     2594                $body .= static::$LE;
     2595                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding);
    23272596                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    2328                 $body .= $this->LE . $this->LE;
    2329                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
     2597                $body .= static::$LE;
     2598                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding);
    23302599                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2331                 $body .= $this->LE . $this->LE;
     2600                $body .= static::$LE;
     2601                if (!empty($this->Ical)) {
     2602                    $body .= $this->getBoundary($this->boundary[2], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', '');
     2603                    $body .= $this->encodeString($this->Ical, $this->Encoding);
     2604                }
    23322605                $body .= $this->endBoundary($this->boundary[2]);
    2333                 $body .= $this->LE;
     2606                $body .= static::$LE;
    23342607                $body .= $this->attachAll('attachment', $this->boundary[1]);
    23352608                break;
    23362609            case 'alt_inline_attach':
    23372610                $body .= $mimepre;
    23382611                $body .= $this->textLine('--' . $this->boundary[1]);
    2339                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
     2612                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
    23402613                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    2341                 $body .= $this->LE;
    2342                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     2614                $body .= static::$LE;
     2615                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding);
    23432616                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    2344                 $body .= $this->LE . $this->LE;
     2617                $body .= static::$LE;
    23452618                $body .= $this->textLine('--' . $this->boundary[2]);
    2346                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
     2619                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
    23472620                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
    2348                 $body .= $this->LE;
    2349                 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
     2621                $body .= static::$LE;
     2622                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding);
    23502623                $body .= $this->encodeString($this->Body, $bodyEncoding);
    2351                 $body .= $this->LE . $this->LE;
     2624                $body .= static::$LE;
    23522625                $body .= $this->attachAll('inline', $this->boundary[3]);
    2353                 $body .= $this->LE;
     2626                $body .= static::$LE;
    23542627                $body .= $this->endBoundary($this->boundary[2]);
    2355                 $body .= $this->LE;
     2628                $body .= static::$LE;
    23562629                $body .= $this->attachAll('attachment', $this->boundary[1]);
    23572630                break;
    23582631            default:
     
    23652638
    23662639        if ($this->isError()) {
    23672640            $body = '';
     2641            if ($this->exceptions) {
     2642                throw new \Exception($this->lang('empty_message'), self::STOP_CRITICAL);
     2643            }
    23682644        } elseif ($this->sign_key_file) {
    23692645            try {
    23702646                if (!defined('PKCS7_TEXT')) {
    2371                     throw new phpmailerException($this->lang('extension_missing') . 'openssl');
     2647                    throw new \Exception($this->lang('extension_missing') . 'openssl');
    23722648                }
    2373                 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
     2649                // @TODO would be nice to use php://temp streams here
    23742650                $file = tempnam(sys_get_temp_dir(), 'mail');
    23752651                if (false === file_put_contents($file, $body)) {
    2376                     throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
     2652                    throw new \Exception($this->lang('signing') . ' Could not write temp file');
    23772653                }
    23782654                $signed = tempnam(sys_get_temp_dir(), 'signed');
    23792655                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
     
    23822658                        $file,
    23832659                        $signed,
    23842660                        'file://' . realpath($this->sign_cert_file),
    2385                         array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
    2386                         null
     2661                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
     2662                        []
    23872663                    );
    23882664                } else {
    23892665                    $sign = @openssl_pkcs7_sign(
    23902666                        $file,
    23912667                        $signed,
    23922668                        'file://' . realpath($this->sign_cert_file),
    2393                         array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
    2394                         null,
     2669                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
     2670                        [],
    23952671                        PKCS7_DETACHED,
    23962672                        $this->sign_extracerts_file
    23972673                    );
    23982674                }
     2675                @unlink($file);
    23992676                if ($sign) {
    2400                     @unlink($file);
    24012677                    $body = file_get_contents($signed);
    24022678                    @unlink($signed);
    24032679                    //The message returned by openssl contains both headers and body, so need to split them up
    24042680                    $parts = explode("\n\n", $body, 2);
    2405                     $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
     2681                    $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE;
    24062682                    $body = $parts[1];
    24072683                } else {
    2408                     @unlink($file);
    24092684                    @unlink($signed);
    2410                     throw new phpmailerException($this->lang('signing') . openssl_error_string());
     2685                    throw new \Exception($this->lang('signing') . openssl_error_string());
    24112686                }
    2412             } catch (phpmailerException $exc) {
     2687            } catch (\Exception $exc) {
    24132688                $body = '';
    24142689                if ($this->exceptions) {
    24152690                    throw $exc;
    24162691                }
    24172692            }
    24182693        }
     2694
    24192695        return $body;
    24202696    }
    24212697
    24222698    /**
    24232699     * Return the start of a message boundary.
    2424      * @access protected
     2700     *
    24252701     * @param string $boundary
    24262702     * @param string $charSet
    24272703     * @param string $contentType
    24282704     * @param string $encoding
     2705     *
    24292706     * @return string
    24302707     */
    24312708    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
    24322709    {
    24332710        $result = '';
    2434         if ($charSet == '') {
     2711        if ('' == $charSet) {
    24352712            $charSet = $this->CharSet;
    24362713        }
    2437         if ($contentType == '') {
     2714        if ('' == $contentType) {
    24382715            $contentType = $this->ContentType;
    24392716        }
    2440         if ($encoding == '') {
     2717        if ('' == $encoding) {
    24412718            $encoding = $this->Encoding;
    24422719        }
    24432720        $result .= $this->textLine('--' . $boundary);
    24442721        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
    2445         $result .= $this->LE;
     2722        $result .= static::$LE;
    24462723        // RFC1341 part 5 says 7bit is assumed if not specified
    2447         if ($encoding != '7bit') {
     2724        if (static::ENCODING_7BIT != $encoding) {
    24482725            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
    24492726        }
    2450         $result .= $this->LE;
     2727        $result .= static::$LE;
    24512728
    24522729        return $result;
    24532730    }
    24542731
    24552732    /**
    24562733     * Return the end of a message boundary.
    2457      * @access protected
     2734     *
    24582735     * @param string $boundary
     2736     *
    24592737     * @return string
    24602738     */
    24612739    protected function endBoundary($boundary)
    24622740    {
    2463         return $this->LE . '--' . $boundary . '--' . $this->LE;
     2741        return static::$LE . '--' . $boundary . '--' . static::$LE;
    24642742    }
    24652743
    24662744    /**
    24672745     * Set the message type.
    24682746     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
    2469      * @access protected
    2470      * @return void
    24712747     */
    24722748    protected function setMessageType()
    24732749    {
    2474         $type = array();
     2750        $type = [];
    24752751        if ($this->alternativeExists()) {
    24762752            $type[] = 'alt';
    24772753        }
     
    24822758            $type[] = 'attach';
    24832759        }
    24842760        $this->message_type = implode('_', $type);
    2485         if ($this->message_type == '') {
     2761        if ('' == $this->message_type) {
    24862762            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
    24872763            $this->message_type = 'plain';
    24882764        }
     
    24902766
    24912767    /**
    24922768     * Format a header line.
    2493      * @access public
    2494      * @param string $name
    2495      * @param string $value
     2769     *
     2770     * @param string     $name
     2771     * @param string|int $value
     2772     *
    24962773     * @return string
    24972774     */
    24982775    public function headerLine($name, $value)
    24992776    {
    2500         return $name . ': ' . $value . $this->LE;
     2777        return $name . ': ' . $value . static::$LE;
    25012778    }
    25022779
    25032780    /**
    25042781     * Return a formatted mail line.
    2505      * @access public
     2782     *
    25062783     * @param string $value
     2784     *
    25072785     * @return string
    25082786     */
    25092787    public function textLine($value)
    25102788    {
    2511         return $value . $this->LE;
     2789        return $value . static::$LE;
    25122790    }
    25132791
    25142792    /**
     
    25172795     * Returns false if the file could not be found or read.
    25182796     * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client.
    25192797     * If you need to do that, fetch the resource yourself and pass it in via a local file or string.
    2520      * @param string $path Path to the attachment.
    2521      * @param string $name Overrides the attachment name.
    2522      * @param string $encoding File encoding (see $Encoding).
    2523      * @param string $type File extension (MIME) type.
     2798     *
     2799     * @param string $path        Path to the attachment
     2800     * @param string $name        Overrides the attachment name
     2801     * @param string $encoding    File encoding (see $Encoding)
     2802     * @param string $type        File extension (MIME) type
    25242803     * @param string $disposition Disposition to use
    2525      * @throws phpmailerException
    2526      * @return boolean
     2804     *
     2805     * @throws \Exception
     2806     *
     2807     * @return bool
    25272808     */
    2528     public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
     2809    public function addAttachment($path, $name = '', $encoding = self::ENCODING_BASE64, $type = '', $disposition = 'attachment')
    25292810    {
    25302811        try {
    2531             if (!self::isPermittedPath($path) or !@is_file($path)) {
    2532                 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
     2812            if (!static::isPermittedPath($path) || !@is_file($path)) {
     2813                throw new \Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
    25332814            }
    25342815
    25352816            // If a MIME type is not specified, try to work it out from the file name
    2536             if ($type == '') {
    2537                 $type = self::filenameToType($path);
     2817            if ('' == $type) {
     2818                $type = static::filenameToType($path);
    25382819            }
    25392820
    25402821            $filename = basename($path);
    2541             if ($name == '') {
     2822            if ('' == $name) {
    25422823                $name = $filename;
    25432824            }
    25442825
    2545             $this->attachment[] = array(
     2826            $this->attachment[] = [
    25462827                0 => $path,
    25472828                1 => $filename,
    25482829                2 => $name,
     
    25502831                4 => $type,
    25512832                5 => false, // isStringAttachment
    25522833                6 => $disposition,
    2553                 7 => 0
    2554             );
    2555 
    2556         } catch (phpmailerException $exc) {
     2834                7 => $name,
     2835            ];
     2836        } catch (\Exception $exc) {
    25572837            $this->setError($exc->getMessage());
    25582838            $this->edebug($exc->getMessage());
    25592839            if ($this->exceptions) {
    25602840                throw $exc;
    25612841            }
     2842
    25622843            return false;
    25632844        }
     2845
    25642846        return true;
    25652847    }
    25662848
    25672849    /**
    25682850     * Return the array of attachments.
     2851     *
    25692852     * @return array
    25702853     */
    25712854    public function getAttachments()
     
    25762859    /**
    25772860     * Attach all file, string, and binary attachments to the message.
    25782861     * Returns an empty string on failure.
    2579      * @access protected
     2862     *
    25802863     * @param string $disposition_type
    25812864     * @param string $boundary
     2865     *
    25822866     * @return string
    25832867     */
    25842868    protected function attachAll($disposition_type, $boundary)
    25852869    {
    25862870        // Return text of body
    2587         $mime = array();
    2588         $cidUniq = array();
    2589         $incl = array();
     2871        $mime = [];
     2872        $cidUniq = [];
     2873        $incl = [];
    25902874
    25912875        // Add all attachments
    25922876        foreach ($this->attachment as $attachment) {
     
    26022886                    $path = $attachment[0];
    26032887                }
    26042888
    2605                 $inclhash = md5(serialize($attachment));
     2889                $inclhash = hash('sha256', serialize($attachment));
    26062890                if (in_array($inclhash, $incl)) {
    26072891                    continue;
    26082892                }
     
    26122896                $type = $attachment[4];
    26132897                $disposition = $attachment[6];
    26142898                $cid = $attachment[7];
    2615                 if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
     2899                if ('inline' == $disposition and array_key_exists($cid, $cidUniq)) {
    26162900                    continue;
    26172901                }
    26182902                $cidUniq[$cid] = true;
    26192903
    2620                 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
     2904                $mime[] = sprintf('--%s%s', $boundary, static::$LE);
    26212905                //Only include a filename property if we have one
    26222906                if (!empty($name)) {
    26232907                    $mime[] = sprintf(
    26242908                        'Content-Type: %s; name="%s"%s',
    26252909                        $type,
    26262910                        $this->encodeHeader($this->secureHeader($name)),
    2627                         $this->LE
     2911                        static::$LE
    26282912                    );
    26292913                } else {
    26302914                    $mime[] = sprintf(
    26312915                        'Content-Type: %s%s',
    26322916                        $type,
    2633                         $this->LE
     2917                        static::$LE
    26342918                    );
    26352919                }
    26362920                // RFC1341 part 5 says 7bit is assumed if not specified
    2637                 if ($encoding != '7bit') {
    2638                     $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
     2921                if (static::ENCODING_7BIT != $encoding) {
     2922                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE);
    26392923                }
    26402924
    2641                 if ($disposition == 'inline') {
    2642                     $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
     2925                if (!empty($cid)) {
     2926                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, static::$LE);
    26432927                }
    26442928
    26452929                // If a filename contains any of these chars, it should be quoted,
     
    26532937                            'Content-Disposition: %s; filename="%s"%s',
    26542938                            $disposition,
    26552939                            $encoded_name,
    2656                             $this->LE . $this->LE
     2940                            static::$LE . static::$LE
    26572941                        );
    26582942                    } else {
    26592943                        if (!empty($encoded_name)) {
     
    26612945                                'Content-Disposition: %s; filename=%s%s',
    26622946                                $disposition,
    26632947                                $encoded_name,
    2664                                 $this->LE . $this->LE
     2948                                static::$LE . static::$LE
    26652949                            );
    26662950                        } else {
    26672951                            $mime[] = sprintf(
    26682952                                'Content-Disposition: %s%s',
    26692953                                $disposition,
    2670                                 $this->LE . $this->LE
     2954                                static::$LE . static::$LE
    26712955                            );
    26722956                        }
    26732957                    }
    26742958                } else {
    2675                     $mime[] = $this->LE;
     2959                    $mime[] = static::$LE;
    26762960                }
    26772961
    26782962                // Encode as string attachment
    26792963                if ($bString) {
    26802964                    $mime[] = $this->encodeString($string, $encoding);
    2681                     if ($this->isError()) {
    2682                         return '';
    2683                     }
    2684                     $mime[] = $this->LE . $this->LE;
    26852965                } else {
    26862966                    $mime[] = $this->encodeFile($path, $encoding);
    2687                     if ($this->isError()) {
    2688                         return '';
    2689                     }
    2690                     $mime[] = $this->LE . $this->LE;
    26912967                }
     2968                if ($this->isError()) {
     2969                    return '';
     2970                }
     2971                $mime[] = static::$LE;
    26922972            }
    26932973        }
    26942974
    2695         $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
     2975        $mime[] = sprintf('--%s--%s', $boundary, static::$LE);
    26962976
    26972977        return implode('', $mime);
    26982978    }
     
    27002980    /**
    27012981     * Encode a file attachment in requested format.
    27022982     * Returns an empty string on failure.
    2703      * @param string $path The full path to the file
     2983     *
     2984     * @param string $path     The full path to the file
    27042985     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
    2705      * @throws phpmailerException
    2706      * @access protected
     2986     *
     2987     * @throws \Exception
     2988     *
    27072989     * @return string
    27082990     */
    2709     protected function encodeFile($path, $encoding = 'base64')
     2991    protected function encodeFile($path, $encoding = self::ENCODING_BASE64)
    27102992    {
    27112993        try {
    2712             if (!self::isPermittedPath($path) or !file_exists($path)) {
    2713                 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
    2714             }
    2715             $magic_quotes = get_magic_quotes_runtime();
    2716             if ($magic_quotes) {
    2717                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
    2718                     set_magic_quotes_runtime(false);
    2719                 } else {
    2720                     //Doesn't exist in PHP 5.4, but we don't need to check because
    2721                     //get_magic_quotes_runtime always returns false in 5.4+
    2722                     //so it will never get here
    2723                     ini_set('magic_quotes_runtime', false);
    2724                 }
     2994            if (!static::isPermittedPath($path) || !file_exists($path)) {
     2995                throw new \Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
    27252996            }
    27262997            $file_buffer = file_get_contents($path);
    2727             $file_buffer = $this->encodeString($file_buffer, $encoding);
    2728             if ($magic_quotes) {
    2729                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
    2730                     set_magic_quotes_runtime($magic_quotes);
    2731                 } else {
    2732                     ini_set('magic_quotes_runtime', $magic_quotes);
    2733                 }
     2998            if (false === $file_buffer) {
     2999                throw new \Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
    27343000            }
     3001            $file_buffer = $this->encodeString($file_buffer, $encoding);
     3002
    27353003            return $file_buffer;
    2736         } catch (Exception $exc) {
     3004        } catch (\Exception $exc) {
    27373005            $this->setError($exc->getMessage());
     3006
    27383007            return '';
    27393008        }
    27403009    }
     
    27423011    /**
    27433012     * Encode a string in requested format.
    27443013     * Returns an empty string on failure.
    2745      * @param string $str The text to encode
     3014     *
     3015     * @param string $str      The text to encode
    27463016     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
    2747      * @access public
     3017     *
    27483018     * @return string
    27493019     */
    2750     public function encodeString($str, $encoding = 'base64')
     3020    public function encodeString($str, $encoding = self::ENCODING_BASE64)
    27513021    {
    27523022        $encoded = '';
    27533023        switch (strtolower($encoding)) {
    2754             case 'base64':
    2755                 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
     3024            case static::ENCODING_BASE64:
     3025                $encoded = chunk_split(
     3026                    base64_encode($str),
     3027                    static::STD_LINE_LENGTH,
     3028                    static::$LE
     3029                );
    27563030                break;
    2757             case '7bit':
    2758             case '8bit':
    2759                 $encoded = $this->fixEOL($str);
     3031            case static::ENCODING_7BIT:
     3032            case static::ENCODING_8BIT:
     3033                $encoded = static::normalizeBreaks($str);
    27603034                // Make sure it ends with a line break
    2761                 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
    2762                     $encoded .= $this->LE;
     3035                if (substr($encoded, -(strlen(static::$LE))) != static::$LE) {
     3036                    $encoded .= static::$LE;
    27633037                }
    27643038                break;
    2765             case 'binary':
     3039            case static::ENCODING_BINARY:
    27663040                $encoded = $str;
    27673041                break;
    2768             case 'quoted-printable':
     3042            case static::ENCODING_QUOTED_PRINTABLE:
    27693043                $encoded = $this->encodeQP($str);
    27703044                break;
    27713045            default:
    27723046                $this->setError($this->lang('encoding') . $encoding);
    27733047                break;
    27743048        }
     3049
    27753050        return $encoded;
    27763051    }
    27773052
    27783053    /**
    2779      * Encode a header string optimally.
    2780      * Picks shortest of Q, B, quoted-printable or none.
    2781      * @access public
    2782      * @param string $str
    2783      * @param string $position
     3054     * Encode a header value (not including its label) optimally.
     3055     * Picks shortest of Q, B, or none. Result includes folding if needed.
     3056     * See RFC822 definitions for phrase, comment and text positions.
     3057     *
     3058     * @param string $str      The header value to encode
     3059     * @param string $position What context the string will be used in
     3060     *
    27843061     * @return string
    27853062     */
    27863063    public function encodeHeader($str, $position = 'text')
     
    27913068                if (!preg_match('/[\200-\377]/', $str)) {
    27923069                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
    27933070                    $encoded = addcslashes($str, "\0..\37\177\\\"");
    2794                     if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
    2795                         return ($encoded);
    2796                     } else {
    2797                         return ("\"$encoded\"");
     3071                    if (($str == $encoded) and !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
     3072                        return $encoded;
    27983073                    }
     3074
     3075                    return "\"$encoded\"";
    27993076                }
    28003077                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
    28013078                break;
    2802             /** @noinspection PhpMissingBreakStatementInspection */
     3079            /* @noinspection PhpMissingBreakStatementInspection */
    28033080            case 'comment':
    28043081                $matchcount = preg_match_all('/[()"]/', $str, $matches);
    2805                 // Intentional fall-through
     3082            //fallthrough
    28063083            case 'text':
    28073084            default:
    28083085                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
    28093086                break;
    28103087        }
    28113088
    2812         //There are no chars that need encoding
    2813         if ($matchcount == 0) {
    2814             return ($str);
    2815         }
    2816 
    2817         $maxlen = 75 - 7 - strlen($this->CharSet);
     3089        //RFCs specify a maximum line length of 78 chars, however mail() will sometimes
     3090        //corrupt messages with headers longer than 65 chars. See #818
     3091        $lengthsub = 'mail' == $this->Mailer ? 13 : 0;
     3092        $maxlen = static::STD_LINE_LENGTH - $lengthsub;
    28183093        // Try to select the encoding which should produce the shortest output
    28193094        if ($matchcount > strlen($str) / 3) {
    28203095            // More than a third of the content will need encoding, so B encoding will be most efficient
    28213096            $encoding = 'B';
    2822             if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
     3097            //This calculation is:
     3098            // max line length
     3099            // - shorten to avoid mail() corruption
     3100            // - Q/B encoding char overhead ("` =?<charset>?[QB]?<content>?=`")
     3101            // - charset name length
     3102            $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
     3103            if ($this->hasMultiBytes($str)) {
    28233104                // Use a custom function which correctly encodes and wraps long
    28243105                // multibyte strings without breaking lines within a character
    28253106                $encoded = $this->base64EncodeWrapMB($str, "\n");
     
    28283109                $maxlen -= $maxlen % 4;
    28293110                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
    28303111            }
    2831         } else {
     3112            $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
     3113        } elseif ($matchcount > 0) {
     3114            //1 or more chars need encoding, use Q-encode
    28323115            $encoding = 'Q';
     3116            //Recalc max line length for Q encoding - see comments on B encode
     3117            $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
    28333118            $encoded = $this->encodeQ($str, $position);
    28343119            $encoded = $this->wrapText($encoded, $maxlen, true);
    2835             $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
     3120            $encoded = str_replace('=' . static::$LE, "\n", trim($encoded));
     3121            $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
     3122        } elseif (strlen($str) > $maxlen) {
     3123            //No chars need encoding, but line is too long, so fold it
     3124            $encoded = trim($this->wrapText($str, $maxlen, false));
     3125            if ($str == $encoded) {
     3126                //Wrapping nicely didn't work, wrap hard instead
     3127                $encoded = trim(chunk_split($str, static::STD_LINE_LENGTH, static::$LE));
     3128            }
     3129            $encoded = str_replace(static::$LE, "\n", trim($encoded));
     3130            $encoded = preg_replace('/^(.*)$/m', ' \\1', $encoded);
     3131        } else {
     3132            //No reformatting needed
     3133            return $str;
    28363134        }
    28373135
    2838         $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
    2839         $encoded = trim(str_replace("\n", $this->LE, $encoded));
    2840 
    2841         return $encoded;
     3136        return trim(static::normalizeBreaks($encoded));
    28423137    }
    28433138
    28443139    /**
    28453140     * Check if a string contains multi-byte characters.
    2846      * @access public
     3141     *
    28473142     * @param string $str multi-byte text to wrap encode
    2848      * @return boolean
     3143     *
     3144     * @return bool
    28493145     */
    28503146    public function hasMultiBytes($str)
    28513147    {
    28523148        if (function_exists('mb_strlen')) {
    2853             return (strlen($str) > mb_strlen($str, $this->CharSet));
    2854         } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
    2855             return false;
     3149            return strlen($str) > mb_strlen($str, $this->CharSet);
    28563150        }
     3151
     3152        // Assume no multibytes (we can't handle without mbstring functions anyway)
     3153        return false;
    28573154    }
    28583155
    28593156    /**
    28603157     * Does a string contain any 8-bit chars (in any charset)?
     3158     *
    28613159     * @param string $text
    2862      * @return boolean
     3160     *
     3161     * @return bool
    28633162     */
    28643163    public function has8bitChars($text)
    28653164    {
    2866         return (boolean)preg_match('/[\x80-\xFF]/', $text);
     3165        return (bool) preg_match('/[\x80-\xFF]/', $text);
    28673166    }
    28683167
    28693168    /**
    28703169     * Encode and wrap long multibyte strings for mail headers
    28713170     * without breaking lines within a character.
    2872      * Adapted from a function by paravoid
    2873      * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
    2874      * @access public
    2875      * @param string $str multi-byte text to wrap encode
     3171     * Adapted from a function by paravoid.
     3172     *
     3173     * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
     3174     *
     3175     * @param string $str       multi-byte text to wrap encode
    28763176     * @param string $linebreak string to use as linefeed/end-of-line
     3177     *
    28773178     * @return string
    28783179     */
    28793180    public function base64EncodeWrapMB($str, $linebreak = null)
     
    28813182        $start = '=?' . $this->CharSet . '?B?';
    28823183        $end = '?=';
    28833184        $encoded = '';
    2884         if ($linebreak === null) {
    2885             $linebreak = $this->LE;
     3185        if (null === $linebreak) {
     3186            $linebreak = static::$LE;
    28863187        }
    28873188
    28883189        $mb_length = mb_strlen($str, $this->CharSet);
     
    28993200                $offset = $avgLength - $lookBack;
    29003201                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
    29013202                $chunk = base64_encode($chunk);
    2902                 $lookBack++;
     3203                ++$lookBack;
    29033204            } while (strlen($chunk) > $length);
    29043205            $encoded .= $chunk . $linebreak;
    29053206        }
    29063207
    29073208        // Chomp the last linefeed
    2908         $encoded = substr($encoded, 0, -strlen($linebreak));
    2909         return $encoded;
     3209        return substr($encoded, 0, -strlen($linebreak));
    29103210    }
    29113211
    29123212    /**
    29133213     * Encode a string in quoted-printable format.
    29143214     * According to RFC2045 section 6.7.
    2915      * @access public
     3215     *
    29163216     * @param string $string The text to encode
    2917      * @param integer $line_max Number of chars allowed on a line before wrapping
     3217     *
    29183218     * @return string
    2919      * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
    29203219     */
    2921     public function encodeQP($string, $line_max = 76)
     3220    public function encodeQP($string)
    29223221    {
    2923         // Use native function if it's available (>= PHP5.3)
    2924         if (function_exists('quoted_printable_encode')) {
    2925             return quoted_printable_encode($string);
    2926         }
    2927         // Fall back to a pure PHP implementation
    2928         $string = str_replace(
    2929             array('%20', '%0D%0A.', '%0D%0A', '%'),
    2930             array(' ', "\r\n=2E", "\r\n", '='),
    2931             rawurlencode($string)
    2932         );
    2933         return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
    2934     }
    2935 
    2936     /**
    2937      * Backward compatibility wrapper for an old QP encoding function that was removed.
    2938      * @see PHPMailer::encodeQP()
    2939      * @access public
    2940      * @param string $string
    2941      * @param integer $line_max
    2942      * @param boolean $space_conv
    2943      * @return string
    2944      * @deprecated Use encodeQP instead.
    2945      */
    2946     public function encodeQPphp(
    2947         $string,
    2948         $line_max = 76,
    2949         /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
    2950     ) {
    2951         return $this->encodeQP($string, $line_max);
     3222        return static::normalizeBreaks(quoted_printable_encode($string));
    29523223    }
    29533224
    29543225    /**
    29553226     * Encode a string using Q encoding.
    2956      * @link http://tools.ietf.org/html/rfc2047
    2957      * @param string $str the text to encode
     3227     *
     3228     * @see http://tools.ietf.org/html/rfc2047#section-4.2
     3229     *
     3230     * @param string $str      the text to encode
    29583231     * @param string $position Where the text is going to be used, see the RFC for what that means
    2959      * @access public
     3232     *
    29603233     * @return string
    29613234     */
    29623235    public function encodeQ($str, $position = 'text')
    29633236    {
    29643237        // There should not be any EOL in the string
    29653238        $pattern = '';
    2966         $encoded = str_replace(array("\r", "\n"), '', $str);
     3239        $encoded = str_replace(["\r", "\n"], '', $str);
    29673240        switch (strtolower($position)) {
    29683241            case 'phrase':
    29693242                // RFC 2047 section 5.3
    29703243                $pattern = '^A-Za-z0-9!*+\/ -';
    29713244                break;
    2972             /** @noinspection PhpMissingBreakStatementInspection */
     3245            /*
     3246             * RFC 2047 section 5.2.
     3247             * Build $pattern without including delimiters and []
     3248             */
     3249            /* @noinspection PhpMissingBreakStatementInspection */
    29733250            case 'comment':
    2974                 // RFC 2047 section 5.2
    29753251                $pattern = '\(\)"';
    2976                 // intentional fall-through
    2977                 // for this reason we build the $pattern without including delimiters and []
     3252            /* Intentional fall through */
    29783253            case 'text':
    29793254            default:
    29803255                // RFC 2047 section 5.1
    29813256                // Replace every high ascii, control, =, ? and _ characters
     3257                /** @noinspection SuspiciousAssignmentsInspection */
    29823258                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
    29833259                break;
    29843260        }
    2985         $matches = array();
     3261        $matches = [];
    29863262        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
    29873263            // If the string contains an '=', make sure it's the first thing we replace
    29883264            // so as to avoid double-encoding
     
    29953271                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
    29963272            }
    29973273        }
    2998         // Replace every spaces to _ (more readable than =20)
     3274        // Replace spaces with _ (more readable than =20)
     3275        // RFC 2047 section 4.2(2)
    29993276        return str_replace(' ', '_', $encoded);
    30003277    }
    30013278
     
    30033280     * Add a string or binary attachment (non-filesystem).
    30043281     * This method can be used to attach ascii or binary data,
    30053282     * such as a BLOB record from a database.
    3006      * @param string $string String attachment data.
    3007      * @param string $filename Name of the attachment.
    3008      * @param string $encoding File encoding (see $Encoding).
    3009      * @param string $type File extension (MIME) type.
     3283     *
     3284     * @param string $string      String attachment data
     3285     * @param string $filename    Name of the attachment
     3286     * @param string $encoding    File encoding (see $Encoding)
     3287     * @param string $type        File extension (MIME) type
    30103288     * @param string $disposition Disposition to use
    3011      * @return void
    30123289     */
    30133290    public function addStringAttachment(
    30143291        $string,
    30153292        $filename,
    3016         $encoding = 'base64',
     3293        $encoding = self::ENCODING_BASE64,
    30173294        $type = '',
    30183295        $disposition = 'attachment'
    30193296    ) {
    30203297        // If a MIME type is not specified, try to work it out from the file name
    3021         if ($type == '') {
    3022             $type = self::filenameToType($filename);
     3298        if ('' == $type) {
     3299            $type = static::filenameToType($filename);
    30233300        }
    30243301        // Append to $attachment array
    3025         $this->attachment[] = array(
     3302        $this->attachment[] = [
    30263303            0 => $string,
    30273304            1 => $filename,
    30283305            2 => basename($filename),
     
    30303307            4 => $type,
    30313308            5 => true, // isStringAttachment
    30323309            6 => $disposition,
    3033             7 => 0
    3034         );
     3310            7 => 0,
     3311        ];
    30353312    }
    30363313
    30373314    /**
     
    30423319     * This is used in HTML messages that embed the images
    30433320     * the HTML refers to using the $cid value.
    30443321     * Never use a user-supplied path to a file!
    3045      * @param string $path Path to the attachment.
    3046      * @param string $cid Content ID of the attachment; Use this to reference
    3047      *        the content when using an embedded image in HTML.
    3048      * @param string $name Overrides the attachment name.
    3049      * @param string $encoding File encoding (see $Encoding).
    3050      * @param string $type File MIME type.
     3322     *
     3323     * @param string $path        Path to the attachment
     3324     * @param string $cid         Content ID of the attachment; Use this to reference
     3325     *                            the content when using an embedded image in HTML
     3326     * @param string $name        Overrides the attachment name
     3327     * @param string $encoding    File encoding (see $Encoding)
     3328     * @param string $type        File MIME type
    30513329     * @param string $disposition Disposition to use
    3052      * @return boolean True on successfully adding an attachment
     3330     *
     3331     * @return bool True on successfully adding an attachment
    30533332     */
    3054     public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
     3333    public function addEmbeddedImage($path, $cid, $name = '', $encoding = self::ENCODING_BASE64, $type = '', $disposition = 'inline')
    30553334    {
    3056         if (!self::isPermittedPath($path) or !@is_file($path)) {
     3335        if (!static::isPermittedPath($path) || !@is_file($path)) {
    30573336            $this->setError($this->lang('file_access') . $path);
     3337
    30583338            return false;
    30593339        }
    30603340
    30613341        // If a MIME type is not specified, try to work it out from the file name
    3062         if ($type == '') {
    3063             $type = self::filenameToType($path);
     3342        if ('' == $type) {
     3343            $type = static::filenameToType($path);
    30643344        }
    30653345
    30663346        $filename = basename($path);
    3067         if ($name == '') {
     3347        if ('' == $name) {
    30683348            $name = $filename;
    30693349        }
    30703350
    30713351        // Append to $attachment array
    3072         $this->attachment[] = array(
     3352        $this->attachment[] = [
    30733353            0 => $path,
    30743354            1 => $filename,
    30753355            2 => $name,
     
    30773357            4 => $type,
    30783358            5 => false, // isStringAttachment
    30793359            6 => $disposition,
    3080             7 => $cid
    3081         );
     3360            7 => $cid,
     3361        ];
     3362
    30823363        return true;
    30833364    }
    30843365
    30853366    /**
    30863367     * Add an embedded stringified attachment.
    30873368     * This can include images, sounds, and just about any other document type.
    3088      * Be sure to set the $type to an image type for images:
    3089      * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
    3090      * @param string $string The attachment binary data.
    3091      * @param string $cid Content ID of the attachment; Use this to reference
    3092      *        the content when using an embedded image in HTML.
    3093      * @param string $name
    3094      * @param string $encoding File encoding (see $Encoding).
    3095      * @param string $type MIME type.
     3369     * If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type.
     3370     *
     3371     * @param string $string      The attachment binary data
     3372     * @param string $cid         Content ID of the attachment; Use this to reference
     3373     *                            the content when using an embedded image in HTML
     3374     * @param string $name        A filename for the attachment. If this contains an extension,
     3375     *                            PHPMailer will attempt to set a MIME type for the attachment.
     3376     *                            For example 'file.jpg' would get an 'image/jpeg' MIME type.
     3377     * @param string $encoding    File encoding (see $Encoding), defaults to 'base64'
     3378     * @param string $type        MIME type - will be used in preference to any automatically derived type
    30963379     * @param string $disposition Disposition to use
    3097      * @return boolean True on successfully adding an attachment
     3380     *
     3381     * @return bool True on successfully adding an attachment
    30983382     */
    30993383    public function addStringEmbeddedImage(
    31003384        $string,
    31013385        $cid,
    31023386        $name = '',
    3103         $encoding = 'base64',
     3387        $encoding = self::ENCODING_BASE64,
    31043388        $type = '',
    31053389        $disposition = 'inline'
    31063390    ) {
    31073391        // If a MIME type is not specified, try to work it out from the name
    3108         if ($type == '' and !empty($name)) {
    3109             $type = self::filenameToType($name);
     3392        if ('' == $type and !empty($name)) {
     3393            $type = static::filenameToType($name);
    31103394        }
    31113395
    31123396        // Append to $attachment array
    3113         $this->attachment[] = array(
     3397        $this->attachment[] = [
    31143398            0 => $string,
    31153399            1 => $name,
    31163400            2 => $name,
     
    31183402            4 => $type,
    31193403            5 => true, // isStringAttachment
    31203404            6 => $disposition,
    3121             7 => $cid
    3122         );
     3405            7 => $cid,
     3406        ];
     3407
    31233408        return true;
    31243409    }
    31253410
    31263411    /**
     3412     * Check if an embedded attachment is present with this cid.
     3413     *
     3414     * @param string $cid
     3415     *
     3416     * @return bool
     3417     */
     3418    protected function cidExists($cid)
     3419    {
     3420        foreach ($this->attachment as $attachment) {
     3421            if ('inline' == $attachment[6] and $cid == $attachment[7]) {
     3422                return true;
     3423            }
     3424        }
     3425
     3426        return false;
     3427    }
     3428
     3429    /**
    31273430     * Check if an inline attachment is present.
    3128      * @access public
    3129      * @return boolean
     3431     *
     3432     * @return bool
    31303433     */
    31313434    public function inlineImageExists()
    31323435    {
    31333436        foreach ($this->attachment as $attachment) {
    3134             if ($attachment[6] == 'inline') {
     3437            if ('inline' == $attachment[6]) {
    31353438                return true;
    31363439            }
    31373440        }
     3441
    31383442        return false;
    31393443    }
    31403444
    31413445    /**
    31423446     * Check if an attachment (non-inline) is present.
    3143      * @return boolean
     3447     *
     3448     * @return bool
    31443449     */
    31453450    public function attachmentExists()
    31463451    {
    31473452        foreach ($this->attachment as $attachment) {
    3148             if ($attachment[6] == 'attachment') {
     3453            if ('attachment' == $attachment[6]) {
    31493454                return true;
    31503455            }
    31513456        }
     3457
    31523458        return false;
    31533459    }
    31543460
    31553461    /**
    31563462     * Check if this message has an alternative body set.
    3157      * @return boolean
     3463     *
     3464     * @return bool
    31583465     */
    31593466    public function alternativeExists()
    31603467    {
     
    31633470
    31643471    /**
    31653472     * Clear queued addresses of given kind.
    3166      * @access protected
     3473     *
    31673474     * @param string $kind 'to', 'cc', or 'bcc'
    3168      * @return void
    31693475     */
    31703476    public function clearQueuedAddresses($kind)
    31713477    {
    3172         $RecipientsQueue = $this->RecipientsQueue;
    3173         foreach ($RecipientsQueue as $address => $params) {
    3174             if ($params[0] == $kind) {
    3175                 unset($this->RecipientsQueue[$address]);
     3478        $this->RecipientsQueue = array_filter(
     3479            $this->RecipientsQueue,
     3480            function ($params) use ($kind) {
     3481                return $params[0] != $kind;
    31763482            }
    3177         }
     3483        );
    31783484    }
    31793485
    31803486    /**
    31813487     * Clear all To recipients.
    3182      * @return void
    31833488     */
    31843489    public function clearAddresses()
    31853490    {
    31863491        foreach ($this->to as $to) {
    31873492            unset($this->all_recipients[strtolower($to[0])]);
    31883493        }
    3189         $this->to = array();
     3494        $this->to = [];
    31903495        $this->clearQueuedAddresses('to');
    31913496    }
    31923497
    31933498    /**
    31943499     * Clear all CC recipients.
    3195      * @return void
    31963500     */
    31973501    public function clearCCs()
    31983502    {
    31993503        foreach ($this->cc as $cc) {
    32003504            unset($this->all_recipients[strtolower($cc[0])]);
    32013505        }
    3202         $this->cc = array();
     3506        $this->cc = [];
    32033507        $this->clearQueuedAddresses('cc');
    32043508    }
    32053509
    32063510    /**
    32073511     * Clear all BCC recipients.
    3208      * @return void
    32093512     */
    32103513    public function clearBCCs()
    32113514    {
    32123515        foreach ($this->bcc as $bcc) {
    32133516            unset($this->all_recipients[strtolower($bcc[0])]);
    32143517        }
    3215         $this->bcc = array();
     3518        $this->bcc = [];
    32163519        $this->clearQueuedAddresses('bcc');
    32173520    }
    32183521
    32193522    /**
    32203523     * Clear all ReplyTo recipients.
    3221      * @return void
    32223524     */
    32233525    public function clearReplyTos()
    32243526    {
    3225         $this->ReplyTo = array();
    3226         $this->ReplyToQueue = array();
     3527        $this->ReplyTo = [];
     3528        $this->ReplyToQueue = [];
    32273529    }
    32283530
    32293531    /**
    32303532     * Clear all recipient types.
    3231      * @return void
    32323533     */
    32333534    public function clearAllRecipients()
    32343535    {
    3235         $this->to = array();
    3236         $this->cc = array();
    3237         $this->bcc = array();
    3238         $this->all_recipients = array();
    3239         $this->RecipientsQueue = array();
     3536        $this->to = [];
     3537        $this->cc = [];
     3538        $this->bcc = [];
     3539        $this->all_recipients = [];
     3540        $this->RecipientsQueue = [];
    32403541    }
    32413542
    32423543    /**
    32433544     * Clear all filesystem, string, and binary attachments.
    3244      * @return void
    32453545     */
    32463546    public function clearAttachments()
    32473547    {
    3248         $this->attachment = array();
     3548        $this->attachment = [];
    32493549    }
    32503550
    32513551    /**
    32523552     * Clear all custom headers.
    3253      * @return void
    32543553     */
    32553554    public function clearCustomHeaders()
    32563555    {
    3257         $this->CustomHeader = array();
     3556        $this->CustomHeader = [];
    32583557    }
    32593558
    32603559    /**
    32613560     * Add an error message to the error container.
    3262      * @access protected
     3561     *
    32633562     * @param string $msg
    3264      * @return void
    32653563     */
    32663564    protected function setError($msg)
    32673565    {
    3268         $this->error_count++;
    3269         if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
     3566        ++$this->error_count;
     3567        if ('smtp' == $this->Mailer and null !== $this->smtp) {
    32703568            $lasterror = $this->smtp->getError();
    32713569            if (!empty($lasterror['error'])) {
    32723570                $msg .= $this->lang('smtp_error') . $lasterror['error'];
    32733571                if (!empty($lasterror['detail'])) {
    3274                     $msg .= ' Detail: '. $lasterror['detail'];
     3572                    $msg .= ' Detail: ' . $lasterror['detail'];
    32753573                }
    32763574                if (!empty($lasterror['smtp_code'])) {
    32773575                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
     
    32863584
    32873585    /**
    32883586     * Return an RFC 822 formatted date.
    3289      * @access public
     3587     *
    32903588     * @return string
    3291      * @static
    32923589     */
    32933590    public static function rfcDate()
    32943591    {
    32953592        // Set the time zone to whatever the default is to avoid 500 errors
    32963593        // Will default to UTC if it's not set properly in php.ini
    32973594        date_default_timezone_set(@date_default_timezone_get());
     3595
    32983596        return date('D, j M Y H:i:s O');
    32993597    }
    33003598
    33013599    /**
    33023600     * Get the server hostname.
    33033601     * Returns 'localhost.localdomain' if unknown.
    3304      * @access protected
     3602     *
    33053603     * @return string
    33063604     */
    33073605    protected function serverHostname()
    33083606    {
    3309         $result = 'localhost.localdomain';
     3607        $result = '';
    33103608        if (!empty($this->Hostname)) {
    33113609            $result = $this->Hostname;
    3312         } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
     3610        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER)) {
    33133611            $result = $_SERVER['SERVER_NAME'];
    3314         } elseif (function_exists('gethostname') && gethostname() !== false) {
     3612        } elseif (function_exists('gethostname') and gethostname() !== false) {
    33153613            $result = gethostname();
    33163614        } elseif (php_uname('n') !== false) {
    33173615            $result = php_uname('n');
    33183616        }
     3617        if (!static::isValidHost($result)) {
     3618            return 'localhost.localdomain';
     3619        }
     3620
    33193621        return $result;
    33203622    }
    33213623
    33223624    /**
     3625     * Validate whether a string contains a valid value to use as a hostname or IP address.
     3626     * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`.
     3627     *
     3628     * @param string $host The host name or IP address to check
     3629     *
     3630     * @return bool
     3631     */
     3632    public static function isValidHost($host)
     3633    {
     3634        //Simple syntax limits
     3635        if (empty($host)
     3636            or !is_string($host)
     3637            or strlen($host) > 256
     3638        ) {
     3639            return false;
     3640        }
     3641        //Looks like a bracketed IPv6 address
     3642        if (trim($host, '[]') != $host) {
     3643            return (bool) filter_var(trim($host, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
     3644        }
     3645        //If removing all the dots results in a numeric string, it must be an IPv4 address.
     3646        //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
     3647        if (is_numeric(str_replace('.', '', $host))) {
     3648            //Is it a valid IPv4 address?
     3649            return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
     3650        }
     3651        if (filter_var('http://' . $host, FILTER_VALIDATE_URL)) {
     3652            //Is it a syntactically valid hostname?
     3653            return true;
     3654        }
     3655
     3656        return false;
     3657    }
     3658
     3659    /**
    33233660     * Get an error message in the current language.
    3324      * @access protected
     3661     *
    33253662     * @param string $key
     3663     *
    33263664     * @return string
    33273665     */
    33283666    protected function lang($key)
     
    33323670        }
    33333671
    33343672        if (array_key_exists($key, $this->language)) {
    3335             if ($key == 'smtp_connect_failed') {
     3673            if ('smtp_connect_failed' == $key) {
    33363674                //Include a link to troubleshooting docs on SMTP connection failure
    33373675                //this is by far the biggest cause of support questions
    33383676                //but it's usually not PHPMailer's fault.
    33393677                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
    33403678            }
     3679
    33413680            return $this->language[$key];
    3342         } else {
    3343             //Return the key as a fallback
    3344             return $key;
    33453681        }
     3682
     3683        //Return the key as a fallback
     3684        return $key;
    33463685    }
    33473686
    33483687    /**
    33493688     * Check if an error occurred.
    3350      * @access public
    3351      * @return boolean True if an error did occur.
     3689     *
     3690     * @return bool True if an error did occur
    33523691     */
    33533692    public function isError()
    33543693    {
    3355         return ($this->error_count > 0);
    3356     }
    3357 
    3358     /**
    3359      * Ensure consistent line endings in a string.
    3360      * Changes every end of line from CRLF, CR or LF to $this->LE.
    3361      * @access public
    3362      * @param string $str String to fixEOL
    3363      * @return string
    3364      */
    3365     public function fixEOL($str)
    3366     {
    3367         // Normalise to \n
    3368         $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
    3369         // Now convert LE as needed
    3370         if ($this->LE !== "\n") {
    3371             $nstr = str_replace("\n", $this->LE, $nstr);
    3372         }
    3373         return $nstr;
     3694        return $this->error_count > 0;
    33743695    }
    33753696
    33763697    /**
    33773698     * Add a custom header.
    33783699     * $name value can be overloaded to contain
    3379      * both header name and value (name:value)
    3380      * @access public
    3381      * @param string $name Custom header name
    3382      * @param string $value Header value
    3383      * @return void
     3700     * both header name and value (name:value).
     3701     *
     3702     * @param string      $name  Custom header name
     3703     * @param string|null $value Header value
    33843704     */
    33853705    public function addCustomHeader($name, $value = null)
    33863706    {
    3387         if ($value === null) {
     3707        if (null === $value) {
    33883708            // Value passed in as name:value
    33893709            $this->CustomHeader[] = explode(':', $name, 2);
    33903710        } else {
    3391             $this->CustomHeader[] = array($name, $value);
     3711            $this->CustomHeader[] = [$name, $value];
    33923712        }
    33933713    }
    33943714
    33953715    /**
    33963716     * Returns all custom headers.
     3717     *
    33973718     * @return array
    33983719     */
    33993720    public function getCustomHeaders()
     
    34093730     * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
    34103731     * will look for an image file in $basedir/images/a.png and convert it to inline.
    34113732     * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
     3733     * Converts data-uri images into embedded attachments.
    34123734     * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
    3413      * @access public
    3414      * @param string $message HTML message string
    3415      * @param string $basedir Absolute path to a base directory to prepend to relative paths to images
    3416      * @param boolean|callable $advanced Whether to use the internal HTML to text converter
    3417      *    or your own custom converter @see PHPMailer::html2text()
     3735     *
     3736     * @param string        $message  HTML message string
     3737     * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
     3738     * @param bool|callable $advanced Whether to use the internal HTML to text converter
     3739     *                                or your own custom converter @see PHPMailer::html2text()
     3740     *
    34183741     * @return string $message The transformed message Body
    34193742     */
    34203743    public function msgHTML($message, $basedir = '', $advanced = false)
    34213744    {
    34223745        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
    34233746        if (array_key_exists(2, $images)) {
    3424             if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
     3747            if (strlen($basedir) > 1 && '/' != substr($basedir, -1)) {
    34253748                // Ensure $basedir has a trailing /
    34263749                $basedir .= '/';
    34273750            }
    34283751            foreach ($images[2] as $imgindex => $url) {
    34293752                // Convert data URIs into embedded images
    3430                 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
    3431                     $data = substr($url, strpos($url, ','));
    3432                     if ($match[2]) {
    3433                         $data = base64_decode($data);
     3753                //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
     3754                if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
     3755                    if (count($match) == 4 and static::ENCODING_BASE64 == $match[2]) {
     3756                        $data = base64_decode($match[3]);
     3757                    } elseif ('' == $match[2]) {
     3758                        $data = rawurldecode($match[3]);
    34343759                    } else {
    3435                         $data = rawurldecode($data);
     3760                        //Not recognised so leave it alone
     3761                        continue;
    34363762                    }
    3437                     $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
    3438                     if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
    3439                         $message = str_replace(
    3440                             $images[0][$imgindex],
    3441                             $images[1][$imgindex] . '="cid:' . $cid . '"',
    3442                             $message
    3443                         );
     3763                    //Hash the decoded data, not the URL so that the same data-URI image used in multiple places
     3764                    //will only be embedded once, even if it used a different encoding
     3765                    $cid = hash('sha256', $data) . '@phpmailer.0'; // RFC2392 S 2
     3766
     3767                    if (!$this->cidExists($cid)) {
     3768                        $this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, static::ENCODING_BASE64, $match[1]);
    34443769                    }
     3770                    $message = str_replace(
     3771                        $images[0][$imgindex],
     3772                        $images[1][$imgindex] . '="cid:' . $cid . '"',
     3773                        $message
     3774                    );
    34453775                    continue;
    34463776                }
    3447                 if (
    3448                     // Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
     3777                if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
    34493778                    !empty($basedir)
    34503779                    // Ignore URLs containing parent dir traversal (..)
    3451                     && (strpos($url, '..') === false)
     3780                    and (strpos($url, '..') === false)
    34523781                    // Do not change urls that are already inline images
    3453                     && substr($url, 0, 4) !== 'cid:'
     3782                    and 0 !== strpos($url, 'cid:')
    34543783                    // Do not change absolute URLs, including anonymous protocol
    3455                     && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
     3784                    and !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
    34563785                ) {
    34573786                    $filename = basename($url);
    34583787                    $directory = dirname($url);
    3459                     if ($directory == '.') {
     3788                    if ('.' == $directory) {
    34603789                        $directory = '';
    34613790                    }
    3462                     $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
    3463                     if (strlen($directory) > 1 && substr($directory, -1) != '/') {
     3791                    $cid = hash('sha256', $url) . '@phpmailer.0'; // RFC2392 S 2
     3792                    if (strlen($basedir) > 1 and '/' != substr($basedir, -1)) {
     3793                        $basedir .= '/';
     3794                    }
     3795                    if (strlen($directory) > 1 and '/' != substr($directory, -1)) {
    34643796                        $directory .= '/';
    34653797                    }
    34663798                    if ($this->addEmbeddedImage(
    34673799                        $basedir . $directory . $filename,
    34683800                        $cid,
    34693801                        $filename,
    3470                         'base64',
    3471                         self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
     3802                        static::ENCODING_BASE64,
     3803                        static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
    34723804                    )
    34733805                    ) {
    34743806                        $message = preg_replace(
     
    34813813            }
    34823814        }
    34833815        $this->isHTML(true);
    3484         // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
    3485         $this->Body = $this->normalizeBreaks($message);
    3486         $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
     3816        // Convert all message body line breaks to LE, makes quoted-printable encoding work much better
     3817        $this->Body = static::normalizeBreaks($message);
     3818        $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
    34873819        if (!$this->alternativeExists()) {
    3488             $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
    3489                 self::CRLF . self::CRLF;
     3820            $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'
     3821                . static::$LE;
    34903822        }
     3823
    34913824        return $this->Body;
    34923825    }
    34933826
     
    34953828     * Convert an HTML string into plain text.
    34963829     * This is used by msgHTML().
    34973830     * Note - older versions of this function used a bundled advanced converter
    3498      * which was been removed for license reasons in #232.
     3831     * which was removed for license reasons in #232.
    34993832     * Example usage:
    3500      * <code>
     3833     *
     3834     * ```php
    35013835     * // Use default conversion
    35023836     * $plain = $mail->html2text($html);
    35033837     * // Use your own custom converter
     
    35053839     *     $converter = new MyHtml2text($html);
    35063840     *     return $converter->get_text();
    35073841     * });
    3508      * </code>
    3509      * @param string $html The HTML text to convert
    3510      * @param boolean|callable $advanced Any boolean value to use the internal converter,
    3511      *   or provide your own callable for custom conversion.
     3842     * ```
     3843     *
     3844     * @param string        $html     The HTML text to convert
     3845     * @param bool|callable $advanced Any boolean value to use the internal converter,
     3846     *                                or provide your own callable for custom conversion
     3847     *
    35123848     * @return string
    35133849     */
    35143850    public function html2text($html, $advanced = false)
     
    35163852        if (is_callable($advanced)) {
    35173853            return call_user_func($advanced, $html);
    35183854        }
     3855
    35193856        return html_entity_decode(
    35203857            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
    35213858            ENT_QUOTES,
     
    35253862
    35263863    /**
    35273864     * Get the MIME type for a file extension.
     3865     *
    35283866     * @param string $ext File extension
    3529      * @access public
    3530      * @return string MIME type of file.
    3531      * @static
     3867     *
     3868     * @return string MIME type of file
    35323869     */
    35333870    public static function _mime_types($ext = '')
    35343871    {
    3535         $mimes = array(
    3536             'xl'    => 'application/excel',
    3537             'js'    => 'application/javascript',
    3538             'hqx'   => 'application/mac-binhex40',
    3539             'cpt'   => 'application/mac-compactpro',
    3540             'bin'   => 'application/macbinary',
    3541             'doc'   => 'application/msword',
    3542             'word'  => 'application/msword',
    3543             'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    3544             'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
    3545             'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
    3546             'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
    3547             'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    3548             'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
    3549             'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    3550             'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
    3551             'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
    3552             'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
     3872        $mimes = [
     3873            'xl' => 'application/excel',
     3874            'js' => 'application/javascript',
     3875            'hqx' => 'application/mac-binhex40',
     3876            'cpt' => 'application/mac-compactpro',
     3877            'bin' => 'application/macbinary',
     3878            'doc' => 'application/msword',
     3879            'word' => 'application/msword',
     3880            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
     3881            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
     3882            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
     3883            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
     3884            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
     3885            'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
     3886            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
     3887            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
     3888            'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
     3889            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
    35533890            'class' => 'application/octet-stream',
    3554             'dll'   => 'application/octet-stream',
    3555             'dms'   => 'application/octet-stream',
    3556             'exe'   => 'application/octet-stream',
    3557             'lha'   => 'application/octet-stream',
    3558             'lzh'   => 'application/octet-stream',
    3559             'psd'   => 'application/octet-stream',
    3560             'sea'   => 'application/octet-stream',
    3561             'so'    => 'application/octet-stream',
    3562             'oda'   => 'application/oda',
    3563             'pdf'   => 'application/pdf',
    3564             'ai'    => 'application/postscript',
    3565             'eps'   => 'application/postscript',
    3566             'ps'    => 'application/postscript',
    3567             'smi'   => 'application/smil',
    3568             'smil'  => 'application/smil',
    3569             'mif'   => 'application/vnd.mif',
    3570             'xls'   => 'application/vnd.ms-excel',
    3571             'ppt'   => 'application/vnd.ms-powerpoint',
     3891            'dll' => 'application/octet-stream',
     3892            'dms' => 'application/octet-stream',
     3893            'exe' => 'application/octet-stream',
     3894            'lha' => 'application/octet-stream',
     3895            'lzh' => 'application/octet-stream',
     3896            'psd' => 'application/octet-stream',
     3897            'sea' => 'application/octet-stream',
     3898            'so' => 'application/octet-stream',
     3899            'oda' => 'application/oda',
     3900            'pdf' => 'application/pdf',
     3901            'ai' => 'application/postscript',
     3902            'eps' => 'application/postscript',
     3903            'ps' => 'application/postscript',
     3904            'smi' => 'application/smil',
     3905            'smil' => 'application/smil',
     3906            'mif' => 'application/vnd.mif',
     3907            'xls' => 'application/vnd.ms-excel',
     3908            'ppt' => 'application/vnd.ms-powerpoint',
    35723909            'wbxml' => 'application/vnd.wap.wbxml',
    3573             'wmlc'  => 'application/vnd.wap.wmlc',
    3574             'dcr'   => 'application/x-director',
    3575             'dir'   => 'application/x-director',
    3576             'dxr'   => 'application/x-director',
    3577             'dvi'   => 'application/x-dvi',
    3578             'gtar'  => 'application/x-gtar',
    3579             'php3'  => 'application/x-httpd-php',
    3580             'php4'  => 'application/x-httpd-php',
    3581             'php'   => 'application/x-httpd-php',
     3910            'wmlc' => 'application/vnd.wap.wmlc',
     3911            'dcr' => 'application/x-director',
     3912            'dir' => 'application/x-director',
     3913            'dxr' => 'application/x-director',
     3914            'dvi' => 'application/x-dvi',
     3915            'gtar' => 'application/x-gtar',
     3916            'php3' => 'application/x-httpd-php',
     3917            'php4' => 'application/x-httpd-php',
     3918            'php' => 'application/x-httpd-php',
    35823919            'phtml' => 'application/x-httpd-php',
    3583             'phps'  => 'application/x-httpd-php-source',
    3584             'swf'   => 'application/x-shockwave-flash',
    3585             'sit'   => 'application/x-stuffit',
    3586             'tar'   => 'application/x-tar',
    3587             'tgz'   => 'application/x-tar',
    3588             'xht'   => 'application/xhtml+xml',
     3920            'phps' => 'application/x-httpd-php-source',
     3921            'swf' => 'application/x-shockwave-flash',
     3922            'sit' => 'application/x-stuffit',
     3923            'tar' => 'application/x-tar',
     3924            'tgz' => 'application/x-tar',
     3925            'xht' => 'application/xhtml+xml',
    35893926            'xhtml' => 'application/xhtml+xml',
    3590             'zip'   => 'application/zip',
    3591             'mid'   => 'audio/midi',
    3592             'midi'  => 'audio/midi',
    3593             'mp2'   => 'audio/mpeg',
    3594             'mp3'   => 'audio/mpeg',
    3595             'mpga'  => 'audio/mpeg',
    3596             'aif'   => 'audio/x-aiff',
    3597             'aifc'  => 'audio/x-aiff',
    3598             'aiff'  => 'audio/x-aiff',
    3599             'ram'   => 'audio/x-pn-realaudio',
    3600             'rm'    => 'audio/x-pn-realaudio',
    3601             'rpm'   => 'audio/x-pn-realaudio-plugin',
    3602             'ra'    => 'audio/x-realaudio',
    3603             'wav'   => 'audio/x-wav',
    3604             'bmp'   => 'image/bmp',
    3605             'gif'   => 'image/gif',
    3606             'jpeg'  => 'image/jpeg',
    3607             'jpe'   => 'image/jpeg',
    3608             'jpg'   => 'image/jpeg',
    3609             'png'   => 'image/png',
    3610             'tiff'  => 'image/tiff',
    3611             'tif'   => 'image/tiff',
    3612             'eml'   => 'message/rfc822',
    3613             'css'   => 'text/css',
    3614             'html'  => 'text/html',
    3615             'htm'   => 'text/html',
     3927            'zip' => 'application/zip',
     3928            'mid' => 'audio/midi',
     3929            'midi' => 'audio/midi',
     3930            'mp2' => 'audio/mpeg',
     3931            'mp3' => 'audio/mpeg',
     3932            'm4a' => 'audio/mp4',
     3933            'mpga' => 'audio/mpeg',
     3934            'aif' => 'audio/x-aiff',
     3935            'aifc' => 'audio/x-aiff',
     3936            'aiff' => 'audio/x-aiff',
     3937            'ram' => 'audio/x-pn-realaudio',
     3938            'rm' => 'audio/x-pn-realaudio',
     3939            'rpm' => 'audio/x-pn-realaudio-plugin',
     3940            'ra' => 'audio/x-realaudio',
     3941            'wav' => 'audio/x-wav',
     3942            'mka' => 'audio/x-matroska',
     3943            'bmp' => 'image/bmp',
     3944            'gif' => 'image/gif',
     3945            'jpeg' => 'image/jpeg',
     3946            'jpe' => 'image/jpeg',
     3947            'jpg' => 'image/jpeg',
     3948            'png' => 'image/png',
     3949            'tiff' => 'image/tiff',
     3950            'tif' => 'image/tiff',
     3951            'webp' => 'image/webp',
     3952            'heif' => 'image/heif',
     3953            'heifs' => 'image/heif-sequence',
     3954            'heic' => 'image/heic',
     3955            'heics' => 'image/heic-sequence',
     3956            'eml' => 'message/rfc822',
     3957            'css' => 'text/css',
     3958            'html' => 'text/html',
     3959            'htm' => 'text/html',
    36163960            'shtml' => 'text/html',
    3617             'log'   => 'text/plain',
    3618             'text'  => 'text/plain',
    3619             'txt'   => 'text/plain',
    3620             'rtx'   => 'text/richtext',
    3621             'rtf'   => 'text/rtf',
    3622             'vcf'   => 'text/vcard',
     3961            'log' => 'text/plain',
     3962            'text' => 'text/plain',
     3963            'txt' => 'text/plain',
     3964            'rtx' => 'text/richtext',
     3965            'rtf' => 'text/rtf',
     3966            'vcf' => 'text/vcard',
    36233967            'vcard' => 'text/vcard',
    3624             'xml'   => 'text/xml',
    3625             'xsl'   => 'text/xml',
    3626             'mpeg'  => 'video/mpeg',
    3627             'mpe'   => 'video/mpeg',
    3628             'mpg'   => 'video/mpeg',
    3629             'mov'   => 'video/quicktime',
    3630             'qt'    => 'video/quicktime',
    3631             'rv'    => 'video/vnd.rn-realvideo',
    3632             'avi'   => 'video/x-msvideo',
    3633             'movie' => 'video/x-sgi-movie'
    3634         );
    3635         if (array_key_exists(strtolower($ext), $mimes)) {
    3636             return $mimes[strtolower($ext)];
     3968            'ics' => 'text/calendar',
     3969            'xml' => 'text/xml',
     3970            'xsl' => 'text/xml',
     3971            'wmv' => 'video/x-ms-wmv',
     3972            'mpeg' => 'video/mpeg',
     3973            'mpe' => 'video/mpeg',
     3974            'mpg' => 'video/mpeg',
     3975            'mp4' => 'video/mp4',
     3976            'm4v' => 'video/mp4',
     3977            'mov' => 'video/quicktime',
     3978            'qt' => 'video/quicktime',
     3979            'rv' => 'video/vnd.rn-realvideo',
     3980            'avi' => 'video/x-msvideo',
     3981            'movie' => 'video/x-sgi-movie',
     3982            'webm' => 'video/webm',
     3983            'mkv' => 'video/x-matroska',
     3984        ];
     3985        $ext = strtolower($ext);
     3986        if (array_key_exists($ext, $mimes)) {
     3987            return $mimes[$ext];
    36373988        }
     3989
    36383990        return 'application/octet-stream';
    36393991    }
    36403992
    36413993    /**
    36423994     * Map a file name to a MIME type.
    36433995     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
     3996     *
    36443997     * @param string $filename A file name or full path, does not need to exist as a file
     3998     *
    36453999     * @return string
    3646      * @static
    36474000     */
    36484001    public static function filenameToType($filename)
    36494002    {
     
    36524005        if (false !== $qpos) {
    36534006            $filename = substr($filename, 0, $qpos);
    36544007        }
    3655         $pathinfo = self::mb_pathinfo($filename);
    3656         return self::_mime_types($pathinfo['extension']);
     4008        $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION);
     4009
     4010        return static::_mime_types($ext);
    36574011    }
    36584012
    36594013    /**
    36604014     * Multi-byte-safe pathinfo replacement.
    3661      * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
    3662      * Works similarly to the one in PHP >= 5.2.0
    3663      * @link http://www.php.net/manual/en/function.pathinfo.php#107461
    3664      * @param string $path A filename or path, does not need to exist as a file
    3665      * @param integer|string $options Either a PATHINFO_* constant,
    3666      *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
     4015     * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
     4016     *
     4017     * @see    http://www.php.net/manual/en/function.pathinfo.php#107461
     4018     *
     4019     * @param string     $path    A filename or path, does not need to exist as a file
     4020     * @param int|string $options Either a PATHINFO_* constant,
     4021     *                            or a string name to return only the specified piece
     4022     *
    36674023     * @return string|array
    3668      * @static
    36694024     */
    36704025    public static function mb_pathinfo($path, $options = null)
    36714026    {
    3672         $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
    3673         $pathinfo = array();
    3674         if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
     4027        $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
     4028        $pathinfo = [];
     4029        if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$#im', $path, $pathinfo)) {
    36754030            if (array_key_exists(1, $pathinfo)) {
    36764031                $ret['dirname'] = $pathinfo[1];
    36774032            }
     
    37104065     * Usage Example:
    37114066     * `$mail->set('SMTPSecure', 'tls');`
    37124067     *   is the same as:
    3713      * `$mail->SMTPSecure = 'tls';`
    3714      * @access public
    3715      * @param string $name The property name to set
    3716      * @param mixed $value The value to set the property to
    3717      * @return boolean
    3718      * @TODO Should this not be using the __set() magic function?
     4068     * `$mail->SMTPSecure = 'tls';`.
     4069     *
     4070     * @param string $name  The property name to set
     4071     * @param mixed  $value The value to set the property to
     4072     *
     4073     * @return bool
    37194074     */
    37204075    public function set($name, $value = '')
    37214076    {
    37224077        if (property_exists($this, $name)) {
    37234078            $this->$name = $value;
     4079
    37244080            return true;
    3725         } else {
    3726             $this->setError($this->lang('variable_set') . $name);
    3727             return false;
    37284081        }
     4082        $this->setError($this->lang('variable_set') . $name);
     4083
     4084        return false;
    37294085    }
    37304086
    37314087    /**
    37324088     * Strip newlines to prevent header injection.
    3733      * @access public
     4089     *
    37344090     * @param string $str
     4091     *
    37354092     * @return string
    37364093     */
    37374094    public function secureHeader($str)
    37384095    {
    3739         return trim(str_replace(array("\r", "\n"), '', $str));
     4096        return trim(str_replace(["\r", "\n"], '', $str));
    37404097    }
    37414098
    37424099    /**
    37434100     * Normalize line breaks in a string.
    37444101     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
    37454102     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
     4103     *
    37464104     * @param string $text
    3747      * @param string $breaktype What kind of line break to use, defaults to CRLF
     4105     * @param string $breaktype What kind of line break to use; defaults to static::$LE
     4106     *
     4107     * @return string
     4108     */
     4109    public static function normalizeBreaks($text, $breaktype = null)
     4110    {
     4111        if (null === $breaktype) {
     4112            $breaktype = static::$LE;
     4113        }
     4114        // Normalise to \n
     4115        $text = str_replace(["\r\n", "\r"], "\n", $text);
     4116        // Now convert LE as needed
     4117        if ("\n" !== $breaktype) {
     4118            $text = str_replace("\n", $breaktype, $text);
     4119        }
     4120
     4121        return $text;
     4122    }
     4123
     4124    /**
     4125     * Return the current line break format string.
     4126     *
    37484127     * @return string
    3749      * @access public
    3750      * @static
    37514128     */
    3752     public static function normalizeBreaks($text, $breaktype = "\r\n")
     4129    public static function getLE()
     4130    {
     4131        return static::$LE;
     4132    }
     4133
     4134    /**
     4135     * Set the line break format string, e.g. "\r\n".
     4136     *
     4137     * @param string $le
     4138     */
     4139    protected static function setLE($le)
    37534140    {
    3754         return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
     4141        static::$LE = $le;
    37554142    }
    37564143
    37574144    /**
    37584145     * Set the public and private key files and password for S/MIME signing.
    3759      * @access public
     4146     *
    37604147     * @param string $cert_filename
    37614148     * @param string $key_filename
    3762      * @param string $key_pass Password for private key
     4149     * @param string $key_pass            Password for private key
    37634150     * @param string $extracerts_filename Optional path to chain certificate
    37644151     */
    37654152    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
     
    37724159
    37734160    /**
    37744161     * Quoted-Printable-encode a DKIM header.
    3775      * @access public
     4162     *
    37764163     * @param string $txt
     4164     *
    37774165     * @return string
    37784166     */
    37794167    public function DKIM_QP($txt)
    37804168    {
    37814169        $line = '';
    3782         for ($i = 0; $i < strlen($txt); $i++) {
     4170        $len = strlen($txt);
     4171        for ($i = 0; $i < $len; ++$i) {
    37834172            $ord = ord($txt[$i]);
    3784             if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
     4173            if (((0x21 <= $ord) and ($ord <= 0x3A)) or $ord == 0x3C or ((0x3E <= $ord) and ($ord <= 0x7E))) {
    37854174                $line .= $txt[$i];
    37864175            } else {
    37874176                $line .= '=' . sprintf('%02X', $ord);
    37884177            }
    37894178        }
     4179
    37904180        return $line;
    37914181    }
    37924182
    37934183    /**
    37944184     * Generate a DKIM signature.
    3795      * @access public
     4185     *
    37964186     * @param string $signHeader
    3797      * @throws phpmailerException
     4187     *
     4188     * @throws \Exception
     4189     *
    37984190     * @return string The DKIM signature value
    37994191     */
    38004192    public function DKIM_Sign($signHeader)
    38014193    {
    38024194        if (!defined('PKCS7_TEXT')) {
    38034195            if ($this->exceptions) {
    3804                 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
     4196                throw new \Exception($this->lang('extension_missing') . 'openssl');
    38054197            }
     4198
    38064199            return '';
    38074200        }
    3808         $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
     4201        $privKeyStr = !empty($this->DKIM_private_string) ?
     4202            $this->DKIM_private_string :
     4203            file_get_contents($this->DKIM_private);
    38094204        if ('' != $this->DKIM_passphrase) {
    38104205            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
    38114206        } else {
    38124207            $privKey = openssl_pkey_get_private($privKeyStr);
    38134208        }
    3814         //Workaround for missing digest algorithms in old PHP & OpenSSL versions
    3815         //@link http://stackoverflow.com/a/11117338/333340
    3816         if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
    3817             in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
    3818             if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
    3819                 openssl_pkey_free($privKey);
    3820                 return base64_encode($signature);
    3821             }
    3822         } else {
    3823             $pinfo = openssl_pkey_get_details($privKey);
    3824             $hash = hash('sha256', $signHeader);
    3825             //'Magic' constant for SHA256 from RFC3447
    3826             //@link https://tools.ietf.org/html/rfc3447#page-43
    3827             $t = '3031300d060960864801650304020105000420' . $hash;
    3828             $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
    3829             $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
    3830 
    3831             if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
    3832                 openssl_pkey_free($privKey);
    3833                 return base64_encode($signature);
    3834             }
     4209        if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
     4210            openssl_pkey_free($privKey);
     4211
     4212            return base64_encode($signature);
    38354213        }
    38364214        openssl_pkey_free($privKey);
     4215
    38374216        return '';
    38384217    }
    38394218
    38404219    /**
    38414220     * Generate a DKIM canonicalization header.
    3842      * @access public
     4221     * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
     4222     * Canonicalized headers should *always* use CRLF, regardless of mailer setting.
     4223     *
     4224     * @see    https://tools.ietf.org/html/rfc6376#section-3.4.2
     4225     *
    38434226     * @param string $signHeader Header
     4227     *
    38444228     * @return string
    38454229     */
    38464230    public function DKIM_HeaderC($signHeader)
    38474231    {
    3848         $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
     4232        //Unfold all header continuation lines
     4233        //Also collapses folded whitespace.
     4234        //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
     4235        //@see https://tools.ietf.org/html/rfc5322#section-2.2
     4236        //That means this may break if you do something daft like put vertical tabs in your headers.
     4237        $signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
    38494238        $lines = explode("\r\n", $signHeader);
    38504239        foreach ($lines as $key => $line) {
     4240            //If the header is missing a :, skip it as it's invalid
     4241            //This is likely to happen because the explode() above will also split
     4242            //on the trailing LE, leaving an empty line
     4243            if (strpos($line, ':') === false) {
     4244                continue;
     4245            }
    38514246            list($heading, $value) = explode(':', $line, 2);
     4247            //Lower-case header name
    38524248            $heading = strtolower($heading);
    3853             $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
    3854             $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
     4249            //Collapse white space within the value
     4250            $value = preg_replace('/[ \t]{2,}/', ' ', $value);
     4251            //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value
     4252            //But then says to delete space before and after the colon.
     4253            //Net result is the same as trimming both ends of the value.
     4254            //by elimination, the same applies to the field name
     4255            $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t");
    38554256        }
    3856         $signHeader = implode("\r\n", $lines);
    3857         return $signHeader;
     4257
     4258        return implode("\r\n", $lines);
    38584259    }
    38594260
    38604261    /**
    38614262     * Generate a DKIM canonicalization body.
    3862      * @access public
     4263     * Uses the 'simple' algorithm from RFC6376 section 3.4.3.
     4264     * Canonicalized bodies should *always* use CRLF, regardless of mailer setting.
     4265     *
     4266     * @see    https://tools.ietf.org/html/rfc6376#section-3.4.3
     4267     *
    38634268     * @param string $body Message Body
     4269     *
    38644270     * @return string
    38654271     */
    38664272    public function DKIM_BodyC($body)
    38674273    {
    3868         if ($body == '') {
     4274        if (empty($body)) {
    38694275            return "\r\n";
    38704276        }
    3871         // stabilize line endings
    3872         $body = str_replace("\r\n", "\n", $body);
    3873         $body = str_replace("\n", "\r\n", $body);
    3874         // END stabilize line endings
    3875         while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
    3876             $body = substr($body, 0, strlen($body) - 2);
    3877         }
    3878         return $body;
     4277        // Normalize line endings to CRLF
     4278        $body = static::normalizeBreaks($body, "\r\n");
     4279
     4280        //Reduce multiple trailing line breaks to a single one
     4281        return rtrim($body, "\r\n") . "\r\n";
    38794282    }
    38804283
    38814284    /**
    38824285     * Create the DKIM header and body in a new message header.
    3883      * @access public
     4286     *
    38844287     * @param string $headers_line Header lines
    3885      * @param string $subject Subject
    3886      * @param string $body Body
     4288     * @param string $subject      Subject
     4289     * @param string $body         Body
     4290     *
    38874291     * @return string
    38884292     */
    38894293    public function DKIM_Add($headers_line, $subject, $body)
     
    38934297        $DKIMquery = 'dns/txt'; // Query method
    38944298        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
    38954299        $subject_header = "Subject: $subject";
    3896         $headers = explode($this->LE, $headers_line);
     4300        $headers = explode(static::$LE, $headers_line);
    38974301        $from_header = '';
    38984302        $to_header = '';
    38994303        $date_header = '';
    39004304        $current = '';
     4305        $copiedHeaderFields = '';
     4306        $foundExtraHeaders = [];
     4307        $extraHeaderKeys = '';
     4308        $extraHeaderValues = '';
     4309        $extraCopyHeaderFields = '';
    39014310        foreach ($headers as $header) {
    39024311            if (strpos($header, 'From:') === 0) {
    39034312                $from_header = $header;
     
    39084317            } elseif (strpos($header, 'Date:') === 0) {
    39094318                $date_header = $header;
    39104319                $current = 'date_header';
     4320            } elseif (!empty($this->DKIM_extraHeaders)) {
     4321                foreach ($this->DKIM_extraHeaders as $extraHeader) {
     4322                    if (strpos($header, $extraHeader . ':') === 0) {
     4323                        $headerValue = $header;
     4324                        foreach ($this->CustomHeader as $customHeader) {
     4325                            if ($customHeader[0] === $extraHeader) {
     4326                                $headerValue = trim($customHeader[0]) .
     4327                                               ': ' .
     4328                                               $this->encodeHeader(trim($customHeader[1]));
     4329                                break;
     4330                            }
     4331                        }
     4332                        $foundExtraHeaders[$extraHeader] = $headerValue;
     4333                        $current = '';
     4334                        break;
     4335                    }
     4336                }
    39114337            } else {
    3912                 if (!empty($$current) && strpos($header, ' =?') === 0) {
     4338                if (!empty($$current) and strpos($header, ' =?') === 0) {
    39134339                    $$current .= $header;
    39144340                } else {
    39154341                    $current = '';
    39164342                }
    39174343            }
    39184344        }
    3919         $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
    3920         $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
    3921         $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
    3922         $subject = str_replace(
    3923             '|',
    3924             '=7C',
    3925             $this->DKIM_QP($subject_header)
    3926         ); // Copied header fields (dkim-quoted-printable)
     4345        foreach ($foundExtraHeaders as $key => $value) {
     4346            $extraHeaderKeys .= ':' . $key;
     4347            $extraHeaderValues .= $value . "\r\n";
     4348            if ($this->DKIM_copyHeaderFields) {
     4349                $extraCopyHeaderFields .= "\t|" . str_replace('|', '=7C', $this->DKIM_QP($value)) . ";\r\n";
     4350            }
     4351        }
     4352        if ($this->DKIM_copyHeaderFields) {
     4353            $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
     4354            $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
     4355            $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
     4356            $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header));
     4357            $copiedHeaderFields = "\tz=$from\r\n" .
     4358                                  "\t|$to\r\n" .
     4359                                  "\t|$date\r\n" .
     4360                                  "\t|$subject;\r\n" .
     4361                                  $extraCopyHeaderFields;
     4362        }
    39274363        $body = $this->DKIM_BodyC($body);
    39284364        $DKIMlen = strlen($body); // Length of body
    39294365        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
     
    39394375            $this->DKIM_selector .
    39404376            ";\r\n" .
    39414377            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
    3942             "\th=From:To:Date:Subject;\r\n" .
     4378            "\th=From:To:Date:Subject" . $extraHeaderKeys . ";\r\n" .
    39434379            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
    3944             "\tz=$from\r\n" .
    3945             "\t|$to\r\n" .
    3946             "\t|$date\r\n" .
    3947             "\t|$subject;\r\n" .
     4380            $copiedHeaderFields .
    39484381            "\tbh=" . $DKIMb64 . ";\r\n" .
    39494382            "\tb=";
    39504383        $toSign = $this->DKIM_HeaderC(
     
    39524385            $to_header . "\r\n" .
    39534386            $date_header . "\r\n" .
    39544387            $subject_header . "\r\n" .
     4388            $extraHeaderValues .
    39554389            $dkimhdrs
    39564390        );
    39574391        $signed = $this->DKIM_Sign($toSign);
    3958         return $dkimhdrs . $signed . "\r\n";
     4392
     4393        return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE;
    39594394    }
    39604395
    39614396    /**
    3962      * Detect if a string contains a line longer than the maximum line length allowed.
     4397     * Detect if a string contains a line longer than the maximum line length
     4398     * allowed by RFC 2822 section 2.1.1.
     4399     *
    39634400     * @param string $str
    3964      * @return boolean
    3965      * @static
     4401     *
     4402     * @return bool
    39664403     */
    39674404    public static function hasLineLongerThanMax($str)
    39684405    {
    3969         //+2 to include CRLF line break for a 1000 total
    3970         return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
     4406        return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str);
    39714407    }
    39724408
    39734409    /**
    39744410     * Allows for public read access to 'to' property.
    3975      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
    3976      * @access public
     4411     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
     4412     *
    39774413     * @return array
    39784414     */
    39794415    public function getToAddresses()
     
    39834419
    39844420    /**
    39854421     * Allows for public read access to 'cc' property.
    3986      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
    3987      * @access public
     4422     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
     4423     *
    39884424     * @return array
    39894425     */
    39904426    public function getCcAddresses()
     
    39944430
    39954431    /**
    39964432     * Allows for public read access to 'bcc' property.
    3997      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
    3998      * @access public
     4433     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
     4434     *
    39994435     * @return array
    40004436     */
    40014437    public function getBccAddresses()
     
    40054441
    40064442    /**
    40074443     * Allows for public read access to 'ReplyTo' property.
    4008      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
    4009      * @access public
     4444     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
     4445     *
    40104446     * @return array
    40114447     */
    40124448    public function getReplyToAddresses()
     
    40164452
    40174453    /**
    40184454     * Allows for public read access to 'all_recipients' property.
    4019      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
    4020      * @access public
     4455     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
     4456     *
    40214457     * @return array
    40224458     */
    40234459    public function getAllRecipientAddresses()
     
    40274463
    40284464    /**
    40294465     * Perform a callback.
    4030      * @param boolean $isSent
    4031      * @param array $to
    4032      * @param array $cc
    4033      * @param array $bcc
     4466     *
     4467     * @param bool   $isSent
     4468     * @param array  $to
     4469     * @param array  $cc
     4470     * @param array  $bcc
    40344471     * @param string $subject
    40354472     * @param string $body
    40364473     * @param string $from
     4474     * @param array  $extra
    40374475     */
    4038     protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
     4476    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra)
    40394477    {
    4040         if (!empty($this->action_function) && is_callable($this->action_function)) {
    4041             $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
    4042             call_user_func_array($this->action_function, $params);
     4478        if (!empty($this->action_function) and is_callable($this->action_function)) {
     4479            call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra);
    40434480        }
    40444481    }
    4045 }
    40464482
    4047 /**
    4048  * PHPMailer exception handler
    4049  * @package PHPMailer
    4050  */
    4051 class phpmailerException extends Exception
    4052 {
    40534483    /**
    4054      * Prettify error message output
    4055      * @return string
     4484     * Get the OAuth instance.
     4485     *
     4486     * @return OAuth
     4487     */
     4488    public function getOAuth()
     4489    {
     4490        return $this->oauth;
     4491    }
     4492
     4493    /**
     4494     * Set an OAuth instance.
     4495     *
     4496     * @param OAuth $oauth
    40564497     */
    4057     public function errorMessage()
     4498    public function setOAuth(OAuth $oauth)
    40584499    {
    4059         $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
    4060         return $errorMsg;
     4500        $this->oauth = $oauth;
    40614501    }
    40624502}
  • src/wp-includes/class-smtp.php

     
    11<?php
    22/**
    33 * PHPMailer RFC821 SMTP email transport class.
    4  * PHP Version 5
    5  * @package PHPMailer
    6  * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
    7  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    8  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    9  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    10  * @author Brent R. Matzelle (original founder)
    11  * @copyright 2014 Marcus Bointon
     4 * PHP Version 5.5.
     5 *
     6 * @see       https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
     7 *
     8 * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
     9 * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
     10 * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     11 * @author    Brent R. Matzelle (original founder)
     12 * @copyright 2012 - 2017 Marcus Bointon
    1213 * @copyright 2010 - 2012 Jim Jagielski
    1314 * @copyright 2004 - 2009 Andy Prevost
    14  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
    15  * @note This program is distributed in the hope that it will be useful - WITHOUT
     15 * @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
    1617 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1718 * FITNESS FOR A PARTICULAR PURPOSE.
    1819 */
    1920
     21namespace PHPMailer\PHPMailer;
     22
    2023/**
    2124 * PHPMailer RFC821 SMTP email transport class.
    2225 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
    23  * @package PHPMailer
    24  * @author Chris Ryan
    25  * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
     26 *
     27 * @author  Chris Ryan
     28 * @author  Marcus Bointon <phpmailer@synchromedia.co.uk>
    2629 */
    2730class SMTP
    2831{
    2932    /**
    3033     * The PHPMailer SMTP version number.
     34     *
    3135     * @var string
    3236     */
    33     const VERSION = '5.2.27';
     37    const VERSION = '6.0.7';
    3438
    3539    /**
    3640     * SMTP line break constant.
     41     *
    3742     * @var string
    3843     */
    39     const CRLF = "\r\n";
     44    const LE = "\r\n";
    4045
    4146    /**
    4247     * The SMTP port to use if one is not specified.
    43      * @var integer
     48     *
     49     * @var int
    4450     */
    45     const DEFAULT_SMTP_PORT = 25;
     51    const DEFAULT_PORT = 25;
    4652
    4753    /**
    48      * The maximum line length allowed by RFC 2822 section 2.1.1
    49      * @var integer
     54     * The maximum line length allowed by RFC 2822 section 2.1.1.
     55     *
     56     * @var int
    5057     */
    5158    const MAX_LINE_LENGTH = 998;
    5259
    5360    /**
    54      * Debug level for no output
     61     * Debug level for no output.
    5562     */
    5663    const DEBUG_OFF = 0;
    5764
    5865    /**
    59      * Debug level to show client -> server messages
     66     * Debug level to show client -> server messages.
    6067     */
    6168    const DEBUG_CLIENT = 1;
    6269
    6370    /**
    64      * Debug level to show client -> server and server -> client messages
     71     * Debug level to show client -> server and server -> client messages.
    6572     */
    6673    const DEBUG_SERVER = 2;
    6774
    6875    /**
    69      * Debug level to show connection status, client -> server and server -> client messages
     76     * Debug level to show connection status, client -> server and server -> client messages.
    7077     */
    7178    const DEBUG_CONNECTION = 3;
    7279
    7380    /**
    74      * Debug level to show all messages
     81     * Debug level to show all messages.
    7582     */
    7683    const DEBUG_LOWLEVEL = 4;
    7784
    7885    /**
    79      * The PHPMailer SMTP Version number.
    80      * @var string
    81      * @deprecated Use the `VERSION` constant instead
    82      * @see SMTP::VERSION
    83      */
    84     public $Version = '5.2.27';
    85 
    86     /**
    87      * SMTP server port number.
    88      * @var integer
    89      * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
    90      * @see SMTP::DEFAULT_SMTP_PORT
    91      */
    92     public $SMTP_PORT = 25;
    93 
    94     /**
    95      * SMTP reply line ending.
    96      * @var string
    97      * @deprecated Use the `CRLF` constant instead
    98      * @see SMTP::CRLF
    99      */
    100     public $CRLF = "\r\n";
    101 
    102     /**
    10386     * Debug output level.
    10487     * Options:
    10588     * * self::DEBUG_OFF (`0`) No debug output, default
    10689     * * self::DEBUG_CLIENT (`1`) Client commands
    10790     * * self::DEBUG_SERVER (`2`) Client commands and server responses
    10891     * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
    109      * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
    110      * @var integer
     92     * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages.
     93     *
     94     * @var int
    11195     */
    11296    public $do_debug = self::DEBUG_OFF;
    11397
     
    117101     * * `echo` Output plain-text as-is, appropriate for CLI
    118102     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
    119103     * * `error_log` Output to error log as configured in php.ini
    120      *
    121104     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
    122      * <code>
     105     *
     106     * ```php
    123107     * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
    124      * </code>
    125      * @var string|callable
     108     * ```
     109     *
     110     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
     111     * level output is used:
     112     *
     113     * ```php
     114     * $mail->Debugoutput = new myPsr3Logger;
     115     * ```
     116     *
     117     * @var string|callable|\Psr\Log\LoggerInterface
    126118     */
    127119    public $Debugoutput = 'echo';
    128120
    129121    /**
    130122     * Whether to use VERP.
    131      * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
    132      * @link http://www.postfix.org/VERP_README.html Info on VERP
    133      * @var boolean
     123     *
     124     * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
     125     * @see http://www.postfix.org/VERP_README.html Info on VERP
     126     *
     127     * @var bool
    134128     */
    135129    public $do_verp = false;
    136130
    137131    /**
    138132     * The timeout value for connection, in seconds.
    139      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
     133     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
    140134     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
    141      * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
    142      * @var integer
     135     *
     136     * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
     137     *
     138     * @var int
    143139     */
    144140    public $Timeout = 300;
    145141
    146142    /**
    147143     * How long to wait for commands to complete, in seconds.
    148      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
    149      * @var integer
     144     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
     145     *
     146     * @var int
    150147     */
    151148    public $Timelimit = 300;
    152149
    153150    /**
    154      * @var array Patterns to extract an SMTP transaction id from reply to a DATA command.
     151     * Patterns to extract an SMTP transaction id from reply to a DATA command.
    155152     * The first capture group in each regex will be used as the ID.
     153     * MS ESMTP returns the message ID, which may not be correct for internal tracking.
     154     *
     155     * @var string[]
    156156     */
    157     protected $smtp_transaction_id_patterns = array(
    158         'exim' => '/[0-9]{3} OK id=(.*)/',
    159         'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
    160         'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/'
    161     );
     157    protected $smtp_transaction_id_patterns = [
     158        'exim' => '/[\d]{3} OK id=(.*)/',
     159        'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/',
     160        'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/',
     161        'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/',
     162        'Amazon_SES' => '/[\d]{3} Ok (.*)/',
     163        'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
     164        'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
     165    ];
    162166
    163167    /**
    164      * @var string The last transaction ID issued in response to a DATA command,
    165      * if one was detected
     168     * The last transaction ID issued in response to a DATA command,
     169     * if one was detected.
     170     *
     171     * @var string|bool|null
    166172     */
    167173    protected $last_smtp_transaction_id;
    168174
    169175    /**
    170176     * The socket for the server connection.
    171      * @var resource
     177     *
     178     * @var ?resource
    172179     */
    173180    protected $smtp_conn;
    174181
    175182    /**
    176183     * Error information, if any, for the last SMTP command.
     184     *
    177185     * @var array
    178186     */
    179     protected $error = array(
     187    protected $error = [
    180188        'error' => '',
    181189        'detail' => '',
    182190        'smtp_code' => '',
    183         'smtp_code_ex' => ''
    184     );
     191        'smtp_code_ex' => '',
     192    ];
    185193
    186194    /**
    187195     * The reply the server sent to us for HELO.
    188196     * If null, no HELO string has yet been received.
     197     *
    189198     * @var string|null
    190199     */
    191200    protected $helo_rply = null;
     
    197206     * represents the server name. In case of HELO it is the only element of the array.
    198207     * Other values can be boolean TRUE or an array containing extension options.
    199208     * If null, no HELO/EHLO string has yet been received.
     209     *
    200210     * @var array|null
    201211     */
    202212    protected $server_caps = null;
    203213
    204214    /**
    205215     * The most recent reply received from the server.
     216     *
    206217     * @var string
    207218     */
    208219    protected $last_reply = '';
    209220
    210221    /**
    211222     * Output debugging info via a user-selected method.
     223     *
     224     * @param string $str   Debug string to output
     225     * @param int    $level The debug level of this message; see DEBUG_* constants
     226     *
    212227     * @see SMTP::$Debugoutput
    213228     * @see SMTP::$do_debug
    214      * @param string $str Debug string to output
    215      * @param integer $level The debug level of this message; see DEBUG_* constants
    216      * @return void
    217229     */
    218230    protected function edebug($str, $level = 0)
    219231    {
    220232        if ($level > $this->do_debug) {
    221233            return;
    222234        }
     235        //Is this a PSR-3 logger?
     236        if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
     237            $this->Debugoutput->debug($str);
     238
     239            return;
     240        }
    223241        //Avoid clash with built-in function names
    224         if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
     242        if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
    225243            call_user_func($this->Debugoutput, $str, $level);
     244
    226245            return;
    227246        }
    228247        switch ($this->Debugoutput) {
     
    232251                break;
    233252            case 'html':
    234253                //Cleans up output a bit for a better looking, HTML-safe output
    235                 echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities(
     254                echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
    236255                    preg_replace('/[\r\n]+/', '', $str),
    237256                    ENT_QUOTES,
    238257                    'UTF-8'
    239                 ) . "<br>\n";
     258                ), "<br>\n";
    240259                break;
    241260            case 'echo':
    242261            default:
    243262                //Normalize line breaks
    244                 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
    245                 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
    246                     "\n",
    247                     "\n                   \t                  ",
    248                     trim($str)
    249                 ) . "\n";
     263                $str = preg_replace('/\r\n|\r/ms', "\n", $str);
     264                echo gmdate('Y-m-d H:i:s'),
     265                "\t",
     266                    //Trim trailing space
     267                trim(
     268                //Indent for readability, except for trailing break
     269                    str_replace(
     270                        "\n",
     271                        "\n                   \t                  ",
     272                        trim($str)
     273                    )
     274                ),
     275                "\n";
    250276        }
    251277    }
    252278
    253279    /**
    254280     * Connect to an SMTP server.
    255      * @param string $host SMTP server IP or host name
    256      * @param integer $port The port number to connect to
    257      * @param integer $timeout How long to wait for the connection to open
    258      * @param array $options An array of options for stream_context_create()
    259      * @access public
    260      * @return boolean
     281     *
     282     * @param string $host    SMTP server IP or host name
     283     * @param int    $port    The port number to connect to
     284     * @param int    $timeout How long to wait for the connection to open
     285     * @param array  $options An array of options for stream_context_create()
     286     *
     287     * @return bool
    261288     */
    262     public function connect($host, $port = null, $timeout = 30, $options = array())
     289    public function connect($host, $port = null, $timeout = 30, $options = [])
    263290    {
    264291        static $streamok;
    265292        //This is enabled by default since 5.0.0 but some providers disable it
    266293        //Check this once and cache the result
    267         if (is_null($streamok)) {
     294        if (null === $streamok) {
    268295            $streamok = function_exists('stream_socket_client');
    269296        }
    270297        // Clear errors to avoid confusion
     
    273300        if ($this->connected()) {
    274301            // Already connected, generate error
    275302            $this->setError('Already connected to a server');
     303
    276304            return false;
    277305        }
    278306        if (empty($port)) {
    279             $port = self::DEFAULT_SMTP_PORT;
     307            $port = self::DEFAULT_PORT;
    280308        }
    281309        // Connect to the SMTP server
    282310        $this->edebug(
    283311            "Connection: opening to $host:$port, timeout=$timeout, options=" .
    284             var_export($options, true),
     312            (count($options) > 0 ? var_export($options, true) : 'array()'),
    285313            self::DEBUG_CONNECTION
    286314        );
    287315        $errno = 0;
    288316        $errstr = '';
    289317        if ($streamok) {
    290318            $socket_context = stream_context_create($options);
    291             set_error_handler(array($this, 'errorHandler'));
     319            set_error_handler([$this, 'errorHandler']);
    292320            $this->smtp_conn = stream_socket_client(
    293                 $host . ":" . $port,
     321                $host . ':' . $port,
    294322                $errno,
    295323                $errstr,
    296324                $timeout,
     
    301329        } else {
    302330            //Fall back to fsockopen which should work in more places, but is missing some features
    303331            $this->edebug(
    304                 "Connection: stream_socket_client not available, falling back to fsockopen",
     332                'Connection: stream_socket_client not available, falling back to fsockopen',
    305333                self::DEBUG_CONNECTION
    306334            );
    307             set_error_handler(array($this, 'errorHandler'));
     335            set_error_handler([$this, 'errorHandler']);
    308336            $this->smtp_conn = fsockopen(
    309337                $host,
    310338                $port,
     
    318346        if (!is_resource($this->smtp_conn)) {
    319347            $this->setError(
    320348                'Failed to connect to server',
    321                 $errno,
    322                 $errstr
     349                '',
     350                (string) $errno,
     351                (string) $errstr
    323352            );
    324353            $this->edebug(
    325354                'SMTP ERROR: ' . $this->error['error']
    326355                . ": $errstr ($errno)",
    327356                self::DEBUG_CLIENT
    328357            );
     358
    329359            return false;
    330360        }
    331361        $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
     
    334364        if (substr(PHP_OS, 0, 3) != 'WIN') {
    335365            $max = ini_get('max_execution_time');
    336366            // Don't bother if unlimited
    337             if ($max != 0 && $timeout > $max) {
     367            if (0 != $max and $timeout > $max) {
    338368                @set_time_limit($timeout);
    339369            }
    340370            stream_set_timeout($this->smtp_conn, $timeout, 0);
     
    342372        // Get any announcement
    343373        $announce = $this->get_lines();
    344374        $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
     375
    345376        return true;
    346377    }
    347378
    348379    /**
    349380     * Initiate a TLS (encrypted) session.
    350      * @access public
    351      * @return boolean
     381     *
     382     * @return bool
    352383     */
    353384    public function startTLS()
    354385    {
     
    367398        }
    368399
    369400        // Begin encrypted connection
    370         set_error_handler(array($this, 'errorHandler'));
     401        set_error_handler([$this, 'errorHandler']);
    371402        $crypto_ok = stream_socket_enable_crypto(
    372403            $this->smtp_conn,
    373404            true,
    374405            $crypto_method
    375406        );
    376407        restore_error_handler();
    377         return $crypto_ok;
     408
     409        return (bool) $crypto_ok;
    378410    }
    379411
    380412    /**
    381413     * Perform SMTP authentication.
    382414     * Must be run after hello().
    383      * @see hello()
     415     *
     416     * @see    hello()
     417     *
    384418     * @param string $username The user name
    385419     * @param string $password The password
    386      * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5)
    387      * @param string $realm The auth realm for NTLM
    388      * @param string $workstation The auth workstation for NTLM
    389      * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
    390      * @return bool True if successfully authenticated.* @access public
     420     * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
     421     * @param OAuth  $OAuth    An optional OAuth instance for XOAUTH2 authentication
     422     *
     423     * @return bool True if successfully authenticated
    391424     */
    392425    public function authenticate(
    393426        $username,
    394427        $password,
    395428        $authtype = null,
    396         $realm = '',
    397         $workstation = '',
    398429        $OAuth = null
    399430    ) {
    400431        if (!$this->server_caps) {
    401432            $this->setError('Authentication is not allowed before HELO/EHLO');
     433
    402434            return false;
    403435        }
    404436
     
    408440                $this->setError('Authentication is not allowed at this stage');
    409441                // 'at this stage' means that auth may be allowed after the stage changes
    410442                // e.g. after STARTTLS
     443
    411444                return false;
    412445            }
    413446
    414             self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
    415             self::edebug(
     447            $this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNSPECIFIED'), self::DEBUG_LOWLEVEL);
     448            $this->edebug(
    416449                'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
    417450                self::DEBUG_LOWLEVEL
    418451            );
    419452
     453            //If we have requested a specific auth type, check the server supports it before trying others
     454            if (null !== $authtype and !in_array($authtype, $this->server_caps['AUTH'])) {
     455                $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
     456                $authtype = null;
     457            }
     458
    420459            if (empty($authtype)) {
    421                 foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) {
     460                //If no auth mechanism is specified, attempt to use these, in this order
     461                //Try CRAM-MD5 first as it's more secure than the others
     462                foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
    422463                    if (in_array($method, $this->server_caps['AUTH'])) {
    423464                        $authtype = $method;
    424465                        break;
     
    426467                }
    427468                if (empty($authtype)) {
    428469                    $this->setError('No supported authentication methods found');
     470
    429471                    return false;
    430472                }
    431473                self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
     
    433475
    434476            if (!in_array($authtype, $this->server_caps['AUTH'])) {
    435477                $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
     478
    436479                return false;
    437480            }
    438481        } elseif (empty($authtype)) {
     
    459502                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
    460503                    return false;
    461504                }
    462                 if (!$this->sendCommand("Username", base64_encode($username), 334)) {
     505                if (!$this->sendCommand('Username', base64_encode($username), 334)) {
    463506                    return false;
    464507                }
    465                 if (!$this->sendCommand("Password", base64_encode($password), 235)) {
     508                if (!$this->sendCommand('Password', base64_encode($password), 235)) {
    466509                    return false;
    467510                }
    468511                break;
     
    479522
    480523                // send encoded credentials
    481524                return $this->sendCommand('Username', base64_encode($response), 235);
     525            case 'XOAUTH2':
     526                //The OAuth instance must be set up prior to requesting auth.
     527                if (null === $OAuth) {
     528                    return false;
     529                }
     530                $oauth = $OAuth->getOauth64();
     531
     532                // Start authentication
     533                if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
     534                    return false;
     535                }
     536                break;
    482537            default:
    483538                $this->setError("Authentication method \"$authtype\" is not supported");
     539
    484540                return false;
    485541        }
     542
    486543        return true;
    487544    }
    488545
    489546    /**
    490547     * Calculate an MD5 HMAC hash.
    491548     * Works like hash_hmac('md5', $data, $key)
    492      * in case that function is not available
     549     * in case that function is not available.
     550     *
    493551     * @param string $data The data to hash
    494      * @param string $key The key to hash with
    495      * @access protected
     552     * @param string $key  The key to hash with
     553     *
    496554     * @return string
    497555     */
    498556    protected function hmac($data, $key)
     
    524582
    525583    /**
    526584     * Check connection state.
    527      * @access public
    528      * @return boolean True if connected.
     585     *
     586     * @return bool True if connected
    529587     */
    530588    public function connected()
    531589    {
     
    538596                    self::DEBUG_CLIENT
    539597                );
    540598                $this->close();
     599
    541600                return false;
    542601            }
     602
    543603            return true; // everything looks good
    544604        }
     605
    545606        return false;
    546607    }
    547608
    548609    /**
    549610     * Close the socket and clean up the state of the class.
    550611     * Don't use this function without first trying to use QUIT.
     612     *
    551613     * @see quit()
    552      * @access public
    553      * @return void
    554614     */
    555615    public function close()
    556616    {
     
    571631     * finializing the mail transaction. $msg_data is the message
    572632     * that is to be send with the headers. Each header needs to be
    573633     * on a single line followed by a <CRLF> with the message headers
    574      * and the message body being separated by and additional <CRLF>.
    575      * Implements rfc 821: DATA <CRLF>
     634     * and the message body being separated by an additional <CRLF>.
     635     * Implements RFC 821: DATA <CRLF>.
     636     *
    576637     * @param string $msg_data Message data to send
    577      * @access public
    578      * @return boolean
     638     *
     639     * @return bool
    579640     */
    580641    public function data($msg_data)
    581642    {
     
    585646        }
    586647
    587648        /* The server is ready to accept data!
    588          * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
     649         * According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
    589650         * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
    590651         * smaller lines to fit within the limit.
    591652         * We will also look for lines that start with a '.' and prepend an additional '.'.
     
    593654         */
    594655
    595656        // Normalize line breaks before exploding
    596         $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
     657        $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
    597658
    598659        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
    599660         * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
     
    602663
    603664        $field = substr($lines[0], 0, strpos($lines[0], ':'));
    604665        $in_headers = false;
    605         if (!empty($field) && strpos($field, ' ') === false) {
     666        if (!empty($field) and strpos($field, ' ') === false) {
    606667            $in_headers = true;
    607668        }
    608669
    609670        foreach ($lines as $line) {
    610             $lines_out = array();
     671            $lines_out = [];
    611672            if ($in_headers and $line == '') {
    612673                $in_headers = false;
    613674            }
     
    642703                if (!empty($line_out) and $line_out[0] == '.') {
    643704                    $line_out = '.' . $line_out;
    644705                }
    645                 $this->client_send($line_out . self::CRLF);
     706                $this->client_send($line_out . static::LE, 'DATA');
    646707            }
    647708        }
    648709
     
    654715        $this->recordLastTransactionID();
    655716        //Restore timelimit
    656717        $this->Timelimit = $savetimelimit;
     718
    657719        return $result;
    658720    }
    659721
     
    663725     * This makes sure that client and server are in a known state.
    664726     * Implements RFC 821: HELO <SP> <domain> <CRLF>
    665727     * and RFC 2821 EHLO.
     728     *
    666729     * @param string $host The host name or IP to connect to
    667      * @access public
    668      * @return boolean
     730     *
     731     * @return bool
    669732     */
    670733    public function hello($host = '')
    671734    {
    672735        //Try extended hello first (RFC 2821)
    673         return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
     736        return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host);
    674737    }
    675738
    676739    /**
    677740     * Send an SMTP HELO or EHLO command.
    678      * Low-level implementation used by hello()
    679      * @see hello()
     741     * Low-level implementation used by hello().
     742     *
    680743     * @param string $hello The HELO string
    681      * @param string $host The hostname to say we are
    682      * @access protected
    683      * @return boolean
     744     * @param string $host  The hostname to say we are
     745     *
     746     * @return bool
     747     *
     748     * @see    hello()
    684749     */
    685750    protected function sendHello($hello, $host)
    686751    {
     
    691756        } else {
    692757            $this->server_caps = null;
    693758        }
     759
    694760        return $noerror;
    695761    }
    696762
    697763    /**
    698764     * Parse a reply to HELO/EHLO command to discover server extensions.
    699765     * In case of HELO, the only parameter that can be discovered is a server name.
    700      * @access protected
    701      * @param string $type - 'HELO' or 'EHLO'
     766     *
     767     * @param string $type `HELO` or `EHLO`
    702768     */
    703769    protected function parseHelloFields($type)
    704770    {
    705         $this->server_caps = array();
     771        $this->server_caps = [];
    706772        $lines = explode("\n", $this->helo_rply);
    707773
    708774        foreach ($lines as $n => $s) {
     
    724790                            break;
    725791                        case 'AUTH':
    726792                            if (!is_array($fields)) {
    727                                 $fields = array();
     793                                $fields = [];
    728794                            }
    729795                            break;
    730796                        default:
     
    742808     * $from. Returns true if successful or false otherwise. If True
    743809     * the mail transaction is started and then one or more recipient
    744810     * commands may be called followed by a data command.
    745      * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     811     * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
     812     *
    746813     * @param string $from Source address of this message
    747      * @access public
    748      * @return boolean
     814     *
     815     * @return bool
    749816     */
    750817    public function mail($from)
    751818    {
    752819        $useVerp = ($this->do_verp ? ' XVERP' : '');
     820
    753821        return $this->sendCommand(
    754822            'MAIL FROM',
    755823            'MAIL FROM:<' . $from . '>' . $useVerp,
     
    760828    /**
    761829     * Send an SMTP QUIT command.
    762830     * Closes the socket if there is no error or the $close_on_error argument is true.
    763      * Implements from rfc 821: QUIT <CRLF>
    764      * @param boolean $close_on_error Should the connection close if an error occurs?
    765      * @access public
    766      * @return boolean
     831     * Implements from RFC 821: QUIT <CRLF>.
     832     *
     833     * @param bool $close_on_error Should the connection close if an error occurs?
     834     *
     835     * @return bool
    767836     */
    768837    public function quit($close_on_error = true)
    769838    {
     
    773842            $this->close();
    774843            $this->error = $err; //Restore any error from the quit command
    775844        }
     845
    776846        return $noerror;
    777847    }
    778848
     
    780850     * Send an SMTP RCPT command.
    781851     * Sets the TO argument to $toaddr.
    782852     * Returns true if the recipient was accepted false if it was rejected.
    783      * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     853     * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>.
     854     *
    784855     * @param string $address The address the message is being sent to
    785      * @access public
    786      * @return boolean
     856     *
     857     * @return bool
    787858     */
    788859    public function recipient($address)
    789860    {
    790861        return $this->sendCommand(
    791862            'RCPT TO',
    792863            'RCPT TO:<' . $address . '>',
    793             array(250, 251)
     864            [250, 251]
    794865        );
    795866    }
    796867
    797868    /**
    798869     * Send an SMTP RSET command.
    799870     * Abort any transaction that is currently in progress.
    800      * Implements rfc 821: RSET <CRLF>
    801      * @access public
    802      * @return boolean True on success.
     871     * Implements RFC 821: RSET <CRLF>.
     872     *
     873     * @return bool True on success
    803874     */
    804875    public function reset()
    805876    {
     
    808879
    809880    /**
    810881     * Send a command to an SMTP server and check its return code.
    811      * @param string $command The command name - not sent to the server
    812      * @param string $commandstring The actual command to send
    813      * @param integer|array $expect One or more expected integer success codes
    814      * @access protected
    815      * @return boolean True on success.
     882     *
     883     * @param string    $command       The command name - not sent to the server
     884     * @param string    $commandstring The actual command to send
     885     * @param int|array $expect        One or more expected integer success codes
     886     *
     887     * @return bool True on success
    816888     */
    817889    protected function sendCommand($command, $commandstring, $expect)
    818890    {
    819891        if (!$this->connected()) {
    820892            $this->setError("Called $command without being connected");
     893
    821894            return false;
    822895        }
    823896        //Reject line breaks in all commands
    824897        if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
    825898            $this->setError("Command '$command' contained line breaks");
     899
    826900            return false;
    827901        }
    828         $this->client_send($commandstring . self::CRLF);
     902        $this->client_send($commandstring . static::LE, $command);
    829903
    830904        $this->last_reply = $this->get_lines();
    831905        // Fetch SMTP code and possible error code explanation
    832         $matches = array();
    833         if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
     906        $matches = [];
     907        if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/', $this->last_reply, $matches)) {
    834908            $code = $matches[1];
    835909            $code_ex = (count($matches) > 2 ? $matches[2] : null);
    836910            // Cut off error code from each response line
    837911            $detail = preg_replace(
    838912                "/{$code}[ -]" .
    839                 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m",
     913                ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
    840914                '',
    841915                $this->last_reply
    842916            );
     
    849923
    850924        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
    851925
    852         if (!in_array($code, (array)$expect)) {
     926        if (!in_array($code, (array) $expect)) {
    853927            $this->setError(
    854928                "$command command failed",
    855929                $detail,
     
    860934                'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
    861935                self::DEBUG_CLIENT
    862936            );
     937
    863938            return false;
    864939        }
    865940
    866941        $this->setError('');
     942
    867943        return true;
    868944    }
    869945
     
    875951     * commands may be called followed by a data command. This command
    876952     * will send the message to the users terminal if they are logged
    877953     * in and send them an email.
    878      * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     954     * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>.
     955     *
    879956     * @param string $from The address the message is from
    880      * @access public
    881      * @return boolean
     957     *
     958     * @return bool
    882959     */
    883960    public function sendAndMail($from)
    884961    {
     
    887964
    888965    /**
    889966     * Send an SMTP VRFY command.
     967     *
    890968     * @param string $name The name to verify
    891      * @access public
    892      * @return boolean
     969     *
     970     * @return bool
    893971     */
    894972    public function verify($name)
    895973    {
    896         return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
     974        return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
    897975    }
    898976
    899977    /**
    900978     * Send an SMTP NOOP command.
    901      * Used to keep keep-alives alive, doesn't actually do anything
    902      * @access public
    903      * @return boolean
     979     * Used to keep keep-alives alive, doesn't actually do anything.
     980     *
     981     * @return bool
    904982     */
    905983    public function noop()
    906984    {
     
    911989     * Send an SMTP TURN command.
    912990     * This is an optional command for SMTP that this class does not support.
    913991     * This method is here to make the RFC821 Definition complete for this class
    914      * and _may_ be implemented in future
    915      * Implements from rfc 821: TURN <CRLF>
    916      * @access public
    917      * @return boolean
     992     * and _may_ be implemented in future.
     993     * Implements from RFC 821: TURN <CRLF>.
     994     *
     995     * @return bool
    918996     */
    919997    public function turn()
    920998    {
    921999        $this->setError('The SMTP TURN command is not implemented');
    9221000        $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
     1001
    9231002        return false;
    9241003    }
    9251004
    9261005    /**
    9271006     * Send raw data to the server.
    928      * @param string $data The data to send
    929      * @access public
    930      * @return integer|boolean The number of bytes sent to the server or false on error
     1007     *
     1008     * @param string $data    The data to send
     1009     * @param string $command Optionally, the command this is part of, used only for controlling debug output
     1010     *
     1011     * @return int|bool The number of bytes sent to the server or false on error
    9311012     */
    932     public function client_send($data)
     1013    public function client_send($data, $command = '')
    9331014    {
    934         $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
    935         set_error_handler(array($this, 'errorHandler'));
     1015        //If SMTP transcripts are left enabled, or debug output is posted online
     1016        //it can leak credentials, so hide credentials in all but lowest level
     1017        if (self::DEBUG_LOWLEVEL > $this->do_debug and
     1018            in_array($command, ['User & Password', 'Username', 'Password'], true)) {
     1019            $this->edebug('CLIENT -> SERVER: <credentials hidden>', self::DEBUG_CLIENT);
     1020        } else {
     1021            $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
     1022        }
     1023        set_error_handler([$this, 'errorHandler']);
    9361024        $result = fwrite($this->smtp_conn, $data);
    9371025        restore_error_handler();
     1026
    9381027        return $result;
    9391028    }
    9401029
    9411030    /**
    9421031     * Get the latest error.
    943      * @access public
     1032     *
    9441033     * @return array
    9451034     */
    9461035    public function getError()
     
    9491038    }
    9501039
    9511040    /**
    952      * Get SMTP extensions available on the server
    953      * @access public
     1041     * Get SMTP extensions available on the server.
     1042     *
    9541043     * @return array|null
    9551044     */
    9561045    public function getServerExtList()
     
    9591048    }
    9601049
    9611050    /**
    962      * A multipurpose method
    963      * The method works in three ways, dependent on argument value and current state
    964      *   1. HELO/EHLO was not sent - returns null and set up $this->error
    965      *   2. HELO was sent
    966      *     $name = 'HELO': returns server name
    967      *     $name = 'EHLO': returns boolean false
    968      *     $name = any string: returns null and set up $this->error
    969      *   3. EHLO was sent
    970      *     $name = 'HELO'|'EHLO': returns server name
    971      *     $name = any string: if extension $name exists, returns boolean True
    972      *       or its options. Otherwise returns boolean False
    973      * In other words, one can use this method to detect 3 conditions:
    974      *  - null returned: handshake was not or we don't know about ext (refer to $this->error)
    975      *  - false returned: the requested feature exactly not exists
    976      *  - positive value returned: the requested feature exists
     1051     * Get metadata about the SMTP server from its HELO/EHLO response.
     1052     * The method works in three ways, dependent on argument value and current state:
     1053     *   1. HELO/EHLO has not been sent - returns null and populates $this->error.
     1054     *   2. HELO has been sent -
     1055     *     $name == 'HELO': returns server name
     1056     *     $name == 'EHLO': returns boolean false
     1057     *     $name == any other string: returns null and populates $this->error
     1058     *   3. EHLO has been sent -
     1059     *     $name == 'HELO'|'EHLO': returns the server name
     1060     *     $name == any other string: if extension $name exists, returns True
     1061     *       or its options (e.g. AUTH mechanisms supported). Otherwise returns False.
     1062     *
    9771063     * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
     1064     *
    9781065     * @return mixed
    9791066     */
    9801067    public function getServerExt($name)
    9811068    {
    9821069        if (!$this->server_caps) {
    9831070            $this->setError('No HELO/EHLO was sent');
    984             return null;
     1071
     1072            return;
    9851073        }
    9861074
    987         // the tight logic knot ;)
    9881075        if (!array_key_exists($name, $this->server_caps)) {
    989             if ($name == 'HELO') {
     1076            if ('HELO' == $name) {
    9901077                return $this->server_caps['EHLO'];
    9911078            }
    992             if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
     1079            if ('EHLO' == $name || array_key_exists('EHLO', $this->server_caps)) {
    9931080                return false;
    9941081            }
    995             $this->setError('HELO handshake was used. Client knows nothing about server extensions');
    996             return null;
     1082            $this->setError('HELO handshake was used; No information about server extensions available');
     1083
     1084            return;
    9971085        }
    9981086
    9991087        return $this->server_caps[$name];
     
    10011089
    10021090    /**
    10031091     * Get the last reply from the server.
    1004      * @access public
     1092     *
    10051093     * @return string
    10061094     */
    10071095    public function getLastReply()
     
    10151103     * With SMTP we can tell if we have more lines to read if the
    10161104     * 4th character is '-' symbol. If it is a space then we don't
    10171105     * need to read anything else.
    1018      * @access protected
     1106     *
    10191107     * @return string
    10201108     */
    10211109    protected function get_lines()
     
    10301118        if ($this->Timelimit > 0) {
    10311119            $endtime = time() + $this->Timelimit;
    10321120        }
    1033         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
     1121        $selR = [$this->smtp_conn];
     1122        $selW = null;
     1123        while (is_resource($this->smtp_conn) and !feof($this->smtp_conn)) {
     1124            //Must pass vars in here as params are by reference
     1125            if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
     1126                $this->edebug(
     1127                    'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
     1128                    self::DEBUG_LOWLEVEL
     1129                );
     1130                break;
     1131            }
     1132            //Deliberate noise suppression - errors are handled afterwards
    10341133            $str = @fgets($this->smtp_conn, 515);
    1035             $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
    1036             $this->edebug("SMTP -> get_lines(): \$str is  \"$str\"", self::DEBUG_LOWLEVEL);
     1134            $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
    10371135            $data .= $str;
    10381136            // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
    10391137            // or 4th character is a space, we are done reading, break the loop,
     
    10601158                break;
    10611159            }
    10621160        }
     1161
    10631162        return $data;
    10641163    }
    10651164
    10661165    /**
    10671166     * Enable or disable VERP address generation.
    1068      * @param boolean $enabled
     1167     *
     1168     * @param bool $enabled
    10691169     */
    10701170    public function setVerp($enabled = false)
    10711171    {
     
    10741174
    10751175    /**
    10761176     * Get VERP address generation mode.
    1077      * @return boolean
     1177     *
     1178     * @return bool
    10781179     */
    10791180    public function getVerp()
    10801181    {
     
    10831184
    10841185    /**
    10851186     * Set error messages and codes.
    1086      * @param string $message The error message
    1087      * @param string $detail Further detail on the error
    1088      * @param string $smtp_code An associated SMTP error code
     1187     *
     1188     * @param string $message      The error message
     1189     * @param string $detail       Further detail on the error
     1190     * @param string $smtp_code    An associated SMTP error code
    10891191     * @param string $smtp_code_ex Extended SMTP code
    10901192     */
    10911193    protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
    10921194    {
    1093         $this->error = array(
     1195        $this->error = [
    10941196            'error' => $message,
    10951197            'detail' => $detail,
    10961198            'smtp_code' => $smtp_code,
    1097             'smtp_code_ex' => $smtp_code_ex
    1098         );
     1199            'smtp_code_ex' => $smtp_code_ex,
     1200        ];
    10991201    }
    11001202
    11011203    /**
    11021204     * Set debug output method.
    1103      * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
     1205     *
     1206     * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it
    11041207     */
    11051208    public function setDebugOutput($method = 'echo')
    11061209    {
     
    11091212
    11101213    /**
    11111214     * Get debug output method.
     1215     *
    11121216     * @return string
    11131217     */
    11141218    public function getDebugOutput()
     
    11181222
    11191223    /**
    11201224     * Set debug output level.
    1121      * @param integer $level
     1225     *
     1226     * @param int $level
    11221227     */
    11231228    public function setDebugLevel($level = 0)
    11241229    {
     
    11271232
    11281233    /**
    11291234     * Get debug output level.
    1130      * @return integer
     1235     *
     1236     * @return int
    11311237     */
    11321238    public function getDebugLevel()
    11331239    {
     
    11361242
    11371243    /**
    11381244     * Set SMTP timeout.
    1139      * @param integer $timeout
     1245     *
     1246     * @param int $timeout The timeout duration in seconds
    11401247     */
    11411248    public function setTimeout($timeout = 0)
    11421249    {
     
    11451252
    11461253    /**
    11471254     * Get SMTP timeout.
    1148      * @return integer
     1255     *
     1256     * @return int
    11491257     */
    11501258    public function getTimeout()
    11511259    {
     
    11541262
    11551263    /**
    11561264     * Reports an error number and string.
    1157      * @param integer $errno The error number returned by PHP.
    1158      * @param string $errmsg The error message returned by PHP.
     1265     *
     1266     * @param int    $errno   The error number returned by PHP
     1267     * @param string $errmsg  The error message returned by PHP
    11591268     * @param string $errfile The file the error occurred in
    1160      * @param integer $errline The line number the error occurred on
     1269     * @param int    $errline The line number the error occurred on
    11611270     */
    11621271    protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
    11631272    {
    11641273        $notice = 'Connection failed.';
    11651274        $this->setError(
    11661275            $notice,
    1167             $errno,
    1168             $errmsg
     1276            $errmsg,
     1277            (string) $errno
    11691278        );
    11701279        $this->edebug(
    1171             $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]",
     1280            "$notice Error #$errno: $errmsg [$errfile line $errline]",
    11721281            self::DEBUG_CONNECTION
    11731282        );
    11741283    }
     
    11791288     * Relies on the host providing the ID in response to a DATA command.
    11801289     * If no reply has been received yet, it will return null.
    11811290     * If no pattern was matched, it will return false.
     1291     *
    11821292     * @return bool|null|string
    11831293     */
    11841294    protected function recordLastTransactionID()
     
    11911301            $this->last_smtp_transaction_id = false;
    11921302            foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
    11931303                if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
    1194                     $this->last_smtp_transaction_id = $matches[1];
     1304                    $this->last_smtp_transaction_id = trim($matches[1]);
     1305                    break;
    11951306                }
    11961307            }
    11971308        }
     
    12031314     * Get the queue/transaction ID of the last SMTP transaction
    12041315     * If no reply has been received yet, it will return null.
    12051316     * If no pattern was matched, it will return false.
     1317     *
    12061318     * @return bool|null|string
     1319     *
    12071320     * @see recordLastTransactionID()
    12081321     */
    12091322    public function getLastTransactionID()
  • src/wp-includes/pluggable.php

     
    210210                global $phpmailer;
    211211
    212212                // (Re)create it, if it's gone missing
    213                 if ( ! ( $phpmailer instanceof PHPMailer ) ) {
     213                if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) {
    214214                        require_once ABSPATH . WPINC . '/class-phpmailer.php';
    215215                        require_once ABSPATH . WPINC . '/class-smtp.php';
    216                         $phpmailer = new PHPMailer( true );
     216                        $phpmailer = new PHPMailer\PHPMailer\PHPMailer( true );
    217217                }
    218218
    219219                // Headers
  • tests/phpunit/includes/mock-mailer.php

     
    11<?php
    22require_once( ABSPATH . '/wp-includes/class-phpmailer.php' );
    33
    4 class MockPHPMailer extends PHPMailer {
     4class MockPHPMailer extends PHPMailer\PHPMailer\PHPMailer {
    55        var $mock_sent = array();
    66
    77        function preSend() {