Source for file smtp.php

Documentation is available at smtp.php

  1. <?php
  2. /*~ class.smtp.php
  3. .---------------------------------------------------------------------------.
  4. |  Software: PHPMailer - PHP email class                                    |
  5. |   Version: 5.2.6                                                          |
  6. |      Site: https://github.com/PHPMailer/PHPMailer/                        |
  7. | ------------------------------------------------------------------------- |
  8. |    Admins: Marcus Bointon                                                 |
  9. |    Admins: Jim Jagielski                                                  |
  10. |   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
  11. |          : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk          |
  12. |          : Jim Jagielski (jimjag) jimjag@gmail.com                        |
  13. |   Founder: Brent R. Matzelle (original founder)                           |
  14. | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
  15. | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
  16. | Copyright (c) 2001-2003, Brent R. Matzelle                                |
  17. | ------------------------------------------------------------------------- |
  18. |   License: Distributed under the Lesser General Public License (LGPL)     |
  19. |            http://www.gnu.org/copyleft/lesser.html                        |
  20. | This program is distributed in the hope that it will be useful - WITHOUT  |
  21. | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
  22. | FITNESS FOR A PARTICULAR PURPOSE.                                         |
  23. '---------------------------------------------------------------------------'
  24. */
  25.  
  26. /**
  27.  * PHPMailer - PHP SMTP email transport class
  28.  * NOTE: Designed for use with PHP version 5 and up
  29.  * @package PHPMailer
  30.  * @author Andy Prevost
  31.  * @author Marcus Bointon
  32.  * @copyright 2004 - 2008 Andy Prevost
  33.  * @author Jim Jagielski
  34.  * @copyright 2010 - 2012 Jim Jagielski
  35.  * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
  36.  */
  37.  
  38. /**
  39.  * PHP RFC821 SMTP client
  40.  *
  41.  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
  42.  * SMTP also provides some utility methods for sending mail to an SMTP server.
  43.  * @author Chris Ryan
  44.  * @package PHPMailer
  45.  */
  46.  
  47. class SMTP {
  48.   /**
  49.    *  SMTP server port
  50.    *  @var int 
  51.    */
  52.   public $SMTP_PORT = 25;
  53.  
  54.   /**
  55.    *  SMTP reply line ending (don't change)
  56.    *  @var string 
  57.    */
  58.   public $CRLF = "\r\n";
  59.  
  60.   /**
  61.    *  Debug output level; 0 for no output
  62.    *  @var int 
  63.    */
  64.   public $do_debug = 0;
  65.  
  66.   /**
  67.    * Sets the function/method to use for debugging output.
  68.    * Right now we only honor 'echo', 'html' or 'error_log'
  69.    * @var string 
  70.    */
  71.   public $Debugoutput     = 'echo';
  72.  
  73.   /**
  74.    *  Sets VERP use on/off (default is off)
  75.    *  @var bool 
  76.    */
  77.   public $do_verp = false;
  78.  
  79.   /**
  80.    * Sets the SMTP timeout value for reads, in seconds
  81.    * @var int 
  82.    */
  83.   public $Timeout         = 15;
  84.  
  85.   /**
  86.    * Sets the SMTP timelimit value for reads, in seconds
  87.    * @var int 
  88.    */
  89.   public $Timelimit       = 30;
  90.  
  91.   /**
  92.    * Sets the SMTP PHPMailer Version number
  93.    * @var string 
  94.    */
  95.   public $Version         = '5.2.6';
  96.  
  97.   /////////////////////////////////////////////////
  98.   // PROPERTIES, PRIVATE AND PROTECTED
  99.   /////////////////////////////////////////////////
  100.  
  101.   /**
  102.    * @var resource The socket to the server
  103.    */
  104.   protected $smtp_conn;
  105.   /**
  106.    * @var string Error message, if any, for the last call
  107.    */
  108.   protected $error;
  109.   /**
  110.    * @var string The reply the server sent to us for HELO
  111.    */
  112.   protected $helo_rply;
  113.  
  114.   /**
  115.    * Outputs debugging info via user-defined method
  116.    * @param string $str 
  117.    */
  118.   protected function edebug($str) {
  119.     switch ($this->Debugoutput) {
  120.       case 'error_log':
  121.         error_log($str);
  122.         break;
  123.       case 'html':
  124.         //Cleans up output a bit for a better looking display that's HTML-safe
  125.         echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8')."<br>\n";
  126.         break;
  127.       case 'echo':
  128.       default:
  129.         //Just echoes exactly what was received
  130.         echo $str;
  131.     }
  132.   }
  133.  
  134.   /**
  135.    * Initialize the class so that the data is in a known state.
  136.    * @access public
  137.    * @return SMTP 
  138.    */
  139.   public function __construct() {
  140.     $this->smtp_conn = 0;
  141.     $this->error = null;
  142.     $this->helo_rply = null;
  143.  
  144.     $this->do_debug = 0;
  145.   }
  146.  
  147.   /////////////////////////////////////////////////
  148.   // CONNECTION FUNCTIONS
  149.   /////////////////////////////////////////////////
  150.  
  151.   /**
  152.    * Connect to an SMTP server
  153.    *
  154.    * SMTP CODE SUCCESS: 220
  155.    * SMTP CODE FAILURE: 421
  156.    * @access public
  157.    * @param string $host SMTP server IP or host name
  158.    * @param int $port The port number to connect to, or use the default port if not specified
  159.    * @param int $timeout How long to wait for the connection to open
  160.    * @param array $options An array of options compatible with stream_context_create()
  161.    * @return bool 
  162.    */
  163.   public function Connect($host, $port = 0, $timeout = 30, $options = array()) {
  164.     // Clear errors to avoid confusion
  165.     $this->error = null;
  166.  
  167.     // Make sure we are __not__ connected
  168.     if($this->connected()) {
  169.       // Already connected, generate error
  170.       $this->error = array('error' => 'Already connected to a server');
  171.       return false;
  172.     }
  173.  
  174.     if(empty($port)) {
  175.       $port = $this->SMTP_PORT;
  176.     }
  177.  
  178.     // Connect to the SMTP server
  179.     $errno = 0;
  180.     $errstr = '';
  181.     $socket_context = stream_context_create($options);
  182.     //Need to suppress errors here as connection failures can be handled at a higher level
  183.     $this->smtp_conn = @stream_socket_client($host.":".$port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context);
  184.  
  185.     // Verify we connected properly
  186.     if(empty($this->smtp_conn)) {
  187.       $this->error = array('error' => 'Failed to connect to server',
  188.                            'errno' => $errno,
  189.                            'errstr' => $errstr);
  190.       if($this->do_debug >= 1) {
  191.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ": $errstr ($errno)");
  192.       }
  193.       return false;
  194.     }
  195.  
  196.     // SMTP server can take longer to respond, give longer timeout for first read
  197.     // Windows does not have support for this timeout function
  198.     if(substr(PHP_OS, 0, 3) != 'WIN') {
  199.       $max = ini_get('max_execution_time');
  200.       if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
  201.         @set_time_limit($timeout);
  202.       }
  203.       stream_set_timeout($this->smtp_conn, $timeout, 0);
  204.     }
  205.  
  206.     // get any announcement
  207.     $announce = $this->get_lines();
  208.  
  209.     if($this->do_debug >= 2) {
  210.       $this->edebug('SMTP -> FROM SERVER:' . $announce);
  211.     }
  212.  
  213.     return true;
  214.   }
  215.  
  216.   /**
  217.    * Initiate a TLS communication with the server.
  218.    *
  219.    * SMTP CODE 220 Ready to start TLS
  220.    * SMTP CODE 501 Syntax error (no parameters allowed)
  221.    * SMTP CODE 454 TLS not available due to temporary reason
  222.    * @access public
  223.    * @return bool success
  224.    */
  225.   public function StartTLS() {
  226.     $this->error = null; # to avoid confusion
  227.  
  228.     if(!$this->connected()) {
  229.       $this->error = array('error' => 'Called StartTLS() without being connected');
  230.       return false;
  231.     }
  232.  
  233.     $this->client_send('STARTTLS' . $this->CRLF);
  234.  
  235.     $rply = $this->get_lines();
  236.     $code = substr($rply, 0, 3);
  237.  
  238.     if($this->do_debug >= 2) {
  239.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  240.     }
  241.  
  242.     if($code != 220) {
  243.       $this->error =
  244.          array('error'     => 'STARTTLS not accepted from server',
  245.                'smtp_code' => $code,
  246.                'smtp_msg'  => substr($rply, 4));
  247.       if($this->do_debug >= 1) {
  248.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  249.       }
  250.       return false;
  251.     }
  252.  
  253.     // Begin encrypted connection
  254.     if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
  255.       return false;
  256.     }
  257.  
  258.     return true;
  259.   }
  260.  
  261.   /**
  262.    * Performs SMTP authentication.  Must be run after running the
  263.    * Hello() method.  Returns true if successfully authenticated.
  264.    * @access public
  265.    * @param string $username 
  266.    * @param string $password 
  267.    * @param string $authtype 
  268.    * @param string $realm 
  269.    * @param string $workstation 
  270.    * @return bool 
  271.    */
  272.   public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') {
  273.     if (empty($authtype)) {
  274.       $authtype = 'LOGIN';
  275.     }
  276.  
  277.     switch ($authtype) {
  278.       case 'PLAIN':
  279.         // Start authentication
  280.         $this->client_send('AUTH PLAIN' . $this->CRLF);
  281.  
  282.         $rply = $this->get_lines();
  283.         $code = substr($rply, 0, 3);
  284.  
  285.         if($code != 334) {
  286.           $this->error =
  287.             array('error' => 'AUTH not accepted from server',
  288.                   'smtp_code' => $code,
  289.                   'smtp_msg' => substr($rply, 4));
  290.           if($this->do_debug >= 1) {
  291.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  292.           }
  293.           return false;
  294.         }
  295.         // Send encoded username and password
  296.           $this->client_send(base64_encode("\0".$username."\0".$password) . $this->CRLF);
  297.  
  298.         $rply = $this->get_lines();
  299.         $code = substr($rply, 0, 3);
  300.  
  301.         if($code != 235) {
  302.           $this->error =
  303.             array('error' => 'Authentication not accepted from server',
  304.                   'smtp_code' => $code,
  305.                   'smtp_msg' => substr($rply, 4));
  306.           if($this->do_debug >= 1) {
  307.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  308.           }
  309.           return false;
  310.         }
  311.         break;
  312.       case 'LOGIN':
  313.         // Start authentication
  314.         $this->client_send('AUTH LOGIN' . $this->CRLF);
  315.  
  316.         $rply = $this->get_lines();
  317.         $code = substr($rply, 0, 3);
  318.  
  319.         if($code != 334) {
  320.           $this->error =
  321.             array('error' => 'AUTH not accepted from server',
  322.                   'smtp_code' => $code,
  323.                   'smtp_msg' => substr($rply, 4));
  324.           if($this->do_debug >= 1) {
  325.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  326.           }
  327.           return false;
  328.         }
  329.  
  330.         // Send encoded username
  331.         $this->client_send(base64_encode($username) . $this->CRLF);
  332.  
  333.         $rply = $this->get_lines();
  334.         $code = substr($rply, 0, 3);
  335.  
  336.         if($code != 334) {
  337.           $this->error =
  338.             array('error' => 'Username not accepted from server',
  339.                   'smtp_code' => $code,
  340.                   'smtp_msg' => substr($rply, 4));
  341.           if($this->do_debug >= 1) {
  342.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  343.           }
  344.           return false;
  345.         }
  346.  
  347.         // Send encoded password
  348.         $this->client_send(base64_encode($password) . $this->CRLF);
  349.  
  350.         $rply = $this->get_lines();
  351.         $code = substr($rply, 0, 3);
  352.  
  353.         if($code != 235) {
  354.           $this->error =
  355.             array('error' => 'Password not accepted from server',
  356.                   'smtp_code' => $code,
  357.                   'smtp_msg' => substr($rply, 4));
  358.           if($this->do_debug >= 1) {
  359.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  360.           }
  361.           return false;
  362.         }
  363.         break;
  364.       case 'NTLM':
  365.         /*
  366.          * ntlm_sasl_client.php
  367.          ** Bundled with Permission
  368.          **
  369.          ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
  370.          ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
  371.          */
  372.         if (file_exists('extras/ntlm_sasl_client.php')) {
  373.          require_once 'extras/ntlm_sasl_client.php';
  374.         }
  375.         $temp = new stdClass();
  376.         $ntlm_client = new ntlm_sasl_client_class;
  377.         if(! $ntlm_client->Initialize($temp)){//let's test if every function its available
  378.             $this->error = array('error' => $temp->error);
  379.             if($this->do_debug >= 1) {
  380.                 $this->edebug('You need to enable some modules in your php.ini file: ' . $this->error['error']);
  381.             }
  382.             return false;
  383.         }
  384.         $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1
  385.  
  386.         $this->client_send('AUTH NTLM ' . base64_encode($msg1) . $this->CRLF);
  387.  
  388.         $rply = $this->get_lines();
  389.         $code = substr($rply, 0, 3);
  390.  
  391.         if($code != 334) {
  392.             $this->error =
  393.                 array('error' => 'AUTH not accepted from server',
  394.                       'smtp_code' => $code,
  395.                       'smtp_msg' => substr($rply, 4));
  396.             if($this->do_debug >= 1) {
  397.                 $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  398.             }
  399.             return false;
  400.         }
  401.  
  402.         $challenge = substr($rply, 3);//though 0 based, there is a white space after the 3 digit number....//msg2
  403.         $challenge = base64_decode($challenge);
  404.         $ntlm_res = $ntlm_client->NTLMResponse(substr($challenge, 24, 8), $password);
  405.         $msg3 = $ntlm_client->TypeMsg3($ntlm_res, $username, $realm, $workstation);//msg3
  406.         // Send encoded username
  407.         $this->client_send(base64_encode($msg3) . $this->CRLF);
  408.  
  409.         $rply = $this->get_lines();
  410.         $code = substr($rply, 0, 3);
  411.  
  412.         if($code != 235) {
  413.             $this->error =
  414.                 array('error' => 'Could not authenticate',
  415.                       'smtp_code' => $code,
  416.                       'smtp_msg' => substr($rply, 4));
  417.             if($this->do_debug >= 1) {
  418.                 $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  419.             }
  420.             return false;
  421.         }
  422.         break;
  423.       case 'CRAM-MD5':
  424.         // Start authentication
  425.         $this->client_send('AUTH CRAM-MD5' . $this->CRLF);
  426.  
  427.         $rply = $this->get_lines();
  428.         $code = substr($rply, 0, 3);
  429.  
  430.         if($code != 334) {
  431.           $this->error =
  432.             array('error' => 'AUTH not accepted from server',
  433.                   'smtp_code' => $code,
  434.                   'smtp_msg' => substr($rply, 4));
  435.           if($this->do_debug >= 1) {
  436.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  437.           }
  438.           return false;
  439.         }
  440.  
  441.         // Get the challenge
  442.         $challenge = base64_decode(substr($rply, 4));
  443.  
  444.         // Build the response
  445.         $response = $username . ' ' . $this->hmac($challenge, $password);
  446.  
  447.         // Send encoded credentials
  448.         $this->client_send(base64_encode($response) . $this->CRLF);
  449.  
  450.         $rply = $this->get_lines();
  451.         $code = substr($rply, 0, 3);
  452.  
  453.         if($code != 235) {
  454.           $this->error =
  455.             array('error' => 'Credentials not accepted from server',
  456.                   'smtp_code' => $code,
  457.                   'smtp_msg' => substr($rply, 4));
  458.           if($this->do_debug >= 1) {
  459.             $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  460.           }
  461.           return false;
  462.         }
  463.         break;
  464.     }
  465.     return true;
  466.   }
  467.  
  468.   /**
  469.    * Works like hash_hmac('md5', $data, $key) in case that function is not available
  470.    * @access protected
  471.    * @param string $data 
  472.    * @param string $key 
  473.    * @return string 
  474.    */
  475.   protected function hmac($data, $key) {
  476.       if (function_exists('hash_hmac')) {
  477.           return hash_hmac('md5', $data, $key);
  478.       }
  479.  
  480.       // The following borrowed from http://php.net/manual/en/function.mhash.php#27225
  481.  
  482.       // RFC 2104 HMAC implementation for php.
  483.       // Creates an md5 HMAC.
  484.       // Eliminates the need to install mhash to compute a HMAC
  485.       // Hacked by Lance Rushing
  486.  
  487.       $b = 64; // byte length for md5
  488.       if (strlen($key) > $b) {
  489.           $key = pack('H*', md5($key));
  490.       }
  491.       $key  = str_pad($key, $b, chr(0x00));
  492.       $ipad = str_pad('', $b, chr(0x36));
  493.       $opad = str_pad('', $b, chr(0x5c));
  494.       $k_ipad = $key ^ $ipad ;
  495.       $k_opad = $key ^ $opad;
  496.  
  497.       return md5($k_opad  . pack('H*', md5($k_ipad . $data)));
  498.   }
  499.  
  500.   /**
  501.    * Returns true if connected to a server otherwise false
  502.    * @access public
  503.    * @return bool 
  504.    */
  505.   public function Connected() {
  506.     if(!empty($this->smtp_conn)) {
  507.       $sock_status = stream_get_meta_data($this->smtp_conn);
  508.       if($sock_status['eof']) {
  509.         // the socket is valid but we are not connected
  510.         if($this->do_debug >= 1) {
  511.             $this->edebug('SMTP -> NOTICE: EOF caught while checking if connected');
  512.         }
  513.         $this->Close();
  514.         return false;
  515.       }
  516.       return true; // everything looks good
  517.     }
  518.     return false;
  519.   }
  520.  
  521.   /**
  522.    * Closes the socket and cleans up the state of the class.
  523.    * It is not considered good to use this function without
  524.    * first trying to use QUIT.
  525.    * @access public
  526.    * @return void 
  527.    */
  528.   public function Close() {
  529.     $this->error = null; // so there is no confusion
  530.     $this->helo_rply = null;
  531.     if(!empty($this->smtp_conn)) {
  532.       // close the connection and cleanup
  533.       fclose($this->smtp_conn);
  534.       $this->smtp_conn = 0;
  535.     }
  536.   }
  537.  
  538.   /////////////////////////////////////////////////
  539.   // SMTP COMMANDS
  540.   /////////////////////////////////////////////////
  541.  
  542.   /**
  543.    * Issues a data command and sends the msg_data to the server
  544.    * finializing the mail transaction. $msg_data is the message
  545.    * that is to be send with the headers. Each header needs to be
  546.    * on a single line followed by a <CRLF> with the message headers
  547.    * and the message body being separated by and additional <CRLF>.
  548.    *
  549.    * Implements rfc 821: DATA <CRLF>
  550.    *
  551.    * SMTP CODE INTERMEDIATE: 354
  552.    *     [data]
  553.    *     <CRLF>.<CRLF>
  554.    *     SMTP CODE SUCCESS: 250
  555.    *     SMTP CODE FAILURE: 552, 554, 451, 452
  556.    * SMTP CODE FAILURE: 451, 554
  557.    * SMTP CODE ERROR  : 500, 501, 503, 421
  558.    * @access public
  559.    * @param string $msg_data 
  560.    * @return bool 
  561.    */
  562.   public function Data($msg_data) {
  563.     $this->error = null; // so no confusion is caused
  564.  
  565.     if(!$this->connected()) {
  566.       $this->error = array(
  567.               'error' => 'Called Data() without being connected');
  568.       return false;
  569.     }
  570.  
  571.     $this->client_send('DATA' . $this->CRLF);
  572.  
  573.     $rply = $this->get_lines();
  574.     $code = substr($rply, 0, 3);
  575.  
  576.     if($this->do_debug >= 2) {
  577.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  578.     }
  579.  
  580.     if($code != 354) {
  581.       $this->error =
  582.         array('error' => 'DATA command not accepted from server',
  583.               'smtp_code' => $code,
  584.               'smtp_msg' => substr($rply, 4));
  585.       if($this->do_debug >= 1) {
  586.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  587.       }
  588.       return false;
  589.     }
  590.  
  591.     /* the server is ready to accept data!
  592.      * according to rfc 821 we should not send more than 1000
  593.      * including the CRLF
  594.      * characters on a single line so we will break the data up
  595.      * into lines by \r and/or \n then if needed we will break
  596.      * each of those into smaller lines to fit within the limit.
  597.      * in addition we will be looking for lines that start with
  598.      * a period '.' and append and additional period '.' to that
  599.      * line. NOTE: this does not count towards limit.
  600.      */
  601.  
  602.     // normalize the line breaks so we know the explode works
  603.     $msg_data = str_replace("\r\n", "\n", $msg_data);
  604.     $msg_data = str_replace("\r", "\n", $msg_data);
  605.     $lines = explode("\n", $msg_data);
  606.  
  607.     /* we need to find a good way to determine is headers are
  608.      * in the msg_data or if it is a straight msg body
  609.      * currently I am assuming rfc 822 definitions of msg headers
  610.      * and if the first field of the first line (':' sperated)
  611.      * does not contain a space then it _should_ be a header
  612.      * and we can process all lines before a blank "" line as
  613.      * headers.
  614.      */
  615.  
  616.     $field = substr($lines[0], 0, strpos($lines[0], ':'));
  617.     $in_headers = false;
  618.     if(!empty($field) && !strstr($field, ' ')) {
  619.       $in_headers = true;
  620.     }
  621.  
  622.     $max_line_length = 998; // used below; set here for ease in change
  623.  
  624.     while(list(, $line) = @each($lines)) {
  625.       $lines_out = null;
  626.       if($line == '' && $in_headers) {
  627.         $in_headers = false;
  628.       }
  629.       // ok we need to break this line up into several smaller lines
  630.       while(strlen($line) > $max_line_length) {
  631.         $pos = strrpos(substr($line, 0, $max_line_length), ' ');
  632.  
  633.         // Patch to fix DOS attack
  634.         if(!$pos) {
  635.           $pos = $max_line_length - 1;
  636.           $lines_out[] = substr($line, 0, $pos);
  637.           $line = substr($line, $pos);
  638.         } else {
  639.           $lines_out[] = substr($line, 0, $pos);
  640.           $line = substr($line, $pos + 1);
  641.         }
  642.  
  643.         /* if processing headers add a LWSP-char to the front of new line
  644.          * rfc 822 on long msg headers
  645.          */
  646.         if($in_headers) {
  647.           $line = "\t" . $line;
  648.         }
  649.       }
  650.       $lines_out[] = $line;
  651.  
  652.       // send the lines to the server
  653.       while(list(, $line_out) = @each($lines_out)) {
  654.         if(strlen($line_out) > 0)
  655.         {
  656.           if(substr($line_out, 0, 1) == '.') {
  657.             $line_out = '.' . $line_out;
  658.           }
  659.         }
  660.         $this->client_send($line_out . $this->CRLF);
  661.       }
  662.     }
  663.  
  664.     // message data has been sent
  665.     $this->client_send($this->CRLF . '.' . $this->CRLF);
  666.  
  667.     $rply = $this->get_lines();
  668.     $code = substr($rply, 0, 3);
  669.  
  670.     if($this->do_debug >= 2) {
  671.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  672.     }
  673.  
  674.     if($code != 250) {
  675.       $this->error =
  676.         array('error' => 'DATA not accepted from server',
  677.               'smtp_code' => $code,
  678.               'smtp_msg' => substr($rply, 4));
  679.       if($this->do_debug >= 1) {
  680.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  681.       }
  682.       return false;
  683.     }
  684.     return true;
  685.   }
  686.  
  687.   /**
  688.    * Sends the HELO command to the smtp server.
  689.    * This makes sure that we and the server are in
  690.    * the same known state.
  691.    *
  692.    * Implements from rfc 821: HELO <SP> <domain> <CRLF>
  693.    *
  694.    * SMTP CODE SUCCESS: 250
  695.    * SMTP CODE ERROR  : 500, 501, 504, 421
  696.    * @access public
  697.    * @param string $host 
  698.    * @return bool 
  699.    */
  700.   public function Hello($host = '') {
  701.     $this->error = null; // so no confusion is caused
  702.  
  703.     if(!$this->connected()) {
  704.       $this->error = array(
  705.             'error' => 'Called Hello() without being connected');
  706.       return false;
  707.     }
  708.  
  709.     // if hostname for HELO was not specified send default
  710.     if(empty($host)) {
  711.       // determine appropriate default to send to server
  712.       $host = 'localhost';
  713.     }
  714.  
  715.     // Send extended hello first (RFC 2821)
  716.     if(!$this->SendHello('EHLO', $host)) {
  717.       if(!$this->SendHello('HELO', $host)) {
  718.         return false;
  719.       }
  720.     }
  721.  
  722.     return true;
  723.   }
  724.  
  725.   /**
  726.    * Sends a HELO/EHLO command.
  727.    * @access protected
  728.    * @param string $hello 
  729.    * @param string $host 
  730.    * @return bool 
  731.    */
  732.   protected function SendHello($hello, $host) {
  733.     $this->client_send($hello . ' ' . $host . $this->CRLF);
  734.  
  735.     $rply = $this->get_lines();
  736.     $code = substr($rply, 0, 3);
  737.  
  738.     if($this->do_debug >= 2) {
  739.       $this->edebug('SMTP -> FROM SERVER: ' . $rply);
  740.     }
  741.  
  742.     if($code != 250) {
  743.       $this->error =
  744.         array('error' => $hello . ' not accepted from server',
  745.               'smtp_code' => $code,
  746.               'smtp_msg' => substr($rply, 4));
  747.       if($this->do_debug >= 1) {
  748.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  749.       }
  750.       return false;
  751.     }
  752.  
  753.     $this->helo_rply = $rply;
  754.  
  755.     return true;
  756.   }
  757.  
  758.   /**
  759.    * Starts a mail transaction from the email address specified in
  760.    * $from. Returns true if successful or false otherwise. If True
  761.    * the mail transaction is started and then one or more Recipient
  762.    * commands may be called followed by a Data command.
  763.    *
  764.    * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
  765.    *
  766.    * SMTP CODE SUCCESS: 250
  767.    * SMTP CODE SUCCESS: 552, 451, 452
  768.    * SMTP CODE SUCCESS: 500, 501, 421
  769.    * @access public
  770.    * @param string $from 
  771.    * @return bool 
  772.    */
  773.   public function Mail($from) {
  774.     $this->error = null; // so no confusion is caused
  775.  
  776.     if(!$this->connected()) {
  777.       $this->error = array(
  778.               'error' => 'Called Mail() without being connected');
  779.       return false;
  780.     }
  781.  
  782.     $useVerp = ($this->do_verp ? ' XVERP' : '');
  783.     $this->client_send('MAIL FROM:<' . $from . '>' . $useVerp . $this->CRLF);
  784.  
  785.     $rply = $this->get_lines();
  786.     $code = substr($rply, 0, 3);
  787.  
  788.     if($this->do_debug >= 2) {
  789.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  790.     }
  791.  
  792.     if($code != 250) {
  793.       $this->error =
  794.         array('error' => 'MAIL not accepted from server',
  795.               'smtp_code' => $code,
  796.               'smtp_msg' => substr($rply, 4));
  797.       if($this->do_debug >= 1) {
  798.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  799.       }
  800.       return false;
  801.     }
  802.     return true;
  803.   }
  804.  
  805.   /**
  806.    * Sends the quit command to the server and then closes the socket
  807.    * if there is no error or the $close_on_error argument is true.
  808.    *
  809.    * Implements from rfc 821: QUIT <CRLF>
  810.    *
  811.    * SMTP CODE SUCCESS: 221
  812.    * SMTP CODE ERROR  : 500
  813.    * @access public
  814.    * @param bool $close_on_error 
  815.    * @return bool 
  816.    */
  817.   public function Quit($close_on_error = true) {
  818.     $this->error = null; // so there is no confusion
  819.  
  820.     if(!$this->connected()) {
  821.       $this->error = array(
  822.               'error' => 'Called Quit() without being connected');
  823.       return false;
  824.     }
  825.  
  826.     // send the quit command to the server
  827.     $this->client_send('quit' . $this->CRLF);
  828.  
  829.     // get any good-bye messages
  830.     $byemsg = $this->get_lines();
  831.  
  832.     if($this->do_debug >= 2) {
  833.       $this->edebug('SMTP -> FROM SERVER:' . $byemsg);
  834.     }
  835.  
  836.     $rval = true;
  837.     $e = null;
  838.  
  839.     $code = substr($byemsg, 0, 3);
  840.     if($code != 221) {
  841.       // use e as a tmp var cause Close will overwrite $this->error
  842.       $e = array('error' => 'SMTP server rejected quit command',
  843.                  'smtp_code' => $code,
  844.                  'smtp_rply' => substr($byemsg, 4));
  845.       $rval = false;
  846.       if($this->do_debug >= 1) {
  847.         $this->edebug('SMTP -> ERROR: ' . $e['error'] . ': ' . $byemsg);
  848.       }
  849.     }
  850.  
  851.     if(empty($e) || $close_on_error) {
  852.       $this->Close();
  853.     }
  854.  
  855.     return $rval;
  856.   }
  857.  
  858.   /**
  859.    * Sends the command RCPT to the SMTP server with the TO: argument of $to.
  860.    * Returns true if the recipient was accepted false if it was rejected.
  861.    *
  862.    * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
  863.    *
  864.    * SMTP CODE SUCCESS: 250, 251
  865.    * SMTP CODE FAILURE: 550, 551, 552, 553, 450, 451, 452
  866.    * SMTP CODE ERROR  : 500, 501, 503, 421
  867.    * @access public
  868.    * @param string $to 
  869.    * @return bool 
  870.    */
  871.   public function Recipient($to) {
  872.     $this->error = null; // so no confusion is caused
  873.  
  874.     if(!$this->connected()) {
  875.       $this->error = array(
  876.               'error' => 'Called Recipient() without being connected');
  877.       return false;
  878.     }
  879.  
  880.     $this->client_send('RCPT TO:<' . $to . '>' . $this->CRLF);
  881.  
  882.     $rply = $this->get_lines();
  883.     $code = substr($rply, 0, 3);
  884.  
  885.     if($this->do_debug >= 2) {
  886.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  887.     }
  888.  
  889.     if($code != 250 && $code != 251) {
  890.       $this->error =
  891.         array('error' => 'RCPT not accepted from server',
  892.               'smtp_code' => $code,
  893.               'smtp_msg' => substr($rply, 4));
  894.       if($this->do_debug >= 1) {
  895.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  896.       }
  897.       return false;
  898.     }
  899.     return true;
  900.   }
  901.  
  902.   /**
  903.    * Sends the RSET command to abort and transaction that is
  904.    * currently in progress. Returns true if successful false
  905.    * otherwise.
  906.    *
  907.    * Implements rfc 821: RSET <CRLF>
  908.    *
  909.    * SMTP CODE SUCCESS: 250
  910.    * SMTP CODE ERROR  : 500, 501, 504, 421
  911.    * @access public
  912.    * @return bool 
  913.    */
  914.   public function Reset() {
  915.     $this->error = null; // so no confusion is caused
  916.  
  917.     if(!$this->connected()) {
  918.       $this->error = array('error' => 'Called Reset() without being connected');
  919.       return false;
  920.     }
  921.  
  922.     $this->client_send('RSET' . $this->CRLF);
  923.  
  924.     $rply = $this->get_lines();
  925.     $code = substr($rply, 0, 3);
  926.  
  927.     if($this->do_debug >= 2) {
  928.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  929.     }
  930.  
  931.     if($code != 250) {
  932.       $this->error =
  933.         array('error' => 'RSET failed',
  934.               'smtp_code' => $code,
  935.               'smtp_msg' => substr($rply, 4));
  936.       if($this->do_debug >= 1) {
  937.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  938.       }
  939.       return false;
  940.     }
  941.  
  942.     return true;
  943.   }
  944.  
  945.   /**
  946.    * Starts a mail transaction from the email address specified in
  947.    * $from. Returns true if successful or false otherwise. If True
  948.    * the mail transaction is started and then one or more Recipient
  949.    * commands may be called followed by a Data command. This command
  950.    * will send the message to the users terminal if they are logged
  951.    * in and send them an email.
  952.    *
  953.    * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
  954.    *
  955.    * SMTP CODE SUCCESS: 250
  956.    * SMTP CODE SUCCESS: 552, 451, 452
  957.    * SMTP CODE SUCCESS: 500, 501, 502, 421
  958.    * @access public
  959.    * @param string $from 
  960.    * @return bool 
  961.    */
  962.   public function SendAndMail($from) {
  963.     $this->error = null; // so no confusion is caused
  964.  
  965.     if(!$this->connected()) {
  966.       $this->error = array(
  967.           'error' => 'Called SendAndMail() without being connected');
  968.       return false;
  969.     }
  970.  
  971.     $this->client_send('SAML FROM:' . $from . $this->CRLF);
  972.  
  973.     $rply = $this->get_lines();
  974.     $code = substr($rply, 0, 3);
  975.  
  976.     if($this->do_debug >= 2) {
  977.       $this->edebug('SMTP -> FROM SERVER:' . $rply);
  978.     }
  979.  
  980.     if($code != 250) {
  981.       $this->error =
  982.         array('error' => 'SAML not accepted from server',
  983.               'smtp_code' => $code,
  984.               'smtp_msg' => substr($rply, 4));
  985.       if($this->do_debug >= 1) {
  986.         $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
  987.       }
  988.       return false;
  989.     }
  990.     return true;
  991.   }
  992.  
  993.   /**
  994.    * This is an optional command for SMTP that this class does not
  995.    * support. This method is here to make the RFC821 Definition
  996.    * complete for this class and __may__ be implimented in the future
  997.    *
  998.    * Implements from rfc 821: TURN <CRLF>
  999.    *
  1000.    * SMTP CODE SUCCESS: 250
  1001.    * SMTP CODE FAILURE: 502
  1002.    * SMTP CODE ERROR  : 500, 503
  1003.    * @access public
  1004.    * @return bool 
  1005.    */
  1006.   public function Turn() {
  1007.     $this->error = array('error' => 'This method, TURN, of the SMTP '.
  1008.                                     'is not implemented');
  1009.     if($this->do_debug >= 1) {
  1010.       $this->edebug('SMTP -> NOTICE: ' . $this->error['error']);
  1011.     }
  1012.     return false;
  1013.   }
  1014.  
  1015.   /**
  1016.   * Sends data to the server
  1017.   * @param string $data 
  1018.   * @access public
  1019.   * @return Integer number of bytes sent to the server or FALSE on error
  1020.   */
  1021.   public function client_send($data) {
  1022.       if ($this->do_debug >= 1) {
  1023.           $this->edebug("CLIENT -> SMTP: $data");
  1024.       }
  1025.       return fwrite($this->smtp_conn, $data);
  1026.   }
  1027.  
  1028.   /**
  1029.   * Get the current error
  1030.   * @access public
  1031.   * @return array 
  1032.   */
  1033.   public function getError() {
  1034.     return $this->error;
  1035.   }
  1036.  
  1037.   /////////////////////////////////////////////////
  1038.   // INTERNAL FUNCTIONS
  1039.   /////////////////////////////////////////////////
  1040.  
  1041.   /**
  1042.    * Read in as many lines as possible
  1043.    * either before eof or socket timeout occurs on the operation.
  1044.    * With SMTP we can tell if we have more lines to read if the
  1045.    * 4th character is '-' symbol. If it is a space then we don't
  1046.    * need to read anything else.
  1047.    * @access protected
  1048.    * @return string 
  1049.    */
  1050.   protected function get_lines() {
  1051.     $data = '';
  1052.     $endtime = 0;
  1053.     /* If for some reason the fp is bad, don't inf loop */
  1054.     if (!is_resource($this->smtp_conn)) {
  1055.       return $data;
  1056.     }
  1057.     stream_set_timeout($this->smtp_conn, $this->Timeout);
  1058.     if ($this->Timelimit > 0) {
  1059.       $endtime = time() + $this->Timelimit;
  1060.     }
  1061.     while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  1062.       $str = @fgets($this->smtp_conn, 515);
  1063.       if($this->do_debug >= 4) {
  1064.         $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
  1065.         $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
  1066.       }
  1067.       $data .= $str;
  1068.       if($this->do_debug >= 4) {
  1069.         $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
  1070.       }
  1071.       // if 4th character is a space, we are done reading, break the loop
  1072.       if(substr($str, 3, 1) == ' ') { break; }
  1073.       // Timed-out? Log and break
  1074.       $info = stream_get_meta_data($this->smtp_conn);
  1075.       if ($info['timed_out']) {
  1076.         if($this->do_debug >= 4) {
  1077.           $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' seconds)');
  1078.         }
  1079.         break;
  1080.       }
  1081.       // Now check if reads took too long
  1082.       if ($endtime) {
  1083.         if (time() > $endtime) {
  1084.           if($this->do_debug >= 4) {
  1085.             $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' seconds)');
  1086.           }
  1087.           break;
  1088.         }
  1089.       }
  1090.     }
  1091.     return $data;
  1092.   }
  1093.  
  1094. }

Documentation generated on Tue, 19 Nov 2013 15:13:59 +0100 by phpDocumentor 1.4.3