Make WordPress Core


Ignore:
Timestamp:
03/03/2014 08:24:31 PM (11 years ago)
Author:
nacin
Message:

Update PHPMailer to 5.2.7 from 5.2.4.

Includes two trivial modifications for WordPress:

  • Doesn't use the autoloader, so the check to enforce the autoloader from the constructor is removed.
  • Requires class-smtp.php for backwards compatibility with direct (non-wp_mail()) uses of PHPMailer, as the autoloader isn't used.

props bpetty.
fixes #25560.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-smtp.php

    r23522 r27385  
    11<?php
    2 /*~ class.smtp.php
    3 .---------------------------------------------------------------------------.
    4 |  Software: PHPMailer - PHP email class                                    |
    5 |   Version: 5.2.4                                                          |
    6 |      Site: https://code.google.com/a/apache-extras.org/p/phpmailer/       |
    7 | ------------------------------------------------------------------------- |
    8 |     Admin: Jim Jagielski (project admininistrator)                        |
    9 |   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
    10 |          : Marcus Bointon (coolbru) coolbru@users.sourceforge.net         |
    11 |          : Jim Jagielski (jimjag) jimjag@gmail.com                        |
    12 |   Founder: Brent R. Matzelle (original founder)                           |
    13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
    14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
    15 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
    16 | ------------------------------------------------------------------------- |
    17 |   License: Distributed under the Lesser General Public License (LGPL)     |
    18 |            http://www.gnu.org/copyleft/lesser.html                        |
    19 | This program is distributed in the hope that it will be useful - WITHOUT  |
    20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
    21 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
    22 '---------------------------------------------------------------------------'
    23 */
    24 
    252/**
    26  * PHPMailer - PHP SMTP email transport class
    27  * NOTE: Designed for use with PHP version 5 and up
    28  * @package PHPMailer
    29  * @author Andy Prevost
    30  * @author Marcus Bointon
     3 * PHPMailer RFC821 SMTP email transport class.
     4 * Version 5.2.7
     5 * PHP version 5.0.0
     6 * @category  PHP
     7 * @package   PHPMailer
     8 * @link      https://github.com/PHPMailer/PHPMailer/
     9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     12 * @copyright 2013 Marcus Bointon
    3113 * @copyright 2004 - 2008 Andy Prevost
    32  * @author Jim Jagielski
    3314 * @copyright 2010 - 2012 Jim Jagielski
    34  * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     15 * @license   http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
    3516 */
    3617
    3718/**
    38  * PHP RFC821 SMTP client
     19 * PHPMailer RFC821 SMTP email transport class.
    3920 *
    40  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
    41  * SMTP also provides some utility methods for sending mail to an SMTP server.
    42  * @author Chris Ryan
    43  * @package PHPMailer
     21 * Implements RFC 821 SMTP commands
     22 * and provides some utility methods for sending mail to an SMTP server.
     23 *
     24 * PHP Version 5.0.0
     25 *
     26 * @category PHP
     27 * @package  PHPMailer
     28 * @link     https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php
     29 * @author   Chris Ryan <unknown@example.com>
     30 * @author   Marcus Bointon <phpmailer@synchromedia.co.uk>
     31 * @license  http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
    4432 */
    4533
    46 class SMTP {
    47   /**
    48    *  SMTP server port
    49    *  @var int
    50    */
    51   public $SMTP_PORT = 25;
    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
     34class SMTP
     35{
     36    /**
     37     * The PHPMailer SMTP Version number.
     38     */
     39    const VERSION = '5.2.7';
     40
     41    /**
     42     * SMTP line break constant.
     43     */
     44    const CRLF = "\r\n";
     45
     46    /**
     47     * The SMTP port to use if one is not specified.
     48     */
     49    const DEFAULT_SMTP_PORT = 25;
     50
     51    /**
     52     * The PHPMailer SMTP Version number.
     53     * @type string
     54     * @deprecated This should be a constant
     55     * @see SMTP::VERSION
     56     */
     57    public $Version = '5.2.7';
     58
     59    /**
     60     * SMTP server port number.
     61     * @type int
     62     * @deprecated This is only ever ued as default value, so should be a constant
     63     * @see SMTP::DEFAULT_SMTP_PORT
     64     */
     65    public $SMTP_PORT = 25;
     66
     67    /**
     68     * SMTP reply line ending
     69     * @type string
     70     * @deprecated Use the class constant instead
     71     * @see SMTP::CRLF
     72     */
     73    public $CRLF = "\r\n";
     74
     75    /**
     76     * Debug output level.
     77     * Options: 0 for no output, 1 for commands, 2 for data and commands
     78     * @type int
     79     */
     80    public $do_debug = 0;
     81
     82    /**
     83     * The function/method to use for debugging output.
     84     * Options: 'echo', 'html' or 'error_log'
     85     * @type string
     86     */
     87    public $Debugoutput = 'echo';
     88
     89    /**
     90     * Whether to use VERP.
     91     * @type bool
     92     */
     93    public $do_verp = false;
     94
     95    /**
     96     * The SMTP timeout value for reads, in seconds.
     97     * @type int
     98     */
     99    public $Timeout = 15;
     100
     101    /**
     102     * The SMTP timelimit value for reads, in seconds.
     103     * @type int
     104     */
     105    public $Timelimit = 30;
     106
     107    /**
     108     * The socket for the server connection.
     109     * @type resource
     110     */
     111    protected $smtp_conn;
     112
     113    /**
     114     * Error message, if any, for the last call.
     115     * @type string
     116     */
     117    protected $error = '';
     118
     119    /**
     120     * The reply the server sent to us for HELO.
     121     * @type string
     122     */
     123    protected $helo_rply = '';
     124
     125    /**
     126     * The most recent reply received from the server.
     127     * @type string
     128     */
     129    protected $last_reply = '';
     130
     131    /**
     132     * Constructor.
     133     * @access public
     134     */
     135    public function __construct()
     136    {
     137        $this->smtp_conn = 0;
     138        $this->error = null;
     139        $this->helo_rply = null;
     140
     141        $this->do_debug = 0;
     142    }
     143
     144    /**
     145     * Output debugging info via a user-selected method.
     146     * @param string $str Debug string to output
     147     * @return void
     148     */
     149    protected function edebug($str)
     150    {
     151        switch ($this->Debugoutput) {
     152            case 'error_log':
     153                //Don't output, just log
     154                error_log($str);
     155                break;
     156            case 'html':
     157                //Cleans up output a bit for a better looking, HTML-safe output
     158                echo htmlentities(
     159                    preg_replace('/[\r\n]+/', '', $str),
     160                    ENT_QUOTES,
     161                    'UTF-8'
     162                )
     163                . "<br>\n";
     164                break;
     165            case 'echo':
     166            default:
     167                //Just echoes whatever was received
     168                echo $str;
     169        }
     170    }
     171
     172    /**
     173     * 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
     176     * @param int $timeout How long to wait for the connection to open
     177     * @param array $options An array of options for stream_context_create()
     178     * @access public
     179     * @return bool
     180     */
     181    public function connect($host, $port = null, $timeout = 30, $options = array())
     182    {
     183        // Clear errors to avoid confusion
     184        $this->error = null;
     185
     186        // Make sure we are __not__ connected
     187        if ($this->connected()) {
     188            // Already connected, generate error
     189            $this->error = array('error' => 'Already connected to a server');
     190            return false;
     191        }
     192
     193        if (empty($port)) {
     194            $port = self::DEFAULT_SMTP_PORT;
     195        }
     196
     197        // Connect to the SMTP server
     198        $errno = 0;
     199        $errstr = '';
     200        $socket_context = stream_context_create($options);
     201        //Suppress errors; connection failures are handled at a higher level
     202        $this->smtp_conn = @stream_socket_client(
     203            $host . ":" . $port,
     204            $errno,
     205            $errstr,
     206            $timeout,
     207            STREAM_CLIENT_CONNECT,
     208            $socket_context
     209        );
     210
     211        // Verify we connected properly
     212        if (empty($this->smtp_conn)) {
     213            $this->error = array(
     214                'error' => 'Failed to connect to server',
     215                'errno' => $errno,
     216                'errstr' => $errstr
     217            );
     218            if ($this->do_debug >= 1) {
     219                $this->edebug(
     220                    'SMTP -> ERROR: ' . $this->error['error']
     221                    . ": $errstr ($errno)"
     222                );
     223            }
     224            return false;
     225        }
     226
     227        // SMTP server can take longer to respond, give longer timeout for first read
     228        // Windows does not have support for this timeout function
     229        if (substr(PHP_OS, 0, 3) != 'WIN') {
     230            $max = ini_get('max_execution_time');
     231            if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
     232                @set_time_limit($timeout);
     233            }
     234            stream_set_timeout($this->smtp_conn, $timeout, 0);
     235        }
     236
     237        // Get any announcement
     238        $announce = $this->get_lines();
     239
     240        if ($this->do_debug >= 2) {
     241            $this->edebug('SMTP -> FROM SERVER:' . $announce);
     242        }
     243
     244        return true;
     245    }
     246
     247    /**
     248     * Initiate a TLS (encrypted) session.
     249     * @access public
     250     * @return bool
     251     */
     252    public function startTLS()
     253    {
     254        if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) {
     255            return false;
     256        }
     257        // Begin encrypted connection
     258        if (!stream_socket_enable_crypto(
     259            $this->smtp_conn,
     260            true,
     261            STREAM_CRYPTO_METHOD_TLS_CLIENT
     262        )
     263        ) {
     264            return false;
     265        }
     266        return true;
     267    }
     268
     269    /**
     270     * Perform SMTP authentication.
     271     * Must be run after hello().
     272     * @see hello()
     273     * @param string $username    The user name
     274     * @param string $password    The password
     275     * @param string $authtype    The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
     276     * @param string $realm       The auth realm for NTLM
     277     * @param string $workstation The auth workstation for NTLM
     278     * @access public
     279     * @return bool True if successfully authenticated.
     280     */
     281    public function authenticate(
     282        $username,
     283        $password,
     284        $authtype = 'LOGIN',
     285        $realm = '',
     286        $workstation = ''
     287    ) {
     288        if (empty($authtype)) {
     289            $authtype = 'LOGIN';
     290        }
     291
     292        switch ($authtype) {
     293            case 'PLAIN':
     294                // Start authentication
     295                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
     296                    return false;
     297                }
     298                // Send encoded username and password
     299                if (!$this->sendCommand(
     300                    'User & Password',
     301                    base64_encode("\0" . $username . "\0" . $password),
     302                    235
     303                )
     304                ) {
     305                    return false;
     306                }
     307                break;
     308            case 'LOGIN':
     309                // Start authentication
     310                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
     311                    return false;
     312                }
     313                if (!$this->sendCommand("Username", base64_encode($username), 334)) {
     314                    return false;
     315                }
     316                if (!$this->sendCommand("Password", base64_encode($password), 235)) {
     317                    return false;
     318                }
     319                break;
     320            case 'NTLM':
     321                /*
     322                 * ntlm_sasl_client.php
     323                 * Bundled with Permission
     324                 *
     325                 * How to telnet in windows:
     326                 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
     327                 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
     328                 */
     329                require_once 'extras/ntlm_sasl_client.php';
     330                $temp = new stdClass();
     331                $ntlm_client = new ntlm_sasl_client_class;
     332                //Check that functions are available
     333                if (!$ntlm_client->Initialize($temp)) {
     334                    $this->error = array('error' => $temp->error);
     335                    if ($this->do_debug >= 1) {
     336                        $this->edebug(
     337                            'You need to enable some modules in your php.ini file: '
     338                            . $this->error['error']
     339                        );
     340                    }
     341                    return false;
     342                }
     343                //msg1
     344                $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
     345
     346                if (!$this->sendCommand(
     347                    'AUTH NTLM',
     348                    'AUTH NTLM ' . base64_encode($msg1),
     349                    334
     350                )
     351                ) {
     352                    return false;
     353                }
     354
     355                //Though 0 based, there is a white space after the 3 digit number
     356                //msg2
     357                $challenge = substr($this->last_reply, 3);
     358                $challenge = base64_decode($challenge);
     359                $ntlm_res = $ntlm_client->NTLMResponse(
     360                    substr($challenge, 24, 8),
     361                    $password
     362                );
     363                //msg3
     364                $msg3 = $ntlm_client->TypeMsg3(
     365                    $ntlm_res,
     366                    $username,
     367                    $realm,
     368                    $workstation
     369                );
     370                // send encoded username
     371                return $this->sendCommand('Username', base64_encode($msg3), 235);
     372                break;
     373            case 'CRAM-MD5':
     374                // Start authentication
     375                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
     376                    return false;
     377                }
     378                // Get the challenge
     379                $challenge = base64_decode(substr($this->last_reply, 4));
     380
     381                // Build the response
     382                $response = $username . ' ' . $this->hmac($challenge, $password);
     383
     384                // send encoded credentials
     385                return $this->sendCommand('Username', base64_encode($response), 235);
     386                break;
     387        }
     388        return true;
     389    }
     390
     391    /**
     392     * Calculate an MD5 HMAC hash.
     393     * Works like hash_hmac('md5', $data, $key)
     394     * in case that function is not available
     395     * @param string $data The data to hash
     396     * @param string $key  The key to hash with
     397     * @access protected
     398     * @return string
     399     */
     400    protected function hmac($data, $key)
     401    {
     402        if (function_exists('hash_hmac')) {
     403            return hash_hmac('md5', $data, $key);
     404        }
     405
     406        // The following borrowed from
     407        // http://php.net/manual/en/function.mhash.php#27225
     408
     409        // RFC 2104 HMAC implementation for php.
     410        // Creates an md5 HMAC.
     411        // Eliminates the need to install mhash to compute a HMAC
     412        // Hacked by Lance Rushing
     413
     414        $b = 64; // byte length for md5
     415        if (strlen($key) > $b) {
     416            $key = pack('H*', md5($key));
     417        }
     418        $key = str_pad($key, $b, chr(0x00));
     419        $ipad = str_pad('', $b, chr(0x36));
     420        $opad = str_pad('', $b, chr(0x5c));
     421        $k_ipad = $key ^ $ipad;
     422        $k_opad = $key ^ $opad;
     423
     424        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
     425    }
     426
     427    /**
     428     * Check connection state.
     429     * @access public
     430     * @return bool True if connected.
     431     */
     432    public function connected()
     433    {
     434        if (!empty($this->smtp_conn)) {
     435            $sock_status = stream_get_meta_data($this->smtp_conn);
     436            if ($sock_status['eof']) {
     437                // the socket is valid but we are not connected
     438                if ($this->do_debug >= 1) {
     439                    $this->edebug(
     440                        'SMTP -> NOTICE: EOF caught while checking if connected'
     441                    );
     442                }
     443                $this->close();
     444                return false;
     445            }
     446            return true; // everything looks good
     447        }
     448        return false;
     449    }
     450
     451    /**
     452     * Close the socket and clean up the state of the class.
     453     * Don't use this function without first trying to use QUIT.
     454     * @see quit()
     455     * @access public
     456     * @return void
     457     */
     458    public function close()
     459    {
     460        $this->error = null; // so there is no confusion
     461        $this->helo_rply = null;
     462        if (!empty($this->smtp_conn)) {
     463            // close the connection and cleanup
     464            fclose($this->smtp_conn);
     465            $this->smtp_conn = 0;
     466        }
     467    }
     468
     469    /**
     470     * Send an SMTP DATA command.
     471     * Issues a data command and sends the msg_data to the server,
     472     * finializing the mail transaction. $msg_data is the message
     473     * that is to be send with the headers. Each header needs to be
     474     * on a single line followed by a <CRLF> with the message headers
     475     * and the message body being separated by and additional <CRLF>.
     476     * Implements rfc 821: DATA <CRLF>
     477     * @param string $msg_data Message data to send
     478     * @access public
     479     * @return bool
     480     */
     481    public function data($msg_data)
     482    {
     483        if (!$this->sendCommand('DATA', 'DATA', 354)) {
     484            return false;
     485        }
     486
     487        /* 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.
    365496         */
    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             }
     497
     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);
     502
     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.
     510         */
     511
     512        $field = substr($lines[0], 0, strpos($lines[0], ':'));
     513        $in_headers = false;
     514        if (!empty($field) && !strstr($field, ' ')) {
     515            $in_headers = true;
     516        }
     517
     518        //RFC 2822 section 2.1.1 limit
     519        $max_line_length = 998;
     520
     521        foreach ($lines as $line) {
     522            $lines_out = null;
     523            if ($line == '' && $in_headers) {
     524                $in_headers = false;
     525            }
     526            // 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;
     533                    $lines_out[] = substr($line, 0, $pos);
     534                    $line = substr($line, $pos);
     535                } else {
     536                    $lines_out[] = substr($line, 0, $pos);
     537                    $line = substr($line, $pos + 1);
     538                }
     539
     540                /* If processing headers add a LWSP-char to the front of new line
     541                 * rfc822 on long msg headers
     542                 */
     543                if ($in_headers) {
     544                    $line = "\t" . $line;
     545                }
     546            }
     547            $lines_out[] = $line;
     548
     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                    }
     555                }
     556                $this->client_send($line_out . self::CRLF);
     557            }
     558        }
     559
     560        // Message data has been sent, complete the command
     561        return $this->sendCommand('DATA END', '.', 250);
     562    }
     563
     564    /**
     565     * Send an SMTP HELO or EHLO command.
     566     * Used to identify the sending server to the receiving server.
     567     * This makes sure that client and server are in a known state.
     568     * Implements from RFC 821: HELO <SP> <domain> <CRLF>
     569     * and RFC 2821 EHLO.
     570     * @param string $host The host name or IP to connect to
     571     * @access public
     572     * @return bool
     573     */
     574    public function hello($host = '')
     575    {
     576        // 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;
     584    }
     585
     586    /**
     587     * Send an SMTP HELO or EHLO command.
     588     * Low-level implementation used by hello()
     589     * @see hello()
     590     * @param string $hello The HELO string
     591     * @param string $host  The hostname to say we are
     592     * @access protected
     593     * @return bool
     594     */
     595    protected function sendHello($hello, $host)
     596    {
     597        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
     598        $this->helo_rply = $this->last_reply;
     599        return $noerror;
     600    }
     601
     602    /**
     603     * Send an SMTP MAIL command.
     604     * Starts a mail transaction from the email address specified in
     605     * $from. Returns true if successful or false otherwise. If True
     606     * the mail transaction is started and then one or more recipient
     607     * commands may be called followed by a data command.
     608     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     609     * @param string $from Source address of this message
     610     * @access public
     611     * @return bool
     612     */
     613    public function mail($from)
     614    {
     615        $useVerp = ($this->do_verp ? ' XVERP' : '');
     616        return $this->sendCommand(
     617            'MAIL FROM',
     618            'MAIL FROM:<' . $from . '>' . $useVerp,
     619            250
     620        );
     621    }
     622
     623    /**
     624     * Send an SMTP QUIT command.
     625     * Closes the socket if there is no error or the $close_on_error argument is true.
     626     * Implements from rfc 821: QUIT <CRLF>
     627     * @param bool $close_on_error Should the connection close if an error occurs?
     628     * @access public
     629     * @return bool
     630     */
     631    public function quit($close_on_error = true)
     632    {
     633        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
     634        $e = $this->error; //Save any error
     635        if ($noerror or $close_on_error) {
     636            $this->close();
     637            $this->error = $e; //Restore any error from the quit command
     638        }
     639        return $noerror;
     640    }
     641
     642    /**
     643     * Send an SMTP RCPT command.
     644     * Sets the TO argument to $to.
     645     * Returns true if the recipient was accepted false if it was rejected.
     646     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     647     * @param string $to The address the message is being sent to
     648     * @access public
     649     * @return bool
     650     */
     651    public function recipient($to)
     652    {
     653        return $this->sendCommand(
     654            'RCPT TO ',
     655            'RCPT TO:<' . $to . '>',
     656            array(250, 251)
     657        );
     658    }
     659
     660    /**
     661     * Send an SMTP RSET command.
     662     * Abort any transaction that is currently in progress.
     663     * Implements rfc 821: RSET <CRLF>
     664     * @access public
     665     * @return bool True on success.
     666     */
     667    public function reset()
     668    {
     669        return $this->sendCommand('RSET', 'RSET', 250);
     670    }
     671
     672    /**
     673     * Send a command to an SMTP server and check its return code.
     674     * @param string $command       The command name - not sent to the server
     675     * @param string $commandstring The actual command to send
     676     * @param int|array $expect     One or more expected integer success codes
     677     * @access protected
     678     * @return bool True on success.
     679     */
     680    protected function sendCommand($command, $commandstring, $expect)
     681    {
     682        if (!$this->connected()) {
     683            $this->error = array(
     684                "error" => "Called $command without being connected"
     685            );
    374686            return false;
    375687        }
    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);
     688        $this->client_send($commandstring . self::CRLF);
     689
     690        $reply = $this->get_lines();
     691        $code = substr($reply, 0, 3);
     692
     693        if ($this->do_debug >= 2) {
     694            $this->edebug('SMTP -> FROM SERVER:' . $reply);
     695        }
     696
     697        if (!in_array($code, (array)$expect)) {
     698            $this->last_reply = null;
     699            $this->error = array(
     700                "error" => "$command command failed",
     701                "smtp_code" => $code,
     702                "detail" => substr($reply, 4)
     703            );
     704            if ($this->do_debug >= 1) {
     705                $this->edebug(
     706                    'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply
     707                );
    391708            }
    392709            return false;
    393710        }
    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             }
    413             return false;
    414         }
    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();
     711
     712        $this->last_reply = $reply;
     713        $this->error = null;
     714        return true;
     715    }
     716
     717    /**
     718     * Send an SMTP SAML command.
     719     * Starts a mail transaction from the email address specified in $from.
     720     * Returns true if successful or false otherwise. If True
     721     * the mail transaction is started and then one or more recipient
     722     * commands may be called followed by a data command. This command
     723     * will send the message to the users terminal if they are logged
     724     * in and send them an email.
     725     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     726     * @param string $from The address the message is from
     727     * @access public
     728     * @return bool
     729     */
     730    public function sendAndMail($from)
     731    {
     732        return $this->sendCommand("SAML", "SAML FROM:$from", 250);
     733    }
     734
     735    /**
     736     * Send an SMTP VRFY command.
     737     * @param string $name The name to verify
     738     * @access public
     739     * @return bool
     740     */
     741    public function verify($name)
     742    {
     743        return $this->sendCommand("VRFY", "VRFY $name", array(250, 251));
     744    }
     745
     746    /**
     747     * Send an SMTP NOOP command.
     748     * Used to keep keep-alives alive, doesn't actually do anything
     749     * @access public
     750     * @return bool
     751     */
     752    public function noop()
     753    {
     754        return $this->sendCommand("NOOP", "NOOP", 250);
     755    }
     756
     757    /**
     758     * Send an SMTP TURN command.
     759     * 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
     762     * Implements from rfc 821: TURN <CRLF>
     763     * @access public
     764     * @return bool
     765     */
     766    public function turn()
     767    {
     768        $this->error = array(
     769            'error' => 'The SMTP TURN command is not implemented'
     770        );
     771        if ($this->do_debug >= 1) {
     772            $this->edebug('SMTP -> NOTICE: ' . $this->error['error']);
     773        }
    434774        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 
     775    }
     776
     777    /**
     778     * Send raw data to the server.
     779     * @param string $data The data to send
     780     * @access public
     781     * @return int|bool The number of bytes sent to the server or FALSE on error
     782     */
     783    public function client_send($data)
     784    {
     785        if ($this->do_debug >= 1) {
     786            $this->edebug("CLIENT -> SMTP: $data");
     787        }
     788        return fwrite($this->smtp_conn, $data);
     789    }
     790
     791    /**
     792     * Get the latest error.
     793     * @access public
     794     * @return array
     795     */
     796    public function getError()
     797    {
     798        return $this->error;
     799    }
     800
     801    /**
     802     * Get the last reply from the server.
     803     * @access public
     804     * @return string
     805     */
     806    public function getLastReply()
     807    {
     808        return $this->last_reply;
     809    }
     810
     811    /**
     812     * Read the SMTP server's response.
     813     * Either before eof or socket timeout occurs on the operation.
     814     * With SMTP we can tell if we have more lines to read if the
     815     * 4th character is '-' symbol. If it is a space then we don't
     816     * need to read anything else.
     817     * @access protected
     818     * @return string
     819     */
     820    protected function get_lines()
     821    {
     822        $data = '';
     823        $endtime = 0;
     824        // If the connection is bad, give up now
     825        if (!is_resource($this->smtp_conn)) {
     826            return $data;
     827        }
     828        stream_set_timeout($this->smtp_conn, $this->Timeout);
     829        if ($this->Timelimit > 0) {
     830            $endtime = time() + $this->Timelimit;
     831        }
     832        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
     833            $str = @fgets($this->smtp_conn, 515);
     834            if ($this->do_debug >= 4) {
     835                $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
     836                $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
     837            }
     838            $data .= $str;
     839            if ($this->do_debug >= 4) {
     840                $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
     841            }
     842            // if 4th character is a space, we are done reading, break the loop
     843            if (substr($str, 3, 1) == ' ') {
     844                break;
     845            }
     846            // Timed-out? Log and break
     847            $info = stream_get_meta_data($this->smtp_conn);
     848            if ($info['timed_out']) {
     849                if ($this->do_debug >= 4) {
     850                    $this->edebug(
     851                        'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
     852                    );
     853                }
     854                break;
     855            }
     856            // 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;
     866                }
     867            }
     868        }
     869        return $data;
     870    }
     871
     872    /**
     873     * Enable or disable VERP address generation.
     874     * @param bool $enabled
     875     */
     876    public function setVerp($enabled = false)
     877    {
     878        $this->do_verp = $enabled;
     879    }
     880
     881    /**
     882     * Get VERP address generation mode.
     883     * @return bool
     884     */
     885    public function getVerp()
     886    {
     887        return $this->do_verp;
     888    }
     889
     890    /**
     891     * Set debug output method.
     892     * @param string $method The function/method to use for debugging output.
     893     */
     894    public function setDebugOutput($method = 'echo')
     895    {
     896        $this->Debugoutput = $method;
     897    }
     898
     899    /**
     900     * Get debug output method.
     901     * @return string
     902     */
     903    public function getDebugOutput()
     904    {
     905        return $this->Debugoutput;
     906    }
     907
     908    /**
     909     * Set debug output level.
     910     * @param int $level
     911     */
     912    public function setDebugLevel($level = 0)
     913    {
     914        $this->do_debug = $level;
     915    }
     916
     917    /**
     918     * Get debug output level.
     919     * @return int
     920     */
     921    public function getDebugLevel()
     922    {
     923        return $this->do_debug;
     924    }
     925
     926    /**
     927     * Set SMTP timeout.
     928     * @param int $timeout
     929     */
     930    public function setTimeout($timeout = 0)
     931    {
     932        $this->Timeout = $timeout;
     933    }
     934
     935    /**
     936     * Get SMTP timeout.
     937     * @return int
     938     */
     939    public function getTimeout()
     940    {
     941        return $this->Timeout;
     942    }
    1002943}
    1003 ?>
Note: See TracChangeset for help on using the changeset viewer.