wp/wp-includes/class-smtp.php
changeset 5 5e2f62d02dcd
parent 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
4:346c88efed21 5:5e2f62d02dcd
     1 <?php
     1 <?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 
       
    25 /**
     2 /**
    26  * PHPMailer - PHP SMTP email transport class
     3  * PHPMailer RFC821 SMTP email transport class.
    27  * NOTE: Designed for use with PHP version 5 and up
     4  * Version 5.2.7
    28  * @package PHPMailer
     5  * PHP version 5.0.0
    29  * @author Andy Prevost
     6  * @category  PHP
    30  * @author Marcus Bointon
     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
    31  * @copyright 2004 - 2008 Andy Prevost
    13  * @copyright 2004 - 2008 Andy Prevost
    32  * @author Jim Jagielski
       
    33  * @copyright 2010 - 2012 Jim Jagielski
    14  * @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)
    35  */
    16  */
    36 
    17 
    37 /**
    18 /**
    38  * PHP RFC821 SMTP client
    19  * PHPMailer RFC821 SMTP email transport class.
    39  *
    20  *
    40  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
    21  * Implements RFC 821 SMTP commands
    41  * SMTP also provides some utility methods for sending mail to an SMTP server.
    22  * and provides some utility methods for sending mail to an SMTP server.
    42  * @author Chris Ryan
    23  *
    43  * @package PHPMailer
    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)
    44  */
    32  */
    45 
    33 
    46 class SMTP {
    34 class SMTP
    47   /**
    35 {
    48    *  SMTP server port
    36     /**
    49    *  @var int
    37      * The PHPMailer SMTP Version number.
    50    */
    38      */
    51   public $SMTP_PORT = 25;
    39     const VERSION = '5.2.7';
    52 
    40 
    53   /**
    41     /**
    54    *  SMTP reply line ending (don't change)
    42      * SMTP line break constant.
    55    *  @var string
    43      */
    56    */
    44     const CRLF = "\r\n";
    57   public $CRLF = "\r\n";
    45 
    58 
    46     /**
    59   /**
    47      * The SMTP port to use if one is not specified.
    60    *  Sets whether debugging is turned on
    48      */
    61    *  @var bool
    49     const DEFAULT_SMTP_PORT = 25;
    62    */
    50 
    63   public $do_debug;       // the level of debug to perform
    51     /**
    64 
    52      * The PHPMailer SMTP Version number.
    65   /**
    53      * @type string
    66    * Sets the function/method to use for debugging output.
    54      * @deprecated This should be a constant
    67    * Right now we only honor "echo" or "error_log"
    55      * @see SMTP::VERSION
    68    * @var string
    56      */
    69    */
    57     public $Version = '5.2.7';
    70   public $Debugoutput     = "echo";
    58 
    71 
    59     /**
    72   /**
    60      * SMTP server port number.
    73    *  Sets VERP use on/off (default is off)
    61      * @type int
    74    *  @var bool
    62      * @deprecated This is only ever ued as default value, so should be a constant
    75    */
    63      * @see SMTP::DEFAULT_SMTP_PORT
    76   public $do_verp = false;
    64      */
    77 
    65     public $SMTP_PORT = 25;
    78   /**
    66 
    79    * Sets the SMTP timeout value for reads, in seconds
    67     /**
    80    * @var int
    68      * SMTP reply line ending
    81    */
    69      * @type string
    82   public $Timeout         = 15;
    70      * @deprecated Use the class constant instead
    83 
    71      * @see SMTP::CRLF
    84   /**
    72      */
    85    * Sets the SMTP timelimit value for reads, in seconds
    73     public $CRLF = "\r\n";
    86    * @var int
    74 
    87    */
    75     /**
    88   public $Timelimit       = 30;
    76      * Debug output level.
    89 
    77      * Options: 0 for no output, 1 for commands, 2 for data and commands
    90   /**
    78      * @type int
    91    * Sets the SMTP PHPMailer Version number
    79      */
    92    * @var string
    80     public $do_debug = 0;
    93    */
    81 
    94   public $Version         = '5.2.4';
    82     /**
    95 
    83      * The function/method to use for debugging output.
    96   /////////////////////////////////////////////////
    84      * Options: 'echo', 'html' or 'error_log'
    97   // PROPERTIES, PRIVATE AND PROTECTED
    85      * @type string
    98   /////////////////////////////////////////////////
    86      */
    99 
    87     public $Debugoutput = 'echo';
   100   /**
    88 
   101    * @var resource The socket to the server
    89     /**
   102    */
    90      * Whether to use VERP.
   103   private $smtp_conn;
    91      * @type bool
   104   /**
    92      */
   105    * @var string Error message, if any, for the last call
    93     public $do_verp = false;
   106    */
    94 
   107   private $error;
    95     /**
   108   /**
    96      * The SMTP timeout value for reads, in seconds.
   109    * @var string The reply the server sent to us for HELO
    97      * @type int
   110    */
    98      */
   111   private $helo_rply;
    99     public $Timeout = 15;
   112 
   100 
   113   /**
   101     /**
   114    * Outputs debugging info via user-defined method
   102      * The SMTP timelimit value for reads, in seconds.
   115    * @param string $str
   103      * @type int
   116    */
   104      */
   117   private function edebug($str) {
   105     public $Timelimit = 30;
   118     if ($this->Debugoutput == "error_log") {
   106 
   119         error_log($str);
   107     /**
   120     } else {
   108      * The socket for the server connection.
   121         echo $str;
   109      * @type resource
   122     }
   110      */
   123   }
   111     protected $smtp_conn;
   124 
   112 
   125   /**
   113     /**
   126    * Initialize the class so that the data is in a known state.
   114      * Error message, if any, for the last call.
   127    * @access public
   115      * @type string
   128    * @return SMTP
   116      */
   129    */
   117     protected $error = '';
   130   public function __construct() {
   118 
   131     $this->smtp_conn = 0;
   119     /**
   132     $this->error = null;
   120      * The reply the server sent to us for HELO.
   133     $this->helo_rply = null;
   121      * @type string
   134 
   122      */
   135     $this->do_debug = 0;
   123     protected $helo_rply = '';
   136   }
   124 
   137 
   125     /**
   138   /////////////////////////////////////////////////
   126      * The most recent reply received from the server.
   139   // CONNECTION FUNCTIONS
   127      * @type string
   140   /////////////////////////////////////////////////
   128      */
   141 
   129     protected $last_reply = '';
   142   /**
   130 
   143    * Connect to the server specified on the port specified.
   131     /**
   144    * If the port is not specified use the default SMTP_PORT.
   132      * Constructor.
   145    * If tval is specified then a connection will try and be
   133      * @access public
   146    * established with the server for that number of seconds.
   134      */
   147    * If tval is not specified the default is 30 seconds to
   135     public function __construct()
   148    * try on the connection.
   136     {
   149    *
   137         $this->smtp_conn = 0;
   150    * SMTP CODE SUCCESS: 220
   138         $this->error = null;
   151    * SMTP CODE FAILURE: 421
   139         $this->helo_rply = null;
   152    * @access public
   140 
   153    * @param string $host
   141         $this->do_debug = 0;
   154    * @param int $port
   142     }
   155    * @param int $tval
   143 
   156    * @return bool
   144     /**
   157    */
   145      * Output debugging info via a user-selected method.
   158   public function Connect($host, $port = 0, $tval = 30) {
   146      * @param string $str Debug string to output
   159     // set the error val to null so there is no confusion
   147      * @return void
   160     $this->error = null;
   148      */
   161 
   149     protected function edebug($str)
   162     // make sure we are __not__ connected
   150     {
   163     if($this->connected()) {
   151         switch ($this->Debugoutput) {
   164       // already connected, generate error
   152             case 'error_log':
   165       $this->error = array("error" => "Already connected to a server");
   153                 //Don't output, just log
   166       return false;
   154                 error_log($str);
   167     }
   155                 break;
   168 
   156             case 'html':
   169     if(empty($port)) {
   157                 //Cleans up output a bit for a better looking, HTML-safe output
   170       $port = $this->SMTP_PORT;
   158                 echo htmlentities(
   171     }
   159                     preg_replace('/[\r\n]+/', '', $str),
   172 
   160                     ENT_QUOTES,
   173     // connect to the smtp server
   161                     'UTF-8'
   174     $this->smtp_conn = @fsockopen($host,    // the host of the server
   162                 )
   175                                  $port,    // the port to use
   163                 . "<br>\n";
   176                                  $errno,   // error number if any
   164                 break;
   177                                  $errstr,  // error message if any
   165             case 'echo':
   178                                  $tval);   // give up after ? secs
   166             default:
   179     // verify we connected properly
   167                 //Just echoes whatever was received
   180     if(empty($this->smtp_conn)) {
   168                 echo $str;
   181       $this->error = array("error" => "Failed to connect to server",
   169         }
   182                            "errno" => $errno,
   170     }
   183                            "errstr" => $errstr);
   171 
   184       if($this->do_debug >= 1) {
   172     /**
   185         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />');
   173      * Connect to an SMTP server.
   186       }
   174      * @param string $host    SMTP server IP or host name
   187       return false;
   175      * @param int $port    The port number to connect to
   188     }
   176      * @param int $timeout How long to wait for the connection to open
   189 
   177      * @param array $options An array of options for stream_context_create()
   190     // SMTP server can take longer to respond, give longer timeout for first read
   178      * @access public
   191     // Windows does not have support for this timeout function
   179      * @return bool
   192     if(substr(PHP_OS, 0, 3) != "WIN") {
   180      */
   193      $max = ini_get('max_execution_time');
   181     public function connect($host, $port = null, $timeout = 30, $options = array())
   194      if ($max != 0 && $tval > $max) { // don't bother if unlimited
   182     {
   195       @set_time_limit($tval);
   183         // Clear errors to avoid confusion
   196      }
   184         $this->error = null;
   197      stream_set_timeout($this->smtp_conn, $tval, 0);
   185 
   198     }
   186         // Make sure we are __not__ connected
   199 
   187         if ($this->connected()) {
   200     // get any announcement
   188             // Already connected, generate error
   201     $announce = $this->get_lines();
   189             $this->error = array('error' => 'Already connected to a server');
   202 
   190             return false;
   203     if($this->do_debug >= 2) {
   191         }
   204       $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />');
   192 
   205     }
   193         if (empty($port)) {
   206 
   194             $port = self::DEFAULT_SMTP_PORT;
   207     return true;
   195         }
   208   }
   196 
   209 
   197         // Connect to the SMTP server
   210   /**
   198         $errno = 0;
   211    * Initiate a TLS communication with the server.
   199         $errstr = '';
   212    *
   200         $socket_context = stream_context_create($options);
   213    * SMTP CODE 220 Ready to start TLS
   201         //Suppress errors; connection failures are handled at a higher level
   214    * SMTP CODE 501 Syntax error (no parameters allowed)
   202         $this->smtp_conn = @stream_socket_client(
   215    * SMTP CODE 454 TLS not available due to temporary reason
   203             $host . ":" . $port,
   216    * @access public
   204             $errno,
   217    * @return bool success
   205             $errstr,
   218    */
   206             $timeout,
   219   public function StartTLS() {
   207             STREAM_CLIENT_CONNECT,
   220     $this->error = null; # to avoid confusion
   208             $socket_context
   221 
   209         );
   222     if(!$this->connected()) {
   210 
   223       $this->error = array("error" => "Called StartTLS() without being connected");
   211         // Verify we connected properly
   224       return false;
   212         if (empty($this->smtp_conn)) {
   225     }
   213             $this->error = array(
   226 
   214                 'error' => 'Failed to connect to server',
   227     fputs($this->smtp_conn,"STARTTLS" . $this->CRLF);
   215                 'errno' => $errno,
   228 
   216                 'errstr' => $errstr
   229     $rply = $this->get_lines();
   217             );
   230     $code = substr($rply,0,3);
   218             if ($this->do_debug >= 1) {
   231 
   219                 $this->edebug(
   232     if($this->do_debug >= 2) {
   220                     'SMTP -> ERROR: ' . $this->error['error']
   233       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
   221                     . ": $errstr ($errno)"
   234     }
   222                 );
   235 
   223             }
   236     if($code != 220) {
   224             return false;
   237       $this->error =
   225         }
   238          array("error"     => "STARTTLS not accepted from server",
   226 
   239                "smtp_code" => $code,
   227         // SMTP server can take longer to respond, give longer timeout for first read
   240                "smtp_msg"  => substr($rply,4));
   228         // Windows does not have support for this timeout function
   241       if($this->do_debug >= 1) {
   229         if (substr(PHP_OS, 0, 3) != 'WIN') {
   242         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   230             $max = ini_get('max_execution_time');
   243       }
   231             if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
   244       return false;
   232                 @set_time_limit($timeout);
   245     }
   233             }
   246 
   234             stream_set_timeout($this->smtp_conn, $timeout, 0);
   247     // Begin encrypted connection
   235         }
   248     if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
   236 
   249       return false;
   237         // Get any announcement
   250     }
   238         $announce = $this->get_lines();
   251 
   239 
   252     return true;
   240         if ($this->do_debug >= 2) {
   253   }
   241             $this->edebug('SMTP -> FROM SERVER:' . $announce);
   254 
   242         }
   255   /**
   243 
   256    * Performs SMTP authentication.  Must be run after running the
   244         return true;
   257    * Hello() method.  Returns true if successfully authenticated.
   245     }
   258    * @access public
   246 
   259    * @param string $username
   247     /**
   260    * @param string $password
   248      * Initiate a TLS (encrypted) session.
   261    * @param string $authtype
   249      * @access public
   262    * @param string $realm
   250      * @return bool
   263    * @param string $workstation
   251      */
   264    * @return bool
   252     public function startTLS()
   265    */
   253     {
   266   public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') {
   254         if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) {
   267     if (empty($authtype)) {
   255             return false;
   268       $authtype = 'LOGIN';
   256         }
   269     }
   257         // Begin encrypted connection
   270 
   258         if (!stream_socket_enable_crypto(
   271     switch ($authtype) {
   259             $this->smtp_conn,
   272       case 'PLAIN':
   260             true,
   273         // Start authentication
   261             STREAM_CRYPTO_METHOD_TLS_CLIENT
   274         fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF);
   262         )
   275     
   263         ) {
   276         $rply = $this->get_lines();
   264             return false;
   277         $code = substr($rply,0,3);
   265         }
   278     
   266         return true;
   279         if($code != 334) {
   267     }
   280           $this->error =
   268 
   281             array("error" => "AUTH not accepted from server",
   269     /**
   282                   "smtp_code" => $code,
   270      * Perform SMTP authentication.
   283                   "smtp_msg" => substr($rply,4));
   271      * Must be run after hello().
   284           if($this->do_debug >= 1) {
   272      * @see hello()
   285             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   273      * @param string $username    The user name
   286           }
   274      * @param string $password    The password
   287           return false;
   275      * @param string $authtype    The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
   288         }
   276      * @param string $realm       The auth realm for NTLM
   289         // Send encoded username and password
   277      * @param string $workstation The auth workstation for NTLM
   290         fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF);
   278      * @access public
   291 
   279      * @return bool True if successfully authenticated.
   292         $rply = $this->get_lines();
   280      */
   293         $code = substr($rply,0,3);
   281     public function authenticate(
   294     
   282         $username,
   295         if($code != 235) {
   283         $password,
   296           $this->error =
   284         $authtype = 'LOGIN',
   297             array("error" => "Authentication not accepted from server",
   285         $realm = '',
   298                   "smtp_code" => $code,
   286         $workstation = ''
   299                   "smtp_msg" => substr($rply,4));
   287     ) {
   300           if($this->do_debug >= 1) {
   288         if (empty($authtype)) {
   301             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   289             $authtype = 'LOGIN';
   302           }
   290         }
   303           return false;
   291 
   304         }
   292         switch ($authtype) {
   305         break;
   293             case 'PLAIN':
   306       case 'LOGIN':
   294                 // Start authentication
   307         // Start authentication
   295                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
   308         fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
   296                     return false;
   309     
   297                 }
   310         $rply = $this->get_lines();
   298                 // Send encoded username and password
   311         $code = substr($rply,0,3);
   299                 if (!$this->sendCommand(
   312     
   300                     'User & Password',
   313         if($code != 334) {
   301                     base64_encode("\0" . $username . "\0" . $password),
   314           $this->error =
   302                     235
   315             array("error" => "AUTH not accepted from server",
   303                 )
   316                   "smtp_code" => $code,
   304                 ) {
   317                   "smtp_msg" => substr($rply,4));
   305                     return false;
   318           if($this->do_debug >= 1) {
   306                 }
   319             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   307                 break;
   320           }
   308             case 'LOGIN':
   321           return false;
   309                 // Start authentication
   322         }
   310                 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
   323     
   311                     return false;
   324         // Send encoded username
   312                 }
   325         fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
   313                 if (!$this->sendCommand("Username", base64_encode($username), 334)) {
   326     
   314                     return false;
   327         $rply = $this->get_lines();
   315                 }
   328         $code = substr($rply,0,3);
   316                 if (!$this->sendCommand("Password", base64_encode($password), 235)) {
   329     
   317                     return false;
   330         if($code != 334) {
   318                 }
   331           $this->error =
   319                 break;
   332             array("error" => "Username not accepted from server",
   320             case 'NTLM':
   333                   "smtp_code" => $code,
   321                 /*
   334                   "smtp_msg" => substr($rply,4));
   322                  * ntlm_sasl_client.php
   335           if($this->do_debug >= 1) {
   323                  * Bundled with Permission
   336             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   324                  *
   337           }
   325                  * How to telnet in windows:
   338           return false;
   326                  * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
   339         }
   327                  * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
   340     
   328                  */
   341         // Send encoded password
   329                 require_once 'extras/ntlm_sasl_client.php';
   342         fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
   330                 $temp = new stdClass();
   343     
   331                 $ntlm_client = new ntlm_sasl_client_class;
   344         $rply = $this->get_lines();
   332                 //Check that functions are available
   345         $code = substr($rply,0,3);
   333                 if (!$ntlm_client->Initialize($temp)) {
   346     
   334                     $this->error = array('error' => $temp->error);
   347         if($code != 235) {
   335                     if ($this->do_debug >= 1) {
   348           $this->error =
   336                         $this->edebug(
   349             array("error" => "Password not accepted from server",
   337                             'You need to enable some modules in your php.ini file: '
   350                   "smtp_code" => $code,
   338                             . $this->error['error']
   351                   "smtp_msg" => substr($rply,4));
   339                         );
   352           if($this->do_debug >= 1) {
   340                     }
   353             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   341                     return false;
   354           }
   342                 }
   355           return false;
   343                 //msg1
   356         }
   344                 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
   357         break;
   345 
   358       case 'NTLM':
   346                 if (!$this->sendCommand(
   359         /*
   347                     'AUTH NTLM',
   360          * ntlm_sasl_client.php
   348                     'AUTH NTLM ' . base64_encode($msg1),
   361          ** Bundled with Permission
   349                     334
   362          **
   350                 )
   363          ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
   351                 ) {
   364          ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
   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         // 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.
   365          */
   496          */
   366         require_once('ntlm_sasl_client.php');
   497 
   367         $temp = new stdClass();
   498         // Normalize the line breaks before exploding
   368         $ntlm_client = new ntlm_sasl_client_class;
   499         $msg_data = str_replace("\r\n", "\n", $msg_data);
   369         if(! $ntlm_client->Initialize($temp)){//let's test if every function its available
   500         $msg_data = str_replace("\r", "\n", $msg_data);
   370             $this->error = array("error" => $temp->error);
   501         $lines = explode("\n", $msg_data);
   371             if($this->do_debug >= 1) {
   502 
   372                 $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF);
   503         /* We need to find a good way to determine if headers are
   373             }
   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             );
   374             return false;
   686             return false;
   375         }
   687         }
   376         $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1
   688         $this->client_send($commandstring . self::CRLF);
   377         
   689 
   378         fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF);
   690         $reply = $this->get_lines();
   379 
   691         $code = substr($reply, 0, 3);
   380         $rply = $this->get_lines();
   692 
   381         $code = substr($rply,0,3);
   693         if ($this->do_debug >= 2) {
   382         
   694             $this->edebug('SMTP -> FROM SERVER:' . $reply);
   383 
   695         }
   384         if($code != 334) {
   696 
   385             $this->error =
   697         if (!in_array($code, (array)$expect)) {
   386                 array("error" => "AUTH not accepted from server",
   698             $this->last_reply = null;
   387                       "smtp_code" => $code,
   699             $this->error = array(
   388                       "smtp_msg" => substr($rply,4));
   700                 "error" => "$command command failed",
   389             if($this->do_debug >= 1) {
   701                 "smtp_code" => $code,
   390                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
   702                 "detail" => substr($reply, 4)
       
   703             );
       
   704             if ($this->do_debug >= 1) {
       
   705                 $this->edebug(
       
   706                     'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply
       
   707                 );
   391             }
   708             }
   392             return false;
   709             return false;
   393         }
   710         }
   394         
   711 
   395         $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2
   712         $this->last_reply = $reply;
   396         $challange = base64_decode($challange);
   713         $this->error = null;
   397         $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password);
   714         return true;
   398         $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3
   715     }
   399         // Send encoded username
   716 
   400         fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF);
   717     /**
   401 
   718      * Send an SMTP SAML command.
   402         $rply = $this->get_lines();
   719      * Starts a mail transaction from the email address specified in $from.
   403         $code = substr($rply,0,3);
   720      * Returns true if successful or false otherwise. If True
   404 
   721      * the mail transaction is started and then one or more recipient
   405         if($code != 235) {
   722      * commands may be called followed by a data command. This command
   406             $this->error =
   723      * will send the message to the users terminal if they are logged
   407                 array("error" => "Could not authenticate",
   724      * in and send them an email.
   408                       "smtp_code" => $code,
   725      * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
   409                       "smtp_msg" => substr($rply,4));
   726      * @param string $from The address the message is from
   410             if($this->do_debug >= 1) {
   727      * @access public
   411                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
   728      * @return bool
   412             }
   729      */
   413             return false;
   730     public function sendAndMail($from)
   414         }
   731     {
   415         break;
   732         return $this->sendCommand("SAML", "SAML FROM:$from", 250);
   416     }
   733     }
   417     return true;
   734 
   418   }
   735     /**
   419 
   736      * Send an SMTP VRFY command.
   420   /**
   737      * @param string $name The name to verify
   421    * Returns true if connected to a server otherwise false
   738      * @access public
   422    * @access public
   739      * @return bool
   423    * @return bool
   740      */
   424    */
   741     public function verify($name)
   425   public function Connected() {
   742     {
   426     if(!empty($this->smtp_conn)) {
   743         return $this->sendCommand("VRFY", "VRFY $name", array(250, 251));
   427       $sock_status = socket_get_status($this->smtp_conn);
   744     }
   428       if($sock_status["eof"]) {
   745 
   429         // the socket is valid but we are not connected
   746     /**
   430         if($this->do_debug >= 1) {
   747      * Send an SMTP NOOP command.
   431             $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected");
   748      * Used to keep keep-alives alive, doesn't actually do anything
   432         }
   749      * @access public
   433         $this->Close();
   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         }
   434         return false;
   774         return false;
   435       }
   775     }
   436       return true; // everything looks good
   776 
   437     }
   777     /**
   438     return false;
   778      * Send raw data to the server.
   439   }
   779      * @param string $data The data to send
   440 
   780      * @access public
   441   /**
   781      * @return int|bool The number of bytes sent to the server or FALSE on error
   442    * Closes the socket and cleans up the state of the class.
   782      */
   443    * It is not considered good to use this function without
   783     public function client_send($data)
   444    * first trying to use QUIT.
   784     {
   445    * @access public
   785         if ($this->do_debug >= 1) {
   446    * @return void
   786             $this->edebug("CLIENT -> SMTP: $data");
   447    */
   787         }
   448   public function Close() {
   788         return fwrite($this->smtp_conn, $data);
   449     $this->error = null; // so there is no confusion
   789     }
   450     $this->helo_rply = null;
   790 
   451     if(!empty($this->smtp_conn)) {
   791     /**
   452       // close the connection and cleanup
   792      * Get the latest error.
   453       fclose($this->smtp_conn);
   793      * @access public
   454       $this->smtp_conn = 0;
   794      * @return array
   455     }
   795      */
   456   }
   796     public function getError()
   457 
   797     {
   458   /////////////////////////////////////////////////
   798         return $this->error;
   459   // SMTP COMMANDS
   799     }
   460   /////////////////////////////////////////////////
   800 
   461 
   801     /**
   462   /**
   802      * Get the last reply from the server.
   463    * Issues a data command and sends the msg_data to the server
   803      * @access public
   464    * finializing the mail transaction. $msg_data is the message
   804      * @return string
   465    * that is to be send with the headers. Each header needs to be
   805      */
   466    * on a single line followed by a <CRLF> with the message headers
   806     public function getLastReply()
   467    * and the message body being seperated by and additional <CRLF>.
   807     {
   468    *
   808         return $this->last_reply;
   469    * Implements rfc 821: DATA <CRLF>
   809     }
   470    *
   810 
   471    * SMTP CODE INTERMEDIATE: 354
   811     /**
   472    *     [data]
   812      * Read the SMTP server's response.
   473    *     <CRLF>.<CRLF>
   813      * Either before eof or socket timeout occurs on the operation.
   474    *     SMTP CODE SUCCESS: 250
   814      * With SMTP we can tell if we have more lines to read if the
   475    *     SMTP CODE FAILURE: 552,554,451,452
   815      * 4th character is '-' symbol. If it is a space then we don't
   476    * SMTP CODE FAILURE: 451,554
   816      * need to read anything else.
   477    * SMTP CODE ERROR  : 500,501,503,421
   817      * @access protected
   478    * @access public
   818      * @return string
   479    * @param string $msg_data
   819      */
   480    * @return bool
   820     protected function get_lines()
   481    */
   821     {
   482   public function Data($msg_data) {
   822         $data = '';
   483     $this->error = null; // so no confusion is caused
   823         $endtime = 0;
   484 
   824         // If the connection is bad, give up now
   485     if(!$this->connected()) {
   825         if (!is_resource($this->smtp_conn)) {
   486       $this->error = array(
   826             return $data;
   487               "error" => "Called Data() without being connected");
   827         }
   488       return false;
   828         stream_set_timeout($this->smtp_conn, $this->Timeout);
   489     }
   829         if ($this->Timelimit > 0) {
   490 
   830             $endtime = time() + $this->Timelimit;
   491     fputs($this->smtp_conn,"DATA" . $this->CRLF);
   831         }
   492 
   832         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
   493     $rply = $this->get_lines();
   833             $str = @fgets($this->smtp_conn, 515);
   494     $code = substr($rply,0,3);
   834             if ($this->do_debug >= 4) {
   495 
   835                 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
   496     if($this->do_debug >= 2) {
   836                 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
   497       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
   837             }
   498     }
   838             $data .= $str;
   499 
   839             if ($this->do_debug >= 4) {
   500     if($code != 354) {
   840                 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
   501       $this->error =
   841             }
   502         array("error" => "DATA command not accepted from server",
   842             // if 4th character is a space, we are done reading, break the loop
   503               "smtp_code" => $code,
   843             if (substr($str, 3, 1) == ' ') {
   504               "smtp_msg" => substr($rply,4));
   844                 break;
   505       if($this->do_debug >= 1) {
   845             }
   506         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   846             // Timed-out? Log and break
   507       }
   847             $info = stream_get_meta_data($this->smtp_conn);
   508       return false;
   848             if ($info['timed_out']) {
   509     }
   849                 if ($this->do_debug >= 4) {
   510 
   850                     $this->edebug(
   511     /* the server is ready to accept data!
   851                         'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
   512      * according to rfc 821 we should not send more than 1000
   852                     );
   513      * including the CRLF
   853                 }
   514      * characters on a single line so we will break the data up
   854                 break;
   515      * into lines by \r and/or \n then if needed we will break
   855             }
   516      * each of those into smaller lines to fit within the limit.
   856             // Now check if reads took too long
   517      * in addition we will be looking for lines that start with
   857             if ($endtime) {
   518      * a period '.' and append and additional period '.' to that
   858                 if (time() > $endtime) {
   519      * line. NOTE: this does not count towards limit.
   859                     if ($this->do_debug >= 4) {
   520      */
   860                         $this->edebug(
   521 
   861                             'SMTP -> get_lines(): timelimit reached ('
   522     // normalize the line breaks so we know the explode works
   862                             . $this->Timelimit . ' sec)'
   523     $msg_data = str_replace("\r\n","\n",$msg_data);
   863                         );
   524     $msg_data = str_replace("\r","\n",$msg_data);
   864                     }
   525     $lines = explode("\n",$msg_data);
   865                     break;
   526 
   866                 }
   527     /* we need to find a good way to determine is headers are
   867             }
   528      * in the msg_data or if it is a straight msg body
   868         }
   529      * currently I am assuming rfc 822 definitions of msg headers
   869         return $data;
   530      * and if the first field of the first line (':' sperated)
   870     }
   531      * does not contain a space then it _should_ be a header
   871 
   532      * and we can process all lines before a blank "" line as
   872     /**
   533      * headers.
   873      * Enable or disable VERP address generation.
   534      */
   874      * @param bool $enabled
   535 
   875      */
   536     $field = substr($lines[0],0,strpos($lines[0],":"));
   876     public function setVerp($enabled = false)
   537     $in_headers = false;
   877     {
   538     if(!empty($field) && !strstr($field," ")) {
   878         $this->do_verp = $enabled;
   539       $in_headers = true;
   879     }
   540     }
   880 
   541 
   881     /**
   542     $max_line_length = 998; // used below; set here for ease in change
   882      * Get VERP address generation mode.
   543 
   883      * @return bool
   544     while(list(,$line) = @each($lines)) {
   884      */
   545       $lines_out = null;
   885     public function getVerp()
   546       if($line == "" && $in_headers) {
   886     {
   547         $in_headers = false;
   887         return $this->do_verp;
   548       }
   888     }
   549       // ok we need to break this line up into several smaller lines
   889 
   550       while(strlen($line) > $max_line_length) {
   890     /**
   551         $pos = strrpos(substr($line,0,$max_line_length)," ");
   891      * Set debug output method.
   552 
   892      * @param string $method The function/method to use for debugging output.
   553         // Patch to fix DOS attack
   893      */
   554         if(!$pos) {
   894     public function setDebugOutput($method = 'echo')
   555           $pos = $max_line_length - 1;
   895     {
   556           $lines_out[] = substr($line,0,$pos);
   896         $this->Debugoutput = $method;
   557           $line = substr($line,$pos);
   897     }
   558         } else {
   898 
   559           $lines_out[] = substr($line,0,$pos);
   899     /**
   560           $line = substr($line,$pos + 1);
   900      * Get debug output method.
   561         }
   901      * @return string
   562 
   902      */
   563         /* if processing headers add a LWSP-char to the front of new line
   903     public function getDebugOutput()
   564          * rfc 822 on long msg headers
   904     {
   565          */
   905         return $this->Debugoutput;
   566         if($in_headers) {
   906     }
   567           $line = "\t" . $line;
   907 
   568         }
   908     /**
   569       }
   909      * Set debug output level.
   570       $lines_out[] = $line;
   910      * @param int $level
   571 
   911      */
   572       // send the lines to the server
   912     public function setDebugLevel($level = 0)
   573       while(list(,$line_out) = @each($lines_out)) {
   913     {
   574         if(strlen($line_out) > 0)
   914         $this->do_debug = $level;
   575         {
   915     }
   576           if(substr($line_out, 0, 1) == ".") {
   916 
   577             $line_out = "." . $line_out;
   917     /**
   578           }
   918      * Get debug output level.
   579         }
   919      * @return int
   580         fputs($this->smtp_conn,$line_out . $this->CRLF);
   920      */
   581       }
   921     public function getDebugLevel()
   582     }
   922     {
   583 
   923         return $this->do_debug;
   584     // message data has been sent
   924     }
   585     fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
   925 
   586 
   926     /**
   587     $rply = $this->get_lines();
   927      * Set SMTP timeout.
   588     $code = substr($rply,0,3);
   928      * @param int $timeout
   589 
   929      */
   590     if($this->do_debug >= 2) {
   930     public function setTimeout($timeout = 0)
   591       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
   931     {
   592     }
   932         $this->Timeout = $timeout;
   593 
   933     }
   594     if($code != 250) {
   934 
   595       $this->error =
   935     /**
   596         array("error" => "DATA not accepted from server",
   936      * Get SMTP timeout.
   597               "smtp_code" => $code,
   937      * @return int
   598               "smtp_msg" => substr($rply,4));
   938      */
   599       if($this->do_debug >= 1) {
   939     public function getTimeout()
   600         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
   940     {
   601       }
   941         return $this->Timeout;
   602       return false;
   942     }
   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 
       
  1002 }
   943 }
  1003 ?>