Net_SMTP
[ class tree: Net_SMTP ] [ index: Net_SMTP ] [ all elements ]

Source for file SMTP.php

Documentation is available at SMTP.php

  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. require_once 'PEAR.php';
  22. require_once 'Net/Socket.php';
  23.  
  24. /**
  25.  * Provides an implementation of the SMTP protocol using PEAR's
  26.  * Net_Socket:: class.
  27.  *
  28.  * @package Net_SMTP
  29.  * @author  Chuck Hagenbuch <chuck@horde.org>
  30.  * @author  Jon Parise <jon@php.net>
  31.  * @author  Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
  32.  *
  33.  * @example basic.php   A basic implementation of the Net_SMTP package.
  34.  */
  35. class Net_SMTP
  36. {
  37.     /**
  38.      * The server to connect to.
  39.      * @var string 
  40.      * @access public
  41.      */
  42.     var $host = 'localhost';
  43.  
  44.     /**
  45.      * The port to connect to.
  46.      * @var int 
  47.      * @access public
  48.      */
  49.     var $port = 25;
  50.  
  51.     /**
  52.      * The value to give when sending EHLO or HELO.
  53.      * @var string 
  54.      * @access public
  55.      */
  56.     var $localhost = 'localhost';
  57.  
  58.     /**
  59.      * List of supported authentication methods, in preferential order.
  60.      * @var array 
  61.      * @access public
  62.      */
  63.     var $auth_methods = array();
  64.  
  65.     /**
  66.      * Use SMTP command pipelining (specified in RFC 2920) if the SMTP
  67.      * server supports it.
  68.      *
  69.      * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(),
  70.      * somlFrom() and samlFrom() do not wait for a response from the
  71.      * SMTP server but return immediately.
  72.      *
  73.      * @var bool 
  74.      * @access public
  75.      */
  76.     var $pipelining = false;
  77.  
  78.     /**
  79.      * Number of pipelined commands.
  80.      * @var int 
  81.      * @access private
  82.      */
  83.     var $_pipelined_commands = 0;
  84.  
  85.     /**
  86.      * Should debugging output be enabled?
  87.      * @var boolean 
  88.      * @access private
  89.      */
  90.     var $_debug = false;
  91.  
  92.     /**
  93.      * Debug output handler.
  94.      * @var callback 
  95.      * @access private
  96.      */
  97.     var $_debug_handler = null;
  98.  
  99.     /**
  100.      * The socket resource being used to connect to the SMTP server.
  101.      * @var resource 
  102.      * @access private
  103.      */
  104.     var $_socket = null;
  105.  
  106.     /**
  107.      * Array of socket options that will be passed to Net_Socket::connect().
  108.      * @see stream_context_create()
  109.      * @var array 
  110.      * @access private
  111.      */
  112.     var $_socket_options = null;
  113.  
  114.     /**
  115.      * The socket I/O timeout value in seconds.
  116.      * @var int 
  117.      * @access private
  118.      */
  119.     var $_timeout = 0;
  120.  
  121.     /**
  122.      * The most recent server response code.
  123.      * @var int 
  124.      * @access private
  125.      */
  126.     var $_code = -1;
  127.  
  128.     /**
  129.      * The most recent server response arguments.
  130.      * @var array 
  131.      * @access private
  132.      */
  133.     var $_arguments = array();
  134.  
  135.     /**
  136.      * Stores the SMTP server's greeting string.
  137.      * @var string 
  138.      * @access private
  139.      */
  140.     var $_greeting = null;
  141.  
  142.     /**
  143.      * Stores detected features of the SMTP server.
  144.      * @var array 
  145.      * @access private
  146.      */
  147.     var $_esmtp = array();
  148.  
  149.     /**
  150.      * Instantiates a new Net_SMTP object, overriding any defaults
  151.      * with parameters that are passed in.
  152.      *
  153.      * If you have SSL support in PHP, you can connect to a server
  154.      * over SSL using an 'ssl://' prefix:
  155.      *
  156.      *   // 465 is a common smtps port.
  157.      *   $smtp = new Net_SMTP('ssl://mail.host.com', 465);
  158.      *   $smtp->connect();
  159.      *
  160.      * @param string  $host       The server to connect to.
  161.      * @param integer $port       The port to connect to.
  162.      * @param string  $localhost  The value to give when sending EHLO or HELO.
  163.      * @param boolean $pipeling   Use SMTP command pipelining
  164.      * @param integer $timeout    Socket I/O timeout in seconds.
  165.      * @param array   $socket_options Socket stream_context_create() options.
  166.      *
  167.      * @access  public
  168.      * @since   1.0
  169.      */
  170.     function Net_SMTP($host = null$port = null$localhost = null,
  171.         $pipelining = false$timeout = 0$socket_options = null)
  172.     {
  173.         if (isset($host)) {
  174.             $this->host = $host;
  175.         }
  176.         if (isset($port)) {
  177.             $this->port = $port;
  178.         }
  179.         if (isset($localhost)) {
  180.             $this->localhost = $localhost;
  181.         }
  182.         $this->pipelining = $pipelining;
  183.  
  184.         $this->_socket = new Net_Socket();
  185.         $this->_socket_options $socket_options;
  186.         $this->_timeout $timeout;
  187.  
  188.         /* Include the Auth_SASL package.  If the package is available, we 
  189.          * enable the authentication methods that depend upon it. */
  190.         if (@include_once 'Auth/SASL.php'{
  191.             $this->setAuthMethod('CRAM-MD5'array($this'_authCram_MD5'));
  192.             $this->setAuthMethod('DIGEST-MD5'array($this'_authDigest_MD5'));
  193.         }
  194.  
  195.         /* These standard authentication methods are always available. */
  196.         $this->setAuthMethod('LOGIN'array($this'_authLogin')false);
  197.         $this->setAuthMethod('PLAIN'array($this'_authPlain')false);
  198.     }
  199.  
  200.     /**
  201.      * Set the socket I/O timeout value in seconds plus microseconds.
  202.      *
  203.      * @param   integer $seconds        Timeout value in seconds.
  204.      * @param   integer $microseconds   Additional value in microseconds.
  205.      *
  206.      * @access  public
  207.      * @since   1.5.0
  208.      */
  209.     function setTimeout($seconds$microseconds = 0{
  210.         return $this->_socket->setTimeout($seconds$microseconds);
  211.     }
  212.  
  213.     /**
  214.      * Set the value of the debugging flag.
  215.      *
  216.      * @param   boolean $debug      New value for the debugging flag.
  217.      *
  218.      * @access  public
  219.      * @since   1.1.0
  220.      */
  221.     function setDebug($debug$handler = null)
  222.     {
  223.         $this->_debug $debug;
  224.         $this->_debug_handler $handler;
  225.     }
  226.  
  227.     /**
  228.      * Write the given debug text to the current debug output handler.
  229.      *
  230.      * @param   string  $message    Debug mesage text.
  231.      *
  232.      * @access  private
  233.      * @since   1.3.3
  234.      */
  235.     function _debug($message)
  236.     {
  237.         if ($this->_debug{
  238.             if ($this->_debug_handler{
  239.                 call_user_func_array($this->_debug_handler,
  240.                                      array(&$this$message));
  241.             else {
  242.                 echo "DEBUG: $message\n";
  243.             }
  244.         }
  245.     }
  246.  
  247.     /**
  248.      * Send the given string of data to the server.
  249.      *
  250.      * @param   string  $data       The string of data to send.
  251.      *
  252.      * @return  mixed   The number of bytes that were actually written,
  253.      *                   or a PEAR_Error object on failure.
  254.      *
  255.      * @access  private
  256.      * @since   1.1.0
  257.      */
  258.     function _send($data)
  259.     {
  260.         $this->_debug("Send: $data");
  261.  
  262.         $result $this->_socket->write($data);
  263.         if (!$result || PEAR::isError($result)) {
  264.             $msg ($result$result->getMessage("unknown error";
  265.             return PEAR::raiseError("Failed to write to socket: $msg",
  266.                                     nullPEAR_ERROR_RETURN);
  267.         }
  268.  
  269.         return $result;
  270.     }
  271.  
  272.     /**
  273.      * Send a command to the server with an optional string of
  274.      * arguments.  A carriage return / linefeed (CRLF) sequence will
  275.      * be appended to each command string before it is sent to the
  276.      * SMTP server - an error will be thrown if the command string
  277.      * already contains any newline characters. Use _send() for
  278.      * commands that must contain newlines.
  279.      *
  280.      * @param   string  $command    The SMTP command to send to the server.
  281.      * @param   string  $args       A string of optional arguments to append
  282.      *                               to the command.
  283.      *
  284.      * @return  mixed   The result of the _send() call.
  285.      *
  286.      * @access  private
  287.      * @since   1.1.0
  288.      */
  289.     function _put($command$args '')
  290.     {
  291.         if (!empty($args)) {
  292.             $command .= ' ' $args;
  293.         }
  294.  
  295.         if (strcspn($command"\r\n"!== strlen($command)) {
  296.             return PEAR::raiseError('Commands cannot contain newlines',
  297.                                     nullPEAR_ERROR_RETURN);
  298.         }
  299.  
  300.         return $this->_send($command "\r\n");
  301.     }
  302.  
  303.     /**
  304.      * Read a reply from the SMTP server.  The reply consists of a response
  305.      * code and a response message.
  306.      *
  307.      * @param   mixed   $valid      The set of valid response codes.  These
  308.      *                               may be specified as an array of integer
  309.      *                               values or as a single integer value.
  310.      * @param   bool    $later      Do not parse the response now, but wait
  311.      *                               until the last command in the pipelined
  312.      *                               command group
  313.      *
  314.      * @return  mixed   True if the server returned a valid response code or
  315.      *                   a PEAR_Error object is an error condition is reached.
  316.      *
  317.      * @access  private
  318.      * @since   1.1.0
  319.      *
  320.      * @see     getResponse
  321.      */
  322.     function _parseResponse($valid$later = false)
  323.     {
  324.         $this->_code = -1;
  325.         $this->_arguments = array();
  326.  
  327.         if ($later{
  328.             $this->_pipelined_commands++;
  329.             return true;
  330.         }
  331.  
  332.         for ($i = 0; $i <= $this->_pipelined_commands$i++{
  333.             while ($line $this->_socket->readLine()) {
  334.                 $this->_debug("Recv: $line");
  335.  
  336.                 /* If we receive an empty line, the connection was closed. */
  337.                 if (empty($line)) {
  338.                     $this->disconnect();
  339.                     return PEAR::raiseError('Connection was closed',
  340.                                             nullPEAR_ERROR_RETURN);
  341.                 }
  342.  
  343.                 /* Read the code and store the rest in the arguments array. */
  344.                 $code substr($line03);
  345.                 $this->_arguments[trim(substr($line4));
  346.  
  347.                 /* Check the syntax of the response code. */
  348.                 if (is_numeric($code)) {
  349.                     $this->_code = (int)$code;
  350.                 else {
  351.                     $this->_code = -1;
  352.                     break;
  353.                 }
  354.  
  355.                 /* If this is not a multiline response, we're done. */
  356.                 if (substr($line31!= '-'{
  357.                     break;
  358.                 }
  359.             }
  360.         }
  361.  
  362.         $this->_pipelined_commands = 0;
  363.  
  364.         /* Compare the server's response code with the valid code/codes. */
  365.         if (is_int($valid&& ($this->_code === $valid)) {
  366.             return true;
  367.         elseif (is_array($valid&& in_array($this->_code$validtrue)) {
  368.             return true;
  369.         }
  370.  
  371.         return PEAR::raiseError('Invalid response code received from server',
  372.                                 $this->_codePEAR_ERROR_RETURN);
  373.     }
  374.  
  375.     /**
  376.      * Issue an SMTP command and verify its response.
  377.      *
  378.      * @param   string  $command    The SMTP command string or data.
  379.      * @param   mixed   $valid      The set of valid response codes.  These
  380.      *                               may be specified as an array of integer
  381.      *                               values or as a single integer value.
  382.      *
  383.      * @return  mixed   True on success or a PEAR_Error object on failure.
  384.      *
  385.      * @access  public
  386.      * @since   1.6.0
  387.      */
  388.     function command($command$valid)
  389.     {
  390.         if (PEAR::isError($error $this->_put($command))) {
  391.             return $error;
  392.         }
  393.         if (PEAR::isError($error $this->_parseResponse($valid))) {
  394.             return $error;
  395.         }
  396.  
  397.         return true;
  398.     }
  399.  
  400.     /**
  401.      * Return a 2-tuple containing the last response from the SMTP server.
  402.      *
  403.      * @return  array   A two-element array: the first element contains the
  404.      *                   response code as an integer and the second element
  405.      *                   contains the response's arguments as a string.
  406.      *
  407.      * @access  public
  408.      * @since   1.1.0
  409.      */
  410.     function getResponse()
  411.     {
  412.         return array($this->_codejoin("\n"$this->_arguments));
  413.     }
  414.  
  415.     /**
  416.      * Return the SMTP server's greeting string.
  417.      *
  418.      * @return  string  A string containing the greeting string, or null if a
  419.      *                   greeting has not been received.
  420.      *
  421.      * @access  public
  422.      * @since   1.3.3
  423.      */
  424.     function getGreeting()
  425.     {
  426.         return $this->_greeting;
  427.     }
  428.  
  429.     /**
  430.      * Attempt to connect to the SMTP server.
  431.      *
  432.      * @param   int     $timeout    The timeout value (in seconds) for the
  433.      *                               socket connection attempt.
  434.      * @param   bool    $persistent Should a persistent socket connection
  435.      *                               be used?
  436.      *
  437.      * @return mixed Returns a PEAR_Error with an error message on any
  438.      *                kind of failure, or true on success.
  439.      * @access public
  440.      * @since  1.0
  441.      */
  442.     function connect($timeout = null$persistent = false)
  443.     {
  444.         $this->_greeting = null;
  445.         $result $this->_socket->connect($this->host$this->port,
  446.                                           $persistent$timeout,
  447.                                           $this->_socket_options);
  448.         if (PEAR::isError($result)) {
  449.             return PEAR::raiseError('Failed to connect socket: ' .
  450.                                     $result->getMessage());
  451.         }
  452.  
  453.         /*
  454.          * Now that we're connected, reset the socket's timeout value for 
  455.          * future I/O operations.  This allows us to have different socket 
  456.          * timeout values for the initial connection (our $timeout parameter) 
  457.          * and all other socket operations.
  458.          */
  459.         if ($this->_timeout > 0{
  460.             if (PEAR::isError($error $this->setTimeout($this->_timeout))) {
  461.                 return $error;
  462.             }
  463.         }
  464.  
  465.         if (PEAR::isError($error $this->_parseResponse(220))) {
  466.             return $error;
  467.         }
  468.  
  469.         /* Extract and store a copy of the server's greeting string. */
  470.         list($this->_greeting$this->getResponse();
  471.  
  472.         if (PEAR::isError($error $this->_negotiate())) {
  473.             return $error;
  474.         }
  475.  
  476.         return true;
  477.     }
  478.  
  479.     /**
  480.      * Attempt to disconnect from the SMTP server.
  481.      *
  482.      * @return mixed Returns a PEAR_Error with an error message on any
  483.      *                kind of failure, or true on success.
  484.      * @access public
  485.      * @since  1.0
  486.      */
  487.     function disconnect()
  488.     {
  489.         if (PEAR::isError($error $this->_put('QUIT'))) {
  490.             return $error;
  491.         }
  492.         if (PEAR::isError($error $this->_parseResponse(221))) {
  493.             return $error;
  494.         }
  495.         if (PEAR::isError($error $this->_socket->disconnect())) {
  496.             return PEAR::raiseError('Failed to disconnect socket: ' .
  497.                                     $error->getMessage());
  498.         }
  499.  
  500.         return true;
  501.     }
  502.  
  503.     /**
  504.      * Attempt to send the EHLO command and obtain a list of ESMTP
  505.      * extensions available, and failing that just send HELO.
  506.      *
  507.      * @return mixed Returns a PEAR_Error with an error message on any
  508.      *                kind of failure, or true on success.
  509.      *
  510.      * @access private
  511.      * @since  1.1.0
  512.      */
  513.     function _negotiate()
  514.     {
  515.         if (PEAR::isError($error $this->_put('EHLO'$this->localhost))) {
  516.             return $error;
  517.         }
  518.  
  519.         if (PEAR::isError($this->_parseResponse(250))) {
  520.             /* If the EHLO failed, try the simpler HELO command. */
  521.             if (PEAR::isError($error $this->_put('HELO'$this->localhost))) {
  522.                 return $error;
  523.             }
  524.             if (PEAR::isError($this->_parseResponse(250))) {
  525.                 return PEAR::raiseError('HELO was not accepted: '$this->_code,
  526.                                         PEAR_ERROR_RETURN);
  527.             }
  528.  
  529.             return true;
  530.         }
  531.  
  532.         foreach ($this->_arguments as $argument{
  533.             $verb strtok($argument' ');
  534.             $arguments substr($argumentstrlen($verb+ 1,
  535.                                 strlen($argumentstrlen($verb- 1);
  536.             $this->_esmtp[$verb$arguments;
  537.         }
  538.  
  539.         if (!isset($this->_esmtp['PIPELINING'])) {
  540.             $this->pipelining = false;
  541.         }
  542.  
  543.         return true;
  544.     }
  545.  
  546.     /**
  547.      * Returns the name of the best authentication method that the server
  548.      * has advertised.
  549.      *
  550.      * @return mixed    Returns a string containing the name of the best
  551.      *                   supported authentication method or a PEAR_Error object
  552.      *                   if a failure condition is encountered.
  553.      * @access private
  554.      * @since  1.1.0
  555.      */
  556.     function _getBestAuthMethod()
  557.     {
  558.         $available_methods explode(' '$this->_esmtp['AUTH']);
  559.  
  560.         foreach ($this->auth_methods as $method => $callback{
  561.             if (in_array($method$available_methods)) {
  562.                 return $method;
  563.             }
  564.         }
  565.  
  566.         return PEAR::raiseError('No supported authentication methods',
  567.                                 nullPEAR_ERROR_RETURN);
  568.     }
  569.  
  570.     /**
  571.      * Attempt to do SMTP authentication.
  572.      *
  573.      * @param string The userid to authenticate as.
  574.      * @param string The password to authenticate with.
  575.      * @param string The requested authentication method.  If none is
  576.      *                specified, the best supported method will be used.
  577.      * @param bool   Flag indicating whether or not TLS should be attempted.
  578.      * @param string An optional authorization identifier.  If specified, this
  579.      *                identifier will be used as the authorization proxy.
  580.      *
  581.      * @return mixed Returns a PEAR_Error with an error message on any
  582.      *                kind of failure, or true on success.
  583.      * @access public
  584.      * @since  1.0
  585.      */
  586.     function auth($uid$pwd $method ''$tls = true$authz '')
  587.     {
  588.         /* We can only attempt a TLS connection if one has been requested,
  589.          * we're running PHP 5.1.0 or later, have access to the OpenSSL 
  590.          * extension, are connected to an SMTP server which supports the 
  591.          * STARTTLS extension, and aren't already connected over a secure 
  592.          * (SSL) socket connection. */
  593.         if ($tls && version_compare(PHP_VERSION'5.1.0''>='&&
  594.             extension_loaded('openssl'&& isset($this->_esmtp['STARTTLS']&&
  595.             strncasecmp($this->host'ssl://'6!== 0{
  596.             /* Start the TLS connection attempt. */
  597.             if (PEAR::isError($result $this->_put('STARTTLS'))) {
  598.                 return $result;
  599.             }
  600.             if (PEAR::isError($result $this->_parseResponse(220))) {
  601.                 return $result;
  602.             }
  603.             if (PEAR::isError($result $this->_socket->enableCrypto(trueSTREAM_CRYPTO_METHOD_TLS_CLIENT))) {
  604.                 return $result;
  605.             elseif ($result !== true{
  606.                 return PEAR::raiseError('STARTTLS failed');
  607.             }
  608.  
  609.             /* Send EHLO again to recieve the AUTH string from the
  610.              * SMTP server. */
  611.             $this->_negotiate();
  612.         }
  613.  
  614.         if (empty($this->_esmtp['AUTH'])) {
  615.             return PEAR::raiseError('SMTP server does not support authentication');
  616.         }
  617.  
  618.         /* If no method has been specified, get the name of the best
  619.          * supported method advertised by the SMTP server. */
  620.         if (empty($method)) {
  621.             if (PEAR::isError($method $this->_getBestAuthMethod())) {
  622.                 /* Return the PEAR_Error object from _getBestAuthMethod(). */
  623.                 return $method;
  624.             }
  625.         else {
  626.             $method strtoupper($method);
  627.             if (!array_key_exists($method$this->auth_methods)) {
  628.                 return PEAR::raiseError("$method is not a supported authentication method");
  629.             }
  630.         }
  631.  
  632.         if (!isset($this->auth_methods[$method])) {
  633.             return PEAR::raiseError("$method is not a supported authentication method");
  634.         }
  635.  
  636.         if (!is_callable($this->auth_methods[$method]false)) {
  637.             return PEAR::raiseError("$method authentication method cannot be called");
  638.         }
  639.  
  640.         if (is_array($this->auth_methods[$method])) {
  641.             list($object$method$this->auth_methods[$method];
  642.             $result $object->{$method}($uid$pwd$authz$this);
  643.         else {
  644.             $func =  $this->auth_methods[$method];
  645.             $result $func($uid$pwd$authz$this);
  646.          }
  647.  
  648.         /* If an error was encountered, return the PEAR_Error object. */
  649.         if (PEAR::isError($result)) {
  650.             return $result;
  651.         }
  652.  
  653.         return true;
  654.     }
  655.  
  656.     /**
  657.      * Add a new authentication method.
  658.      *
  659.      * @param string    The authentication method name (e.g. 'PLAIN')
  660.      * @param mixed     The authentication callback (given as the name of a
  661.      *                   function or as an (object, method name) array).
  662.      * @param bool      Should the new method be prepended to the list of
  663.      *                   available methods?  This is the default behavior,
  664.      *                   giving the new method the highest priority.
  665.      *
  666.      * @return  mixed   True on success or a PEAR_Error object on failure.
  667.      *
  668.      * @access public
  669.      * @since  1.6.0
  670.      */
  671.     function setAuthMethod($name$callback$prepend = true)
  672.     {
  673.         if (!is_string($name)) {
  674.             return PEAR::raiseError('Method name is not a string');
  675.         }
  676.  
  677.         if (!is_string($callback&& !is_array($callback)) {
  678.             return PEAR::raiseError('Method callback must be string or array');
  679.         }
  680.  
  681.         if (is_array($callback)) {
  682.             if (!is_object($callback[0]|| !is_string($callback[1]))
  683.                 return PEAR::raiseError('Bad mMethod callback array');
  684.         }
  685.  
  686.         if ($prepend{
  687.             $this->auth_methods = array_merge(array($name => $callback),
  688.                                               $this->auth_methods);
  689.         else {
  690.             $this->auth_methods[$name$callback;
  691.         }
  692.  
  693.         return true;
  694.     }
  695.  
  696.     /**
  697.      * Authenticates the user using the DIGEST-MD5 method.
  698.      *
  699.      * @param string The userid to authenticate as.
  700.      * @param string The password to authenticate with.
  701.      * @param string The optional authorization proxy identifier.
  702.      *
  703.      * @return mixed Returns a PEAR_Error with an error message on any
  704.      *                kind of failure, or true on success.
  705.      * @access private
  706.      * @since  1.1.0
  707.      */
  708.     function _authDigest_MD5($uid$pwd$authz '')
  709.     {
  710.         if (PEAR::isError($error $this->_put('AUTH''DIGEST-MD5'))) {
  711.             return $error;
  712.         }
  713.         /* 334: Continue authentication request */
  714.         if (PEAR::isError($error $this->_parseResponse(334))) {
  715.             /* 503: Error: already authenticated */
  716.             if ($this->_code === 503{
  717.                 return true;
  718.             }
  719.             return $error;
  720.         }
  721.  
  722.         $challenge base64_decode($this->_arguments[0]);
  723.         $digest &Auth_SASL::factory('digest-md5');
  724.         $auth_str base64_encode($digest->getResponse($uid$pwd$challenge,
  725.                                                        $this->host"smtp",
  726.                                                        $authz));
  727.  
  728.         if (PEAR::isError($error $this->_put($auth_str))) {
  729.             return $error;
  730.         }
  731.         /* 334: Continue authentication request */
  732.         if (PEAR::isError($error $this->_parseResponse(334))) {
  733.             return $error;
  734.         }
  735.  
  736.         /* We don't use the protocol's third step because SMTP doesn't
  737.          * allow subsequent authentication, so we just silently ignore
  738.          * it. */
  739.         if (PEAR::isError($error $this->_put(''))) {
  740.             return $error;
  741.         }
  742.         /* 235: Authentication successful */
  743.         if (PEAR::isError($error $this->_parseResponse(235))) {
  744.             return $error;
  745.         }
  746.     }
  747.  
  748.     /**
  749.      * Authenticates the user using the CRAM-MD5 method.
  750.      *
  751.      * @param string The userid to authenticate as.
  752.      * @param string The password to authenticate with.
  753.      * @param string The optional authorization proxy identifier.
  754.      *
  755.      * @return mixed Returns a PEAR_Error with an error message on any
  756.      *                kind of failure, or true on success.
  757.      * @access private
  758.      * @since  1.1.0
  759.      */
  760.     function _authCRAM_MD5($uid$pwd$authz '')
  761.     {
  762.         if (PEAR::isError($error $this->_put('AUTH''CRAM-MD5'))) {
  763.             return $error;
  764.         }
  765.         /* 334: Continue authentication request */
  766.         if (PEAR::isError($error $this->_parseResponse(334))) {
  767.             /* 503: Error: already authenticated */
  768.             if ($this->_code === 503{
  769.                 return true;
  770.             }
  771.             return $error;
  772.         }
  773.  
  774.         $challenge base64_decode($this->_arguments[0]);
  775.         $cram &Auth_SASL::factory('cram-md5');
  776.         $auth_str base64_encode($cram->getResponse($uid$pwd$challenge));
  777.  
  778.         if (PEAR::isError($error $this->_put($auth_str))) {
  779.             return $error;
  780.         }
  781.  
  782.         /* 235: Authentication successful */
  783.         if (PEAR::isError($error $this->_parseResponse(235))) {
  784.             return $error;
  785.         }
  786.     }
  787.  
  788.     /**
  789.      * Authenticates the user using the LOGIN method.
  790.      *
  791.      * @param string The userid to authenticate as.
  792.      * @param string The password to authenticate with.
  793.      * @param string The optional authorization proxy identifier.
  794.      *
  795.      * @return mixed Returns a PEAR_Error with an error message on any
  796.      *                kind of failure, or true on success.
  797.      * @access private
  798.      * @since  1.1.0
  799.      */
  800.     function _authLogin($uid$pwd$authz '')
  801.     {
  802.         if (PEAR::isError($error $this->_put('AUTH''LOGIN'))) {
  803.             return $error;
  804.         }
  805.         /* 334: Continue authentication request */
  806.         if (PEAR::isError($error $this->_parseResponse(334))) {
  807.             /* 503: Error: already authenticated */
  808.             if ($this->_code === 503{
  809.                 return true;
  810.             }
  811.             return $error;
  812.         }
  813.  
  814.         if (PEAR::isError($error $this->_put(base64_encode($uid)))) {
  815.             return $error;
  816.         }
  817.         /* 334: Continue authentication request */
  818.         if (PEAR::isError($error $this->_parseResponse(334))) {
  819.             return $error;
  820.         }
  821.  
  822.         if (PEAR::isError($error $this->_put(base64_encode($pwd)))) {
  823.             return $error;
  824.         }
  825.  
  826.         /* 235: Authentication successful */
  827.         if (PEAR::isError($error $this->_parseResponse(235))) {
  828.             return $error;
  829.         }
  830.  
  831.         return true;
  832.     }
  833.  
  834.     /**
  835.      * Authenticates the user using the PLAIN method.
  836.      *
  837.      * @param string The userid to authenticate as.
  838.      * @param string The password to authenticate with.
  839.      * @param string The optional authorization proxy identifier.
  840.      *
  841.      * @return mixed Returns a PEAR_Error with an error message on any
  842.      *                kind of failure, or true on success.
  843.      * @access private
  844.      * @since  1.1.0
  845.      */
  846.     function _authPlain($uid$pwd$authz '')
  847.     {
  848.         if (PEAR::isError($error $this->_put('AUTH''PLAIN'))) {
  849.             return $error;
  850.         }
  851.         /* 334: Continue authentication request */
  852.         if (PEAR::isError($error $this->_parseResponse(334))) {
  853.             /* 503: Error: already authenticated */
  854.             if ($this->_code === 503{
  855.                 return true;
  856.             }
  857.             return $error;
  858.         }
  859.  
  860.         $auth_str base64_encode($authz chr(0$uid chr(0$pwd);
  861.  
  862.         if (PEAR::isError($error $this->_put($auth_str))) {
  863.             return $error;
  864.         }
  865.  
  866.         /* 235: Authentication successful */
  867.         if (PEAR::isError($error $this->_parseResponse(235))) {
  868.             return $error;
  869.         }
  870.  
  871.         return true;
  872.     }
  873.  
  874.     /**
  875.      * Send the HELO command.
  876.      *
  877.      * @param string The domain name to say we are.
  878.      *
  879.      * @return mixed Returns a PEAR_Error with an error message on any
  880.      *                kind of failure, or true on success.
  881.      * @access public
  882.      * @since  1.0
  883.      */
  884.     function helo($domain)
  885.     {
  886.         if (PEAR::isError($error $this->_put('HELO'$domain))) {
  887.             return $error;
  888.         }
  889.         if (PEAR::isError($error $this->_parseResponse(250))) {
  890.             return $error;
  891.         }
  892.  
  893.         return true;
  894.     }
  895.  
  896.     /**
  897.      * Return the list of SMTP service extensions advertised by the server.
  898.      *
  899.      * @return array The list of SMTP service extensions.
  900.      * @access public
  901.      * @since 1.3
  902.      */
  903.     function getServiceExtensions()
  904.     {
  905.         return $this->_esmtp;
  906.     }
  907.  
  908.     /**
  909.      * Send the MAIL FROM: command.
  910.      *
  911.      * @param string $sender    The sender (reverse path) to set.
  912.      * @param string $params    String containing additional MAIL parameters,
  913.      *                           such as the NOTIFY flags defined by RFC 1891
  914.      *                           or the VERP protocol.
  915.      *
  916.      *                           If $params is an array, only the 'verp' option
  917.      *                           is supported.  If 'verp' is true, the XVERP
  918.      *                           parameter is appended to the MAIL command.  If
  919.      *                           the 'verp' value is a string, the full
  920.      *                           XVERP=value parameter is appended.
  921.      *
  922.      * @return mixed Returns a PEAR_Error with an error message on any
  923.      *                kind of failure, or true on success.
  924.      * @access public
  925.      * @since  1.0
  926.      */
  927.     function mailFrom($sender$params = null)
  928.     {
  929.         $args = "FROM:<$sender>";
  930.  
  931.         /* Support the deprecated array form of $params. */
  932.         if (is_array($params&& isset($params['verp'])) {
  933.             /* XVERP */
  934.             if ($params['verp'=== true{
  935.                 $args .= ' XVERP';
  936.  
  937.             /* XVERP=something */
  938.             elseif (trim($params['verp'])) {
  939.                 $args .= ' XVERP=' $params['verp'];
  940.             }
  941.         elseif (is_string($params&& !empty($params)) {
  942.             $args .= ' ' $params;
  943.         }
  944.  
  945.         if (PEAR::isError($error $this->_put('MAIL'$args))) {
  946.             return $error;
  947.         }
  948.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  949.             return $error;
  950.         }
  951.  
  952.         return true;
  953.     }
  954.  
  955.     /**
  956.      * Send the RCPT TO: command.
  957.      *
  958.      * @param string $recipient The recipient (forward path) to add.
  959.      * @param string $params    String containing additional RCPT parameters,
  960.      *                           such as the NOTIFY flags defined by RFC 1891.
  961.      *
  962.      * @return mixed Returns a PEAR_Error with an error message on any
  963.      *                kind of failure, or true on success.
  964.      *
  965.      * @access public
  966.      * @since  1.0
  967.      */
  968.     function rcptTo($recipient$params = null)
  969.     {
  970.         $args = "TO:<$recipient>";
  971.         if (is_string($params)) {
  972.             $args .= ' ' $params;
  973.         }
  974.  
  975.         if (PEAR::isError($error $this->_put('RCPT'$args))) {
  976.             return $error;
  977.         }
  978.         if (PEAR::isError($error $this->_parseResponse(array(250251)$this->pipelining))) {
  979.             return $error;
  980.         }
  981.  
  982.         return true;
  983.     }
  984.  
  985.     /**
  986.      * Quote the data so that it meets SMTP standards.
  987.      *
  988.      * This is provided as a separate public function to facilitate
  989.      * easier overloading for the cases where it is desirable to
  990.      * customize the quoting behavior.
  991.      *
  992.      * @param string $data  The message text to quote. The string must be passed
  993.      *                       by reference, and the text will be modified in place.
  994.      *
  995.      * @access public
  996.      * @since  1.2
  997.      */
  998.     function quotedata(&$data)
  999.     {
  1000.         /* Because a single leading period (.) signifies an end to the
  1001.          * data, legitimate leading periods need to be "doubled" ('..'). */
  1002.         $data preg_replace('/^\./m''..'$data);
  1003.  
  1004.         /* Change Unix (\n) and Mac (\r) linefeeds into CRLF's (\r\n). */
  1005.         $data preg_replace('/(?:\r\n|\n|\r(?!\n))/'"\r\n"$data);
  1006.     }
  1007.  
  1008.     /**
  1009.      * Send the DATA command.
  1010.      *
  1011.      * @param mixed $data     The message data, either as a string or an open
  1012.      *                         file resource.
  1013.      * @param string $headers The message headers.  If $headers is provided,
  1014.      *                         $data is assumed to contain only body data.
  1015.      *
  1016.      * @return mixed Returns a PEAR_Error with an error message on any
  1017.      *                kind of failure, or true on success.
  1018.      * @access public
  1019.      * @since  1.0
  1020.      */
  1021.     function data($data$headers = null)
  1022.     {
  1023.         /* Verify that $data is a supported type. */
  1024.         if (!is_string($data&& !is_resource($data)) {
  1025.             return PEAR::raiseError('Expected a string or file resource');
  1026.         }
  1027.  
  1028.         /* Start by considering the size of the optional headers string.  We
  1029.          * also account for the addition 4 character "\r\n\r\n" separator
  1030.          * sequence. */
  1031.         $size (is_null($headers)) ? 0 : strlen($headers+ 4;
  1032.  
  1033.         if (is_resource($data)) {
  1034.             $stat fstat($data);
  1035.             if ($stat === false{
  1036.                 return PEAR::raiseError('Failed to get file size');
  1037.             }
  1038.             $size += $stat['size'];
  1039.         else {
  1040.             $size += strlen($data);
  1041.         }
  1042.  
  1043.         /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
  1044.          * that no fixed maximum message size is in force".  Furthermore, it
  1045.          * says that if "the parameter is omitted no information is conveyed
  1046.          * about the server's fixed maximum message size". */
  1047.         $limit (isset($this->_esmtp['SIZE'])) $this->_esmtp['SIZE': 0;
  1048.         if ($limit > 0 && $size >= $limit{
  1049.             $this->disconnect();
  1050.             return PEAR::raiseError('Message size exceeds server limit');
  1051.         }
  1052.  
  1053.         /* Initiate the DATA command. */
  1054.         if (PEAR::isError($error $this->_put('DATA'))) {
  1055.             return $error;
  1056.         }
  1057.         if (PEAR::isError($error $this->_parseResponse(354))) {
  1058.             return $error;
  1059.         }
  1060.  
  1061.         /* If we have a separate headers string, send it first. */
  1062.         if (!is_null($headers)) {
  1063.             $this->quotedata($headers);
  1064.             if (PEAR::isError($result $this->_send($headers "\r\n\r\n"))) {
  1065.                 return $result;
  1066.             }
  1067.         }
  1068.  
  1069.         /* Now we can send the message body data. */
  1070.         if (is_resource($data)) {
  1071.             /* Stream the contents of the file resource out over our socket 
  1072.              * connection, line by line.  Each line must be run through the 
  1073.              * quoting routine. */
  1074.             while (strlen($line fread($data8192)) > 0{
  1075.                 /* If the last character is an newline, we need to grab the
  1076.                  * next character to check to see if it is a period. */
  1077.                 while (!feof($data)) {
  1078.                     $char fread($data1);
  1079.                     $line .= $char;
  1080.                     if ($char != "\n"{
  1081.                         break;
  1082.                     }
  1083.                 }
  1084.                 $this->quotedata($line);
  1085.                 if (PEAR::isError($result $this->_send($line))) {
  1086.                     return $result;
  1087.                 }
  1088.             }
  1089.         else {
  1090.             /*
  1091.              * Break up the data by sending one chunk (up to 512k) at a time.  
  1092.              * This approach reduces our peak memory usage.
  1093.              */
  1094.             for ($offset = 0; $offset $size;{
  1095.                 $end $offset + 512000;
  1096.  
  1097.                 /*
  1098.                  * Ensure we don't read beyond our data size or span multiple 
  1099.                  * lines.  quotedata() can't properly handle character data 
  1100.                  * that's split across two line break boundaries.
  1101.                  */
  1102.                 if ($end >= $size{
  1103.                     $end $size;
  1104.                 else {
  1105.                     for ($end $size$end++{
  1106.                         if ($data[$end!= "\n"{
  1107.                             break;
  1108.                         }
  1109.                     }
  1110.                 }
  1111.  
  1112.                 /* Extract our chunk and run it through the quoting routine. */
  1113.                 $chunk substr($data$offset$end $offset);
  1114.                 $this->quotedata($chunk);
  1115.  
  1116.                 /* If we run into a problem along the way, abort. */
  1117.                 if (PEAR::isError($result $this->_send($chunk))) {
  1118.                     return $result;
  1119.                 }
  1120.  
  1121.                 /* Advance the offset to the end of this chunk. */
  1122.                 $offset $end;
  1123.             }
  1124.         }
  1125.  
  1126.         /* Finally, send the DATA terminator sequence. */
  1127.         if (PEAR::isError($result $this->_send("\r\n.\r\n"))) {
  1128.             return $result;
  1129.         }
  1130.  
  1131.         /* Verify that the data was successfully received by the server. */
  1132.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  1133.             return $error;
  1134.         }
  1135.  
  1136.         return true;
  1137.     }
  1138.  
  1139.     /**
  1140.      * Send the SEND FROM: command.
  1141.      *
  1142.      * @param string The reverse path to send.
  1143.      *
  1144.      * @return mixed Returns a PEAR_Error with an error message on any
  1145.      *                kind of failure, or true on success.
  1146.      * @access public
  1147.      * @since  1.2.6
  1148.      */
  1149.     function sendFrom($path)
  1150.     {
  1151.         if (PEAR::isError($error $this->_put('SEND'"FROM:<$path>"))) {
  1152.             return $error;
  1153.         }
  1154.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  1155.             return $error;
  1156.         }
  1157.  
  1158.         return true;
  1159.     }
  1160.  
  1161.     /**
  1162.      * Backwards-compatibility wrapper for sendFrom().
  1163.      *
  1164.      * @param string The reverse path to send.
  1165.      *
  1166.      * @return mixed Returns a PEAR_Error with an error message on any
  1167.      *                kind of failure, or true on success.
  1168.      *
  1169.      * @access      public
  1170.      * @since       1.0
  1171.      * @deprecated  1.2.6
  1172.      */
  1173.     function send_from($path)
  1174.     {
  1175.         return sendFrom($path);
  1176.     }
  1177.  
  1178.     /**
  1179.      * Send the SOML FROM: command.
  1180.      *
  1181.      * @param string The reverse path to send.
  1182.      *
  1183.      * @return mixed Returns a PEAR_Error with an error message on any
  1184.      *                kind of failure, or true on success.
  1185.      * @access public
  1186.      * @since  1.2.6
  1187.      */
  1188.     function somlFrom($path)
  1189.     {
  1190.         if (PEAR::isError($error $this->_put('SOML'"FROM:<$path>"))) {
  1191.             return $error;
  1192.         }
  1193.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  1194.             return $error;
  1195.         }
  1196.  
  1197.         return true;
  1198.     }
  1199.  
  1200.     /**
  1201.      * Backwards-compatibility wrapper for somlFrom().
  1202.      *
  1203.      * @param string The reverse path to send.
  1204.      *
  1205.      * @return mixed Returns a PEAR_Error with an error message on any
  1206.      *                kind of failure, or true on success.
  1207.      *
  1208.      * @access      public
  1209.      * @since       1.0
  1210.      * @deprecated  1.2.6
  1211.      */
  1212.     function soml_from($path)
  1213.     {
  1214.         return somlFrom($path);
  1215.     }
  1216.  
  1217.     /**
  1218.      * Send the SAML FROM: command.
  1219.      *
  1220.      * @param string The reverse path to send.
  1221.      *
  1222.      * @return mixed Returns a PEAR_Error with an error message on any
  1223.      *                kind of failure, or true on success.
  1224.      * @access public
  1225.      * @since  1.2.6
  1226.      */
  1227.     function samlFrom($path)
  1228.     {
  1229.         if (PEAR::isError($error $this->_put('SAML'"FROM:<$path>"))) {
  1230.             return $error;
  1231.         }
  1232.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  1233.             return $error;
  1234.         }
  1235.  
  1236.         return true;
  1237.     }
  1238.  
  1239.     /**
  1240.      * Backwards-compatibility wrapper for samlFrom().
  1241.      *
  1242.      * @param string The reverse path to send.
  1243.      *
  1244.      * @return mixed Returns a PEAR_Error with an error message on any
  1245.      *                kind of failure, or true on success.
  1246.      *
  1247.      * @access      public
  1248.      * @since       1.0
  1249.      * @deprecated  1.2.6
  1250.      */
  1251.     function saml_from($path)
  1252.     {
  1253.         return samlFrom($path);
  1254.     }
  1255.  
  1256.     /**
  1257.      * Send the RSET command.
  1258.      *
  1259.      * @return mixed Returns a PEAR_Error with an error message on any
  1260.      *                kind of failure, or true on success.
  1261.      * @access public
  1262.      * @since  1.0
  1263.      */
  1264.     function rset()
  1265.     {
  1266.         if (PEAR::isError($error $this->_put('RSET'))) {
  1267.             return $error;
  1268.         }
  1269.         if (PEAR::isError($error $this->_parseResponse(250$this->pipelining))) {
  1270.             return $error;
  1271.         }
  1272.  
  1273.         return true;
  1274.     }
  1275.  
  1276.     /**
  1277.      * Send the VRFY command.
  1278.      *
  1279.      * @param string The string to verify
  1280.      *
  1281.      * @return mixed Returns a PEAR_Error with an error message on any
  1282.      *                kind of failure, or true on success.
  1283.      * @access public
  1284.      * @since  1.0
  1285.      */
  1286.     function vrfy($string)
  1287.     {
  1288.         /* Note: 251 is also a valid response code */
  1289.         if (PEAR::isError($error $this->_put('VRFY'$string))) {
  1290.             return $error;
  1291.         }
  1292.         if (PEAR::isError($error $this->_parseResponse(array(250252)))) {
  1293.             return $error;
  1294.         }
  1295.  
  1296.         return true;
  1297.     }
  1298.  
  1299.     /**
  1300.      * Send the NOOP command.
  1301.      *
  1302.      * @return mixed Returns a PEAR_Error with an error message on any
  1303.      *                kind of failure, or true on success.
  1304.      * @access public
  1305.      * @since  1.0
  1306.      */
  1307.     function noop()
  1308.     {
  1309.         if (PEAR::isError($error $this->_put('NOOP'))) {
  1310.             return $error;
  1311.         }
  1312.         if (PEAR::isError($error $this->_parseResponse(250))) {
  1313.             return $error;
  1314.         }
  1315.  
  1316.         return true;
  1317.     }
  1318.  
  1319.     /**
  1320.      * Backwards-compatibility method.  identifySender()'s functionality is
  1321.      * now handled internally.
  1322.      *
  1323.      * @return  boolean     This method always return true.
  1324.      *
  1325.      * @access  public
  1326.      * @since   1.0
  1327.      */
  1328.     function identifySender()
  1329.     {
  1330.         return true;
  1331.     }
  1332.  
  1333. }

Documentation generated on Fri, 05 Jul 2013 00:30:04 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.