home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 March / PCWorld_2003-03_cd.bin / Software / Vyzkuste / phptriad / phptriad2-2-1.exe / php / pear / Net / SMTP.php < prev    next >
Encoding:
PHP Script  |  2001-08-28  |  13.9 KB  |  401 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.      * The last line read from the server.
  65.      * @var string
  66.      */
  67.     var $lastline;
  68.     
  69.     /**
  70.      * Constructor
  71.      *
  72.      * Instantiates a new Net_SMTP object, overriding any defaults
  73.      * with parameters that are passed in.
  74.      *
  75.      * @param string The server to connect to.
  76.      * @param int The port to connect to.
  77.      * @param string The value to give when sending EHLO or HELO.
  78.      */
  79.     function Net_SMTP($host = null, $port = null, $localhost = null) {
  80.         if (isset($host)) $this->host = $host;
  81.         if (isset($port)) $this->port = $port;
  82.         if (isset($localhost)) $this->localhost = $localhost;
  83.     }
  84.     
  85.     /**
  86.      * Attempt to connect to the SMTP server.
  87.      *
  88.      * @return mixed Returns a PEAR_Error with an error message on any
  89.      *               kind of failure, or true on success.
  90.      * @access public
  91.      */
  92.     function connect() {
  93.         include_once 'Net/Socket.php';
  94.         
  95.         if (PEAR::isError($this->socket = new Net_Socket())) { return new PEAR_Error('unable to create a socket object'); }
  96.         if (PEAR::isError($this->socket->connect($this->host, $this->port))) { return new PEAR_Error('unable to open socket'); }
  97.         
  98.         if (PEAR::isError($this->validateResponse('220'))) { return new PEAR_Error('smtp server not 220 ready'); }
  99.         if (!$this->identifySender()) { return new PEAR_Error('unable to identify smtp server'); }
  100.         
  101.         return true;
  102.     }
  103.     
  104.     /**
  105.      * Attempt to disconnect from the SMTP server.
  106.      *
  107.      * @return mixed Returns a PEAR_Error with an error message on any
  108.      *               kind of failure, or true on success.
  109.      * @access public
  110.      */
  111.     function disconnect() {
  112.         if (PEAR::isError($this->socket->write("QUIT\r\n"))) { return new PEAR_Error('write to socket failed'); }
  113.         if (!$this->validateResponse('221')) { return new PEAR_Error('221 Bye not received'); }
  114.         if (PEAR::isError($this->socket->disconnect())) { return new PEAR_Error('socket disconnect failed'); }
  115.         
  116.         return true;
  117.     }
  118.     
  119.     /**
  120.      * Attempt to do SMTP authentication.
  121.      *
  122.      * @param string The userid to authenticate as.
  123.      * @param string The password to authenticate with.
  124.      *
  125.      * @return mixed Returns a PEAR_Error with an error message on any
  126.      *               kind of failure, or true on success.
  127.      * @access public
  128.      */
  129.     function auth($uid, $pwd) {
  130.         /* Note: not currently checking if AUTH LOGIN is allowed */    
  131.         /* Note: only allows one authentication mechanism */ 
  132.         
  133.         if (!isset($this->esmtp['AUTH'])) { return new PEAR_Error('auth not supported'); }
  134.         
  135.         if (PEAR::isError($this->socket->write("AUTH LOGIN\r\n"))) { return new PEAR_Error('write to socket failed'); }
  136.         if (!$this->validateResponse('334')) { return new PEAR_Error('AUTH LOGIN not recognized'); }
  137.         
  138.         if (PEAR::isError($this->socket->write(base64_encode($uid) . "\n"))) { return new PEAR_Error('write to socket failed'); }
  139.         if (!$this->validateResponse('334')) { return new PEAR_Error('354 not received'); }
  140.         
  141.         if (PEAR::isError($this->socket->write(base64_encode($pwd) . "\n"))) { return new PEAR_Error('write to socket failed'); }
  142.         if (!$this->validateResponse('235')) { return new PEAR_Error('235 not received'); }
  143.         
  144.         return true;
  145.     }
  146.     
  147.     /**
  148.      * Send the HELO command.
  149.      * 
  150.      * @param string The domain name to say we are.
  151.      *
  152.      * @return mixed Returns a PEAR_Error with an error message on any
  153.      *               kind of failure, or true on success.
  154.      * @access public
  155.      */
  156.     function helo($domain) {
  157.         if (PEAR::isError($this->socket->write("HELO $domain\r\n"))) { return new PEAR_Error('write to socket failed'); }
  158.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  159.         
  160.         return true;
  161.     }
  162.     
  163.     /**
  164.      * Send the MAIL FROM: command.
  165.      * 
  166.      * @param string The sender (reverse path) to set.
  167.      *
  168.      * @return mixed Returns a PEAR_Error with an error message on any
  169.      *               kind of failure, or true on success.
  170.      * @access public
  171.      */
  172.     function mailFrom($reverse_path) {
  173.         if (PEAR::isError($this->socket->write("MAIL FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  174.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  175.         
  176.         return true;
  177.     }
  178.     
  179.     /**
  180.      * Send the RCPT TO: command.
  181.      * 
  182.      * @param string The recipient (forward path) to add.
  183.      *
  184.      * @return mixed Returns a PEAR_Error with an error message on any
  185.      *               kind of failure, or true on success.
  186.      * @access public
  187.      */
  188.     function rcptTo($forward_path) {
  189.         /* Note: 251 is also a valid response code */
  190.         
  191.         if (PEAR::isError($this->socket->write("RCPT TO:<$forward_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  192.         if (!($this->validateResponse('250'))) { return new PEAR_Error($this->lastline); }
  193.         
  194.         return true;
  195.     }
  196.     
  197.     /**
  198.      * Send the DATA command.
  199.      * 
  200.      * @param string The message body to send.
  201.      *
  202.      * @return mixed Returns a PEAR_Error with an error message on any
  203.      *               kind of failure, or true on success.
  204.      * @access public
  205.      */
  206.     function data($data) {
  207.         $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
  208.         $data = preg_replace("/\n\n/", "\n\r\n", $data);
  209.         $data = preg_replace("/\n\./", "\n..", $data);
  210.         
  211.         if (PEAR::isError($this->socket->write("DATA\r\n"))) { return new PEAR_Error('write to socket failed'); }
  212.         if (!($this->validateResponse('354'))) { return new PEAR_Error('354 not received'); }
  213.         if (PEAR::isError($this->socket->write($data . "\r\n.\r\n"))) { return new PEAR_Error('write to socket failed'); }
  214.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  215.         
  216.         return true;
  217.     }
  218.     
  219.     /**
  220.      * Send the SEND FROM: command.
  221.      * 
  222.      * @param string The reverse path to send.
  223.      *
  224.      * @return mixed Returns a PEAR_Error with an error message on any
  225.      *               kind of failure, or true on success.
  226.      * @access public
  227.      */
  228.     function send_from($reverse_path) {
  229.         if (PEAR::isError($this->socket->write("SEND FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  230.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  231.         
  232.         return true;
  233.     }
  234.     
  235.     /**
  236.      * Send the SOML FROM: command.
  237.      * 
  238.      * @param string The reverse path to send.
  239.      *
  240.      * @return mixed Returns a PEAR_Error with an error message on any
  241.      *               kind of failure, or true on success.
  242.      * @access public
  243.      */
  244.     function soml_from($reverse_path) {
  245.         if (PEAR::isError($this->socket->write("SOML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  246.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  247.         
  248.         return true;
  249.     }
  250.     
  251.     /**
  252.      * Send the SAML FROM: command.
  253.      * 
  254.      * @param string The reverse path to send.
  255.      *
  256.      * @return mixed Returns a PEAR_Error with an error message on any
  257.      *               kind of failure, or true on success.
  258.      * @access public
  259.      */
  260.     function saml_from($reverse_path) {
  261.         if (PEAR::isError($this->socket->write("SAML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); }
  262.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  263.         
  264.         return true;
  265.     }
  266.     
  267.     /**
  268.      * Send the RSET command.
  269.      * 
  270.      * @return mixed Returns a PEAR_Error with an error message on any
  271.      *               kind of failure, or true on success.
  272.      * @access public
  273.      */
  274.     function rset() {
  275.         if (PEAR::isError($this->socket->write("RSET\r\n"))) { return new PEAR_Error('write to socket failed'); }
  276.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  277.         
  278.         return true;
  279.     }
  280.     
  281.     /**
  282.      * Send the VRFY command.
  283.      * 
  284.      * @param string The string to verify
  285.      *
  286.      * @return mixed Returns a PEAR_Error with an error message on any
  287.      *               kind of failure, or true on success.
  288.      * @access public
  289.      */
  290.     function vrfy($string) {
  291.         /* Note: 251 is also a valid response code */
  292.         if (PEAR::isError($this->socket->write("VRFY $string\r\n"))) { return new PEAR_Error('write to socket failed'); }
  293.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  294.         
  295.         return true;
  296.     }
  297.     
  298.     /**
  299.      * Send the NOOP command.
  300.      * 
  301.      * @return mixed Returns a PEAR_Error with an error message on any
  302.      *               kind of failure, or true on success.
  303.      * @access public
  304.      */
  305.     function noop() {
  306.         if (PEAR::isError($this->socket->write("NOOP\r\n"))) { return new PEAR_Error('write to socket failed'); }
  307.         if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); }
  308.         
  309.         return true;
  310.     }
  311.     
  312.     /**
  313.      * Attempt to send the EHLO command and obtain a list of ESMTP
  314.      * extensions available, and failing that just send HELO.
  315.      * 
  316.      * @return mixed Returns a PEAR_Error with an error message on any
  317.      *               kind of failure, or true on success.
  318.      * @access private
  319.      */
  320.     function identifySender() {
  321.         if (PEAR::isError($this->socket->write("EHLO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); }
  322.         
  323.         $extensions = array();
  324.         if (!($this->validateAndParseResponse('250', $extensions))) { 
  325.             if (PEAR::isError($this->socket->write("HELO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); }
  326.             if (!($this->validateResponse('250'))) { return new PEAR_Error('HELO not accepted', $this->code); }
  327.             return true;
  328.         }    
  329.         
  330.         for ($i = 0; $i < count($extensions); $i++) {
  331.             $verb = strtok($extensions[$i], ' ');
  332.             $arguments = substr($extensions[$i], strlen($verb) + 1, strlen($extensions[$i]) - strlen($verb) - 2);
  333.             $this->esmtp[$verb] = $arguments;
  334.         }
  335.         return true;
  336.     }
  337.     
  338.     /**
  339.      * Read a response from the server and see if the response code
  340.      * matches what we are expecting.
  341.      * 
  342.      * @param int The response code we are expecting.
  343.      *
  344.      * @return boolean True if we get what we expect, false otherwise.
  345.      * @access private
  346.      */
  347.     function validateResponse($code) {
  348.         while ($this->lastline = $this->socket->readLine()) {
  349.             $reply_code = strtok($this->lastline, ' ');
  350.             if (!(strcmp($code, $reply_code))) {
  351.                 $this->code = $reply_code;
  352.                 return true;
  353.             } else {
  354.                 $reply_code = strtok($this->lastline, '-');
  355.                 if (strcmp($code, $reply_code)) {
  356.                     $this->code = $reply_code;
  357.                     return false;
  358.                 }
  359.             }
  360.         }
  361.         
  362.         return false;
  363.     }
  364.     
  365.     /**
  366.      * Read a response from the server and see if the response code
  367.      * matches what we are expecting. Also save the rest of the
  368.      * response in the array passed by reference as the second
  369.      * argument.
  370.      *
  371.      * @param int The response code we are expecting.
  372.      * @param array An array to dump the rest of the response into.
  373.      *
  374.      * @return boolean True if we get what we expect, false otherwise.
  375.      * @access private
  376.      */
  377.     function validateAndParseResponse($code, &$arguments) {
  378.         $arguments = array();
  379.         
  380.         while ($this->lastline = $this->socket->readLine()) {
  381.             $reply_code = strtok($this->lastline, ' ');
  382.             if (!(strcmp($code, $reply_code))) {
  383.                 $arguments[] = substr($this->lastline, strlen($code) + 1, strlen($this->lastline) - strlen($code) - 1);
  384.                 $this->code = $reply_code;
  385.                 return true;
  386.             } else {
  387.                 $reply_code = strtok($this->lastline, '-');
  388.                 if (strcmp($code, $reply_code)) {
  389.                     $this->code = $reply_code;
  390.                     return false;
  391.                 }
  392.             }
  393.             $arguments[] = substr($this->lastline, strlen($code) + 1, strlen($this->lastline) - strlen($code) - 1);
  394.         }
  395.         
  396.         return false;
  397.     }
  398.     
  399. }
  400. ?>
  401.