Make WordPress Core


Ignore:
Timestamp:
01/06/2017 06:07:32 AM (8 years ago)
Author:
dd32
Message:

Mail: Upgrade PHPMailer to 5.2.21.

Merges [39645], [36083], [33142], [33124], [29783], [27385] to the 3.8 branch.
See #37210.

Location:
branches/3.8
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/3.8

  • branches/3.8/src/wp-includes/class-smtp.php

    r23522 r39730  
    11<?php
    2 /*~ class.smtp.php
    3 .---------------------------------------------------------------------------.
    4 |  Software: PHPMailer - PHP email class                                    |
    5 |   Version: 5.2.4                                                          |
    6 |      Site: https://code.google.com/a/apache-extras.org/p/phpmailer/       |
    7 | ------------------------------------------------------------------------- |
    8 |     Admin: Jim Jagielski (project admininistrator)                        |
    9 |   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
    10 |          : Marcus Bointon (coolbru) coolbru@users.sourceforge.net         |
    11 |          : Jim Jagielski (jimjag) jimjag@gmail.com                        |
    12 |   Founder: Brent R. Matzelle (original founder)                           |
    13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
    14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
    15 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
    16 | ------------------------------------------------------------------------- |
    17 |   License: Distributed under the Lesser General Public License (LGPL)     |
    18 |            http://www.gnu.org/copyleft/lesser.html                        |
    19 | This program is distributed in the hope that it will be useful - WITHOUT  |
    20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
    21 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
    22 '---------------------------------------------------------------------------'
    23 */
    24 
    252/**
    26  * PHPMailer - PHP SMTP email transport class
    27  * NOTE: Designed for use with PHP version 5 and up
     3 * PHPMailer RFC821 SMTP email transport class.
     4 * PHP Version 5
    285 * @package PHPMailer
    29  * @author Andy Prevost
    30  * @author Marcus Bointon
    31  * @copyright 2004 - 2008 Andy Prevost
    32  * @author Jim Jagielski
     6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
     7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
     8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     10 * @author Brent R. Matzelle (original founder)
     11 * @copyright 2014 Marcus Bointon
    3312 * @copyright 2010 - 2012 Jim Jagielski
    34  * @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.
    3518 */
    3619
    3720/**
    38  * PHP RFC821 SMTP client
    39  *
    40  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
    41  * SMTP also provides some utility methods for sending mail to an SMTP server.
     21 * PHPMailer RFC821 SMTP email transport class.
     22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
     23 * @package PHPMailer
    4224 * @author Chris Ryan
    43  * @package PHPMailer
     25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
    4426 */
    45 
    46 class SMTP {
    47   /**
    48    *  SMTP server port
    49    *  @var int
    50    */
    51   public $SMTP_PORT = 25;
    52 
    53   /**
    54    *  SMTP reply line ending (don't change)
    55    *  @var string
    56    */
    57   public $CRLF = "\r\n";
    58 
    59   /**
    60    *  Sets whether debugging is turned on
    61    *  @var bool
    62    */
    63   public $do_debug;       // the level of debug to perform
    64 
    65   /**
    66    * Sets the function/method to use for debugging output.
    67    * Right now we only honor "echo" or "error_log"
    68    * @var string
    69    */
    70   public $Debugoutput     = "echo";
    71 
    72   /**
    73    *  Sets VERP use on/off (default is off)
    74    *  @var bool
    75    */
    76   public $do_verp = false;
    77 
    78   /**
    79    * Sets the SMTP timeout value for reads, in seconds
    80    * @var int
    81    */
    82   public $Timeout         = 15;
    83 
    84   /**
    85    * Sets the SMTP timelimit value for reads, in seconds
    86    * @var int
    87    */
    88   public $Timelimit       = 30;
    89 
    90   /**
    91    * Sets the SMTP PHPMailer Version number
    92    * @var string
    93    */
    94   public $Version         = '5.2.4';
    95 
    96   /////////////////////////////////////////////////
    97   // PROPERTIES, PRIVATE AND PROTECTED
    98   /////////////////////////////////////////////////
    99 
    100   /**
    101    * @var resource The socket to the server
    102    */
    103   private $smtp_conn;
    104   /**
    105    * @var string Error message, if any, for the last call
    106    */
    107   private $error;
    108   /**
    109    * @var string The reply the server sent to us for HELO
    110    */
    111   private $helo_rply;
    112 
    113   /**
    114    * Outputs debugging info via user-defined method
    115    * @param string $str
    116    */
    117   private function edebug($str) {
    118     if ($this->Debugoutput == "error_log") {
    119         error_log($str);
    120     } else {
    121         echo $str;
    122     }
    123   }
    124 
    125   /**
    126    * Initialize the class so that the data is in a known state.
    127    * @access public
    128    * @return SMTP
    129    */
    130   public function __construct() {
    131     $this->smtp_conn = 0;
    132     $this->error = null;
    133     $this->helo_rply = null;
    134 
    135     $this->do_debug = 0;
    136   }
    137 
    138   /////////////////////////////////////////////////
    139   // CONNECTION FUNCTIONS
    140   /////////////////////////////////////////////////
    141 
    142   /**
    143    * Connect to the server specified on the port specified.
    144    * If the port is not specified use the default SMTP_PORT.
    145    * If tval is specified then a connection will try and be
    146    * established with the server for that number of seconds.
    147    * If tval is not specified the default is 30 seconds to
    148    * try on the connection.
    149    *
    150    * SMTP CODE SUCCESS: 220
    151    * SMTP CODE FAILURE: 421
    152    * @access public
    153    * @param string $host
    154    * @param int $port
    155    * @param int $tval
    156    * @return bool
    157    */
    158   public function Connect($host, $port = 0, $tval = 30) {
    159     // set the error val to null so there is no confusion
    160     $this->error = null;
    161 
    162     // make sure we are __not__ connected
    163     if($this->connected()) {
    164       // already connected, generate error
    165       $this->error = array("error" => "Already connected to a server");
    166       return false;
    167     }
    168 
    169     if(empty($port)) {
    170       $port = $this->SMTP_PORT;
    171     }
    172 
    173     // connect to the smtp server
    174     $this->smtp_conn = @fsockopen($host,    // the host of the server
    175                                  $port,    // the port to use
    176                                  $errno,   // error number if any
    177                                  $errstr,  // error message if any
    178                                  $tval);   // give up after ? secs
    179     // verify we connected properly
    180     if(empty($this->smtp_conn)) {
    181       $this->error = array("error" => "Failed to connect to server",
    182                            "errno" => $errno,
    183                            "errstr" => $errstr);
    184       if($this->do_debug >= 1) {
    185         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />');
    186       }
    187       return false;
    188     }
    189 
    190     // SMTP server can take longer to respond, give longer timeout for first read
    191     // Windows does not have support for this timeout function
    192     if(substr(PHP_OS, 0, 3) != "WIN") {
    193      $max = ini_get('max_execution_time');
    194      if ($max != 0 && $tval > $max) { // don't bother if unlimited
    195       @set_time_limit($tval);
    196      }
    197      stream_set_timeout($this->smtp_conn, $tval, 0);
    198     }
    199 
    200     // get any announcement
    201     $announce = $this->get_lines();
    202 
    203     if($this->do_debug >= 2) {
    204       $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />');
    205     }
    206 
    207     return true;
    208   }
    209 
    210   /**
    211    * Initiate a TLS communication with the server.
    212    *
    213    * SMTP CODE 220 Ready to start TLS
    214    * SMTP CODE 501 Syntax error (no parameters allowed)
    215    * SMTP CODE 454 TLS not available due to temporary reason
    216    * @access public
    217    * @return bool success
    218    */
    219   public function StartTLS() {
    220     $this->error = null; # to avoid confusion
    221 
    222     if(!$this->connected()) {
    223       $this->error = array("error" => "Called StartTLS() without being connected");
    224       return false;
    225     }
    226 
    227     fputs($this->smtp_conn,"STARTTLS" . $this->CRLF);
    228 
    229     $rply = $this->get_lines();
    230     $code = substr($rply,0,3);
    231 
    232     if($this->do_debug >= 2) {
    233       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    234     }
    235 
    236     if($code != 220) {
    237       $this->error =
    238          array("error"     => "STARTTLS not accepted from server",
    239                "smtp_code" => $code,
    240                "smtp_msg"  => substr($rply,4));
    241       if($this->do_debug >= 1) {
    242         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    243       }
    244       return false;
    245     }
    246 
    247     // Begin encrypted connection
    248     if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
    249       return false;
    250     }
    251 
    252     return true;
    253   }
    254 
    255   /**
    256    * Performs SMTP authentication.  Must be run after running the
    257    * Hello() method.  Returns true if successfully authenticated.
    258    * @access public
    259    * @param string $username
    260    * @param string $password
    261    * @param string $authtype
    262    * @param string $realm
    263    * @param string $workstation
    264    * @return bool
    265    */
    266   public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') {
    267     if (empty($authtype)) {
    268       $authtype = 'LOGIN';
    269     }
    270 
    271     switch ($authtype) {
    272       case 'PLAIN':
    273         // Start authentication
    274         fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF);
    275    
    276         $rply = $this->get_lines();
    277         $code = substr($rply,0,3);
    278    
    279         if($code != 334) {
    280           $this->error =
    281             array("error" => "AUTH not accepted from server",
    282                   "smtp_code" => $code,
    283                   "smtp_msg" => substr($rply,4));
    284           if($this->do_debug >= 1) {
    285             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    286           }
    287           return false;
    288         }
    289         // Send encoded username and password
    290         fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF);
    291 
    292         $rply = $this->get_lines();
    293         $code = substr($rply,0,3);
    294    
    295         if($code != 235) {
    296           $this->error =
    297             array("error" => "Authentication not accepted from server",
    298                   "smtp_code" => $code,
    299                   "smtp_msg" => substr($rply,4));
    300           if($this->do_debug >= 1) {
    301             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    302           }
    303           return false;
    304         }
    305         break;
    306       case 'LOGIN':
    307         // Start authentication
    308         fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
    309    
    310         $rply = $this->get_lines();
    311         $code = substr($rply,0,3);
    312    
    313         if($code != 334) {
    314           $this->error =
    315             array("error" => "AUTH not accepted from server",
    316                   "smtp_code" => $code,
    317                   "smtp_msg" => substr($rply,4));
    318           if($this->do_debug >= 1) {
    319             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    320           }
    321           return false;
    322         }
    323    
    324         // Send encoded username
    325         fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
    326    
    327         $rply = $this->get_lines();
    328         $code = substr($rply,0,3);
    329    
    330         if($code != 334) {
    331           $this->error =
    332             array("error" => "Username not accepted from server",
    333                   "smtp_code" => $code,
    334                   "smtp_msg" => substr($rply,4));
    335           if($this->do_debug >= 1) {
    336             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    337           }
    338           return false;
    339         }
    340    
    341         // Send encoded password
    342         fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
    343    
    344         $rply = $this->get_lines();
    345         $code = substr($rply,0,3);
    346    
    347         if($code != 235) {
    348           $this->error =
    349             array("error" => "Password not accepted from server",
    350                   "smtp_code" => $code,
    351                   "smtp_msg" => substr($rply,4));
    352           if($this->do_debug >= 1) {
    353             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    354           }
    355           return false;
    356         }
    357         break;
    358       case 'NTLM':
    359         /*
    360          * ntlm_sasl_client.php
    361          ** Bundled with Permission
    362          **
    363          ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
    364          ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
     27class SMTP
     28{
     29    /**
     30     * The PHPMailer SMTP version number.
     31     * @var string
     32     */
     33    const VERSION = '5.2.21';
     34
     35    /**
     36     * SMTP line break constant.
     37     * @var string
     38     */
     39    const CRLF = "\r\n";
     40
     41    /**
     42     * The SMTP port to use if one is not specified.
     43     * @var integer
     44     */
     45    const DEFAULT_SMTP_PORT = 25;
     46
     47    /**
     48     * The maximum line length allowed by RFC 2822 section 2.1.1
     49     * @var integer
     50     */
     51    const MAX_LINE_LENGTH = 998;
     52
     53    /**
     54     * Debug level for no output
     55     */
     56    const DEBUG_OFF = 0;
     57
     58    /**
     59     * Debug level to show client -> server messages
     60     */
     61    const DEBUG_CLIENT = 1;
     62
     63    /**
     64     * Debug level to show client -> server and server -> client messages
     65     */
     66    const DEBUG_SERVER = 2;
     67
     68    /**
     69     * Debug level to show connection status, client -> server and server -> client messages
     70     */
     71    const DEBUG_CONNECTION = 3;
     72
     73    /**
     74     * Debug level to show all messages
     75     */
     76    const DEBUG_LOWLEVEL = 4;
     77
     78    /**
     79     * The PHPMailer SMTP Version number.
     80     * @var string
     81     * @deprecated Use the `VERSION` constant instead
     82     * @see SMTP::VERSION
     83     */
     84    public $Version = '5.2.21';
     85
     86    /**
     87     * SMTP server port number.
     88     * @var integer
     89     * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
     90     * @see SMTP::DEFAULT_SMTP_PORT
     91     */
     92    public $SMTP_PORT = 25;
     93
     94    /**
     95     * SMTP reply line ending.
     96     * @var string
     97     * @deprecated Use the `CRLF` constant instead
     98     * @see SMTP::CRLF
     99     */
     100    public $CRLF = "\r\n";
     101
     102    /**
     103     * Debug output level.
     104     * Options:
     105     * * self::DEBUG_OFF (`0`) No debug output, default
     106     * * self::DEBUG_CLIENT (`1`) Client commands
     107     * * self::DEBUG_SERVER (`2`) Client commands and server responses
     108     * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
     109     * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
     110     * @var integer
     111     */
     112    public $do_debug = self::DEBUG_OFF;
     113
     114    /**
     115     * How to handle debug output.
     116     * Options:
     117     * * `echo` Output plain-text as-is, appropriate for CLI
     118     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
     119     * * `error_log` Output to error log as configured in php.ini
     120     *
     121     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
     122     * <code>
     123     * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
     124     * </code>
     125     * @var string|callable
     126     */
     127    public $Debugoutput = 'echo';
     128
     129    /**
     130     * Whether to use VERP.
     131     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
     132     * @link http://www.postfix.org/VERP_README.html Info on VERP
     133     * @var boolean
     134     */
     135    public $do_verp = false;
     136
     137    /**
     138     * The timeout value for connection, in seconds.
     139     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
     140     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
     141     * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
     142     * @var integer
     143     */
     144    public $Timeout = 300;
     145
     146    /**
     147     * How long to wait for commands to complete, in seconds.
     148     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
     149     * @var integer
     150     */
     151    public $Timelimit = 300;
     152
     153    /**
     154     * @var array patterns to extract smtp transaction id from smtp reply
     155     * Only first capture group will be use, use non-capturing group to deal with it
     156     * Extend this class to override this property to fulfil your needs.
     157     */
     158    protected $smtp_transaction_id_patterns = array(
     159        'exim' => '/[0-9]{3} OK id=(.*)/',
     160        'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
     161        'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/'
     162    );
     163
     164    /**
     165     * The socket for the server connection.
     166     * @var resource
     167     */
     168    protected $smtp_conn;
     169
     170    /**
     171     * Error information, if any, for the last SMTP command.
     172     * @var array
     173     */
     174    protected $error = array(
     175        'error' => '',
     176        'detail' => '',
     177        'smtp_code' => '',
     178        'smtp_code_ex' => ''
     179    );
     180
     181    /**
     182     * The reply the server sent to us for HELO.
     183     * If null, no HELO string has yet been received.
     184     * @var string|null
     185     */
     186    protected $helo_rply = null;
     187
     188    /**
     189     * The set of SMTP extensions sent in reply to EHLO command.
     190     * Indexes of the array are extension names.
     191     * Value at index 'HELO' or 'EHLO' (according to command that was sent)
     192     * represents the server name. In case of HELO it is the only element of the array.
     193     * Other values can be boolean TRUE or an array containing extension options.
     194     * If null, no HELO/EHLO string has yet been received.
     195     * @var array|null
     196     */
     197    protected $server_caps = null;
     198
     199    /**
     200     * The most recent reply received from the server.
     201     * @var string
     202     */
     203    protected $last_reply = '';
     204
     205    /**
     206     * Output debugging info via a user-selected method.
     207     * @see SMTP::$Debugoutput
     208     * @see SMTP::$do_debug
     209     * @param string $str Debug string to output
     210     * @param integer $level The debug level of this message; see DEBUG_* constants
     211     * @return void
     212     */
     213    protected function edebug($str, $level = 0)
     214    {
     215        if ($level > $this->do_debug) {
     216            return;
     217        }
     218        //Avoid clash with built-in function names
     219        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
     220            call_user_func($this->Debugoutput, $str, $level);
     221            return;
     222        }
     223        switch ($this->Debugoutput) {
     224            case 'error_log':
     225                //Don't output, just log
     226                error_log($str);
     227                break;
     228            case 'html':
     229                //Cleans up output a bit for a better looking, HTML-safe output
     230                echo htmlentities(
     231                    preg_replace('/[\r\n]+/', '', $str),
     232                    ENT_QUOTES,
     233                    'UTF-8'
     234                )
     235                . "<br>\n";
     236                break;
     237            case 'echo':
     238            default:
     239                //Normalize line breaks
     240                $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
     241                echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
     242                    "\n",
     243                    "\n                   \t                  ",
     244                    trim($str)
     245                )."\n";
     246        }
     247    }
     248
     249    /**
     250     * Connect to an SMTP server.
     251     * @param string $host SMTP server IP or host name
     252     * @param integer $port The port number to connect to
     253     * @param integer $timeout How long to wait for the connection to open
     254     * @param array $options An array of options for stream_context_create()
     255     * @access public
     256     * @return boolean
     257     */
     258    public function connect($host, $port = null, $timeout = 30, $options = array())
     259    {
     260        static $streamok;
     261        //This is enabled by default since 5.0.0 but some providers disable it
     262        //Check this once and cache the result
     263        if (is_null($streamok)) {
     264            $streamok = function_exists('stream_socket_client');
     265        }
     266        // Clear errors to avoid confusion
     267        $this->setError('');
     268        // Make sure we are __not__ connected
     269        if ($this->connected()) {
     270            // Already connected, generate error
     271            $this->setError('Already connected to a server');
     272            return false;
     273        }
     274        if (empty($port)) {
     275            $port = self::DEFAULT_SMTP_PORT;
     276        }
     277        // Connect to the SMTP server
     278        $this->edebug(
     279            "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),
     280            self::DEBUG_CONNECTION
     281        );
     282        $errno = 0;
     283        $errstr = '';
     284        if ($streamok) {
     285            $socket_context = stream_context_create($options);
     286            set_error_handler(array($this, 'errorHandler'));
     287            $this->smtp_conn = stream_socket_client(
     288                $host . ":" . $port,
     289                $errno,
     290                $errstr,
     291                $timeout,
     292                STREAM_CLIENT_CONNECT,
     293                $socket_context
     294            );
     295            restore_error_handler();
     296        } else {
     297            //Fall back to fsockopen which should work in more places, but is missing some features
     298            $this->edebug(
     299                "Connection: stream_socket_client not available, falling back to fsockopen",
     300                self::DEBUG_CONNECTION
     301            );
     302            set_error_handler(array($this, 'errorHandler'));
     303            $this->smtp_conn = fsockopen(
     304                $host,
     305                $port,
     306                $errno,
     307                $errstr,
     308                $timeout
     309            );
     310            restore_error_handler();
     311        }
     312        // Verify we connected properly
     313        if (!is_resource($this->smtp_conn)) {
     314            $this->setError(
     315                'Failed to connect to server',
     316                $errno,
     317                $errstr
     318            );
     319            $this->edebug(
     320                'SMTP ERROR: ' . $this->error['error']
     321                . ": $errstr ($errno)",
     322                self::DEBUG_CLIENT
     323            );
     324            return false;
     325        }
     326        $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
     327        // SMTP server can take longer to respond, give longer timeout for first read
     328        // Windows does not have support for this timeout function
     329        if (substr(PHP_OS, 0, 3) != 'WIN') {
     330            $max = ini_get('max_execution_time');
     331            // Don't bother if unlimited
     332            if ($max != 0 && $timeout > $max) {
     333                @set_time_limit($timeout);
     334            }
     335            stream_set_timeout($this->smtp_conn, $timeout, 0);
     336        }
     337        // Get any announcement
     338        $announce = $this->get_lines();
     339        $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
     340        return true;
     341    }
     342
     343    /**
     344     * Initiate a TLS (encrypted) session.
     345     * @access public
     346     * @return boolean
     347     */
     348    public function startTLS()
     349    {
     350        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
     351            return false;
     352        }
     353
     354        //Allow the best TLS version(s) we can
     355        $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
     356
     357        //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
     358        //so add them back in manually if we can
     359        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
     360            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
     361            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
     362        }
     363
     364        // Begin encrypted connection
     365        if (!stream_socket_enable_crypto(
     366            $this->smtp_conn,
     367            true,
     368            $crypto_method
     369        )) {
     370            return false;
     371        }
     372        return true;
     373    }
     374
     375    /**
     376     * Perform SMTP authentication.
     377     * Must be run after hello().
     378     * @see hello()
     379     * @param string $username The user name
     380     * @param string $password The password
     381     * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5)
     382     * @param string $realm The auth realm for NTLM
     383     * @param string $workstation The auth workstation for NTLM
     384     * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
     385     * @return bool True if successfully authenticated.* @access public
     386     */
     387    public function authenticate(
     388        $username,
     389        $password,
     390        $authtype = null,
     391        $realm = '',
     392        $workstation = '',
     393        $OAuth = null
     394    ) {
     395        if (!$this->server_caps) {
     396            $this->setError('Authentication is not allowed before HELO/EHLO');
     397            return false;
     398        }
     399
     400        if (array_key_exists('EHLO', $this->server_caps)) {
     401        // SMTP extensions are available. Let's try to find a proper authentication method
     402
     403            if (!array_key_exists('AUTH', $this->server_caps)) {
     404                $this->setError('Authentication is not allowed at this stage');
     405                // 'at this stage' means that auth may be allowed after the stage changes
     406                // e.g. after STARTTLS
     407                return false;
     408            }
     409
     410            self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
     411            self::edebug(
     412                'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
     413                self::DEBUG_LOWLEVEL
     414            );
     415
     416            if (empty($authtype)) {
     417                foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) {
     418                    if (in_array($method, $this->server_caps['AUTH'])) {
     419                        $authtype = $method;
     420                        break;
     421                    }
     422                }
     423                if (empty($authtype)) {
     424                    $this->setError('No supported authentication methods found');
     425                    return false;
     426                }
     427                self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
     428            }
     429
     430            if (!in_array($authtype, $this->server_caps['AUTH'])) {
     431                $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
     432                return false;
     433            }
     434        } elseif (empty($authtype)) {
     435            $authtype = 'LOGIN';
     436        }
     437        switch ($authtype) {
     438            case 'PLAIN':
     439                // Start authentication
     440                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
     441                    return false;
     442                }
     443                // Send encoded username and password
     444                if (!$this->sendCommand(
     445                    'User & Password',
     446                    base64_encode("\0" . $username . "\0" . $password),
     447                    235
     448                )
     449                ) {
     450                    return false;
     451                }
     452                break;
     453            case 'LOGIN':
     454                // Start authentication
     455                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
     456                    return false;
     457                }
     458                if (!$this->sendCommand("Username", base64_encode($username), 334)) {
     459                    return false;
     460                }
     461                if (!$this->sendCommand("Password", base64_encode($password), 235)) {
     462                    return false;
     463                }
     464                break;
     465            case 'CRAM-MD5':
     466                // Start authentication
     467                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
     468                    return false;
     469                }
     470                // Get the challenge
     471                $challenge = base64_decode(substr($this->last_reply, 4));
     472
     473                // Build the response
     474                $response = $username . ' ' . $this->hmac($challenge, $password);
     475
     476                // send encoded credentials
     477                return $this->sendCommand('Username', base64_encode($response), 235);
     478            default:
     479                $this->setError("Authentication method \"$authtype\" is not supported");
     480                return false;
     481        }
     482        return true;
     483    }
     484
     485    /**
     486     * Calculate an MD5 HMAC hash.
     487     * Works like hash_hmac('md5', $data, $key)
     488     * in case that function is not available
     489     * @param string $data The data to hash
     490     * @param string $key  The key to hash with
     491     * @access protected
     492     * @return string
     493     */
     494    protected function hmac($data, $key)
     495    {
     496        if (function_exists('hash_hmac')) {
     497            return hash_hmac('md5', $data, $key);
     498        }
     499
     500        // The following borrowed from
     501        // http://php.net/manual/en/function.mhash.php#27225
     502
     503        // RFC 2104 HMAC implementation for php.
     504        // Creates an md5 HMAC.
     505        // Eliminates the need to install mhash to compute a HMAC
     506        // by Lance Rushing
     507
     508        $bytelen = 64; // byte length for md5
     509        if (strlen($key) > $bytelen) {
     510            $key = pack('H*', md5($key));
     511        }
     512        $key = str_pad($key, $bytelen, chr(0x00));
     513        $ipad = str_pad('', $bytelen, chr(0x36));
     514        $opad = str_pad('', $bytelen, chr(0x5c));
     515        $k_ipad = $key ^ $ipad;
     516        $k_opad = $key ^ $opad;
     517
     518        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
     519    }
     520
     521    /**
     522     * Check connection state.
     523     * @access public
     524     * @return boolean True if connected.
     525     */
     526    public function connected()
     527    {
     528        if (is_resource($this->smtp_conn)) {
     529            $sock_status = stream_get_meta_data($this->smtp_conn);
     530            if ($sock_status['eof']) {
     531                // The socket is valid but we are not connected
     532                $this->edebug(
     533                    'SMTP NOTICE: EOF caught while checking if connected',
     534                    self::DEBUG_CLIENT
     535                );
     536                $this->close();
     537                return false;
     538            }
     539            return true; // everything looks good
     540        }
     541        return false;
     542    }
     543
     544    /**
     545     * Close the socket and clean up the state of the class.
     546     * Don't use this function without first trying to use QUIT.
     547     * @see quit()
     548     * @access public
     549     * @return void
     550     */
     551    public function close()
     552    {
     553        $this->setError('');
     554        $this->server_caps = null;
     555        $this->helo_rply = null;
     556        if (is_resource($this->smtp_conn)) {
     557            // close the connection and cleanup
     558            fclose($this->smtp_conn);
     559            $this->smtp_conn = null; //Makes for cleaner serialization
     560            $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
     561        }
     562    }
     563
     564    /**
     565     * Send an SMTP DATA command.
     566     * Issues a data command and sends the msg_data to the server,
     567     * finializing the mail transaction. $msg_data is the message
     568     * that is to be send with the headers. Each header needs to be
     569     * on a single line followed by a <CRLF> with the message headers
     570     * and the message body being separated by and additional <CRLF>.
     571     * Implements rfc 821: DATA <CRLF>
     572     * @param string $msg_data Message data to send
     573     * @access public
     574     * @return boolean
     575     */
     576    public function data($msg_data)
     577    {
     578        //This will use the standard timelimit
     579        if (!$this->sendCommand('DATA', 'DATA', 354)) {
     580            return false;
     581        }
     582
     583        /* The server is ready to accept data!
     584         * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
     585         * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
     586         * smaller lines to fit within the limit.
     587         * We will also look for lines that start with a '.' and prepend an additional '.'.
     588         * NOTE: this does not count towards line-length limit.
    365589         */
    366         require_once('ntlm_sasl_client.php');
    367         $temp = new stdClass();
    368         $ntlm_client = new ntlm_sasl_client_class;
    369         if(! $ntlm_client->Initialize($temp)){//let's test if every function its available
    370             $this->error = array("error" => $temp->error);
    371             if($this->do_debug >= 1) {
    372                 $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF);
    373             }
     590
     591        // Normalize line breaks before exploding
     592        $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
     593
     594        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
     595         * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
     596         * process all lines before a blank line as headers.
     597         */
     598
     599        $field = substr($lines[0], 0, strpos($lines[0], ':'));
     600        $in_headers = false;
     601        if (!empty($field) && strpos($field, ' ') === false) {
     602            $in_headers = true;
     603        }
     604
     605        foreach ($lines as $line) {
     606            $lines_out = array();
     607            if ($in_headers and $line == '') {
     608                $in_headers = false;
     609            }
     610            //Break this line up into several smaller lines if it's too long
     611            //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
     612            while (isset($line[self::MAX_LINE_LENGTH])) {
     613                //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
     614                //so as to avoid breaking in the middle of a word
     615                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
     616                //Deliberately matches both false and 0
     617                if (!$pos) {
     618                    //No nice break found, add a hard break
     619                    $pos = self::MAX_LINE_LENGTH - 1;
     620                    $lines_out[] = substr($line, 0, $pos);
     621                    $line = substr($line, $pos);
     622                } else {
     623                    //Break at the found point
     624                    $lines_out[] = substr($line, 0, $pos);
     625                    //Move along by the amount we dealt with
     626                    $line = substr($line, $pos + 1);
     627                }
     628                //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
     629                if ($in_headers) {
     630                    $line = "\t" . $line;
     631                }
     632            }
     633            $lines_out[] = $line;
     634
     635            //Send the lines to the server
     636            foreach ($lines_out as $line_out) {
     637                //RFC2821 section 4.5.2
     638                if (!empty($line_out) and $line_out[0] == '.') {
     639                    $line_out = '.' . $line_out;
     640                }
     641                $this->client_send($line_out . self::CRLF);
     642            }
     643        }
     644
     645        //Message data has been sent, complete the command
     646        //Increase timelimit for end of DATA command
     647        $savetimelimit = $this->Timelimit;
     648        $this->Timelimit = $this->Timelimit * 2;
     649        $result = $this->sendCommand('DATA END', '.', 250);
     650        //Restore timelimit
     651        $this->Timelimit = $savetimelimit;
     652        return $result;
     653    }
     654
     655    /**
     656     * Send an SMTP HELO or EHLO command.
     657     * Used to identify the sending server to the receiving server.
     658     * This makes sure that client and server are in a known state.
     659     * Implements RFC 821: HELO <SP> <domain> <CRLF>
     660     * and RFC 2821 EHLO.
     661     * @param string $host The host name or IP to connect to
     662     * @access public
     663     * @return boolean
     664     */
     665    public function hello($host = '')
     666    {
     667        //Try extended hello first (RFC 2821)
     668        return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
     669    }
     670
     671    /**
     672     * Send an SMTP HELO or EHLO command.
     673     * Low-level implementation used by hello()
     674     * @see hello()
     675     * @param string $hello The HELO string
     676     * @param string $host The hostname to say we are
     677     * @access protected
     678     * @return boolean
     679     */
     680    protected function sendHello($hello, $host)
     681    {
     682        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
     683        $this->helo_rply = $this->last_reply;
     684        if ($noerror) {
     685            $this->parseHelloFields($hello);
     686        } else {
     687            $this->server_caps = null;
     688        }
     689        return $noerror;
     690    }
     691
     692    /**
     693     * Parse a reply to HELO/EHLO command to discover server extensions.
     694     * In case of HELO, the only parameter that can be discovered is a server name.
     695     * @access protected
     696     * @param string $type - 'HELO' or 'EHLO'
     697     */
     698    protected function parseHelloFields($type)
     699    {
     700        $this->server_caps = array();
     701        $lines = explode("\n", $this->helo_rply);
     702
     703        foreach ($lines as $n => $s) {
     704            //First 4 chars contain response code followed by - or space
     705            $s = trim(substr($s, 4));
     706            if (empty($s)) {
     707                continue;
     708            }
     709            $fields = explode(' ', $s);
     710            if (!empty($fields)) {
     711                if (!$n) {
     712                    $name = $type;
     713                    $fields = $fields[0];
     714                } else {
     715                    $name = array_shift($fields);
     716                    switch ($name) {
     717                        case 'SIZE':
     718                            $fields = ($fields ? $fields[0] : 0);
     719                            break;
     720                        case 'AUTH':
     721                            if (!is_array($fields)) {
     722                                $fields = array();
     723                            }
     724                            break;
     725                        default:
     726                            $fields = true;
     727                    }
     728                }
     729                $this->server_caps[$name] = $fields;
     730            }
     731        }
     732    }
     733
     734    /**
     735     * Send an SMTP MAIL command.
     736     * Starts a mail transaction from the email address specified in
     737     * $from. Returns true if successful or false otherwise. If True
     738     * the mail transaction is started and then one or more recipient
     739     * commands may be called followed by a data command.
     740     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     741     * @param string $from Source address of this message
     742     * @access public
     743     * @return boolean
     744     */
     745    public function mail($from)
     746    {
     747        $useVerp = ($this->do_verp ? ' XVERP' : '');
     748        return $this->sendCommand(
     749            'MAIL FROM',
     750            'MAIL FROM:<' . $from . '>' . $useVerp,
     751            250
     752        );
     753    }
     754
     755    /**
     756     * Send an SMTP QUIT command.
     757     * Closes the socket if there is no error or the $close_on_error argument is true.
     758     * Implements from rfc 821: QUIT <CRLF>
     759     * @param boolean $close_on_error Should the connection close if an error occurs?
     760     * @access public
     761     * @return boolean
     762     */
     763    public function quit($close_on_error = true)
     764    {
     765        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
     766        $err = $this->error; //Save any error
     767        if ($noerror or $close_on_error) {
     768            $this->close();
     769            $this->error = $err; //Restore any error from the quit command
     770        }
     771        return $noerror;
     772    }
     773
     774    /**
     775     * Send an SMTP RCPT command.
     776     * Sets the TO argument to $toaddr.
     777     * Returns true if the recipient was accepted false if it was rejected.
     778     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     779     * @param string $address The address the message is being sent to
     780     * @access public
     781     * @return boolean
     782     */
     783    public function recipient($address)
     784    {
     785        return $this->sendCommand(
     786            'RCPT TO',
     787            'RCPT TO:<' . $address . '>',
     788            array(250, 251)
     789        );
     790    }
     791
     792    /**
     793     * Send an SMTP RSET command.
     794     * Abort any transaction that is currently in progress.
     795     * Implements rfc 821: RSET <CRLF>
     796     * @access public
     797     * @return boolean True on success.
     798     */
     799    public function reset()
     800    {
     801        return $this->sendCommand('RSET', 'RSET', 250);
     802    }
     803
     804    /**
     805     * Send a command to an SMTP server and check its return code.
     806     * @param string $command The command name - not sent to the server
     807     * @param string $commandstring The actual command to send
     808     * @param integer|array $expect One or more expected integer success codes
     809     * @access protected
     810     * @return boolean True on success.
     811     */
     812    protected function sendCommand($command, $commandstring, $expect)
     813    {
     814        if (!$this->connected()) {
     815            $this->setError("Called $command without being connected");
    374816            return false;
    375817        }
    376         $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1
    377        
    378         fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF);
    379 
    380         $rply = $this->get_lines();
    381         $code = substr($rply,0,3);
    382        
    383 
    384         if($code != 334) {
    385             $this->error =
    386                 array("error" => "AUTH not accepted from server",
    387                       "smtp_code" => $code,
    388                       "smtp_msg" => substr($rply,4));
    389             if($this->do_debug >= 1) {
    390                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
    391             }
     818        //Reject line breaks in all commands
     819        if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
     820            $this->setError("Command '$command' contained line breaks");
    392821            return false;
    393822        }
    394        
    395         $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2
    396         $challange = base64_decode($challange);
    397         $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password);
    398         $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3
    399         // Send encoded username
    400         fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF);
    401 
    402         $rply = $this->get_lines();
    403         $code = substr($rply,0,3);
    404 
    405         if($code != 235) {
    406             $this->error =
    407                 array("error" => "Could not authenticate",
    408                       "smtp_code" => $code,
    409                       "smtp_msg" => substr($rply,4));
    410             if($this->do_debug >= 1) {
    411                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
    412             }
     823        $this->client_send($commandstring . self::CRLF);
     824
     825        $this->last_reply = $this->get_lines();
     826        // Fetch SMTP code and possible error code explanation
     827        $matches = array();
     828        if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
     829            $code = $matches[1];
     830            $code_ex = (count($matches) > 2 ? $matches[2] : null);
     831            // Cut off error code from each response line
     832            $detail = preg_replace(
     833                "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",
     834                '',
     835                $this->last_reply
     836            );
     837        } else {
     838            // Fall back to simple parsing if regex fails
     839            $code = substr($this->last_reply, 0, 3);
     840            $code_ex = null;
     841            $detail = substr($this->last_reply, 4);
     842        }
     843
     844        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
     845
     846        if (!in_array($code, (array)$expect)) {
     847            $this->setError(
     848                "$command command failed",
     849                $detail,
     850                $code,
     851                $code_ex
     852            );
     853            $this->edebug(
     854                'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
     855                self::DEBUG_CLIENT
     856            );
    413857            return false;
    414858        }
    415         break;
    416     }
    417     return true;
    418   }
    419 
    420   /**
    421    * Returns true if connected to a server otherwise false
    422    * @access public
    423    * @return bool
    424    */
    425   public function Connected() {
    426     if(!empty($this->smtp_conn)) {
    427       $sock_status = socket_get_status($this->smtp_conn);
    428       if($sock_status["eof"]) {
    429         // the socket is valid but we are not connected
    430         if($this->do_debug >= 1) {
    431             $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected");
    432         }
    433         $this->Close();
     859
     860        $this->setError('');
     861        return true;
     862    }
     863
     864    /**
     865     * Send an SMTP SAML command.
     866     * Starts a mail transaction from the email address specified in $from.
     867     * Returns true if successful or false otherwise. If True
     868     * the mail transaction is started and then one or more recipient
     869     * commands may be called followed by a data command. This command
     870     * will send the message to the users terminal if they are logged
     871     * in and send them an email.
     872     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     873     * @param string $from The address the message is from
     874     * @access public
     875     * @return boolean
     876     */
     877    public function sendAndMail($from)
     878    {
     879        return $this->sendCommand('SAML', "SAML FROM:$from", 250);
     880    }
     881
     882    /**
     883     * Send an SMTP VRFY command.
     884     * @param string $name The name to verify
     885     * @access public
     886     * @return boolean
     887     */
     888    public function verify($name)
     889    {
     890        return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
     891    }
     892
     893    /**
     894     * Send an SMTP NOOP command.
     895     * Used to keep keep-alives alive, doesn't actually do anything
     896     * @access public
     897     * @return boolean
     898     */
     899    public function noop()
     900    {
     901        return $this->sendCommand('NOOP', 'NOOP', 250);
     902    }
     903
     904    /**
     905     * Send an SMTP TURN command.
     906     * This is an optional command for SMTP that this class does not support.
     907     * This method is here to make the RFC821 Definition complete for this class
     908     * and _may_ be implemented in future
     909     * Implements from rfc 821: TURN <CRLF>
     910     * @access public
     911     * @return boolean
     912     */
     913    public function turn()
     914    {
     915        $this->setError('The SMTP TURN command is not implemented');
     916        $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
    434917        return false;
    435       }
    436       return true; // everything looks good
    437     }
    438     return false;
    439   }
    440 
    441   /**
    442    * Closes the socket and cleans up the state of the class.
    443    * It is not considered good to use this function without
    444    * first trying to use QUIT.
    445    * @access public
    446    * @return void
    447    */
    448   public function Close() {
    449     $this->error = null; // so there is no confusion
    450     $this->helo_rply = null;
    451     if(!empty($this->smtp_conn)) {
    452       // close the connection and cleanup
    453       fclose($this->smtp_conn);
    454       $this->smtp_conn = 0;
    455     }
    456   }
    457 
    458   /////////////////////////////////////////////////
    459   // SMTP COMMANDS
    460   /////////////////////////////////////////////////
    461 
    462   /**
    463    * Issues a data command and sends the msg_data to the server
    464    * finializing the mail transaction. $msg_data is the message
    465    * that is to be send with the headers. Each header needs to be
    466    * on a single line followed by a <CRLF> with the message headers
    467    * and the message body being seperated by and additional <CRLF>.
    468    *
    469    * Implements rfc 821: DATA <CRLF>
    470    *
    471    * SMTP CODE INTERMEDIATE: 354
    472    *     [data]
    473    *     <CRLF>.<CRLF>
    474    *     SMTP CODE SUCCESS: 250
    475    *     SMTP CODE FAILURE: 552,554,451,452
    476    * SMTP CODE FAILURE: 451,554
    477    * SMTP CODE ERROR  : 500,501,503,421
    478    * @access public
    479    * @param string $msg_data
    480    * @return bool
    481    */
    482   public function Data($msg_data) {
    483     $this->error = null; // so no confusion is caused
    484 
    485     if(!$this->connected()) {
    486       $this->error = array(
    487               "error" => "Called Data() without being connected");
    488       return false;
    489     }
    490 
    491     fputs($this->smtp_conn,"DATA" . $this->CRLF);
    492 
    493     $rply = $this->get_lines();
    494     $code = substr($rply,0,3);
    495 
    496     if($this->do_debug >= 2) {
    497       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    498     }
    499 
    500     if($code != 354) {
    501       $this->error =
    502         array("error" => "DATA command not accepted from server",
    503               "smtp_code" => $code,
    504               "smtp_msg" => substr($rply,4));
    505       if($this->do_debug >= 1) {
    506         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    507       }
    508       return false;
    509     }
    510 
    511     /* the server is ready to accept data!
    512      * according to rfc 821 we should not send more than 1000
    513      * including the CRLF
    514      * characters on a single line so we will break the data up
    515      * into lines by \r and/or \n then if needed we will break
    516      * each of those into smaller lines to fit within the limit.
    517      * in addition we will be looking for lines that start with
    518      * a period '.' and append and additional period '.' to that
    519      * line. NOTE: this does not count towards limit.
    520      */
    521 
    522     // normalize the line breaks so we know the explode works
    523     $msg_data = str_replace("\r\n","\n",$msg_data);
    524     $msg_data = str_replace("\r","\n",$msg_data);
    525     $lines = explode("\n",$msg_data);
    526 
    527     /* we need to find a good way to determine is headers are
    528      * in the msg_data or if it is a straight msg body
    529      * currently I am assuming rfc 822 definitions of msg headers
    530      * and if the first field of the first line (':' sperated)
    531      * does not contain a space then it _should_ be a header
    532      * and we can process all lines before a blank "" line as
    533      * headers.
    534      */
    535 
    536     $field = substr($lines[0],0,strpos($lines[0],":"));
    537     $in_headers = false;
    538     if(!empty($field) && !strstr($field," ")) {
    539       $in_headers = true;
    540     }
    541 
    542     $max_line_length = 998; // used below; set here for ease in change
    543 
    544     while(list(,$line) = @each($lines)) {
    545       $lines_out = null;
    546       if($line == "" && $in_headers) {
    547         $in_headers = false;
    548       }
    549       // ok we need to break this line up into several smaller lines
    550       while(strlen($line) > $max_line_length) {
    551         $pos = strrpos(substr($line,0,$max_line_length)," ");
    552 
    553         // Patch to fix DOS attack
    554         if(!$pos) {
    555           $pos = $max_line_length - 1;
    556           $lines_out[] = substr($line,0,$pos);
    557           $line = substr($line,$pos);
    558         } else {
    559           $lines_out[] = substr($line,0,$pos);
    560           $line = substr($line,$pos + 1);
    561         }
    562 
    563         /* if processing headers add a LWSP-char to the front of new line
    564          * rfc 822 on long msg headers
    565          */
    566         if($in_headers) {
    567           $line = "\t" . $line;
    568         }
    569       }
    570       $lines_out[] = $line;
    571 
    572       // send the lines to the server
    573       while(list(,$line_out) = @each($lines_out)) {
    574         if(strlen($line_out) > 0)
    575         {
    576           if(substr($line_out, 0, 1) == ".") {
    577             $line_out = "." . $line_out;
    578           }
    579         }
    580         fputs($this->smtp_conn,$line_out . $this->CRLF);
    581       }
    582     }
    583 
    584     // message data has been sent
    585     fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
    586 
    587     $rply = $this->get_lines();
    588     $code = substr($rply,0,3);
    589 
    590     if($this->do_debug >= 2) {
    591       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    592     }
    593 
    594     if($code != 250) {
    595       $this->error =
    596         array("error" => "DATA not accepted from server",
    597               "smtp_code" => $code,
    598               "smtp_msg" => substr($rply,4));
    599       if($this->do_debug >= 1) {
    600         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    601       }
    602       return false;
    603     }
    604     return true;
    605   }
    606 
    607   /**
    608    * Sends the HELO command to the smtp server.
    609    * This makes sure that we and the server are in
    610    * the same known state.
    611    *
    612    * Implements from rfc 821: HELO <SP> <domain> <CRLF>
    613    *
    614    * SMTP CODE SUCCESS: 250
    615    * SMTP CODE ERROR  : 500, 501, 504, 421
    616    * @access public
    617    * @param string $host
    618    * @return bool
    619    */
    620   public function Hello($host = '') {
    621     $this->error = null; // so no confusion is caused
    622 
    623     if(!$this->connected()) {
    624       $this->error = array(
    625             "error" => "Called Hello() without being connected");
    626       return false;
    627     }
    628 
    629     // if hostname for HELO was not specified send default
    630     if(empty($host)) {
    631       // determine appropriate default to send to server
    632       $host = "localhost";
    633     }
    634 
    635     // Send extended hello first (RFC 2821)
    636     if(!$this->SendHello("EHLO", $host)) {
    637       if(!$this->SendHello("HELO", $host)) {
    638         return false;
    639       }
    640     }
    641 
    642     return true;
    643   }
    644 
    645   /**
    646    * Sends a HELO/EHLO command.
    647    * @access private
    648    * @param string $hello
    649    * @param string $host
    650    * @return bool
    651    */
    652   private function SendHello($hello, $host) {
    653     fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
    654 
    655     $rply = $this->get_lines();
    656     $code = substr($rply,0,3);
    657 
    658     if($this->do_debug >= 2) {
    659       $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '<br />');
    660     }
    661 
    662     if($code != 250) {
    663       $this->error =
    664         array("error" => $hello . " not accepted from server",
    665               "smtp_code" => $code,
    666               "smtp_msg" => substr($rply,4));
    667       if($this->do_debug >= 1) {
    668         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    669       }
    670       return false;
    671     }
    672 
    673     $this->helo_rply = $rply;
    674 
    675     return true;
    676   }
    677 
    678   /**
    679    * Starts a mail transaction from the email address specified in
    680    * $from. Returns true if successful or false otherwise. If True
    681    * the mail transaction is started and then one or more Recipient
    682    * commands may be called followed by a Data command.
    683    *
    684    * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
    685    *
    686    * SMTP CODE SUCCESS: 250
    687    * SMTP CODE SUCCESS: 552,451,452
    688    * SMTP CODE SUCCESS: 500,501,421
    689    * @access public
    690    * @param string $from
    691    * @return bool
    692    */
    693   public function Mail($from) {
    694     $this->error = null; // so no confusion is caused
    695 
    696     if(!$this->connected()) {
    697       $this->error = array(
    698               "error" => "Called Mail() without being connected");
    699       return false;
    700     }
    701 
    702     $useVerp = ($this->do_verp ? " XVERP" : "");
    703     fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);
    704 
    705     $rply = $this->get_lines();
    706     $code = substr($rply,0,3);
    707 
    708     if($this->do_debug >= 2) {
    709       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    710     }
    711 
    712     if($code != 250) {
    713       $this->error =
    714         array("error" => "MAIL not accepted from server",
    715               "smtp_code" => $code,
    716               "smtp_msg" => substr($rply,4));
    717       if($this->do_debug >= 1) {
    718         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    719       }
    720       return false;
    721     }
    722     return true;
    723   }
    724 
    725   /**
    726    * Sends the quit command to the server and then closes the socket
    727    * if there is no error or the $close_on_error argument is true.
    728    *
    729    * Implements from rfc 821: QUIT <CRLF>
    730    *
    731    * SMTP CODE SUCCESS: 221
    732    * SMTP CODE ERROR  : 500
    733    * @access public
    734    * @param bool $close_on_error
    735    * @return bool
    736    */
    737   public function Quit($close_on_error = true) {
    738     $this->error = null; // so there is no confusion
    739 
    740     if(!$this->connected()) {
    741       $this->error = array(
    742               "error" => "Called Quit() without being connected");
    743       return false;
    744     }
    745 
    746     // send the quit command to the server
    747     fputs($this->smtp_conn,"quit" . $this->CRLF);
    748 
    749     // get any good-bye messages
    750     $byemsg = $this->get_lines();
    751 
    752     if($this->do_debug >= 2) {
    753       $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '<br />');
    754     }
    755 
    756     $rval = true;
    757     $e = null;
    758 
    759     $code = substr($byemsg,0,3);
    760     if($code != 221) {
    761       // use e as a tmp var cause Close will overwrite $this->error
    762       $e = array("error" => "SMTP server rejected quit command",
    763                  "smtp_code" => $code,
    764                  "smtp_rply" => substr($byemsg,4));
    765       $rval = false;
    766       if($this->do_debug >= 1) {
    767         $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '<br />');
    768       }
    769     }
    770 
    771     if(empty($e) || $close_on_error) {
    772       $this->Close();
    773     }
    774 
    775     return $rval;
    776   }
    777 
    778   /**
    779    * Sends the command RCPT to the SMTP server with the TO: argument of $to.
    780    * Returns true if the recipient was accepted false if it was rejected.
    781    *
    782    * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
    783    *
    784    * SMTP CODE SUCCESS: 250,251
    785    * SMTP CODE FAILURE: 550,551,552,553,450,451,452
    786    * SMTP CODE ERROR  : 500,501,503,421
    787    * @access public
    788    * @param string $to
    789    * @return bool
    790    */
    791   public function Recipient($to) {
    792     $this->error = null; // so no confusion is caused
    793 
    794     if(!$this->connected()) {
    795       $this->error = array(
    796               "error" => "Called Recipient() without being connected");
    797       return false;
    798     }
    799 
    800     fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
    801 
    802     $rply = $this->get_lines();
    803     $code = substr($rply,0,3);
    804 
    805     if($this->do_debug >= 2) {
    806       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    807     }
    808 
    809     if($code != 250 && $code != 251) {
    810       $this->error =
    811         array("error" => "RCPT not accepted from server",
    812               "smtp_code" => $code,
    813               "smtp_msg" => substr($rply,4));
    814       if($this->do_debug >= 1) {
    815         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    816       }
    817       return false;
    818     }
    819     return true;
    820   }
    821 
    822   /**
    823    * Sends the RSET command to abort and transaction that is
    824    * currently in progress. Returns true if successful false
    825    * otherwise.
    826    *
    827    * Implements rfc 821: RSET <CRLF>
    828    *
    829    * SMTP CODE SUCCESS: 250
    830    * SMTP CODE ERROR  : 500,501,504,421
    831    * @access public
    832    * @return bool
    833    */
    834   public function Reset() {
    835     $this->error = null; // so no confusion is caused
    836 
    837     if(!$this->connected()) {
    838       $this->error = array(
    839               "error" => "Called Reset() without being connected");
    840       return false;
    841     }
    842 
    843     fputs($this->smtp_conn,"RSET" . $this->CRLF);
    844 
    845     $rply = $this->get_lines();
    846     $code = substr($rply,0,3);
    847 
    848     if($this->do_debug >= 2) {
    849       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    850     }
    851 
    852     if($code != 250) {
    853       $this->error =
    854         array("error" => "RSET failed",
    855               "smtp_code" => $code,
    856               "smtp_msg" => substr($rply,4));
    857       if($this->do_debug >= 1) {
    858         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    859       }
    860       return false;
    861     }
    862 
    863     return true;
    864   }
    865 
    866   /**
    867    * Starts a mail transaction from the email address specified in
    868    * $from. Returns true if successful or false otherwise. If True
    869    * the mail transaction is started and then one or more Recipient
    870    * commands may be called followed by a Data command. This command
    871    * will send the message to the users terminal if they are logged
    872    * in and send them an email.
    873    *
    874    * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
    875    *
    876    * SMTP CODE SUCCESS: 250
    877    * SMTP CODE SUCCESS: 552,451,452
    878    * SMTP CODE SUCCESS: 500,501,502,421
    879    * @access public
    880    * @param string $from
    881    * @return bool
    882    */
    883   public function SendAndMail($from) {
    884     $this->error = null; // so no confusion is caused
    885 
    886     if(!$this->connected()) {
    887       $this->error = array(
    888           "error" => "Called SendAndMail() without being connected");
    889       return false;
    890     }
    891 
    892     fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
    893 
    894     $rply = $this->get_lines();
    895     $code = substr($rply,0,3);
    896 
    897     if($this->do_debug >= 2) {
    898       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
    899     }
    900 
    901     if($code != 250) {
    902       $this->error =
    903         array("error" => "SAML not accepted from server",
    904               "smtp_code" => $code,
    905               "smtp_msg" => substr($rply,4));
    906       if($this->do_debug >= 1) {
    907         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
    908       }
    909       return false;
    910     }
    911     return true;
    912   }
    913 
    914   /**
    915    * This is an optional command for SMTP that this class does not
    916    * support. This method is here to make the RFC821 Definition
    917    * complete for this class and __may__ be implimented in the future
    918    *
    919    * Implements from rfc 821: TURN <CRLF>
    920    *
    921    * SMTP CODE SUCCESS: 250
    922    * SMTP CODE FAILURE: 502
    923    * SMTP CODE ERROR  : 500, 503
    924    * @access public
    925    * @return bool
    926    */
    927   public function Turn() {
    928     $this->error = array("error" => "This method, TURN, of the SMTP ".
    929                                     "is not implemented");
    930     if($this->do_debug >= 1) {
    931       $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '<br />');
    932     }
    933     return false;
    934   }
    935 
    936   /**
    937   * Get the current error
    938   * @access public
    939   * @return array
    940   */
    941   public function getError() {
    942     return $this->error;
    943   }
    944 
    945   /////////////////////////////////////////////////
    946   // INTERNAL FUNCTIONS
    947   /////////////////////////////////////////////////
    948 
    949   /**
    950    * Read in as many lines as possible
    951    * either before eof or socket timeout occurs on the operation.
    952    * With SMTP we can tell if we have more lines to read if the
    953    * 4th character is '-' symbol. If it is a space then we don't
    954    * need to read anything else.
    955    * @access private
    956    * @return string
    957    */
    958   private function get_lines() {
    959     $data = "";
    960     $endtime = 0;
    961     /* If for some reason the fp is bad, don't inf loop */
    962     if (!is_resource($this->smtp_conn)) {
    963       return $data;
    964     }
    965     stream_set_timeout($this->smtp_conn, $this->Timeout);
    966     if ($this->Timelimit > 0) {
    967       $endtime = time() + $this->Timelimit;
    968     }
    969     while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
    970       $str = @fgets($this->smtp_conn,515);
    971       if($this->do_debug >= 4) {
    972         $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />');
    973         $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />');
    974       }
    975       $data .= $str;
    976       if($this->do_debug >= 4) {
    977         $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />');
    978       }
    979       // if 4th character is a space, we are done reading, break the loop
    980       if(substr($str,3,1) == " ") { break; }
    981       // Timed-out? Log and break
    982       $info = stream_get_meta_data($this->smtp_conn);
    983       if ($info['timed_out']) {
    984         if($this->do_debug >= 4) {
    985           $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds) <br />");
    986         }
    987         break;
    988       }
    989       // Now check if reads took too long
    990       if ($endtime) {
    991         if (time() > $endtime) {
    992           if($this->do_debug >= 4) {
    993             $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds) <br />");
    994           }
    995           break;
    996         }
    997       }
    998     }
    999     return $data;
    1000   }
    1001 
     918    }
     919
     920    /**
     921     * Send raw data to the server.
     922     * @param string $data The data to send
     923     * @access public
     924     * @return integer|boolean The number of bytes sent to the server or false on error
     925     */
     926    public function client_send($data)
     927    {
     928        $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
     929        return fwrite($this->smtp_conn, $data);
     930    }
     931
     932    /**
     933     * Get the latest error.
     934     * @access public
     935     * @return array
     936     */
     937    public function getError()
     938    {
     939        return $this->error;
     940    }
     941
     942    /**
     943     * Get SMTP extensions available on the server
     944     * @access public
     945     * @return array|null
     946     */
     947    public function getServerExtList()
     948    {
     949        return $this->server_caps;
     950    }
     951
     952    /**
     953     * A multipurpose method
     954     * The method works in three ways, dependent on argument value and current state
     955     *   1. HELO/EHLO was not sent - returns null and set up $this->error
     956     *   2. HELO was sent
     957     *     $name = 'HELO': returns server name
     958     *     $name = 'EHLO': returns boolean false
     959     *     $name = any string: returns null and set up $this->error
     960     *   3. EHLO was sent
     961     *     $name = 'HELO'|'EHLO': returns server name
     962     *     $name = any string: if extension $name exists, returns boolean True
     963     *       or its options. Otherwise returns boolean False
     964     * In other words, one can use this method to detect 3 conditions:
     965     *  - null returned: handshake was not or we don't know about ext (refer to $this->error)
     966     *  - false returned: the requested feature exactly not exists
     967     *  - positive value returned: the requested feature exists
     968     * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
     969     * @return mixed
     970     */
     971    public function getServerExt($name)
     972    {
     973        if (!$this->server_caps) {
     974            $this->setError('No HELO/EHLO was sent');
     975            return null;
     976        }
     977
     978        // the tight logic knot ;)
     979        if (!array_key_exists($name, $this->server_caps)) {
     980            if ($name == 'HELO') {
     981                return $this->server_caps['EHLO'];
     982            }
     983            if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
     984                return false;
     985            }
     986            $this->setError('HELO handshake was used. Client knows nothing about server extensions');
     987            return null;
     988        }
     989
     990        return $this->server_caps[$name];
     991    }
     992
     993    /**
     994     * Get the last reply from the server.
     995     * @access public
     996     * @return string
     997     */
     998    public function getLastReply()
     999    {
     1000        return $this->last_reply;
     1001    }
     1002
     1003    /**
     1004     * Read the SMTP server's response.
     1005     * Either before eof or socket timeout occurs on the operation.
     1006     * With SMTP we can tell if we have more lines to read if the
     1007     * 4th character is '-' symbol. If it is a space then we don't
     1008     * need to read anything else.
     1009     * @access protected
     1010     * @return string
     1011     */
     1012    protected function get_lines()
     1013    {
     1014        // If the connection is bad, give up straight away
     1015        if (!is_resource($this->smtp_conn)) {
     1016            return '';
     1017        }
     1018        $data = '';
     1019        $endtime = 0;
     1020        stream_set_timeout($this->smtp_conn, $this->Timeout);
     1021        if ($this->Timelimit > 0) {
     1022            $endtime = time() + $this->Timelimit;
     1023        }
     1024        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
     1025            $str = @fgets($this->smtp_conn, 515);
     1026            $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
     1027            $this->edebug("SMTP -> get_lines(): \$str is  \"$str\"", self::DEBUG_LOWLEVEL);
     1028            $data .= $str;
     1029            // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
     1030            if ((isset($str[3]) and $str[3] == ' ')) {
     1031                break;
     1032            }
     1033            // Timed-out? Log and break
     1034            $info = stream_get_meta_data($this->smtp_conn);
     1035            if ($info['timed_out']) {
     1036                $this->edebug(
     1037                    'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
     1038                    self::DEBUG_LOWLEVEL
     1039                );
     1040                break;
     1041            }
     1042            // Now check if reads took too long
     1043            if ($endtime and time() > $endtime) {
     1044                $this->edebug(
     1045                    'SMTP -> get_lines(): timelimit reached ('.
     1046                    $this->Timelimit . ' sec)',
     1047                    self::DEBUG_LOWLEVEL
     1048                );
     1049                break;
     1050            }
     1051        }
     1052        return $data;
     1053    }
     1054
     1055    /**
     1056     * Enable or disable VERP address generation.
     1057     * @param boolean $enabled
     1058     */
     1059    public function setVerp($enabled = false)
     1060    {
     1061        $this->do_verp = $enabled;
     1062    }
     1063
     1064    /**
     1065     * Get VERP address generation mode.
     1066     * @return boolean
     1067     */
     1068    public function getVerp()
     1069    {
     1070        return $this->do_verp;
     1071    }
     1072
     1073    /**
     1074     * Set error messages and codes.
     1075     * @param string $message The error message
     1076     * @param string $detail Further detail on the error
     1077     * @param string $smtp_code An associated SMTP error code
     1078     * @param string $smtp_code_ex Extended SMTP code
     1079     */
     1080    protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
     1081    {
     1082        $this->error = array(
     1083            'error' => $message,
     1084            'detail' => $detail,
     1085            'smtp_code' => $smtp_code,
     1086            'smtp_code_ex' => $smtp_code_ex
     1087        );
     1088    }
     1089
     1090    /**
     1091     * Set debug output method.
     1092     * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
     1093     */
     1094    public function setDebugOutput($method = 'echo')
     1095    {
     1096        $this->Debugoutput = $method;
     1097    }
     1098
     1099    /**
     1100     * Get debug output method.
     1101     * @return string
     1102     */
     1103    public function getDebugOutput()
     1104    {
     1105        return $this->Debugoutput;
     1106    }
     1107
     1108    /**
     1109     * Set debug output level.
     1110     * @param integer $level
     1111     */
     1112    public function setDebugLevel($level = 0)
     1113    {
     1114        $this->do_debug = $level;
     1115    }
     1116
     1117    /**
     1118     * Get debug output level.
     1119     * @return integer
     1120     */
     1121    public function getDebugLevel()
     1122    {
     1123        return $this->do_debug;
     1124    }
     1125
     1126    /**
     1127     * Set SMTP timeout.
     1128     * @param integer $timeout
     1129     */
     1130    public function setTimeout($timeout = 0)
     1131    {
     1132        $this->Timeout = $timeout;
     1133    }
     1134
     1135    /**
     1136     * Get SMTP timeout.
     1137     * @return integer
     1138     */
     1139    public function getTimeout()
     1140    {
     1141        return $this->Timeout;
     1142    }
     1143
     1144    /**
     1145     * Reports an error number and string.
     1146     * @param integer $errno The error number returned by PHP.
     1147     * @param string $errmsg The error message returned by PHP.
     1148     */
     1149    protected function errorHandler($errno, $errmsg)
     1150    {
     1151        $notice = 'Connection: Failed to connect to server.';
     1152        $this->setError(
     1153            $notice,
     1154            $errno,
     1155            $errmsg
     1156        );
     1157        $this->edebug(
     1158            $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg,
     1159            self::DEBUG_CONNECTION
     1160        );
     1161    }
     1162
     1163    /**
     1164     * Will return the ID of the last smtp transaction based on a list of patterns provided
     1165     * in SMTP::$smtp_transaction_id_patterns.
     1166     * If no reply has been received yet, it will return null.
     1167     * If no pattern has been matched, it will return false.
     1168     * @return bool|null|string
     1169     */
     1170    public function getLastTransactionID()
     1171    {
     1172        $reply = $this->getLastReply();
     1173
     1174        if (empty($reply)) {
     1175            return null;
     1176        }
     1177
     1178        foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
     1179            if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
     1180                return $matches[1];
     1181            }
     1182        }
     1183
     1184        return false;
     1185    }
    10021186}
    1003 ?>
Note: See TracChangeset for help on using the changeset viewer.