WordPress.org

Make WordPress Core

Ticket #28909: 28909.diff

File 28909.diff, 84.6 KB (added by MattyRob, 5 years ago)
  • src/wp-includes/class-phpmailer.php

     
    11<?php
    22/**
    33 * PHPMailer - PHP email creation and transport class.
    4  * PHP Version 5.0.0
    5  * Version 5.2.7
     4 * PHP Version 5
    65 * @package PHPMailer
    7  * @link https://github.com/PHPMailer/PHPMailer/
    8  * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
     7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    98 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    109 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    1110 * @author Brent R. Matzelle (original founder)
    12  * @copyright 2013 Marcus Bointon
     11 * @copyright 2012 - 2014 Marcus Bointon
    1312 * @copyright 2010 - 2012 Jim Jagielski
    1413 * @copyright 2004 - 2009 Andy Prevost
    1514 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
     
    1817 * FITNESS FOR A PARTICULAR PURPOSE.
    1918 */
    2019
    21 if (version_compare(PHP_VERSION, '5.0.0', '<')) {
    22     exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
    23 }
    24 
    2520/**
    2621 * PHPMailer - PHP email creation and transport class.
    27  * PHP Version 5.0.0
    2822 * @package PHPMailer
    29  * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
    3024 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    3125 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    3226 * @author Brent R. Matzelle (original founder)
    33  * @copyright 2013 Marcus Bointon
    34  * @copyright 2010 - 2012 Jim Jagielski
    35  * @copyright 2004 - 2009 Andy Prevost
    3627 */
    3728class PHPMailer
    3829{
     
    4031     * The PHPMailer Version number.
    4132     * @type string
    4233     */
    43     public $Version = '5.2.7';
     34    public $Version = '5.2.8';
    4435
    4536    /**
    4637     * Email priority.
     
    9788     * The Return-Path of the message.
    9889     * If empty, it will be set to either From or Sender.
    9990     * @type string
     91     * @deprecated Email senders should never set a return-path header;
     92     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
     93     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
    10094     */
    10195    public $ReturnPath = '';
    10296
     
    299293
    300294    /**
    301295     * SMTP class debug output mode.
    302      * Options: 0 = off, 1 = commands, 2 = commands and data
     296     * Options:
     297     *   0: no output
     298     *   1: commands
     299     *   2: data and commands
     300     *   3: as 2 plus connection status
     301     *   4: low level data output
    303302     * @type int
    304303     * @see SMTP::$do_debug
    305304     */
     
    306305    public $SMTPDebug = 0;
    307306
    308307    /**
    309      * The function/method to use for debugging output.
    310      * Options: "echo" or "error_log"
     308     * How to handle debug output.
     309     * Options:
     310     *   'echo': Output plain-text as-is, appropriate for CLI
     311     *   'html': Output escaped, line breaks converted to <br>, appropriate for browser output
     312     *   'error_log': Output to error log as configured in php.ini
    311313     * @type string
    312314     * @see SMTP::$Debugoutput
    313315     */
    314     public $Debugoutput = "echo";
     316    public $Debugoutput = 'echo';
    315317
    316318    /**
    317319     * Whether to keep SMTP connection open after each message.
     
    339341     * Whether to generate VERP addresses on send.
    340342     * Only applicable when sending via SMTP.
    341343     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
     344     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
    342345     * @type bool
    343346     */
    344347    public $do_verp = false;
     
    396399     * The function that handles the result of the send email action.
    397400     * It is called out by send() for each email sent.
    398401     *
    399      * Value can be:
    400      * - 'function_name' for function names
    401      * - 'Class::Method' for static method calls
    402      * - array($object, 'Method') for calling methods on $object
    403      * See http://php.net/is_callable manual page for more details.
     402     * Value can be any php callable: http://www.php.net/is_callable
    404403     *
    405404     * Parameters:
    406405     *   bool    $result        result of the send action
     
    410409     *   string  $subject       the subject
    411410     *   string  $body          the email body
    412411     *   string  $from          email address of sender
    413      *
    414412     * @type string
    415413     */
    416414    public $action_function = '';
     
    597595     */
    598596    private function mailPassthru($to, $subject, $body, $header, $params)
    599597    {
     598        //Check overloading of mail function to avoid double-encoding
     599        if (ini_get('mbstring.func_overload') & 1) {
     600            $subject = $this->secureHeader($subject);
     601        } else {
     602            $subject = $this->encodeHeader($this->secureHeader($subject));
     603        }
    600604        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
    601             $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);
     605            $result = @mail($to, $subject, $body, $header);
    602606        } else {
    603             $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);
     607            $result = @mail($to, $subject, $body, $header, $params);
    604608        }
    605         return $rt;
     609        return $result;
    606610    }
    607611
    608612    /**
     
    627631                break;
    628632            case 'echo':
    629633            default:
    630                 //Just echoes exactly what was received
    631                 echo $str;
     634                echo $str."\n";
    632635        }
    633636    }
    634637
     
    670673     */
    671674    public function isSendmail()
    672675    {
    673         if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
    674             $this->Sendmail = '/var/qmail/bin/sendmail';
     676        $ini_sendmail_path = ini_get('sendmail_path');
     677       
     678        if (!stristr($ini_sendmail_path, 'sendmail')) {
     679            $this->Sendmail = '/usr/sbin/sendmail';
     680        } else {
     681            $this->Sendmail = $ini_sendmail_path;
    675682        }
    676683        $this->Mailer = 'sendmail';
    677684    }
     
    682689     */
    683690    public function isQmail()
    684691    {
    685         if (stristr(ini_get('sendmail_path'), 'qmail')) {
    686             $this->Sendmail = '/var/qmail/bin/sendmail';
     692        $ini_sendmail_path = ini_get('sendmail_path');
     693       
     694        if (!stristr($ini_sendmail_path, 'qmail')) {
     695            $this->Sendmail = '/var/qmail/bin/qmail-inject';
     696        } else {
     697            $this->Sendmail = $ini_sendmail_path;
    687698        }
    688         $this->Mailer = 'sendmail';
     699        $this->Mailer = 'qmail';
    689700    }
    690701
    691702    /**
     
    748759    {
    749760        if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
    750761            $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
     762            $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
    751763            if ($this->exceptions) {
    752764                throw new phpmailerException('Invalid recipient array: ' . $kind);
    753765            }
    754             $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
    755766            return false;
    756767        }
    757768        $address = trim($address);
     
    758769        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    759770        if (!$this->validateAddress($address)) {
    760771            $this->setError($this->lang('invalid_address') . ': ' . $address);
     772            $this->edebug($this->lang('invalid_address') . ': ' . $address);
    761773            if ($this->exceptions) {
    762774                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
    763775            }
    764             $this->edebug($this->lang('invalid_address') . ': ' . $address);
    765776            return false;
    766777        }
    767778        if ($kind != 'Reply-To') {
     
    793804        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    794805        if (!$this->validateAddress($address)) {
    795806            $this->setError($this->lang('invalid_address') . ': ' . $address);
     807            $this->edebug($this->lang('invalid_address') . ': ' . $address);
    796808            if ($this->exceptions) {
    797809                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
    798810            }
    799             $this->edebug($this->lang('invalid_address') . ': ' . $address);
    800811            return false;
    801812        }
    802813        $this->From = $address;
     
    825836     * Check that a string looks like an email address.
    826837     * @param string $address The email address to check
    827838     * @param string $patternselect A selector for the validation pattern to use :
    828      *   'auto' - pick best one automatically;
    829      *   'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
    830      *   'pcre' - use old PCRE implementation;
    831      *   'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough;
    832      *   'noregex' - super fast, really dumb.
     839     * * `auto` Pick strictest one automatically;
     840     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
     841     * * `pcre` Use old PCRE implementation;
     842     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
     843     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
     844     * * `noregex` Don't use a regex: super fast, really dumb.
    833845     * @return bool
    834846     * @static
    835847     * @access public
     
    836848     */
    837849    public static function validateAddress($address, $patternselect = 'auto')
    838850    {
    839         if ($patternselect == 'auto') {
    840             if (defined(
    841                 'PCRE_VERSION'
    842             )
    843             ) { //Check this instead of extension_loaded so it works when that function is disabled
     851        if (!$patternselect or $patternselect == 'auto') {
     852            if (defined('PCRE_VERSION')) { //Check this constant so it works when extension_loaded() is disabled
    844853                if (version_compare(PCRE_VERSION, '8.0') >= 0) {
    845854                    $patternselect = 'pcre8';
    846855                } else {
     
    858867        switch ($patternselect) {
    859868            case 'pcre8':
    860869                /**
    861                  * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
    862                  * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
    863                  * not allow a@b type valid addresses :(
     870                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
    864871                 * @link http://squiloople.com/2009/12/20/email-address-validation/
    865872                 * @copyright 2009-2010 Michael Rushton
    866873                 * Feel free to use and redistribute this code. But please keep this copyright notice.
     
    894901                    $address
    895902                );
    896903                break;
     904            case 'html5':
     905                /**
     906                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
     907                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
     908                 */
     909                return (bool)preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
     910                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address);
     911                break;
    897912            case 'php':
    898913            default:
    899914                return (bool)filter_var($address, FILTER_VALIDATE_EMAIL);
     
    911926    /**
    912927     * Create a message and send it.
    913928     * Uses the sending method specified by $Mailer.
    914      * Returns false on error - Use the ErrorInfo variable to view description of the error.
    915929     * @throws phpmailerException
    916      * @return bool
     930     * @return bool false on error - See the ErrorInfo property for details of the error.
    917931     */
    918932    public function send()
    919933    {
     
    922936                return false;
    923937            }
    924938            return $this->postSend();
    925         } catch (phpmailerException $e) {
     939        } catch (phpmailerException $exc) {
    926940            $this->mailHeader = '';
    927             $this->setError($e->getMessage());
     941            $this->setError($exc->getMessage());
    928942            if ($this->exceptions) {
    929                 throw $e;
     943                throw $exc;
    930944            }
    931945            return false;
    932946        }
     
    940954    public function preSend()
    941955    {
    942956        try {
    943             $this->mailHeader = "";
     957            $this->mailHeader = '';
    944958            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
    945959                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
    946960            }
     
    964978            // an extra header list which createHeader() doesn't fold in
    965979            if ($this->Mailer == 'mail') {
    966980                if (count($this->to) > 0) {
    967                     $this->mailHeader .= $this->addrAppend("To", $this->to);
     981                    $this->mailHeader .= $this->addrAppend('To', $this->to);
    968982                } else {
    969                     $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;");
     983                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
    970984                }
    971985                $this->mailHeader .= $this->headerLine(
    972986                    'Subject',
     
    9901004            }
    9911005            return true;
    9921006
    993         } catch (phpmailerException $e) {
    994             $this->setError($e->getMessage());
     1007        } catch (phpmailerException $exc) {
     1008            $this->setError($exc->getMessage());
    9951009            if ($this->exceptions) {
    996                 throw $e;
     1010                throw $exc;
    9971011            }
    9981012            return false;
    9991013        }
     
    10111025            // Choose the mailer and send through it
    10121026            switch ($this->Mailer) {
    10131027                case 'sendmail':
     1028                case 'qmail':
    10141029                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
    10151030                case 'smtp':
    10161031                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
     
    10171032                case 'mail':
    10181033                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
    10191034                default:
     1035                    $sendMethod = $this->Mailer.'Send';
     1036                    if (method_exists($this, $sendMethod)) {
     1037                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
     1038                    }
     1039                   
    10201040                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
    10211041            }
    1022         } catch (phpmailerException $e) {
    1023             $this->setError($e->getMessage());
     1042        } catch (phpmailerException $exc) {
     1043            $this->setError($exc->getMessage());
     1044            $this->edebug($exc->getMessage());
    10241045            if ($this->exceptions) {
    1025                 throw $e;
     1046                throw $exc;
    10261047            }
    1027             $this->edebug($e->getMessage() . "\n");
    10281048        }
    10291049        return false;
    10301050    }
     
    10411061    protected function sendmailSend($header, $body)
    10421062    {
    10431063        if ($this->Sender != '') {
    1044             $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
     1064            if ($this->Mailer == 'qmail') {
     1065                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
     1066            } else {
     1067                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
     1068            }
    10451069        } else {
    1046             $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
     1070            if ($this->Mailer == 'qmail') {
     1071                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
     1072            } else {
     1073                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
     1074            }
    10471075        }
    10481076        if ($this->SingleTo === true) {
    10491077            foreach ($this->SingleToArray as $val) {
     
    10501078                if (!@$mail = popen($sendmail, 'w')) {
    10511079                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
    10521080                }
    1053                 fputs($mail, "To: " . $val . "\n");
     1081                fputs($mail, 'To: ' . $val . "\n");
    10541082                fputs($mail, $header);
    10551083                fputs($mail, $body);
    10561084                $result = pclose($mail);
     
    10901118    protected function mailSend($header, $body)
    10911119    {
    10921120        $toArr = array();
    1093         foreach ($this->to as $t) {
    1094             $toArr[] = $this->addrFormat($t);
     1121        foreach ($this->to as $toaddr) {
     1122            $toArr[] = $this->addrFormat($toaddr);
    10951123        }
    10961124        $to = implode(', ', $toArr);
    10971125
    10981126        if (empty($this->Sender)) {
    1099             $params = " ";
     1127            $params = ' ';
    11001128        } else {
    1101             $params = sprintf("-f%s", $this->Sender);
     1129            $params = sprintf('-f%s', $this->Sender);
    11021130        }
    11031131        if ($this->Sender != '' and !ini_get('safe_mode')) {
    11041132            $old_from = ini_get('sendmail_from');
    11051133            ini_set('sendmail_from', $this->Sender);
    11061134        }
    1107         $rt = false;
     1135        $result = false;
    11081136        if ($this->SingleTo === true && count($toArr) > 1) {
    11091137            foreach ($toArr as $val) {
    1110                 $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
     1138                $result = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
    11111139                // implement call back function if it exists
    1112                 $isSent = ($rt == 1) ? 1 : 0;
     1140                $isSent = ($result == 1) ? 1 : 0;
    11131141                $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
    11141142            }
    11151143        } else {
    1116             $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
     1144            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
    11171145            // implement call back function if it exists
    1118             $isSent = ($rt == 1) ? 1 : 0;
     1146            $isSent = ($result == 1) ? 1 : 0;
    11191147            $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
    11201148        }
    11211149        if (isset($old_from)) {
    11221150            ini_set('sendmail_from', $old_from);
    11231151        }
    1124         if (!$rt) {
     1152        if (!$result) {
    11251153            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
    11261154        }
    11271155        return true;
     
    11351163    public function getSMTPInstance()
    11361164    {
    11371165        if (!is_object($this->smtp)) {
    1138             require_once 'class-smtp.php';
     1166                        require_once 'class-smtp.php';
    11391167            $this->smtp = new SMTP;
    11401168        }
    11411169        return $this->smtp;
     
    11661194            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
    11671195        }
    11681196
    1169         // Attempt to send attach all recipients
     1197        // Attempt to send to all recipients
    11701198        foreach ($this->to as $to) {
    11711199            if (!$this->smtp->recipient($to[0])) {
    11721200                $bad_rcpt[] = $to[0];
     
    11951223            $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
    11961224        }
    11971225
    1198         if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
    1199             throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt));
    1200         }
    1201         if (!$this->smtp->data($header . $body)) {
     1226        //Only send the DATA command if we have viable recipients
     1227        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
    12021228            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
    12031229        }
    12041230        if ($this->SMTPKeepAlive == true) {
     
    12071233            $this->smtp->quit();
    12081234            $this->smtp->close();
    12091235        }
     1236        if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
     1237            throw new phpmailerException(
     1238                $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
     1239                self::STOP_CONTINUE
     1240            );
     1241        }
    12101242        return true;
    12111243    }
    12121244
     
    12341266        $this->smtp->setDebugLevel($this->SMTPDebug);
    12351267        $this->smtp->setDebugOutput($this->Debugoutput);
    12361268        $this->smtp->setVerp($this->do_verp);
    1237         $tls = ($this->SMTPSecure == 'tls');
    1238         $ssl = ($this->SMTPSecure == 'ssl');
    12391269        $hosts = explode(';', $this->Host);
    12401270        $lastexception = null;
    12411271
    12421272        foreach ($hosts as $hostentry) {
    12431273            $hostinfo = array();
    1244             $host = $hostentry;
     1274            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
     1275                //Not a valid host entry
     1276                continue;
     1277            }
     1278            //$hostinfo[2]: optional ssl or tls prefix
     1279            //$hostinfo[3]: the hostname
     1280            //$hostinfo[4]: optional port number
     1281            //The host string prefix can temporarily override the current setting for SMTPSecure
     1282            //If it's not specified, the default value is used
     1283            $prefix = '';
     1284            $tls = ($this->SMTPSecure == 'tls');
     1285            if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
     1286                $prefix = 'ssl://';
     1287                $tls = false; //Can't have SSL and TLS at once
     1288            } elseif ($hostinfo[2] == 'tls') {
     1289                $tls = true;
     1290                //tls doesn't use a prefix
     1291            }
     1292            $host = $hostinfo[3];
    12451293            $port = $this->Port;
    1246             if (preg_match(
    1247                 '/^(.+):([0-9]+)$/',
    1248                 $hostentry,
    1249                 $hostinfo
    1250             )
    1251             ) { //If $hostentry contains 'address:port', override default
    1252                 $host = $hostinfo[1];
    1253                 $port = $hostinfo[2];
     1294            $tport = (integer)$hostinfo[4];
     1295            if ($tport > 0 and $tport < 65536) {
     1296                $port = $tport;
    12541297            }
    1255             if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) {
     1298            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
    12561299                try {
    12571300                    if ($this->Helo) {
    12581301                        $hello = $this->Helo;
     
    12811324                        }
    12821325                    }
    12831326                    return true;
    1284                 } catch (phpmailerException $e) {
    1285                     $lastexception = $e;
     1327                } catch (phpmailerException $exc) {
     1328                    $lastexception = $exc;
    12861329                    //We must have connected, but then failed TLS or Auth, so close connection nicely
    12871330                    $this->smtp->quit();
    12881331                }
     
    13201363     * @return bool
    13211364     * @access public
    13221365     */
    1323     public function setLanguage($langcode = 'en', $lang_path = 'language/')
     1366    public function setLanguage($langcode = 'en', $lang_path = '')
    13241367    {
    1325         //Define full set of translatable strings
     1368        //Define full set of translatable strings in English
    13261369        $PHPMAILER_LANG = array(
    13271370            'authenticate' => 'SMTP Error: Could not authenticate.',
    13281371            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
     
    13431386            'smtp_error' => 'SMTP server error: ',
    13441387            'variable_set' => 'Cannot set or reset variable: '
    13451388        );
    1346         //Overwrite language-specific strings.
    1347         //This way we'll never have missing translations - no more "language string failed to load"!
    1348         $l = true;
     1389        if (empty($lang_path)) {
     1390            //Calculate an absolute path so it can work if CWD is not here
     1391            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
     1392        }
     1393        $foundlang = true;
     1394        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
    13491395        if ($langcode != 'en') { //There is no English translation file
    1350             $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php';
     1396            //Make sure language file path is readable
     1397            if (!is_readable($lang_file)) {
     1398                $foundlang = false;
     1399            } else {
     1400                //Overwrite language-specific strings.
     1401                //This way we'll never have missing translations.
     1402                $foundlang = include $lang_file;
     1403            }
    13511404        }
    13521405        $this->language = $PHPMAILER_LANG;
    1353         return ($l == true); //Returns false if language not found
     1406        return ($foundlang == true); //Returns false if language not found
    13541407    }
    13551408
    13561409    /**
     
    13751428    public function addrAppend($type, $addr)
    13761429    {
    13771430        $addresses = array();
    1378         foreach ($addr as $a) {
    1379             $addresses[] = $this->addrFormat($a);
     1431        foreach ($addr as $address) {
     1432            $addresses[] = $this->addrFormat($address);
    13801433        }
    13811434        return $type . ': ' . implode(', ', $addresses) . $this->LE;
    13821435    }
     
    13931446        if (empty($addr[1])) { // No name provided
    13941447            return $this->secureHeader($addr[0]);
    13951448        } else {
    1396             return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader(
     1449            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
    13971450                $addr[0]
    1398             ) . ">";
     1451            ) . '>';
    13991452        }
    14001453    }
    14011454
     
    14121465     */
    14131466    public function wrapText($message, $length, $qp_mode = false)
    14141467    {
    1415         $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
     1468        $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
    14161469        // If utf-8 encoding is used, we will need to make sure we don't
    14171470        // split multibyte characters when we wrap
    1418         $is_utf8 = (strtolower($this->CharSet) == "utf-8");
     1471        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
    14191472        $lelen = strlen($this->LE);
    14201473        $crlflen = strlen(self::CRLF);
    14211474
     
    14381491                            $len = $space_left;
    14391492                            if ($is_utf8) {
    14401493                                $len = $this->utf8CharBoundary($word, $len);
    1441                             } elseif (substr($word, $len - 1, 1) == "=") {
     1494                            } elseif (substr($word, $len - 1, 1) == '=') {
    14421495                                $len--;
    1443                             } elseif (substr($word, $len - 2, 1) == "=") {
     1496                            } elseif (substr($word, $len - 2, 1) == '=') {
    14441497                                $len -= 2;
    14451498                            }
    14461499                            $part = substr($word, 0, $len);
    14471500                            $word = substr($word, $len);
    14481501                            $buf .= ' ' . $part;
    1449                             $message .= $buf . sprintf("=%s", self::CRLF);
     1502                            $message .= $buf . sprintf('=%s', self::CRLF);
    14501503                        } else {
    14511504                            $message .= $buf . $soft_break;
    14521505                        }
     
    14591512                        $len = $length;
    14601513                        if ($is_utf8) {
    14611514                            $len = $this->utf8CharBoundary($word, $len);
    1462                         } elseif (substr($word, $len - 1, 1) == "=") {
     1515                        } elseif (substr($word, $len - 1, 1) == '=') {
    14631516                            $len--;
    1464                         } elseif (substr($word, $len - 2, 1) == "=") {
     1517                        } elseif (substr($word, $len - 2, 1) == '=') {
    14651518                            $len -= 2;
    14661519                        }
    14671520                        $part = substr($word, 0, $len);
     
    14681521                        $word = substr($word, $len);
    14691522
    14701523                        if (strlen($word) > 0) {
    1471                             $message .= $part . sprintf("=%s", self::CRLF);
     1524                            $message .= $part . sprintf('=%s', self::CRLF);
    14721525                        } else {
    14731526                            $buf = $part;
    14741527                        }
     
    15041557        $lookBack = 3;
    15051558        while (!$foundSplitPos) {
    15061559            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
    1507             $encodedCharPos = strpos($lastChunk, "=");
     1560            $encodedCharPos = strpos($lastChunk, '=');
    15081561            if ($encodedCharPos !== false) {
    15091562                // Found start of encoded character byte within $lookBack block.
    15101563                // Check the encoded byte value (the 2 chars after the '=')
     
    15311584        return $maxLength;
    15321585    }
    15331586
    1534 
    15351587    /**
    15361588     * Set the body wrapping.
    15371589     * @access public
     
    15721624        $this->boundary[3] = 'b3_' . $uniq_id;
    15731625
    15741626        if ($this->MessageDate == '') {
    1575             $result .= $this->headerLine('Date', self::rfcDate());
    1576         } else {
    1577             $result .= $this->headerLine('Date', $this->MessageDate);
     1627            $this->MessageDate = self::rfcDate();
    15781628        }
     1629        $result .= $this->headerLine('Date', $this->MessageDate);
    15791630
    1580         if ($this->ReturnPath) {
    1581             $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>');
    1582         } elseif ($this->Sender == '') {
    1583             $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>');
    1584         } else {
    1585             $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>');
    1586         }
    15871631
    15881632        // To be created automatically by mail()
    1589         if ($this->Mailer != 'mail') {
    1590             if ($this->SingleTo === true) {
    1591                 foreach ($this->to as $t) {
    1592                     $this->SingleToArray[] = $this->addrFormat($t);
     1633        if ($this->SingleTo === true) {
     1634            if ($this->Mailer != 'mail') {
     1635                foreach ($this->to as $toaddr) {
     1636                    $this->SingleToArray[] = $this->addrFormat($toaddr);
    15931637                }
    1594             } else {
    1595                 if (count($this->to) > 0) {
     1638            }
     1639        } else {
     1640            if (count($this->to) > 0) {
     1641                if ($this->Mailer != 'mail') {
    15961642                    $result .= $this->addrAppend('To', $this->to);
    1597                 } elseif (count($this->cc) == 0) {
    1598                     $result .= $this->headerLine('To', 'undisclosed-recipients:;');
    15991643                }
     1644            } elseif (count($this->cc) == 0) {
     1645                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
    16001646            }
    16011647        }
    16021648
     
    16081654        }
    16091655
    16101656        // sendmail and mail() extract Bcc from the header before sending
    1611         if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
     1657        if ((
     1658                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
     1659            )
     1660            and count($this->bcc) > 0
     1661        ) {
    16121662            $result .= $this->addrAppend('Bcc', $this->bcc);
    16131663        }
    16141664
     
    16241674        if ($this->MessageID != '') {
    16251675            $this->lastMessageID = $this->MessageID;
    16261676        } else {
    1627             $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname());
     1677            $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
    16281678        }
    16291679        $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
    16301680        $result .= $this->headerLine('X-Priority', $this->Priority);
     
    16671717    public function getMailMIME()
    16681718    {
    16691719        $result = '';
     1720        $ismultipart = true;
    16701721        switch ($this->message_type) {
    16711722            case 'inline':
    16721723                $result .= $this->headerLine('Content-Type', 'multipart/related;');
     
    16871738            default:
    16881739                // Catches case 'plain': and case '':
    16891740                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
     1741                $ismultipart = false;
    16901742                break;
    16911743        }
    16921744        //RFC1341 part 5 says 7bit is assumed if not specified
    16931745        if ($this->Encoding != '7bit') {
    1694             $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
     1746            //RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
     1747            if ($ismultipart) {
     1748                if ($this->Encoding == '8bit') {
     1749                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
     1750                }
     1751                //The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
     1752            } else {
     1753                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
     1754            }
    16951755        }
    16961756
    16971757        if ($this->Mailer != 'mail') {
     
    17041764    /**
    17051765     * Returns the whole MIME message.
    17061766     * Includes complete headers and body.
    1707      * Only valid post PreSend().
    1708      * @see PHPMailer::PreSend()
     1767     * Only valid post preSend().
     1768     * @see PHPMailer::preSend()
    17091769     * @access public
    17101770     * @return string
    17111771     */
     
    17321792
    17331793        $this->setWordWrap();
    17341794
     1795        $bodyEncoding = $this->Encoding;
     1796        $bodyCharSet = $this->CharSet;
     1797        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
     1798            $bodyEncoding = '7bit';
     1799            $bodyCharSet = 'us-ascii';
     1800        }
     1801        $altBodyEncoding = $this->Encoding;
     1802        $altBodyCharSet = $this->CharSet;
     1803        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
     1804            $altBodyEncoding = '7bit';
     1805            $altBodyCharSet = 'us-ascii';
     1806        }
    17351807        switch ($this->message_type) {
    17361808            case 'inline':
    1737                 $body .= $this->getBoundary($this->boundary[1], '', '', '');
    1738                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1809                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
     1810                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17391811                $body .= $this->LE . $this->LE;
    17401812                $body .= $this->attachAll('inline', $this->boundary[1]);
    17411813                break;
    17421814            case 'attach':
    1743                 $body .= $this->getBoundary($this->boundary[1], '', '', '');
    1744                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1815                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
     1816                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17451817                $body .= $this->LE . $this->LE;
    17461818                $body .= $this->attachAll('attachment', $this->boundary[1]);
    17471819                break;
     
    17501822                $body .= $this->headerLine('Content-Type', 'multipart/related;');
    17511823                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    17521824                $body .= $this->LE;
    1753                 $body .= $this->getBoundary($this->boundary[2], '', '', '');
    1754                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1825                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
     1826                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17551827                $body .= $this->LE . $this->LE;
    17561828                $body .= $this->attachAll('inline', $this->boundary[2]);
    17571829                $body .= $this->LE;
     
    17581830                $body .= $this->attachAll('attachment', $this->boundary[1]);
    17591831                break;
    17601832            case 'alt':
    1761                 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
    1762                 $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1833                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     1834                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    17631835                $body .= $this->LE . $this->LE;
    1764                 $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');
    1765                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1836                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
     1837                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17661838                $body .= $this->LE . $this->LE;
    17671839                if (!empty($this->Ical)) {
    17681840                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
     
    17721844                $body .= $this->endBoundary($this->boundary[1]);
    17731845                break;
    17741846            case 'alt_inline':
    1775                 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
    1776                 $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1847                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     1848                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    17771849                $body .= $this->LE . $this->LE;
    17781850                $body .= $this->textLine('--' . $this->boundary[1]);
    17791851                $body .= $this->headerLine('Content-Type', 'multipart/related;');
    17801852                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    17811853                $body .= $this->LE;
    1782                 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
    1783                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1854                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
     1855                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17841856                $body .= $this->LE . $this->LE;
    17851857                $body .= $this->attachAll('inline', $this->boundary[2]);
    17861858                $body .= $this->LE;
     
    17911863                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
    17921864                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    17931865                $body .= $this->LE;
    1794                 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
    1795                 $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1866                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     1867                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    17961868                $body .= $this->LE . $this->LE;
    1797                 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
    1798                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1869                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
     1870                $body .= $this->encodeString($this->Body, $bodyEncoding);
    17991871                $body .= $this->LE . $this->LE;
    18001872                $body .= $this->endBoundary($this->boundary[2]);
    18011873                $body .= $this->LE;
     
    18061878                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
    18071879                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
    18081880                $body .= $this->LE;
    1809                 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
    1810                 $body .= $this->encodeString($this->AltBody, $this->Encoding);
     1881                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
     1882                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
    18111883                $body .= $this->LE . $this->LE;
    18121884                $body .= $this->textLine('--' . $this->boundary[2]);
    18131885                $body .= $this->headerLine('Content-Type', 'multipart/related;');
    18141886                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
    18151887                $body .= $this->LE;
    1816                 $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');
    1817                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1888                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
     1889                $body .= $this->encodeString($this->Body, $bodyEncoding);
    18181890                $body .= $this->LE . $this->LE;
    18191891                $body .= $this->attachAll('inline', $this->boundary[3]);
    18201892                $body .= $this->LE;
     
    18241896                break;
    18251897            default:
    18261898                // catch case 'plain' and case ''
    1827                 $body .= $this->encodeString($this->Body, $this->Encoding);
     1899                $body .= $this->encodeString($this->Body, $bodyEncoding);
    18281900                break;
    18291901        }
    18301902
     
    18351907                if (!defined('PKCS7_TEXT')) {
    18361908                    throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
    18371909                }
     1910                //TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
    18381911                $file = tempnam(sys_get_temp_dir(), 'mail');
    18391912                file_put_contents($file, $body); //TODO check this worked
    18401913                $signed = tempnam(sys_get_temp_dir(), 'signed');
     
    18541927                    @unlink($signed);
    18551928                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
    18561929                }
    1857             } catch (phpmailerException $e) {
     1930            } catch (phpmailerException $exc) {
    18581931                $body = '';
    18591932                if ($this->exceptions) {
    1860                     throw $e;
     1933                    throw $exc;
    18611934                }
    18621935            }
    18631936        }
     
    18861959            $encoding = $this->Encoding;
    18871960        }
    18881961        $result .= $this->textLine('--' . $boundary);
    1889         $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);
     1962        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
    18901963        $result .= $this->LE;
    1891         $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
     1964        //RFC1341 part 5 says 7bit is assumed if not specified
     1965        if ($encoding != '7bit') {
     1966            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
     1967        }
    18921968        $result .= $this->LE;
    18931969
    18941970        return $result;
     
    19161992    {
    19171993        $this->message_type = array();
    19181994        if ($this->alternativeExists()) {
    1919             $this->message_type[] = "alt";
     1995            $this->message_type[] = 'alt';
    19201996        }
    19211997        if ($this->inlineImageExists()) {
    1922             $this->message_type[] = "inline";
     1998            $this->message_type[] = 'inline';
    19231999        }
    19242000        if ($this->attachmentExists()) {
    1925             $this->message_type[] = "attach";
     2001            $this->message_type[] = 'attach';
    19262002        }
    1927         $this->message_type = implode("_", $this->message_type);
    1928         if ($this->message_type == "") {
    1929             $this->message_type = "plain";
     2003        $this->message_type = implode('_', $this->message_type);
     2004        if ($this->message_type == '') {
     2005            $this->message_type = 'plain';
    19302006        }
    19312007    }
    19322008
     
    19922068                7 => 0
    19932069            );
    19942070
    1995         } catch (phpmailerException $e) {
    1996             $this->setError($e->getMessage());
     2071        } catch (phpmailerException $exc) {
     2072            $this->setError($exc->getMessage());
     2073            $this->edebug($exc->getMessage());
    19972074            if ($this->exceptions) {
    1998                 throw $e;
     2075                throw $exc;
    19992076            }
    2000             $this->edebug($e->getMessage() . "\n");
    20012077            return false;
    20022078        }
    20032079        return true;
     
    20562132                }
    20572133                $cidUniq[$cid] = true;
    20582134
    2059                 $mime[] = sprintf("--%s%s", $boundary, $this->LE);
     2135                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
    20602136                $mime[] = sprintf(
    2061                     "Content-Type: %s; name=\"%s\"%s",
     2137                    'Content-Type: %s; name="%s"%s',
    20622138                    $type,
    20632139                    $this->encodeHeader($this->secureHeader($name)),
    20642140                    $this->LE
    20652141                );
    2066                 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
     2142                //RFC1341 part 5 says 7bit is assumed if not specified
     2143                if ($encoding != '7bit') {
     2144                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
     2145                }
    20672146
    20682147                if ($disposition == 'inline') {
    2069                     $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
     2148                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
    20702149                }
    20712150
    20722151                // If a filename contains any of these chars, it should be quoted,
     
    20762155                if (!(empty($disposition))) {
    20772156                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
    20782157                        $mime[] = sprintf(
    2079                             "Content-Disposition: %s; filename=\"%s\"%s",
     2158                            'Content-Disposition: %s; filename="%s"%s',
    20802159                            $disposition,
    20812160                            $this->encodeHeader($this->secureHeader($name)),
    20822161                            $this->LE . $this->LE
     
    20832162                        );
    20842163                    } else {
    20852164                        $mime[] = sprintf(
    2086                             "Content-Disposition: %s; filename=%s%s",
     2165                            'Content-Disposition: %s; filename=%s%s',
    20872166                            $disposition,
    20882167                            $this->encodeHeader($this->secureHeader($name)),
    20892168                            $this->LE . $this->LE
     
    21102189            }
    21112190        }
    21122191
    2113         $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
     2192        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
    21142193
    2115         return implode("", $mime);
     2194        return implode('', $mime);
    21162195    }
    21172196
    21182197    /**
     
    21492228                }
    21502229            }
    21512230            return $file_buffer;
    2152         } catch (Exception $e) {
    2153             $this->setError($e->getMessage());
     2231        } catch (Exception $exc) {
     2232            $this->setError($exc->getMessage());
    21542233            return '';
    21552234        }
    21562235    }
     
    22012280     */
    22022281    public function encodeHeader($str, $position = 'text')
    22032282    {
    2204         $x = 0;
     2283        $matchcount = 0;
    22052284        switch (strtolower($position)) {
    22062285            case 'phrase':
    22072286                if (!preg_match('/[\200-\377]/', $str)) {
    2208                     // Can't use addslashes as we don't know what value has magic_quotes_sybase
     2287                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
    22092288                    $encoded = addcslashes($str, "\0..\37\177\\\"");
    22102289                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
    22112290                        return ($encoded);
     
    22132292                        return ("\"$encoded\"");
    22142293                    }
    22152294                }
    2216                 $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
     2295                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
    22172296                break;
    22182297            /** @noinspection PhpMissingBreakStatementInspection */
    22192298            case 'comment':
    2220                 $x = preg_match_all('/[()"]/', $str, $matches);
     2299                $matchcount = preg_match_all('/[()"]/', $str, $matches);
    22212300                // Intentional fall-through
    22222301            case 'text':
    22232302            default:
    2224                 $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
     2303                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
    22252304                break;
    22262305        }
    22272306
    2228         if ($x == 0) { //There are no chars that need encoding
     2307        if ($matchcount == 0) { //There are no chars that need encoding
    22292308            return ($str);
    22302309        }
    22312310
    22322311        $maxlen = 75 - 7 - strlen($this->CharSet);
    22332312        // Try to select the encoding which should produce the shortest output
    2234         if ($x > strlen($str) / 3) {
     2313        if ($matchcount > strlen($str) / 3) {
    22352314            //More than a third of the content will need encoding, so B encoding will be most efficient
    22362315            $encoding = 'B';
    22372316            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
     
    22502329            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
    22512330        }
    22522331
    2253         $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded);
     2332        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
    22542333        $encoded = trim(str_replace("\n", $this->LE, $encoded));
    22552334
    22562335        return $encoded;
     
    22722351    }
    22732352
    22742353    /**
     2354     * Does a string contain any 8-bit chars (in any charset)?
     2355     * @param string $text
     2356     * @return bool
     2357     */
     2358    public function has8bitChars($text)
     2359    {
     2360        return (bool)preg_match('/[\x80-\xFF]/', $text);
     2361    }
     2362
     2363    /**
    22752364     * Encode and wrap long multibyte strings for mail headers
    22762365     * without breaking lines within a character.
    2277      * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
     2366     * Adapted from a function by paravoid
     2367     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
    22782368     * @access public
    22792369     * @param string $str multi-byte text to wrap encode
    2280      * @param string $lf string to use as linefeed/end-of-line
     2370     * @param string $linebreak string to use as linefeed/end-of-line
    22812371     * @return string
    22822372     */
    2283     public function base64EncodeWrapMB($str, $lf = null)
     2373    public function base64EncodeWrapMB($str, $linebreak = null)
    22842374    {
    2285         $start = "=?" . $this->CharSet . "?B?";
    2286         $end = "?=";
    2287         $encoded = "";
    2288         if ($lf === null) {
    2289             $lf = $this->LE;
     2375        $start = '=?' . $this->CharSet . '?B?';
     2376        $end = '?=';
     2377        $encoded = '';
     2378        if ($linebreak === null) {
     2379            $linebreak = $this->LE;
    22902380        }
    22912381
    22922382        $mb_length = mb_strlen($str, $this->CharSet);
     
    23052395                $chunk = base64_encode($chunk);
    23062396                $lookBack++;
    23072397            } while (strlen($chunk) > $length);
    2308             $encoded .= $chunk . $lf;
     2398            $encoded .= $chunk . $linebreak;
    23092399        }
    23102400
    23112401        // Chomp the last linefeed
    2312         $encoded = substr($encoded, 0, -strlen($lf));
     2402        $encoded = substr($encoded, 0, -strlen($linebreak));
    23132403        return $encoded;
    23142404    }
    23152405
     
    23202410     * @param string $string The text to encode
    23212411     * @param integer $line_max Number of chars allowed on a line before wrapping
    23222412     * @return string
    2323      * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
     2413     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
    23242414     */
    23252415    public function encodeQP($string, $line_max = 76)
    23262416    {
    23272417        if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
    2328             return quoted_printable_encode($string);
     2418            return $this->fixEOL(quoted_printable_encode($string));
    23292419        }
    23302420        //Fall back to a pure PHP implementation
    23312421        $string = str_replace(
     
    23342424            rawurlencode($string)
    23352425        );
    23362426        $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
    2337         return $string;
     2427        return $this->fixEOL($string);
    23382428    }
    23392429
    23402430    /**
     
    23902480        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
    23912481            //If the string contains an '=', make sure it's the first thing we replace
    23922482            //so as to avoid double-encoding
    2393             $s = array_search('=', $matches[0]);
    2394             if ($s !== false) {
    2395                 unset($matches[0][$s]);
     2483            $eqkey = array_search('=', $matches[0]);
     2484            if ($eqkey !== false) {
     2485                unset($matches[0][$eqkey]);
    23962486                array_unshift($matches[0], '=');
    23972487            }
    23982488            foreach (array_unique($matches[0]) as $char) {
     
    26802770     */
    26812771    protected function serverHostname()
    26822772    {
     2773        $result = 'localhost.localdomain';
    26832774        if (!empty($this->Hostname)) {
    26842775            $result = $this->Hostname;
    2685         } elseif (isset($_SERVER['SERVER_NAME'])) {
     2776        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
    26862777            $result = $_SERVER['SERVER_NAME'];
    2687         } else {
    2688             $result = 'localhost.localdomain';
     2778        } elseif (function_exists('gethostname') && gethostname() !== false) {
     2779            $result = gethostname();
     2780        } elseif (php_uname('n') !== false) {
     2781            $result = php_uname('n');
    26892782        }
    2690 
    26912783        return $result;
    26922784    }
    26932785
     
    27702862     */
    27712863    public function msgHTML($message, $basedir = '', $advanced = false)
    27722864    {
    2773         preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
     2865        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
    27742866        if (isset($images[2])) {
    2775             foreach ($images[2] as $i => $url) {
     2867            foreach ($images[2] as $imgindex => $url) {
    27762868                // do not change urls for absolute images (thanks to corvuscorax)
    27772869                if (!preg_match('#^[A-z]+://#', $url)) {
    27782870                    $filename = basename($url);
     
    27962888                    )
    27972889                    ) {
    27982890                        $message = preg_replace(
    2799                             "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",
    2800                             $images[1][$i] . "=\"cid:" . $cid . "\"",
     2891                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
     2892                            $images[1][$imgindex] . '="cid:' . $cid . '"',
    28012893                            $message
    28022894                        );
    28032895                    }
     
    28052897            }
    28062898        }
    28072899        $this->isHTML(true);
    2808         if (empty($this->AltBody)) {
    2809             $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
    2810         }
    28112900        //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
    28122901        $this->Body = $this->normalizeBreaks($message);
    28132902        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
     2903        if (empty($this->AltBody)) {
     2904            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
     2905                self::CRLF . self::CRLF;
     2906        }
    28142907        return $this->Body;
    28152908    }
    28162909
     
    28242917    {
    28252918        if ($advanced) {
    28262919            require_once 'extras/class.html2text.php';
    2827             $h = new html2text($html);
    2828             return $h->get_text();
     2920            $htmlconverter = new html2text($html);
     2921            return $htmlconverter->get_text();
    28292922        }
    28302923        return html_entity_decode(
    28312924            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
     
    29203013            'txt' => 'text/plain',
    29213014            'rtx' => 'text/richtext',
    29223015            'rtf' => 'text/rtf',
     3016            'vcf' => 'text/vcard',
     3017            'vcard' => 'text/vcard',
    29233018            'xml' => 'text/xml',
    29243019            'xsl' => 'text/xml',
    29253020            'mpeg' => 'video/mpeg',
     
    29663061    public static function mb_pathinfo($path, $options = null)
    29673062    {
    29683063        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
    2969         $m = array();
    2970         preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
    2971         if (array_key_exists(1, $m)) {
    2972             $ret['dirname'] = $m[1];
     3064        $pathinfo = array();
     3065        preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo);
     3066        if (array_key_exists(1, $pathinfo)) {
     3067            $ret['dirname'] = $pathinfo[1];
    29733068        }
    2974         if (array_key_exists(2, $m)) {
    2975             $ret['basename'] = $m[2];
     3069        if (array_key_exists(2, $pathinfo)) {
     3070            $ret['basename'] = $pathinfo[2];
    29763071        }
    2977         if (array_key_exists(5, $m)) {
    2978             $ret['extension'] = $m[5];
     3072        if (array_key_exists(5, $pathinfo)) {
     3073            $ret['extension'] = $pathinfo[5];
    29793074        }
    2980         if (array_key_exists(3, $m)) {
    2981             $ret['filename'] = $m[3];
     3075        if (array_key_exists(3, $pathinfo)) {
     3076            $ret['filename'] = $pathinfo[3];
    29823077        }
    29833078        switch ($options) {
    29843079            case PATHINFO_DIRNAME:
     
    30243119            } else {
    30253120                throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
    30263121            }
    3027         } catch (Exception $e) {
    3028             $this->setError($e->getMessage());
    3029             if ($e->getCode() == self::STOP_CRITICAL) {
     3122        } catch (Exception $exc) {
     3123            $this->setError($exc->getMessage());
     3124            if ($exc->getCode() == self::STOP_CRITICAL) {
    30303125                return false;
    30313126            }
    30323127        }
     
    30613156
    30623157
    30633158    /**
    3064      * Set the private key file and password for S/MIME signing.
     3159     * Set the public and private key files and password for S/MIME signing.
    30653160     * @access public
    30663161     * @param string $cert_filename
    30673162     * @param string $key_filename
     
    30883183            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
    30893184                $line .= $txt[$i];
    30903185            } else {
    3091                 $line .= "=" . sprintf("%02X", $ord);
     3186                $line .= '=' . sprintf('%02X', $ord);
    30923187            }
    30933188        }
    30943189        return $line;
     
    30973192    /**
    30983193     * Generate a DKIM signature.
    30993194     * @access public
    3100      * @param string $s Header
     3195     * @param string $signheader Header
    31013196     * @throws phpmailerException
    31023197     * @return string
    31033198     */
    3104     public function DKIM_Sign($s)
     3199    public function DKIM_Sign($signheader)
    31053200    {
    31063201        if (!defined('PKCS7_TEXT')) {
    31073202            if ($this->exceptions) {
    3108                 throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.');
     3203                throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
    31093204            }
    31103205            return '';
    31113206        }
     
    31153210        } else {
    31163211            $privKey = $privKeyStr;
    31173212        }
    3118         if (openssl_sign($s, $signature, $privKey)) {
     3213        if (openssl_sign($signheader, $signature, $privKey)) {
    31193214            return base64_encode($signature);
    31203215        }
    31213216        return '';
     
    31243219    /**
    31253220     * Generate a DKIM canonicalization header.
    31263221     * @access public
    3127      * @param string $s Header
     3222     * @param string $signheader Header
    31283223     * @return string
    31293224     */
    3130     public function DKIM_HeaderC($s)
     3225    public function DKIM_HeaderC($signheader)
    31313226    {
    3132         $s = preg_replace("/\r\n\s+/", " ", $s);
    3133         $lines = explode("\r\n", $s);
     3227        $signheader = preg_replace('/\r\n\s+/', ' ', $signheader);
     3228        $lines = explode("\r\n", $signheader);
    31343229        foreach ($lines as $key => $line) {
    3135             list($heading, $value) = explode(":", $line, 2);
     3230            list($heading, $value) = explode(':', $line, 2);
    31363231            $heading = strtolower($heading);
    3137             $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces
    3138             $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value
     3232            $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
     3233            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
    31393234        }
    3140         $s = implode("\r\n", $lines);
    3141         return $s;
     3235        $signheader = implode("\r\n", $lines);
     3236        return $signheader;
    31423237    }
    31433238
    31443239    /**
     
    32053300        ); // Copied header fields (dkim-quoted-printable)
    32063301        $body = $this->DKIM_BodyC($body);
    32073302        $DKIMlen = strlen($body); // Length of body
    3208         $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body
    3209         $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";";
    3210         $dkimhdrs = "DKIM-Signature: v=1; a=" .
    3211             $DKIMsignatureType . "; q=" .
    3212             $DKIMquery . "; l=" .
    3213             $DKIMlen . "; s=" .
     3303        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
     3304        $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
     3305        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
     3306            $DKIMsignatureType . '; q=' .
     3307            $DKIMquery . '; l=' .
     3308            $DKIMlen . '; s=' .
    32143309            $this->DKIM_selector .
    32153310            ";\r\n" .
    3216             "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" .
     3311            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
    32173312            "\th=From:To:Subject;\r\n" .
    3218             "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" .
     3313            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
    32193314            "\tz=$from\r\n" .
    32203315            "\t|$to\r\n" .
    32213316            "\t|$subject;\r\n" .
     
    32293324    }
    32303325
    32313326    /**
     3327     * Allows for public read access to 'to' property.
     3328     * @access public
     3329     * @return array
     3330     */
     3331    public function getToAddresses()
     3332    {
     3333        return $this->to;
     3334    }
     3335
     3336    /**
     3337     * Allows for public read access to 'cc' property.
     3338     * @access public
     3339     * @return array
     3340     */
     3341    public function getCcAddresses()
     3342    {
     3343        return $this->cc;
     3344    }
     3345
     3346    /**
     3347     * Allows for public read access to 'bcc' property.
     3348     * @access public
     3349     * @return array
     3350     */
     3351    public function getBccAddresses()
     3352    {
     3353        return $this->bcc;
     3354    }
     3355
     3356    /**
     3357     * Allows for public read access to 'ReplyTo' property.
     3358     * @access public
     3359     * @return array
     3360     */
     3361    public function getReplyToAddresses()
     3362    {
     3363        return $this->ReplyTo;
     3364    }
     3365
     3366    /**
     3367     * Allows for public read access to 'all_recipients' property.
     3368     * @access public
     3369     * @return array
     3370     */
     3371    public function getAllRecipientAddresses()
     3372    {
     3373        return $this->all_recipients;
     3374    }
     3375
     3376    /**
    32323377     * Perform a callback.
    32333378     * @param bool $isSent
    32343379     * @param string $to
  • src/wp-includes/class-smtp.php

     
    11<?php
    22/**
    33 * PHPMailer RFC821 SMTP email transport class.
    4  * Version 5.2.7
    5  * PHP version 5.0.0
    6  * @category  PHP
    7  * @package   PHPMailer
    8  * @link      https://github.com/PHPMailer/PHPMailer/
    9  * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     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>
    108 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
    119 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
    12  * @copyright 2013 Marcus Bointon
    13  * @copyright 2004 - 2008 Andy Prevost
     10 * @author Brent R. Matzelle (original founder)
     11 * @copyright 2014 Marcus Bointon
    1412 * @copyright 2010 - 2012 Jim Jagielski
    15  * @license   http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     13 * @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
     16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     17 * FITNESS FOR A PARTICULAR PURPOSE.
    1618 */
    1719
    1820/**
    1921 * PHPMailer RFC821 SMTP email transport class.
    20  *
    21  * Implements RFC 821 SMTP commands
    22  * and provides some utility methods for sending mail to an SMTP server.
    23  *
    24  * PHP Version 5.0.0
    25  *
    26  * @category PHP
    27  * @package  PHPMailer
    28  * @link     https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php
    29  * @author   Chris Ryan <unknown@example.com>
    30  * @author   Marcus Bointon <phpmailer@synchromedia.co.uk>
    31  * @license  http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
     23 * @package PHPMailer
     24 * @author Chris Ryan <unknown@example.com>
     25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
    3226 */
    33 
    3427class SMTP
    3528{
    3629    /**
    37      * The PHPMailer SMTP Version number.
     30     * The PHPMailer SMTP version number.
     31     * @type string
    3832     */
    39     const VERSION = '5.2.7';
     33    const VERSION = '5.2.8';
    4034
    4135    /**
    4236     * SMTP line break constant.
     37     * @type string
    4338     */
    4439    const CRLF = "\r\n";
    4540
    4641    /**
    4742     * The SMTP port to use if one is not specified.
     43     * @type int
    4844     */
    4945    const DEFAULT_SMTP_PORT = 25;
    5046
    5147    /**
     48     * The maximum line length allowed by RFC 2822 section 2.1.1
     49     * @type int
     50     */
     51    const MAX_LINE_LENGTH = 998;
     52
     53    /**
    5254     * The PHPMailer SMTP Version number.
    5355     * @type string
    54      * @deprecated This should be a constant
     56     * @deprecated Use the constant instead
    5557     * @see SMTP::VERSION
    5658     */
    57     public $Version = '5.2.7';
     59    public $Version = '5.2.8';
    5860
    5961    /**
    6062     * SMTP server port number.
    6163     * @type int
    62      * @deprecated This is only ever ued as default value, so should be a constant
     64     * @deprecated This is only ever used as a default value, so use the constant instead
    6365     * @see SMTP::DEFAULT_SMTP_PORT
    6466     */
    6567    public $SMTP_PORT = 25;
    6668
    6769    /**
    68      * SMTP reply line ending
     70     * SMTP reply line ending.
    6971     * @type string
    70      * @deprecated Use the class constant instead
     72     * @deprecated Use the constant instead
    7173     * @see SMTP::CRLF
    7274     */
    7375    public $CRLF = "\r\n";
     
    7476
    7577    /**
    7678     * Debug output level.
    77      * Options: 0 for no output, 1 for commands, 2 for data and commands
     79     * Options:
     80     * * `0` No output
     81     * * `1` Commands
     82     * * `2` Data and commands
     83     * * `3` As 2 plus connection status
     84     * * `4` Low-level data output
    7885     * @type int
    7986     */
    8087    public $do_debug = 0;
    8188
    8289    /**
    83      * The function/method to use for debugging output.
    84      * Options: 'echo', 'html' or 'error_log'
     90     * How to handle debug output.
     91     * Options:
     92     * * `echo` Output plain-text as-is, appropriate for CLI
     93     * * `html` Output escaped, line breaks converted to <br>, appropriate for browser output
     94     * * `error_log` Output to error log as configured in php.ini
    8595     * @type string
    8696     */
    8797    public $Debugoutput = 'echo';
     
    8898
    8999    /**
    90100     * Whether to use VERP.
     101     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
     102     * @link http://www.postfix.org/VERP_README.html Info on VERP
    91103     * @type bool
    92104     */
    93105    public $do_verp = false;
    94106
    95107    /**
    96      * The SMTP timeout value for reads, in seconds.
     108     * The timeout value for connection, in seconds.
     109     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
     110     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
     111     * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
    97112     * @type int
    98113     */
    99     public $Timeout = 15;
     114    public $Timeout = 300;
    100115
    101116    /**
    102117     * The SMTP timelimit value for reads, in seconds.
     
    137152        $this->smtp_conn = 0;
    138153        $this->error = null;
    139154        $this->helo_rply = null;
    140 
    141155        $this->do_debug = 0;
    142156    }
    143157
     
    164178                break;
    165179            case 'echo':
    166180            default:
    167                 //Just echoes whatever was received
    168                 echo $str;
     181                echo gmdate('Y-m-d H:i:s')."\t".trim($str)."\n";
    169182        }
    170183    }
    171184
    172185    /**
    173186     * Connect to an SMTP server.
    174      * @param string $host    SMTP server IP or host name
    175      * @param int $port    The port number to connect to
     187     * @param string $host SMTP server IP or host name
     188     * @param int $port The port number to connect to
    176189     * @param int $timeout How long to wait for the connection to open
    177190     * @param array $options An array of options for stream_context_create()
    178191     * @access public
     
    182195    {
    183196        // Clear errors to avoid confusion
    184197        $this->error = null;
    185 
    186198        // Make sure we are __not__ connected
    187199        if ($this->connected()) {
    188200            // Already connected, generate error
     
    189201            $this->error = array('error' => 'Already connected to a server');
    190202            return false;
    191203        }
    192 
    193204        if (empty($port)) {
    194205            $port = self::DEFAULT_SMTP_PORT;
    195206        }
    196 
    197207        // Connect to the SMTP server
     208        if ($this->do_debug >= 3) {
     209            $this->edebug('Connection: opening');
     210        }
    198211        $errno = 0;
    199212        $errstr = '';
    200213        $socket_context = stream_context_create($options);
     
    207220            STREAM_CLIENT_CONNECT,
    208221            $socket_context
    209222        );
    210 
    211223        // Verify we connected properly
    212224        if (empty($this->smtp_conn)) {
    213225            $this->error = array(
     
    217229            );
    218230            if ($this->do_debug >= 1) {
    219231                $this->edebug(
    220                     'SMTP -> ERROR: ' . $this->error['error']
     232                    'SMTP ERROR: ' . $this->error['error']
    221233                    . ": $errstr ($errno)"
    222234                );
    223235            }
    224236            return false;
    225237        }
    226 
     238        if ($this->do_debug >= 3) {
     239            $this->edebug('Connection: opened');
     240        }
    227241        // SMTP server can take longer to respond, give longer timeout for first read
    228242        // Windows does not have support for this timeout function
    229243        if (substr(PHP_OS, 0, 3) != 'WIN') {
     
    233247            }
    234248            stream_set_timeout($this->smtp_conn, $timeout, 0);
    235249        }
    236 
    237250        // Get any announcement
    238251        $announce = $this->get_lines();
    239 
    240252        if ($this->do_debug >= 2) {
    241             $this->edebug('SMTP -> FROM SERVER:' . $announce);
     253            $this->edebug('SERVER -> CLIENT: ' . $announce);
    242254        }
    243 
    244255        return true;
    245256    }
    246257
     
    251262     */
    252263    public function startTLS()
    253264    {
    254         if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) {
     265        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
    255266            return false;
    256267        }
    257268        // Begin encrypted connection
     
    259270            $this->smtp_conn,
    260271            true,
    261272            STREAM_CRYPTO_METHOD_TLS_CLIENT
    262         )
    263         ) {
     273        )) {
    264274            return false;
    265275        }
    266276        return true;
     
    288298        if (empty($authtype)) {
    289299            $authtype = 'LOGIN';
    290300        }
    291 
    292301        switch ($authtype) {
    293302            case 'PLAIN':
    294303                // Start authentication
     
    351360                ) {
    352361                    return false;
    353362                }
    354 
    355363                //Though 0 based, there is a white space after the 3 digit number
    356364                //msg2
    357365                $challenge = substr($this->last_reply, 3);
     
    411419        // Eliminates the need to install mhash to compute a HMAC
    412420        // Hacked by Lance Rushing
    413421
    414         $b = 64; // byte length for md5
    415         if (strlen($key) > $b) {
     422        $bytelen = 64; // byte length for md5
     423        if (strlen($key) > $bytelen) {
    416424            $key = pack('H*', md5($key));
    417425        }
    418         $key = str_pad($key, $b, chr(0x00));
    419         $ipad = str_pad('', $b, chr(0x36));
    420         $opad = str_pad('', $b, chr(0x5c));
     426        $key = str_pad($key, $bytelen, chr(0x00));
     427        $ipad = str_pad('', $bytelen, chr(0x36));
     428        $opad = str_pad('', $bytelen, chr(0x5c));
    421429        $k_ipad = $key ^ $ipad;
    422430        $k_opad = $key ^ $opad;
    423431
     
    437445                // the socket is valid but we are not connected
    438446                if ($this->do_debug >= 1) {
    439447                    $this->edebug(
    440                         'SMTP -> NOTICE: EOF caught while checking if connected'
     448                        'SMTP NOTICE: EOF caught while checking if connected'
    441449                    );
    442450                }
    443451                $this->close();
     
    462470        if (!empty($this->smtp_conn)) {
    463471            // close the connection and cleanup
    464472            fclose($this->smtp_conn);
     473            if ($this->do_debug >= 3) {
     474                $this->edebug('Connection: closed');
     475            }
    465476            $this->smtp_conn = 0;
    466477        }
    467478    }
     
    483494        if (!$this->sendCommand('DATA', 'DATA', 354)) {
    484495            return false;
    485496        }
    486 
    487497        /* The server is ready to accept data!
    488          * according to rfc821 we should not send more than 1000
    489          * including the CRLF
    490          * characters on a single line so we will break the data up
    491          * into lines by \r and/or \n then if needed we will break
    492          * each of those into smaller lines to fit within the limit.
    493          * in addition we will be looking for lines that start with
    494          * a period '.' and append and additional period '.' to that
    495          * line. NOTE: this does not count towards limit.
     498         * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
     499         * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
     500         * smaller lines to fit within the limit.
     501         * We will also look for lines that start with a '.' and prepend an additional '.'.
     502         * NOTE: this does not count towards line-length limit.
    496503         */
    497504
    498         // Normalize the line breaks before exploding
    499         $msg_data = str_replace("\r\n", "\n", $msg_data);
    500         $msg_data = str_replace("\r", "\n", $msg_data);
    501         $lines = explode("\n", $msg_data);
     505        // Normalize line breaks before exploding
     506        $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
    502507
    503         /* We need to find a good way to determine if headers are
    504          * in the msg_data or if it is a straight msg body
    505          * currently I am assuming rfc822 definitions of msg headers
    506          * and if the first field of the first line (':' separated)
    507          * does not contain a space then it _should_ be a header
    508          * and we can process all lines before a blank "" line as
    509          * headers.
     508        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
     509         * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
     510         * process all lines before a blank line as headers.
    510511         */
    511512
    512513        $field = substr($lines[0], 0, strpos($lines[0], ':'));
    513514        $in_headers = false;
    514         if (!empty($field) && !strstr($field, ' ')) {
     515        if (!empty($field) && strpos($field, ' ') === false) {
    515516            $in_headers = true;
    516517        }
    517518
    518         //RFC 2822 section 2.1.1 limit
    519         $max_line_length = 998;
    520 
    521519        foreach ($lines as $line) {
    522             $lines_out = null;
    523             if ($line == '' && $in_headers) {
     520            $lines_out = array();
     521            if ($in_headers and $line == '') {
    524522                $in_headers = false;
    525523            }
    526524            // ok we need to break this line up into several smaller lines
    527             while (strlen($line) > $max_line_length) {
    528                 $pos = strrpos(substr($line, 0, $max_line_length), ' ');
    529 
    530                 // Patch to fix DOS attack
    531                 if (!$pos) {
    532                     $pos = $max_line_length - 1;
     525            //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
     526            while (isset($line[self::MAX_LINE_LENGTH])) {
     527                //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
     528                //so as to avoid breaking in the middle of a word
     529                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
     530                if (!$pos) { //Deliberately matches both false and 0
     531                    //No nice break found, add a hard break
     532                    $pos = self::MAX_LINE_LENGTH - 1;
    533533                    $lines_out[] = substr($line, 0, $pos);
    534534                    $line = substr($line, $pos);
    535535                } else {
     536                    //Break at the found point
    536537                    $lines_out[] = substr($line, 0, $pos);
     538                    //Move along by the amount we dealt with
    537539                    $line = substr($line, $pos + 1);
    538540                }
    539 
    540541                /* If processing headers add a LWSP-char to the front of new line
    541                  * rfc822 on long msg headers
     542                 * RFC822 section 3.1.1
    542543                 */
    543544                if ($in_headers) {
    544545                    $line = "\t" . $line;
     
    546547            }
    547548            $lines_out[] = $line;
    548549
    549             // send the lines to the server
    550             while (list(, $line_out) = @each($lines_out)) {
    551                 if (strlen($line_out) > 0) {
    552                     if (substr($line_out, 0, 1) == '.') {
    553                         $line_out = '.' . $line_out;
    554                     }
     550            // Send the lines to the server
     551            foreach ($lines_out as $line_out) {
     552                //RFC2821 section 4.5.2
     553                if (!empty($line_out) and $line_out[0] == '.') {
     554                    $line_out = '.' . $line_out;
    555555                }
    556556                $this->client_send($line_out . self::CRLF);
    557557            }
     
    565565     * Send an SMTP HELO or EHLO command.
    566566     * Used to identify the sending server to the receiving server.
    567567     * This makes sure that client and server are in a known state.
    568      * Implements from RFC 821: HELO <SP> <domain> <CRLF>
     568     * Implements RFC 821: HELO <SP> <domain> <CRLF>
    569569     * and RFC 2821 EHLO.
    570570     * @param string $host The host name or IP to connect to
    571571     * @access public
     
    574574    public function hello($host = '')
    575575    {
    576576        // Try extended hello first (RFC 2821)
    577         if (!$this->sendHello('EHLO', $host)) {
    578             if (!$this->sendHello('HELO', $host)) {
    579                 return false;
    580             }
    581         }
    582 
    583         return true;
     577        return (bool)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
    584578    }
    585579
    586580    /**
     
    588582     * Low-level implementation used by hello()
    589583     * @see hello()
    590584     * @param string $hello The HELO string
    591      * @param string $host  The hostname to say we are
     585     * @param string $host The hostname to say we are
    592586     * @access protected
    593587     * @return bool
    594588     */
     
    631625    public function quit($close_on_error = true)
    632626    {
    633627        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
    634         $e = $this->error; //Save any error
     628        $err = $this->error; //Save any error
    635629        if ($noerror or $close_on_error) {
    636630            $this->close();
    637             $this->error = $e; //Restore any error from the quit command
     631            $this->error = $err; //Restore any error from the quit command
    638632        }
    639633        return $noerror;
    640634    }
     
    641635
    642636    /**
    643637     * Send an SMTP RCPT command.
    644      * Sets the TO argument to $to.
     638     * Sets the TO argument to $toaddr.
    645639     * Returns true if the recipient was accepted false if it was rejected.
    646640     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
    647      * @param string $to The address the message is being sent to
     641     * @param string $toaddr The address the message is being sent to
    648642     * @access public
    649643     * @return bool
    650644     */
    651     public function recipient($to)
     645    public function recipient($toaddr)
    652646    {
    653647        return $this->sendCommand(
    654             'RCPT TO ',
    655             'RCPT TO:<' . $to . '>',
     648            'RCPT TO',
     649            'RCPT TO:<' . $toaddr . '>',
    656650            array(250, 251)
    657651        );
    658652    }
     
    681675    {
    682676        if (!$this->connected()) {
    683677            $this->error = array(
    684                 "error" => "Called $command without being connected"
     678                'error' => "Called $command without being connected"
    685679            );
    686680            return false;
    687681        }
     
    691685        $code = substr($reply, 0, 3);
    692686
    693687        if ($this->do_debug >= 2) {
    694             $this->edebug('SMTP -> FROM SERVER:' . $reply);
     688            $this->edebug('SERVER -> CLIENT: ' . $reply);
    695689        }
    696690
    697691        if (!in_array($code, (array)$expect)) {
    698692            $this->last_reply = null;
    699693            $this->error = array(
    700                 "error" => "$command command failed",
    701                 "smtp_code" => $code,
    702                 "detail" => substr($reply, 4)
     694                'error' => "$command command failed",
     695                'smtp_code' => $code,
     696                'detail' => substr($reply, 4)
    703697            );
    704698            if ($this->do_debug >= 1) {
    705699                $this->edebug(
    706                     'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply
     700                    'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply
    707701                );
    708702            }
    709703            return false;
     
    729723     */
    730724    public function sendAndMail($from)
    731725    {
    732         return $this->sendCommand("SAML", "SAML FROM:$from", 250);
     726        return $this->sendCommand('SAML', "SAML FROM:$from", 250);
    733727    }
    734728
    735729    /**
     
    740734     */
    741735    public function verify($name)
    742736    {
    743         return $this->sendCommand("VRFY", "VRFY $name", array(250, 251));
     737        return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
    744738    }
    745739
    746740    /**
     
    751745     */
    752746    public function noop()
    753747    {
    754         return $this->sendCommand("NOOP", "NOOP", 250);
     748        return $this->sendCommand('NOOP', 'NOOP', 250);
    755749    }
    756750
    757751    /**
    758752     * Send an SMTP TURN command.
    759753     * This is an optional command for SMTP that this class does not support.
    760      * This method is here to make the RFC821 Definition
    761      * complete for this class and __may__ be implemented in future
     754     * This method is here to make the RFC821 Definition complete for this class
     755     * and _may_ be implemented in future
    762756     * Implements from rfc 821: TURN <CRLF>
    763757     * @access public
    764758     * @return bool
     
    769763            'error' => 'The SMTP TURN command is not implemented'
    770764        );
    771765        if ($this->do_debug >= 1) {
    772             $this->edebug('SMTP -> NOTICE: ' . $this->error['error']);
     766            $this->edebug('SMTP NOTICE: ' . $this->error['error']);
    773767        }
    774768        return false;
    775769    }
     
    778772     * Send raw data to the server.
    779773     * @param string $data The data to send
    780774     * @access public
    781      * @return int|bool The number of bytes sent to the server or FALSE on error
     775     * @return int|bool The number of bytes sent to the server or false on error
    782776     */
    783777    public function client_send($data)
    784778    {
    785779        if ($this->do_debug >= 1) {
    786             $this->edebug("CLIENT -> SMTP: $data");
     780            $this->edebug("CLIENT -> SERVER: $data");
    787781        }
    788782        return fwrite($this->smtp_conn, $data);
    789783    }
     
    819813     */
    820814    protected function get_lines()
    821815    {
     816        // If the connection is bad, give up straight away
     817        if (!is_resource($this->smtp_conn)) {
     818            return '';
     819        }
    822820        $data = '';
    823821        $endtime = 0;
    824         // If the connection is bad, give up now
    825         if (!is_resource($this->smtp_conn)) {
    826             return $data;
    827         }
    828822        stream_set_timeout($this->smtp_conn, $this->Timeout);
    829823        if ($this->Timelimit > 0) {
    830824            $endtime = time() + $this->Timelimit;
     
    839833            if ($this->do_debug >= 4) {
    840834                $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
    841835            }
    842             // if 4th character is a space, we are done reading, break the loop
    843             if (substr($str, 3, 1) == ' ') {
     836            // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
     837            if ((isset($str[3]) and $str[3] == ' ')) {
    844838                break;
    845839            }
    846840            // Timed-out? Log and break
     
    854848                break;
    855849            }
    856850            // Now check if reads took too long
    857             if ($endtime) {
    858                 if (time() > $endtime) {
    859                     if ($this->do_debug >= 4) {
    860                         $this->edebug(
    861                             'SMTP -> get_lines(): timelimit reached ('
    862                             . $this->Timelimit . ' sec)'
    863                         );
    864                     }
    865                     break;
     851            if ($endtime and time() > $endtime) {
     852                if ($this->do_debug >= 4) {
     853                    $this->edebug(
     854                        'SMTP -> get_lines(): timelimit reached ('.
     855                        $this->Timelimit . ' sec)'
     856                    );
    866857                }
     858                break;
    867859            }
    868860        }
    869861        return $data;