home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Net / SMTP.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  32.7 KB  |  1,083 lines

  1. <?php
  2. /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 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. // |          Jon Parise <jon@php.net>                                    |
  18. // |          Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>      |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: SMTP.php,v 1.63 2008/06/10 05:39:12 jon Exp $
  22.  
  23. require_once 'PEAR.php';
  24. require_once 'Net/Socket.php';
  25.  
  26. /**
  27.  * Provides an implementation of the SMTP protocol using PEAR's
  28.  * Net_Socket:: class.
  29.  *
  30.  * @package Net_SMTP
  31.  * @author  Chuck Hagenbuch <chuck@horde.org>
  32.  * @author  Jon Parise <jon@php.net>
  33.  * @author  Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
  34.  *
  35.  * @example basic.php   A basic implementation of the Net_SMTP package.
  36.  */
  37. class Net_SMTP
  38. {
  39.     /**
  40.      * The server to connect to.
  41.      * @var string
  42.      * @access public
  43.      */
  44.     var $host = 'localhost';
  45.  
  46.     /**
  47.      * The port to connect to.
  48.      * @var int
  49.      * @access public
  50.      */
  51.     var $port = 25;
  52.  
  53.     /**
  54.      * The value to give when sending EHLO or HELO.
  55.      * @var string
  56.      * @access public
  57.      */
  58.     var $localhost = 'localhost';
  59.  
  60.     /**
  61.      * List of supported authentication methods, in preferential order.
  62.      * @var array
  63.      * @access public
  64.      */
  65.     var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
  66.  
  67.     /**
  68.      * Use SMTP command pipelining (specified in RFC 2920) if the SMTP
  69.      * server supports it.
  70.      *
  71.      * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(),
  72.      * somlFrom() and samlFrom() do not wait for a response from the
  73.      * SMTP server but return immediately.
  74.      *
  75.      * @var bool
  76.      * @access public
  77.      */
  78.     var $pipelining = false;
  79.  
  80.     /**
  81.      * Number of pipelined commands.
  82.      * @var int
  83.      * @access private
  84.      */
  85.     var $_pipelined_commands = 0;
  86.  
  87.     /**
  88.      * Should debugging output be enabled?
  89.      * @var boolean
  90.      * @access private
  91.      */
  92.     var $_debug = false;
  93.  
  94.     /**
  95.      * The socket resource being used to connect to the SMTP server.
  96.      * @var resource
  97.      * @access private
  98.      */
  99.     var $_socket = null;
  100.  
  101.     /**
  102.      * The most recent server response code.
  103.      * @var int
  104.      * @access private
  105.      */
  106.     var $_code = -1;
  107.  
  108.     /**
  109.      * The most recent server response arguments.
  110.      * @var array
  111.      * @access private
  112.      */
  113.     var $_arguments = array();
  114.  
  115.     /**
  116.      * Stores detected features of the SMTP server.
  117.      * @var array
  118.      * @access private
  119.      */
  120.     var $_esmtp = array();
  121.  
  122.     /**
  123.      * Instantiates a new Net_SMTP object, overriding any defaults
  124.      * with parameters that are passed in.
  125.      *
  126.      * If you have SSL support in PHP, you can connect to a server
  127.      * over SSL using an 'ssl://' prefix:
  128.      *
  129.      *   // 465 is a common smtps port.
  130.      *   $smtp = new Net_SMTP('ssl://mail.host.com', 465);
  131.      *   $smtp->connect();
  132.      *
  133.      * @param string  $host       The server to connect to.
  134.      * @param integer $port       The port to connect to.
  135.      * @param string  $localhost  The value to give when sending EHLO or HELO.
  136.      * @param boolean $pipeling   Use SMTP command pipelining
  137.      *
  138.      * @access  public
  139.      * @since   1.0
  140.      */
  141.     function Net_SMTP($host = null, $port = null, $localhost = null, $pipelining = false)
  142.     {
  143.         if (isset($host)) {
  144.             $this->host = $host;
  145.         }
  146.         if (isset($port)) {
  147.             $this->port = $port;
  148.         }
  149.         if (isset($localhost)) {
  150.             $this->localhost = $localhost;
  151.         }
  152.         $this->pipelining = $pipelining;
  153.  
  154.         $this->_socket = new Net_Socket();
  155.  
  156.         /* Include the Auth_SASL package.  If the package is not
  157.          * available, we disable the authentication methods that
  158.          * depend upon it. */
  159.         if ((@include_once 'Auth/SASL.php') === false) {
  160.             $pos = array_search('DIGEST-MD5', $this->auth_methods);
  161.             unset($this->auth_methods[$pos]);
  162.             $pos = array_search('CRAM-MD5', $this->auth_methods);
  163.             unset($this->auth_methods[$pos]);
  164.         }
  165.     }
  166.  
  167.     /**
  168.      * Set the value of the debugging flag.
  169.      *
  170.      * @param   boolean $debug      New value for the debugging flag.
  171.      *
  172.      * @access  public
  173.      * @since   1.1.0
  174.      */
  175.     function setDebug($debug)
  176.     {
  177.         $this->_debug = $debug;
  178.     }
  179.  
  180.     /**
  181.      * Send the given string of data to the server.
  182.      *
  183.      * @param   string  $data       The string of data to send.
  184.      *
  185.      * @return  mixed   True on success or a PEAR_Error object on failure.
  186.      *
  187.      * @access  private
  188.      * @since   1.1.0
  189.      */
  190.     function _send($data)
  191.     {
  192.         if ($this->_debug) {
  193.             echo "DEBUG: Send: $data\n";
  194.         }
  195.  
  196.         if (PEAR::isError($error = $this->_socket->write($data))) {
  197.             return PEAR::raiseError('Failed to write to socket: ' .
  198.                                     $error->getMessage());
  199.         }
  200.  
  201.         return true;
  202.     }
  203.  
  204.     /**
  205.      * Send a command to the server with an optional string of
  206.      * arguments.  A carriage return / linefeed (CRLF) sequence will
  207.      * be appended to each command string before it is sent to the
  208.      * SMTP server - an error will be thrown if the command string
  209.      * already contains any newline characters. Use _send() for
  210.      * commands that must contain newlines.
  211.      *
  212.      * @param   string  $command    The SMTP command to send to the server.
  213.      * @param   string  $args       A string of optional arguments to append
  214.      *                              to the command.
  215.      *
  216.      * @return  mixed   The result of the _send() call.
  217.      *
  218.      * @access  private
  219.      * @since   1.1.0
  220.      */
  221.     function _put($command, $args = '')
  222.     {
  223.         if (!empty($args)) {
  224.             $command .= ' ' . $args;
  225.         }
  226.  
  227.         if (strcspn($command, "\r\n") !== strlen($command)) {
  228.             return PEAR::raiseError('Commands cannot contain newlines');
  229.         }
  230.  
  231.         return $this->_send($command . "\r\n");
  232.     }
  233.  
  234.     /**
  235.      * Read a reply from the SMTP server.  The reply consists of a response
  236.      * code and a response message.
  237.      *
  238.      * @param   mixed   $valid      The set of valid response codes.  These
  239.      *                              may be specified as an array of integer
  240.      *                              values or as a single integer value.
  241.      * @param   bool    $later      Do not parse the response now, but wait
  242.      *                              until the last command in the pipelined
  243.      *                              command group
  244.      *
  245.      * @return  mixed   True if the server returned a valid response code or
  246.      *                  a PEAR_Error object is an error condition is reached.
  247.      *
  248.      * @access  private
  249.      * @since   1.1.0
  250.      *
  251.      * @see     getResponse
  252.      */
  253.     function _parseResponse($valid, $later = false)
  254.     {
  255.         $this->_code = -1;
  256.         $this->_arguments = array();
  257.  
  258.         if ($later) {
  259.             $this->_pipelined_commands++;
  260.             return true;
  261.         }
  262.  
  263.         for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
  264.             while ($line = $this->_socket->readLine()) {
  265.                 if ($this->_debug) {
  266.                     echo "DEBUG: Recv: $line\n";
  267.                 }
  268.  
  269.                 /* If we receive an empty line, the connection has been closed. */
  270.                 if (empty($line)) {
  271.                     $this->disconnect();
  272.                     return PEAR::raiseError('Connection was unexpectedly closed');
  273.                 }
  274.  
  275.                 /* Read the code and store the rest in the arguments array. */
  276.                 $code = substr($line, 0, 3);
  277.                 $this->_arguments[] = trim(substr($line, 4));
  278.  
  279.                 /* Check the syntax of the response code. */
  280.                 if (is_numeric($code)) {
  281.                     $this->_code = (int)$code;
  282.                 } else {
  283.                     $this->_code = -1;
  284.                     break;
  285.                 }
  286.  
  287.                 /* If this is not a multiline response, we're done. */
  288.                 if (substr($line, 3, 1) != '-') {
  289.                     break;
  290.                 }
  291.             }
  292.         }
  293.  
  294.         $this->_pipelined_commands = 0;
  295.  
  296.         /* Compare the server's response code with the valid code/codes. */
  297.         if (is_int($valid) && ($this->_code === $valid)) {
  298.             return true;
  299.         } elseif (is_array($valid) && in_array($this->_code, $valid, true)) {
  300.             return true;
  301.         }
  302.  
  303.         return PEAR::raiseError('Invalid response code received from server',
  304.                                 $this->_code);
  305.     }
  306.  
  307.     /**
  308.      * Return a 2-tuple containing the last response from the SMTP server.
  309.      *
  310.      * @return  array   A two-element array: the first element contains the
  311.      *                  response code as an integer and the second element
  312.      *                  contains the response's arguments as a string.
  313.      *
  314.      * @access  public
  315.      * @since   1.1.0
  316.      */
  317.     function getResponse()
  318.     {
  319.         return array($this->_code, join("\n", $this->_arguments));
  320.     }
  321.  
  322.     /**
  323.      * Attempt to connect to the SMTP server.
  324.      *
  325.      * @param   int     $timeout    The timeout value (in seconds) for the
  326.      *                              socket connection.
  327.      * @param   bool    $persistent Should a persistent socket connection
  328.      *                              be used?
  329.      *
  330.      * @return mixed Returns a PEAR_Error with an error message on any
  331.      *               kind of failure, or true on success.
  332.      * @access public
  333.      * @since  1.0
  334.      */
  335.     function connect($timeout = null, $persistent = false)
  336.     {
  337.         $result = $this->_socket->connect($this->host, $this->port,
  338.                                           $persistent, $timeout);
  339.         if (PEAR::isError($result)) {
  340.             return PEAR::raiseError('Failed to connect socket: ' .
  341.                                     $result->getMessage());
  342.         }
  343.  
  344.         if (PEAR::isError($error = $this->_parseResponse(220))) {
  345.             return $error;
  346.         }
  347.         if (PEAR::isError($error = $this->_negotiate())) {
  348.             return $error;
  349.         }
  350.  
  351.         return true;
  352.     }
  353.  
  354.     /**
  355.      * Attempt to disconnect from the SMTP server.
  356.      *
  357.      * @return mixed Returns a PEAR_Error with an error message on any
  358.      *               kind of failure, or true on success.
  359.      * @access public
  360.      * @since  1.0
  361.      */
  362.     function disconnect()
  363.     {
  364.         if (PEAR::isError($error = $this->_put('QUIT'))) {
  365.             return $error;
  366.         }
  367.         if (PEAR::isError($error = $this->_parseResponse(221))) {
  368.             return $error;
  369.         }
  370.         if (PEAR::isError($error = $this->_socket->disconnect())) {
  371.             return PEAR::raiseError('Failed to disconnect socket: ' .
  372.                                     $error->getMessage());
  373.         }
  374.  
  375.         return true;
  376.     }
  377.  
  378.     /**
  379.      * Attempt to send the EHLO command and obtain a list of ESMTP
  380.      * extensions available, and failing that just send HELO.
  381.      *
  382.      * @return mixed Returns a PEAR_Error with an error message on any
  383.      *               kind of failure, or true on success.
  384.      *
  385.      * @access private
  386.      * @since  1.1.0
  387.      */
  388.     function _negotiate()
  389.     {
  390.         if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
  391.             return $error;
  392.         }
  393.  
  394.         if (PEAR::isError($this->_parseResponse(250))) {
  395.             /* If we receive a 503 response, we're already authenticated. */
  396.             if ($this->_code === 503) {
  397.                 return true;
  398.             }
  399.  
  400.             /* If the EHLO failed, try the simpler HELO command. */
  401.             if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
  402.                 return $error;
  403.             }
  404.             if (PEAR::isError($this->_parseResponse(250))) {
  405.                 return PEAR::raiseError('HELO was not accepted: ', $this->_code);
  406.             }
  407.  
  408.             return true;
  409.         }
  410.  
  411.         foreach ($this->_arguments as $argument) {
  412.             $verb = strtok($argument, ' ');
  413.             $arguments = substr($argument, strlen($verb) + 1,
  414.                                 strlen($argument) - strlen($verb) - 1);
  415.             $this->_esmtp[$verb] = $arguments;
  416.         }
  417.  
  418.         if (!isset($this->_esmtp['PIPELINING'])) {
  419.             $this->pipelining = false;
  420.         }
  421.  
  422.         return true;
  423.     }
  424.  
  425.     /**
  426.      * Returns the name of the best authentication method that the server
  427.      * has advertised.
  428.      *
  429.      * @return mixed    Returns a string containing the name of the best
  430.      *                  supported authentication method or a PEAR_Error object
  431.      *                  if a failure condition is encountered.
  432.      * @access private
  433.      * @since  1.1.0
  434.      */
  435.     function _getBestAuthMethod()
  436.     {
  437.         $available_methods = explode(' ', $this->_esmtp['AUTH']);
  438.  
  439.         foreach ($this->auth_methods as $method) {
  440.             if (in_array($method, $available_methods)) {
  441.                 return $method;
  442.             }
  443.         }
  444.  
  445.         return PEAR::raiseError('No supported authentication methods');
  446.     }
  447.  
  448.     /**
  449.      * Attempt to do SMTP authentication.
  450.      *
  451.      * @param string The userid to authenticate as.
  452.      * @param string The password to authenticate with.
  453.      * @param string The requested authentication method.  If none is
  454.      *               specified, the best supported method will be used.
  455.      *
  456.      * @return mixed Returns a PEAR_Error with an error message on any
  457.      *               kind of failure, or true on success.
  458.      * @access public
  459.      * @since  1.0
  460.      */
  461.     function auth($uid, $pwd , $method = '')
  462.     {
  463.         if (empty($this->_esmtp['AUTH'])) {
  464.             if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
  465.                 if (!isset($this->_esmtp['STARTTLS'])) {
  466.                     return PEAR::raiseError('SMTP server does not support authentication');
  467.                 }
  468.                 if (PEAR::isError($result = $this->_put('STARTTLS'))) {
  469.                     return $result;
  470.                 }
  471.                 if (PEAR::isError($result = $this->_parseResponse(220))) {
  472.                     return $result;
  473.                 }
  474.                 if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
  475.                     return $result;
  476.                 } elseif ($result !== true) {
  477.                     return PEAR::raiseError('STARTTLS failed');
  478.                 }
  479.  
  480.                 /* Send EHLO again to recieve the AUTH string from the
  481.                  * SMTP server. */
  482.                 $this->_negotiate();
  483.                 if (empty($this->_esmtp['AUTH'])) {
  484.                     return PEAR::raiseError('SMTP server does not support authentication');
  485.                 }
  486.             } else {
  487.                 return PEAR::raiseError('SMTP server does not support authentication');
  488.             }
  489.         }
  490.  
  491.         /* If no method has been specified, get the name of the best
  492.          * supported method advertised by the SMTP server. */
  493.         if (empty($method)) {
  494.             if (PEAR::isError($method = $this->_getBestAuthMethod())) {
  495.                 /* Return the PEAR_Error object from _getBestAuthMethod(). */
  496.                 return $method;
  497.             }
  498.         } else {
  499.             $method = strtoupper($method);
  500.             if (!in_array($method, $this->auth_methods)) {
  501.                 return PEAR::raiseError("$method is not a supported authentication method");
  502.             }
  503.         }
  504.  
  505.         switch ($method) {
  506.         case 'DIGEST-MD5':
  507.             $result = $this->_authDigest_MD5($uid, $pwd);
  508.             break;
  509.  
  510.         case 'CRAM-MD5':
  511.             $result = $this->_authCRAM_MD5($uid, $pwd);
  512.             break;
  513.  
  514.         case 'LOGIN':
  515.             $result = $this->_authLogin($uid, $pwd);
  516.             break;
  517.  
  518.         case 'PLAIN':
  519.             $result = $this->_authPlain($uid, $pwd);
  520.             break;
  521.  
  522.         default:
  523.             $result = PEAR::raiseError("$method is not a supported authentication method");
  524.             break;
  525.         }
  526.  
  527.         /* If an error was encountered, return the PEAR_Error object. */
  528.         if (PEAR::isError($result)) {
  529.             return $result;
  530.         }
  531.  
  532.         return true;
  533.     }
  534.  
  535.     /**
  536.      * Authenticates the user using the DIGEST-MD5 method.
  537.      *
  538.      * @param string The userid to authenticate as.
  539.      * @param string The password to authenticate with.
  540.      *
  541.      * @return mixed Returns a PEAR_Error with an error message on any
  542.      *               kind of failure, or true on success.
  543.      * @access private
  544.      * @since  1.1.0
  545.      */
  546.     function _authDigest_MD5($uid, $pwd)
  547.     {
  548.         if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
  549.             return $error;
  550.         }
  551.         /* 334: Continue authentication request */
  552.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  553.             /* 503: Error: already authenticated */
  554.             if ($this->_code === 503) {
  555.                 return true;
  556.             }
  557.             return $error;
  558.         }
  559.  
  560.         $challenge = base64_decode($this->_arguments[0]);
  561.         $digest = &Auth_SASL::factory('digestmd5');
  562.         $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
  563.                                                        $this->host, "smtp"));
  564.  
  565.         if (PEAR::isError($error = $this->_put($auth_str))) {
  566.             return $error;
  567.         }
  568.         /* 334: Continue authentication request */
  569.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  570.             return $error;
  571.         }
  572.  
  573.         /* We don't use the protocol's third step because SMTP doesn't
  574.          * allow subsequent authentication, so we just silently ignore
  575.          * it. */
  576.         if (PEAR::isError($error = $this->_put(''))) {
  577.             return $error;
  578.         }
  579.         /* 235: Authentication successful */
  580.         if (PEAR::isError($error = $this->_parseResponse(235))) {
  581.             return $error;
  582.         }
  583.     }
  584.  
  585.     /**
  586.      * Authenticates the user using the CRAM-MD5 method.
  587.      *
  588.      * @param string The userid to authenticate as.
  589.      * @param string The password to authenticate with.
  590.      *
  591.      * @return mixed Returns a PEAR_Error with an error message on any
  592.      *               kind of failure, or true on success.
  593.      * @access private
  594.      * @since  1.1.0
  595.      */
  596.     function _authCRAM_MD5($uid, $pwd)
  597.     {
  598.         if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
  599.             return $error;
  600.         }
  601.         /* 334: Continue authentication request */
  602.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  603.             /* 503: Error: already authenticated */
  604.             if ($this->_code === 503) {
  605.                 return true;
  606.             }
  607.             return $error;
  608.         }
  609.  
  610.         $challenge = base64_decode($this->_arguments[0]);
  611.         $cram = &Auth_SASL::factory('crammd5');
  612.         $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
  613.  
  614.         if (PEAR::isError($error = $this->_put($auth_str))) {
  615.             return $error;
  616.         }
  617.  
  618.         /* 235: Authentication successful */
  619.         if (PEAR::isError($error = $this->_parseResponse(235))) {
  620.             return $error;
  621.         }
  622.     }
  623.  
  624.     /**
  625.      * Authenticates the user using the LOGIN method.
  626.      *
  627.      * @param string The userid to authenticate as.
  628.      * @param string The password to authenticate with.
  629.      *
  630.      * @return mixed Returns a PEAR_Error with an error message on any
  631.      *               kind of failure, or true on success.
  632.      * @access private
  633.      * @since  1.1.0
  634.      */
  635.     function _authLogin($uid, $pwd)
  636.     {
  637.         if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
  638.             return $error;
  639.         }
  640.         /* 334: Continue authentication request */
  641.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  642.             /* 503: Error: already authenticated */
  643.             if ($this->_code === 503) {
  644.                 return true;
  645.             }
  646.             return $error;
  647.         }
  648.  
  649.         if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
  650.             return $error;
  651.         }
  652.         /* 334: Continue authentication request */
  653.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  654.             return $error;
  655.         }
  656.  
  657.         if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
  658.             return $error;
  659.         }
  660.  
  661.         /* 235: Authentication successful */
  662.         if (PEAR::isError($error = $this->_parseResponse(235))) {
  663.             return $error;
  664.         }
  665.  
  666.         return true;
  667.     }
  668.  
  669.     /**
  670.      * Authenticates the user using the PLAIN method.
  671.      *
  672.      * @param string The userid to authenticate as.
  673.      * @param string The password to authenticate with.
  674.      *
  675.      * @return mixed Returns a PEAR_Error with an error message on any
  676.      *               kind of failure, or true on success.
  677.      * @access private
  678.      * @since  1.1.0
  679.      */
  680.     function _authPlain($uid, $pwd)
  681.     {
  682.         if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
  683.             return $error;
  684.         }
  685.         /* 334: Continue authentication request */
  686.         if (PEAR::isError($error = $this->_parseResponse(334))) {
  687.             /* 503: Error: already authenticated */
  688.             if ($this->_code === 503) {
  689.                 return true;
  690.             }
  691.             return $error;
  692.         }
  693.  
  694.         $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
  695.  
  696.         if (PEAR::isError($error = $this->_put($auth_str))) {
  697.             return $error;
  698.         }
  699.  
  700.         /* 235: Authentication successful */
  701.         if (PEAR::isError($error = $this->_parseResponse(235))) {
  702.             return $error;
  703.         }
  704.  
  705.         return true;
  706.     }
  707.  
  708.     /**
  709.      * Send the HELO command.
  710.      *
  711.      * @param string The domain name to say we are.
  712.      *
  713.      * @return mixed Returns a PEAR_Error with an error message on any
  714.      *               kind of failure, or true on success.
  715.      * @access public
  716.      * @since  1.0
  717.      */
  718.     function helo($domain)
  719.     {
  720.         if (PEAR::isError($error = $this->_put('HELO', $domain))) {
  721.             return $error;
  722.         }
  723.         if (PEAR::isError($error = $this->_parseResponse(250))) {
  724.             return $error;
  725.         }
  726.  
  727.         return true;
  728.     }
  729.  
  730.     /**
  731.      * Return the list of SMTP service extensions advertised by the server.
  732.      *
  733.      * @return array The list of SMTP service extensions.
  734.      * @access public
  735.      * @since 1.3
  736.      */
  737.     function getServiceExtensions()
  738.     {
  739.         return $this->_esmtp;
  740.     }
  741.  
  742.     /**
  743.      * Send the MAIL FROM: command.
  744.      *
  745.      * @param string $sender    The sender (reverse path) to set.
  746.      * @param string $params    String containing additional MAIL parameters,
  747.      *                          such as the NOTIFY flags defined by RFC 1891
  748.      *                          or the VERP protocol.
  749.      *
  750.      *                          If $params is an array, only the 'verp' option
  751.      *                          is supported.  If 'verp' is true, the XVERP
  752.      *                          parameter is appended to the MAIL command.  If
  753.      *                          the 'verp' value is a string, the full
  754.      *                          XVERP=value parameter is appended.
  755.      *
  756.      * @return mixed Returns a PEAR_Error with an error message on any
  757.      *               kind of failure, or true on success.
  758.      * @access public
  759.      * @since  1.0
  760.      */
  761.     function mailFrom($sender, $params = null)
  762.     {
  763.         $args = "FROM:<$sender>";
  764.  
  765.         /* Support the deprecated array form of $params. */
  766.         if (is_array($params) && isset($params['verp'])) {
  767.             /* XVERP */
  768.             if ($params['verp'] === true) {
  769.                 $args .= ' XVERP';
  770.  
  771.             /* XVERP=something */
  772.             } elseif (trim($params['verp'])) {
  773.                 $args .= ' XVERP=' . $params['verp'];
  774.             }
  775.         } elseif (is_string($params)) {
  776.             $args .= ' ' . $params;
  777.         }
  778.  
  779.         if (PEAR::isError($error = $this->_put('MAIL', $args))) {
  780.             return $error;
  781.         }
  782.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  783.             return $error;
  784.         }
  785.  
  786.         return true;
  787.     }
  788.  
  789.     /**
  790.      * Send the RCPT TO: command.
  791.      *
  792.      * @param string $recipient The recipient (forward path) to add.
  793.      * @param string $params    String containing additional RCPT parameters,
  794.      *                          such as the NOTIFY flags defined by RFC 1891.
  795.      *
  796.      * @return mixed Returns a PEAR_Error with an error message on any
  797.      *               kind of failure, or true on success.
  798.      *
  799.      * @access public
  800.      * @since  1.0
  801.      */
  802.     function rcptTo($recipient, $params = null)
  803.     {
  804.         $args = "TO:<$recipient>";
  805.         if (is_string($params)) {
  806.             $args .= ' ' . $params;
  807.         }
  808.  
  809.         if (PEAR::isError($error = $this->_put('RCPT', $args))) {
  810.             return $error;
  811.         }
  812.         if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
  813.             return $error;
  814.         }
  815.  
  816.         return true;
  817.     }
  818.  
  819.     /**
  820.      * Quote the data so that it meets SMTP standards.
  821.      *
  822.      * This is provided as a separate public function to facilitate
  823.      * easier overloading for the cases where it is desirable to
  824.      * customize the quoting behavior.
  825.      *
  826.      * @param string $data  The message text to quote. The string must be passed
  827.      *                      by reference, and the text will be modified in place.
  828.      *
  829.      * @access public
  830.      * @since  1.2
  831.      */
  832.     function quotedata(&$data)
  833.     {
  834.         /* Change Unix (\n) and Mac (\r) linefeeds into
  835.          * Internet-standard CRLF (\r\n) linefeeds. */
  836.         $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
  837.  
  838.         /* Because a single leading period (.) signifies an end to the
  839.          * data, legitimate leading periods need to be "doubled"
  840.          * (e.g. '..'). */
  841.         $data = str_replace("\n.", "\n..", $data);
  842.     }
  843.  
  844.     /**
  845.      * Send the DATA command.
  846.      *
  847.      * @param string $data  The message body to send.
  848.      *
  849.      * @return mixed Returns a PEAR_Error with an error message on any
  850.      *               kind of failure, or true on success.
  851.      * @access public
  852.      * @since  1.0
  853.      */
  854.     function data($data)
  855.     {
  856.         /* RFC 1870, section 3, subsection 3 states "a value of zero
  857.          * indicates that no fixed maximum message size is in force".
  858.          * Furthermore, it says that if "the parameter is omitted no
  859.          * information is conveyed about the server's fixed maximum
  860.          * message size". */
  861.         if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
  862.             if (strlen($data) >= $this->_esmtp['SIZE']) {
  863.                 $this->disconnect();
  864.                 return PEAR::raiseError('Message size excedes the server limit');
  865.             }
  866.         }
  867.  
  868.         /* Quote the data based on the SMTP standards. */
  869.         $this->quotedata($data);
  870.  
  871.         if (PEAR::isError($error = $this->_put('DATA'))) {
  872.             return $error;
  873.         }
  874.         if (PEAR::isError($error = $this->_parseResponse(354))) {
  875.             return $error;
  876.         }
  877.  
  878.         if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
  879.             return $result;
  880.         }
  881.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  882.             return $error;
  883.         }
  884.  
  885.         return true;
  886.     }
  887.  
  888.     /**
  889.      * Send the SEND FROM: command.
  890.      *
  891.      * @param string The reverse path to send.
  892.      *
  893.      * @return mixed Returns a PEAR_Error with an error message on any
  894.      *               kind of failure, or true on success.
  895.      * @access public
  896.      * @since  1.2.6
  897.      */
  898.     function sendFrom($path)
  899.     {
  900.         if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
  901.             return $error;
  902.         }
  903.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  904.             return $error;
  905.         }
  906.  
  907.         return true;
  908.     }
  909.  
  910.     /**
  911.      * Backwards-compatibility wrapper for sendFrom().
  912.      *
  913.      * @param string The reverse path to send.
  914.      *
  915.      * @return mixed Returns a PEAR_Error with an error message on any
  916.      *               kind of failure, or true on success.
  917.      *
  918.      * @access      public
  919.      * @since       1.0
  920.      * @deprecated  1.2.6
  921.      */
  922.     function send_from($path)
  923.     {
  924.         return sendFrom($path);
  925.     }
  926.  
  927.     /**
  928.      * Send the SOML FROM: command.
  929.      *
  930.      * @param string The reverse path to send.
  931.      *
  932.      * @return mixed Returns a PEAR_Error with an error message on any
  933.      *               kind of failure, or true on success.
  934.      * @access public
  935.      * @since  1.2.6
  936.      */
  937.     function somlFrom($path)
  938.     {
  939.         if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
  940.             return $error;
  941.         }
  942.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  943.             return $error;
  944.         }
  945.  
  946.         return true;
  947.     }
  948.  
  949.     /**
  950.      * Backwards-compatibility wrapper for somlFrom().
  951.      *
  952.      * @param string The reverse path to send.
  953.      *
  954.      * @return mixed Returns a PEAR_Error with an error message on any
  955.      *               kind of failure, or true on success.
  956.      *
  957.      * @access      public
  958.      * @since       1.0
  959.      * @deprecated  1.2.6
  960.      */
  961.     function soml_from($path)
  962.     {
  963.         return somlFrom($path);
  964.     }
  965.  
  966.     /**
  967.      * Send the SAML FROM: command.
  968.      *
  969.      * @param string The reverse path to send.
  970.      *
  971.      * @return mixed Returns a PEAR_Error with an error message on any
  972.      *               kind of failure, or true on success.
  973.      * @access public
  974.      * @since  1.2.6
  975.      */
  976.     function samlFrom($path)
  977.     {
  978.         if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
  979.             return $error;
  980.         }
  981.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  982.             return $error;
  983.         }
  984.  
  985.         return true;
  986.     }
  987.  
  988.     /**
  989.      * Backwards-compatibility wrapper for samlFrom().
  990.      *
  991.      * @param string The reverse path to send.
  992.      *
  993.      * @return mixed Returns a PEAR_Error with an error message on any
  994.      *               kind of failure, or true on success.
  995.      *
  996.      * @access      public
  997.      * @since       1.0
  998.      * @deprecated  1.2.6
  999.      */
  1000.     function saml_from($path)
  1001.     {
  1002.         return samlFrom($path);
  1003.     }
  1004.  
  1005.     /**
  1006.      * Send the RSET command.
  1007.      *
  1008.      * @return mixed Returns a PEAR_Error with an error message on any
  1009.      *               kind of failure, or true on success.
  1010.      * @access public
  1011.      * @since  1.0
  1012.      */
  1013.     function rset()
  1014.     {
  1015.         if (PEAR::isError($error = $this->_put('RSET'))) {
  1016.             return $error;
  1017.         }
  1018.         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  1019.             return $error;
  1020.         }
  1021.  
  1022.         return true;
  1023.     }
  1024.  
  1025.     /**
  1026.      * Send the VRFY command.
  1027.      *
  1028.      * @param string The string to verify
  1029.      *
  1030.      * @return mixed Returns a PEAR_Error with an error message on any
  1031.      *               kind of failure, or true on success.
  1032.      * @access public
  1033.      * @since  1.0
  1034.      */
  1035.     function vrfy($string)
  1036.     {
  1037.         /* Note: 251 is also a valid response code */
  1038.         if (PEAR::isError($error = $this->_put('VRFY', $string))) {
  1039.             return $error;
  1040.         }
  1041.         if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
  1042.             return $error;
  1043.         }
  1044.  
  1045.         return true;
  1046.     }
  1047.  
  1048.     /**
  1049.      * Send the NOOP command.
  1050.      *
  1051.      * @return mixed Returns a PEAR_Error with an error message on any
  1052.      *               kind of failure, or true on success.
  1053.      * @access public
  1054.      * @since  1.0
  1055.      */
  1056.     function noop()
  1057.     {
  1058.         if (PEAR::isError($error = $this->_put('NOOP'))) {
  1059.             return $error;
  1060.         }
  1061.         if (PEAR::isError($error = $this->_parseResponse(250))) {
  1062.             return $error;
  1063.         }
  1064.  
  1065.         return true;
  1066.     }
  1067.  
  1068.     /**
  1069.      * Backwards-compatibility method.  identifySender()'s functionality is
  1070.      * now handled internally.
  1071.      *
  1072.      * @return  boolean     This method always return true.
  1073.      *
  1074.      * @access  public
  1075.      * @since   1.0
  1076.      */
  1077.     function identifySender()
  1078.     {
  1079.         return true;
  1080.     }
  1081.  
  1082. }
  1083.