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

Documentation generated on Sat, 30 Nov 2019 18:46:06 -0500 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.