home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 August / PCWorld_2001-08_cd.bin / Komunikace / phptriad / phptriadsetup2-11.exe / php / pear / Net / SMTP.php < prev    next >
PHP Script  |  2001-01-10  |  14KB  |  395 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0                                                      |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2001 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Chuck Hagenbuch <chuck@horde.org>                           |
  17. // +----------------------------------------------------------------------+
  18.  
  19. require_once 'PEAR.php';
  20.  
  21. /**
  22.  * Provides an implementation of the SMTP protocol using PEAR's
  23.  * Net_Socket:: class.
  24.  */
  25. class Net_SMTP extends PEAR {
  26.     
  27.     /**
  28.      * The server to connect to.
  29.      * @var string
  30.      */
  31.     var $host = 'localhost';
  32.     
  33.     /**
  34.      * The port to connect to.
  35.      * @var int
  36.      */
  37.     var $port = 25;
  38.     
  39.     /**
  40.      * The value to give when sending EHLO or HELO.
  41.      * @var string
  42.      */
  43.     var $localhost = 'localhost';
  44.     
  45.     /**
  46.      * The socket resource being used to connect to the SMTP server.
  47.      * @var resource
  48.      */
  49.     var $socket;
  50.     
  51.     /**
  52.      * The most recent reply code
  53.      * @var int
  54.      */
  55.     var $code;
  56.     
  57.     /**
  58.      * Stores detected features of the SMTP server.
  59.      * @var array
  60.      */
  61.     var $esmtp;
  62.     
  63.     /**
  64.      * Constructor
  65.      *
  66.      * Instantiates a new Net_SMTP object, overriding any defaults
  67.      * with parameters that are passed in.
  68.      *
  69.      * @param string The server to connect to.
  70.      * @param int The port to connect to.
  71.      * @param string The value to give when sending EHLO or HELO.
  72.      */
  73.     function Net_SMTP($host = null, $port = null, $localhost = null) {
  74.         if (isset($host)) $this->host = $host;
  75.         if (isset($port)) $this->port = $port;
  76.         if (isset($localhost)) $this->localhost = $localhost;
  77.     }
  78.     
  79.     /**
  80.      * Attempt to connect to the SMTP server.
  81.      *
  82.      * @return mixed Returns a PEAR_Error with an error message on any
  83.      *               kind of failure, or true on success.
  84.      * @access public
  85.      */
  86.     function connect() {
  87.         include_once 'Net/Socket.php';
  88.         
  89.         if (PEAR::isError($this->socket = new Net_Socket())) { return new PEAR_Error('unable to create a socket object'); }
  90.         if (PEAR::isError($this->socket->connect($this->host, $this->port))) { return new PEAR_Error('unable to open socket'); }
  91.         
  92.         if (PEAR::isError($this->validateResponse('220'))) { return new PEAR_Error('smtp server not 220 ready'); }
  93.         if (!$this->identifySender()) { return new PEAR_Error('unable to identify smtp server'); }
  94.         
  95.         return true;
  96.     }
  97.     
  98.     /**
  99.      * Attempt to disconnect from the SMTP server.
  100.      *
  101.      * @return mixed Returns a PEAR_Error with an error message on any
  102.      *               kind of failure, or true on success.
  103.      * @access public
  104.      */
  105.     function disconnect() {
  106.         if (PEAR::isError($this->socket->write("QUIT\r\n"))) { return new PEAR_Error('write to socket failed'); }
  107.         if (!$this->validateResponse('221')) { return new PEAR_Error('221 Bye not received'); }
  108.         if (PEAR::isError($this->socket->disconnect())) { return new PEAR_Error('socket disconnect failed'); }
  109.         
  110.         return true;
  111.     }
  112.     
  113.     /**
  114.      * Attempt to do SMTP authentication.
  115.      *
  116.      * @param string The userid to authenticate as.
  117.      * @param string The password to authenticate with.
  118.      *
  119.      * @return mixed Returns a PEAR_Error with an error message on any
  120.      *               kind of failure, or true on success.
  121.      * @access public
  122.      */
  123.     function auth($uid, $pwd) {
  124.         /* Note: not currently checking if AUTH LOGIN is allowed */    
  125.         /* Note: only allows one authentication mechanism */ 
  126.         
  127.         if (!isset($this->esmtp['AUTH'])) { return new PEAR_Error('auth not supported'); }
  128.         
  129.         if (PEAR::isError($this->socket->write("AUTH LOGIN\r\n"))) { return new PEAR_Error('write to socket failed'); }
  130.         if (!$this->validateResponse('334')) { return new PEAR_Error('AUTH LOGIN not recognized'); }
  131.         
  132.         if (PEAR::isError($this->socket->write(base64_encode($uid) . "\n"))) { return new PEAR_Error('write to socket failed'); }
  133.         if (!$this->validateResponse('334')) { return new PEAR_Error('354 not received'); }
  134.         
  135.         if (PEAR::isError($this->socket->write(base64_encode($pwd) . "\n"))) { return new PEAR_Error('write to socket failed'); }
  136.         if (!$this->validateResponse('235')) { return new PEAR_Error('235 not received'); }
  137.         
  138.         return true;
  139.     }
  140.     
  141.     /**
  142.      * Send the HELO command.
  143.      * 
  144.      * @param string The domain name to say we are.
  145.      *
  146.      * @return mixed Returns a PEAR_Error with an error message on any
  147.      *               kind of failure, or true on success.
  148.      * @access public
  149.      */
  150.     function helo($domain) {
  151.         if (PEAR::isError($this->socket->write("HELO $domain\r\n"))) { return new PEAR_Error('write to socket failed'); }
  152.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  153.         
  154.         return true;
  155.     }
  156.     
  157.     /**
  158.      * Send the MAIL FROM: command.
  159.      * 
  160.      * @param string The sender (reverse path) to set.
  161.      *
  162.      * @return mixed Returns a PEAR_Error with an error message on any
  163.      *               kind of failure, or true on success.
  164.      * @access public
  165.      */
  166.     function mailFrom($reverse_path) {
  167.         if (PEAR::isError($this->socket->write("MAIL FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  168.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  169.         
  170.         return true;
  171.     }
  172.     
  173.     /**
  174.      * Send the RCPT TO: command.
  175.      * 
  176.      * @param string The recipient (forward path) to add.
  177.      *
  178.      * @return mixed Returns a PEAR_Error with an error message on any
  179.      *               kind of failure, or true on success.
  180.      * @access public
  181.      */
  182.     function rcptTo($forward_path) {
  183.         /* Note: 251 is also a valid response code */
  184.         
  185.         if (PEAR::isError($this->socket->write("RCPT TO:<$forward_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  186.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  187.         
  188.         return true;
  189.     }
  190.     
  191.     /**
  192.      * Send the DATA command.
  193.      * 
  194.      * @param string The message body to send.
  195.      *
  196.      * @return mixed Returns a PEAR_Error with an error message on any
  197.      *               kind of failure, or true on success.
  198.      * @access public
  199.      */
  200.     function data($data) {
  201.         $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
  202.         $data = preg_replace("/\n\n/", "\n\r\n", $data);
  203.         $data = preg_replace("/^(\..*)/", ".\\1", $data);
  204.         
  205.         if (PEAR::isError($this->socket->write("DATA\r\n"))) { return new PEAR_Error('write to socket failed'); }
  206.         if (!($this->validateResponse('354'))) { return new PEAR_Error('354 not received'); }
  207.         if (PEAR::isError($this->socket->write($data . "\r\n.\r\n"))) { return new PEAR_Error('write to socket failed'); }
  208.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  209.         
  210.         return true;
  211.     }
  212.     
  213.     /**
  214.      * Send the SEND FROM: command.
  215.      * 
  216.      * @param string The reverse path to send.
  217.      *
  218.      * @return mixed Returns a PEAR_Error with an error message on any
  219.      *               kind of failure, or true on success.
  220.      * @access public
  221.      */
  222.     function send_from($reverse_path) {
  223.         if (PEAR::isError($this->socket->write("SEND FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  224.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  225.         
  226.         return true;
  227.     }
  228.     
  229.     /**
  230.      * Send the SOML FROM: command.
  231.      * 
  232.      * @param string The reverse path to send.
  233.      *
  234.      * @return mixed Returns a PEAR_Error with an error message on any
  235.      *               kind of failure, or true on success.
  236.      * @access public
  237.      */
  238.     function soml_from($reverse_path) {
  239.         if (PEAR::isError($this->socket->write("SOML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  240.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  241.         
  242.         return true;
  243.     }
  244.     
  245.     /**
  246.      * Send the SAML FROM: command.
  247.      * 
  248.      * @param string The reverse path to send.
  249.      *
  250.      * @return mixed Returns a PEAR_Error with an error message on any
  251.      *               kind of failure, or true on success.
  252.      * @access public
  253.      */
  254.     function saml_from($reverse_path) {
  255.         if (PEAR::isError($this->socket->write("SAML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  256.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  257.         
  258.         return true;
  259.     }
  260.     
  261.     /**
  262.      * Send the RSET command.
  263.      * 
  264.      * @return mixed Returns a PEAR_Error with an error message on any
  265.      *               kind of failure, or true on success.
  266.      * @access public
  267.      */
  268.     function rset() {
  269.         if (PEAR::isError($this->socket->write("RSET\r\n"))) { return new PEAR_Error('write to socket failed'); }
  270.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  271.         
  272.         return true;
  273.     }
  274.     
  275.     /**
  276.      * Send the VRFY command.
  277.      * 
  278.      * @param string The string to verify
  279.      *
  280.      * @return mixed Returns a PEAR_Error with an error message on any
  281.      *               kind of failure, or true on success.
  282.      * @access public
  283.      */
  284.     function vrfy($string) {
  285.         /* Note: 251 is also a valid response code */
  286.         if (PEAR::isError($this->socket->write("VRFY $string\r\n"))) { return new PEAR_Error('write to socket failed'); }
  287.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  288.         
  289.         return true;
  290.     }
  291.     
  292.     /**
  293.      * Send the NOOP command.
  294.      * 
  295.      * @return mixed Returns a PEAR_Error with an error message on any
  296.      *               kind of failure, or true on success.
  297.      * @access public
  298.      */
  299.     function noop() {
  300.         if (PEAR::isError($this->socket->write("NOOP\r\n"))) { return new PEAR_Error('write to socket failed'); }
  301.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  302.         
  303.         return true;
  304.     }
  305.     
  306.     /**
  307.      * Attempt to send the EHLO command and obtain a list of ESMTP
  308.      * extensions available, and failing that just send HELO.
  309.      * 
  310.      * @return mixed Returns a PEAR_Error with an error message on any
  311.      *               kind of failure, or true on success.
  312.      * @access private
  313.      */
  314.     function identifySender() {
  315.         if (PEAR::isError($this->socket->write("EHLO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); }
  316.         
  317.         $extensions = array();
  318.         if (!($this->validateAndParseResponse('250', $extensions))) { 
  319.             if (PEAR::isError($this->socket->write("HELO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); }
  320.             if (!($this->validateResponse('250'))) { return new PEAR_Error('HELO not accepted', $this->code); }
  321.             return true;
  322.         }    
  323.         
  324.         for ($i = 0; $i < count($extensions); $i++) {
  325.             $verb = strtok($extensions[$i], ' ');
  326.             $arguments = substr($extensions[$i], strlen($verb) + 1, strlen($extensions[$i]) - strlen($verb) - 2);
  327.             $this->esmtp[$verb] = $arguments;
  328.         }
  329.         return true;
  330.     }
  331.     
  332.     /**
  333.      * Read a response from the server and see if the response code
  334.      * matches what we are expecting.
  335.      * 
  336.      * @param int The response code we are expecting.
  337.      *
  338.      * @return boolean True if we get what we expect, false otherwise.
  339.      * @access private
  340.      */
  341.     function validateResponse($code) {
  342.         while ($response = $this->socket->readLine()) {
  343.             $reply_code = strtok($response, ' ');
  344.             if (!(strcmp($code, $reply_code))) {
  345.                 $this->code = $reply_code;
  346.                 return true;
  347.             } else {
  348.                 $reply_code = strtok($response, '-');
  349.                 if (strcmp($code, $reply_code)) {
  350.                     $this->code = $reply_code;
  351.                     return false;
  352.                 }
  353.             }
  354.         }
  355.         
  356.         return false;
  357.     }
  358.     
  359.     /**
  360.      * Read a response from the server and see if the response code
  361.      * matches what we are expecting. Also save the rest of the
  362.      * response in the array passed by reference as the second
  363.      * argument.
  364.      *
  365.      * @param int The response code we are expecting.
  366.      * @param array An array to dump the rest of the response into.
  367.      *
  368.      * @return boolean True if we get what we expect, false otherwise.
  369.      * @access private
  370.      */
  371.     function validateAndParseResponse($code, &$arguments) {
  372.         $arguments = array();
  373.         
  374.         while ($response = $this->socket->readLine()) {
  375.             $reply_code = strtok($response, ' ');
  376.             if (!(strcmp($code, $reply_code))) {
  377.                 $arguments[] = substr($response, strlen($code) + 1, strlen($response) - strlen($code) - 1);
  378.                 $this->code = $reply_code;
  379.                 return true;
  380.             } else {
  381.                 $reply_code = strtok($response, '-');
  382.                 if (strcmp($code, $reply_code)) {
  383.                     $this->code = $reply_code;
  384.                     return false;
  385.                 }
  386.             }
  387.             $arguments[] = substr($response, strlen($code) + 1, strlen($response) - strlen($code) - 1);
  388.         }
  389.         
  390.         return false;
  391.     }
  392.     
  393. }
  394. ?>
  395.