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-2017 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.     }
  212.  
  213.     /**
  214.      * Set the socket I/O timeout value in seconds plus microseconds.
  215.      *
  216.      * @param integer $seconds      Timeout value in seconds.
  217.      * @param integer $microseconds Additional value in microseconds.
  218.      *
  219.      * @since 1.5.0
  220.      */
  221.     public function setTimeout($seconds$microseconds = 0)
  222.     {
  223.         return $this->socket->setTimeout($seconds$microseconds);
  224.     }
  225.  
  226.     /**
  227.      * Set the value of the debugging flag.
  228.      *
  229.      * @param boolean  $debug   New value for the debugging flag.
  230.      * @param callback $handler Debug handler callback
  231.      *
  232.      * @since 1.1.0
  233.      */
  234.     public function setDebug($debug$handler = null)
  235.     {
  236.         $this->debug         = $debug;
  237.         $this->debug_handler = $handler;
  238.     }
  239.  
  240.     /**
  241.      * Write the given debug text to the current debug output handler.
  242.      *
  243.      * @param string $message Debug mesage text.
  244.      *
  245.      * @since 1.3.3
  246.      */
  247.     protected function debug($message)
  248.     {
  249.         if ($this->debug{
  250.             if ($this->debug_handler{
  251.                 call_user_func_array(
  252.                     $this->debug_handlerarray(&$this$message)
  253.                 );
  254.             else {
  255.                 echo "DEBUG: $message\n";
  256.             }
  257.         }
  258.     }
  259.  
  260.     /**
  261.      * Send the given string of data to the server.
  262.      *
  263.      * @param string $data The string of data to send.
  264.      *
  265.      * @return mixed The number of bytes that were actually written,
  266.      *                or a PEAR_Error object on failure.
  267.      *
  268.      * @since 1.1.0
  269.      */
  270.     protected function send($data)
  271.     {
  272.         $this->debug("Send: $data");
  273.  
  274.         $result $this->socket->write($data);
  275.         if (!$result || PEAR::isError($result)) {
  276.             $msg $result $result->getMessage("unknown error";
  277.             return PEAR::raiseError("Failed to write to socket: $msg");
  278.         }
  279.  
  280.         return $result;
  281.     }
  282.  
  283.     /**
  284.      * Send a command to the server with an optional string of
  285.      * arguments.  A carriage return / linefeed (CRLF) sequence will
  286.      * be appended to each command string before it is sent to the
  287.      * SMTP server - an error will be thrown if the command string
  288.      * already contains any newline characters. Use send() for
  289.      * commands that must contain newlines.
  290.      *
  291.      * @param string $command The SMTP command to send to the server.
  292.      * @param string $args    A string of optional arguments to append
  293.      *                         to the command.
  294.      *
  295.      * @return mixed The result of the send() call.
  296.      *
  297.      * @since 1.1.0
  298.      */
  299.     protected function put($command$args '')
  300.     {
  301.         if (!empty($args)) {
  302.             $command .= ' ' $args;
  303.         }
  304.  
  305.         if (strcspn($command"\r\n"!== strlen($command)) {
  306.             return PEAR::raiseError('Commands cannot contain newlines');
  307.         }
  308.  
  309.         return $this->send($command "\r\n");
  310.     }
  311.  
  312.     /**
  313.      * Read a reply from the SMTP server.  The reply consists of a response
  314.      * code and a response message.
  315.      *
  316.      * @param mixed $valid The set of valid response codes.  These
  317.      *                      may be specified as an array of integer
  318.      *                      values or as a single integer value.
  319.      * @param bool  $later Do not parse the response now, but wait
  320.      *                      until the last command in the pipelined
  321.      *                      command group
  322.      *
  323.      * @return mixed True if the server returned a valid response code or
  324.      *                a PEAR_Error object is an error condition is reached.
  325.      *
  326.      * @since 1.1.0
  327.      *
  328.      * @see getResponse
  329.      */
  330.     protected function parseResponse($valid$later = false)
  331.     {
  332.         $this->code      = -1;
  333.         $this->arguments = array();
  334.  
  335.         if ($later{
  336.             $this->pipelined_commands++;
  337.             return true;
  338.         }
  339.  
  340.         for ($i = 0; $i <= $this->pipelined_commands$i++{
  341.             while ($line $this->socket->readLine()) {
  342.                 $this->debug("Recv: $line");
  343.  
  344.                 /* If we receive an empty line, the connection was closed. */
  345.                 if (empty($line)) {
  346.                     $this->disconnect();
  347.                     return PEAR::raiseError('Connection was closed');
  348.                 }
  349.  
  350.                 /* Read the code and store the rest in the arguments array. */
  351.                 $code substr($line03);
  352.                 $this->arguments[trim(substr($line4));
  353.  
  354.                 /* Check the syntax of the response code. */
  355.                 if (is_numeric($code)) {
  356.                     $this->code = (int)$code;
  357.                 else {
  358.                     $this->code = -1;
  359.                     break;
  360.                 }
  361.  
  362.                 /* If this is not a multiline response, we're done. */
  363.                 if (substr($line31!= '-'{
  364.                     break;
  365.                 }
  366.             }
  367.         }
  368.  
  369.         $this->pipelined_commands = 0;
  370.  
  371.         /* Compare the server's response code with the valid code/codes. */
  372.         if (is_int($valid&& ($this->code === $valid)) {
  373.             return true;
  374.         elseif (is_array($valid&& in_array($this->code$validtrue)) {
  375.             return true;
  376.         }
  377.  
  378.         return PEAR::raiseError('Invalid response code received from server'$this->code);
  379.     }
  380.  
  381.     /**
  382.      * Issue an SMTP command and verify its response.
  383.      *
  384.      * @param string $command The SMTP command string or data.
  385.      * @param mixed  $valid   The set of valid response codes. These
  386.      *                         may be specified as an array of integer
  387.      *                         values or as a single integer value.
  388.      *
  389.      * @return mixed True on success or a PEAR_Error object on failure.
  390.      *
  391.      * @since 1.6.0
  392.      */
  393.     public function command($command$valid)
  394.     {
  395.         if (PEAR::isError($error $this->put($command))) {
  396.             return $error;
  397.         }
  398.         if (PEAR::isError($error $this->parseResponse($valid))) {
  399.             return $error;
  400.         }
  401.  
  402.         return true;
  403.     }
  404.  
  405.     /**
  406.      * Return a 2-tuple containing the last response from the SMTP server.
  407.      *
  408.      * @return array A two-element array: the first element contains the
  409.      *                response code as an integer and the second element
  410.      *                contains the response's arguments as a string.
  411.      *
  412.      * @since 1.1.0
  413.      */
  414.     public function getResponse()
  415.     {
  416.         return array($this->codejoin("\n"$this->arguments));
  417.     }
  418.  
  419.     /**
  420.      * Return the SMTP server's greeting string.
  421.      *
  422.      * @return string A string containing the greeting string, or null if
  423.      *                 a greeting has not been received.
  424.      *
  425.      * @since 1.3.3
  426.      */
  427.     public function getGreeting()
  428.     {
  429.         return $this->greeting;
  430.     }
  431.  
  432.     /**
  433.      * Attempt to connect to the SMTP server.
  434.      *
  435.      * @param int  $timeout    The timeout value (in seconds) for the
  436.      *                          socket connection attempt.
  437.      * @param bool $persistent Should a persistent socket connection
  438.      *                          be used?
  439.      *
  440.      * @return mixed Returns a PEAR_Error with an error message on any
  441.      *                kind of failure, or true on success.
  442.      * @since 1.0
  443.      */
  444.     public function connect($timeout = null$persistent = false)
  445.     {
  446.         $this->greeting = null;
  447.  
  448.         $result $this->socket->connect(
  449.             $this->host$this->port$persistent$timeout$this->socket_options
  450.         );
  451.  
  452.         if (PEAR::isError($result)) {
  453.             return PEAR::raiseError(
  454.                 'Failed to connect socket: ' $result->getMessage()
  455.             );
  456.         }
  457.  
  458.         /*
  459.          * Now that we're connected, reset the socket's timeout value for
  460.          * future I/O operations.  This allows us to have different socket
  461.          * timeout values for the initial connection (our $timeout parameter)
  462.          * and all other socket operations.
  463.          */
  464.         if ($this->timeout > 0{
  465.             if (PEAR::isError($error $this->setTimeout($this->timeout))) {
  466.                 return $error;
  467.             }
  468.         }
  469.  
  470.         if (PEAR::isError($error $this->parseResponse(220))) {
  471.             return $error;
  472.         }
  473.  
  474.         /* Extract and store a copy of the server's greeting string. */
  475.         list($this->greeting$this->getResponse();
  476.  
  477.         if (PEAR::isError($error $this->negotiate())) {
  478.             return $error;
  479.         }
  480.  
  481.         return true;
  482.     }
  483.  
  484.     /**
  485.      * Attempt to disconnect from the SMTP server.
  486.      *
  487.      * @return mixed Returns a PEAR_Error with an error message on any
  488.      *                kind of failure, or true on success.
  489.      * @since 1.0
  490.      */
  491.     public function disconnect()
  492.     {
  493.         if (PEAR::isError($error $this->put('QUIT'))) {
  494.             return $error;
  495.         }
  496.         if (PEAR::isError($error $this->parseResponse(221))) {
  497.             return $error;
  498.         }
  499.         if (PEAR::isError($error $this->socket->disconnect())) {
  500.             return PEAR::raiseError(
  501.                 'Failed to disconnect socket: ' $error->getMessage()
  502.             );
  503.         }
  504.  
  505.         return true;
  506.     }
  507.  
  508.     /**
  509.      * Attempt to send the EHLO command and obtain a list of ESMTP
  510.      * extensions available, and failing that just send HELO.
  511.      *
  512.      * @return mixed Returns a PEAR_Error with an error message on any
  513.      *                kind of failure, or true on success.
  514.      *
  515.      * @since 1.1.0
  516.      */
  517.     protected function negotiate()
  518.     {
  519.         if (PEAR::isError($error $this->put('EHLO'$this->localhost))) {
  520.             return $error;
  521.         }
  522.  
  523.         if (PEAR::isError($this->parseResponse(250))) {
  524.             /* If the EHLO failed, try the simpler HELO command. */
  525.             if (PEAR::isError($error $this->put('HELO'$this->localhost))) {
  526.                 return $error;
  527.             }
  528.             if (PEAR::isError($this->parseResponse(250))) {
  529.                 return PEAR::raiseError('HELO was not accepted'$this->code);
  530.             }
  531.  
  532.             return true;
  533.         }
  534.  
  535.         foreach ($this->arguments as $argument{
  536.             $verb      strtok($argument' ');
  537.             $len       strlen($verb);
  538.             $arguments substr($argument$len + 1strlen($argument$len - 1);
  539.             $this->esmtp[$verb$arguments;
  540.         }
  541.  
  542.         if (!isset($this->esmtp['PIPELINING'])) {
  543.             $this->pipelining = false;
  544.         }
  545.  
  546.         return true;
  547.     }
  548.  
  549.     /**
  550.      * Returns the name of the best authentication method that the server
  551.      * has advertised.
  552.      *
  553.      * @return mixed Returns a string containing the name of the best
  554.      *                supported authentication method or a PEAR_Error object
  555.      *                if a failure condition is encountered.
  556.      * @since 1.1.0
  557.      */
  558.     protected function getBestAuthMethod()
  559.     {
  560.         $available_methods explode(' '$this->esmtp['AUTH']);
  561.  
  562.         foreach ($this->auth_methods as $method => $callback{
  563.             if (in_array($method$available_methods)) {
  564.                 return $method;
  565.             }
  566.         }
  567.  
  568.         return PEAR::raiseError('No supported authentication methods');
  569.     }
  570.  
  571.     /**
  572.      * Attempt to do SMTP authentication.
  573.      *
  574.      * @param string $uid    The userid to authenticate as.
  575.      * @param string $pwd    The password to authenticate with.
  576.      * @param string $method The requested authentication method.  If none is
  577.      *                        specified, the best supported method will be used.
  578.      * @param bool   $tls    Flag indicating whether or not TLS should be attempted.
  579.      * @param string $authz  An optional authorization identifier.  If specified, this
  580.      *                        identifier will be used as the authorization proxy.
  581.      *
  582.      * @return mixed Returns a PEAR_Error with an error message on any
  583.      *                kind of failure, or true on success.
  584.      * @since 1.0
  585.      */
  586.     public 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.         {
  597.             /* Start the TLS connection attempt. */
  598.             if (PEAR::isError($result $this->put('STARTTLS'))) {
  599.                 return $result;
  600.             }
  601.             if (PEAR::isError($result $this->parseResponse(220))) {
  602.                 return $result;
  603.             }
  604.             if (isset($this->socket_options['ssl']['crypto_method'])) {
  605.                 $crypto_method $this->socket_options['ssl']['crypto_method'];
  606.             else {
  607.                 /* STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT constant does not exist
  608.                  * and STREAM_CRYPTO_METHOD_SSLv23_CLIENT constant is
  609.                  * inconsistent across PHP versions. */
  610.                 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT
  611.                                  | @STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
  612.                                  | @STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
  613.             }
  614.             if (PEAR::isError($result $this->socket->enableCrypto(true$crypto_method))) {
  615.                 return $result;
  616.             elseif ($result !== true{
  617.                 return PEAR::raiseError('STARTTLS failed');
  618.             }
  619.  
  620.             /* Send EHLO again to recieve the AUTH string from the
  621.              * SMTP server. */
  622.             $this->negotiate();
  623.         }
  624.  
  625.         if (empty($this->esmtp['AUTH'])) {
  626.             return PEAR::raiseError('SMTP server does not support authentication');
  627.         }
  628.  
  629.         /* If no method has been specified, get the name of the best
  630.          * supported method advertised by the SMTP server. */
  631.         if (empty($method)) {
  632.             if (PEAR::isError($method $this->getBestAuthMethod())) {
  633.                 /* Return the PEAR_Error object from _getBestAuthMethod(). */
  634.                 return $method;
  635.             }
  636.         else {
  637.             $method strtoupper($method);
  638.             if (!array_key_exists($method$this->auth_methods)) {
  639.                 return PEAR::raiseError("$method is not a supported authentication method");
  640.             }
  641.         }
  642.  
  643.         if (!isset($this->auth_methods[$method])) {
  644.             return PEAR::raiseError("$method is not a supported authentication method");
  645.         }
  646.  
  647.         if (!is_callable($this->auth_methods[$method]false)) {
  648.             return PEAR::raiseError("$method authentication method cannot be called");
  649.         }
  650.  
  651.         if (is_array($this->auth_methods[$method])) {
  652.             list($object$method$this->auth_methods[$method];
  653.             $result $object->{$method}($uid$pwd$authz$this);
  654.         else {
  655.             $func   $this->auth_methods[$method];
  656.             $result $func($uid$pwd$authz$this);
  657.         }
  658.  
  659.         /* If an error was encountered, return the PEAR_Error object. */
  660.         if (PEAR::isError($result)) {
  661.             return $result;
  662.         }
  663.  
  664.         return true;
  665.     }
  666.  
  667.     /**
  668.      * Add a new authentication method.
  669.      *
  670.      * @param string $name     The authentication method name (e.g. 'PLAIN')
  671.      * @param mixed  $callback The authentication callback (given as the name of a
  672.      *                          function or as an (object, method name) array).
  673.      * @param bool   $prepend  Should the new method be prepended to the list of
  674.      *                          available methods?  This is the default behavior,
  675.      *                          giving the new method the highest priority.
  676.      *
  677.      * @return mixed True on success or a PEAR_Error object on failure.
  678.      *
  679.      * @since 1.6.0
  680.      */
  681.     public function setAuthMethod($name$callback$prepend = true)
  682.     {
  683.         if (!is_string($name)) {
  684.             return PEAR::raiseError('Method name is not a string');
  685.         }
  686.  
  687.         if (!is_string($callback&& !is_array($callback)) {
  688.             return PEAR::raiseError('Method callback must be string or array');
  689.         }
  690.  
  691.         if (is_array($callback)) {
  692.             if (!is_object($callback[0]|| !is_string($callback[1])) {
  693.                 return PEAR::raiseError('Bad mMethod callback array');
  694.             }
  695.         }
  696.  
  697.         if ($prepend{
  698.             $this->auth_methods = array_merge(
  699.                 array($name => $callback)$this->auth_methods
  700.             );
  701.         else {
  702.             $this->auth_methods[$name$callback;
  703.         }
  704.  
  705.         return true;
  706.     }
  707.  
  708.     /**
  709.      * Authenticates the user using the DIGEST-MD5 method.
  710.      *
  711.      * @param string $uid   The userid to authenticate as.
  712.      * @param string $pwd   The password to authenticate with.
  713.      * @param string $authz The optional authorization proxy identifier.
  714.      *
  715.      * @return mixed Returns a PEAR_Error with an error message on any
  716.      *                kind of failure, or true on success.
  717.      * @since 1.1.0
  718.      */
  719.     protected function authDigestMD5($uid$pwd$authz '')
  720.     {
  721.         if (PEAR::isError($error $this->put('AUTH''DIGEST-MD5'))) {
  722.             return $error;
  723.         }
  724.         /* 334: Continue authentication request */
  725.         if (PEAR::isError($error $this->parseResponse(334))) {
  726.             /* 503: Error: already authenticated */
  727.             if ($this->code === 503{
  728.                 return true;
  729.             }
  730.             return $error;
  731.         }
  732.  
  733.         $auth_sasl = new Auth_SASL;
  734.         $digest    $auth_sasl->factory('digest-md5');
  735.         $challenge base64_decode($this->arguments[0]);
  736.         $auth_str  base64_encode(
  737.             $digest->getResponse($uid$pwd$challenge$this->host"smtp"$authz)
  738.         );
  739.  
  740.         if (PEAR::isError($error $this->put($auth_str))) {
  741.             return $error;
  742.         }
  743.         /* 334: Continue authentication request */
  744.         if (PEAR::isError($error $this->parseResponse(334))) {
  745.             return $error;
  746.         }
  747.  
  748.         /* We don't use the protocol's third step because SMTP doesn't
  749.          * allow subsequent authentication, so we just silently ignore
  750.          * it. */
  751.         if (PEAR::isError($error $this->put(''))) {
  752.             return $error;
  753.         }
  754.         /* 235: Authentication successful */
  755.         if (PEAR::isError($error $this->parseResponse(235))) {
  756.             return $error;
  757.         }
  758.     }
  759.  
  760.     /**
  761.      * Authenticates the user using the CRAM-MD5 method.
  762.      *
  763.      * @param string $uid   The userid to authenticate as.
  764.      * @param string $pwd   The password to authenticate with.
  765.      * @param string $authz The optional authorization proxy identifier.
  766.      *
  767.      * @return mixed Returns a PEAR_Error with an error message on any
  768.      *                kind of failure, or true on success.
  769.      * @since 1.1.0
  770.      */
  771.     protected function authCRAMMD5($uid$pwd$authz '')
  772.     {
  773.         if (PEAR::isError($error $this->put('AUTH''CRAM-MD5'))) {
  774.             return $error;
  775.         }
  776.         /* 334: Continue authentication request */
  777.         if (PEAR::isError($error $this->parseResponse(334))) {
  778.             /* 503: Error: already authenticated */
  779.             if ($this->code === 503{
  780.                 return true;
  781.             }
  782.             return $error;
  783.         }
  784.  
  785.         $auth_sasl = new Auth_SASL;
  786.         $challenge base64_decode($this->arguments[0]);
  787.         $cram      $auth_sasl->factory('cram-md5');
  788.         $auth_str  base64_encode($cram->getResponse($uid$pwd$challenge));
  789.  
  790.         if (PEAR::isError($error $this->put($auth_str))) {
  791.             return $error;
  792.         }
  793.  
  794.         /* 235: Authentication successful */
  795.         if (PEAR::isError($error $this->parseResponse(235))) {
  796.             return $error;
  797.         }
  798.     }
  799.  
  800.     /**
  801.      * Authenticates the user using the LOGIN method.
  802.      *
  803.      * @param string $uid   The userid to authenticate as.
  804.      * @param string $pwd   The password to authenticate with.
  805.      * @param string $authz The optional authorization proxy identifier.
  806.      *
  807.      * @return mixed Returns a PEAR_Error with an error message on any
  808.      *                kind of failure, or true on success.
  809.      * @since 1.1.0
  810.      */
  811.     protected function authLogin($uid$pwd$authz '')
  812.     {
  813.         if (PEAR::isError($error $this->put('AUTH''LOGIN'))) {
  814.             return $error;
  815.         }
  816.         /* 334: Continue authentication request */
  817.         if (PEAR::isError($error $this->parseResponse(334))) {
  818.             /* 503: Error: already authenticated */
  819.             if ($this->code === 503{
  820.                 return true;
  821.             }
  822.             return $error;
  823.         }
  824.  
  825.         if (PEAR::isError($error $this->put(base64_encode($uid)))) {
  826.             return $error;
  827.         }
  828.         /* 334: Continue authentication request */
  829.         if (PEAR::isError($error $this->parseResponse(334))) {
  830.             return $error;
  831.         }
  832.  
  833.         if (PEAR::isError($error $this->put(base64_encode($pwd)))) {
  834.             return $error;
  835.         }
  836.  
  837.         /* 235: Authentication successful */
  838.         if (PEAR::isError($error $this->parseResponse(235))) {
  839.             return $error;
  840.         }
  841.  
  842.         return true;
  843.     }
  844.  
  845.     /**
  846.      * Authenticates the user using the PLAIN method.
  847.      *
  848.      * @param string $uid   The userid to authenticate as.
  849.      * @param string $pwd   The password to authenticate with.
  850.      * @param string $authz The optional authorization proxy identifier.
  851.      *
  852.      * @return mixed Returns a PEAR_Error with an error message on any
  853.      *                kind of failure, or true on success.
  854.      * @since 1.1.0
  855.      */
  856.     protected function authPlain($uid$pwd$authz '')
  857.     {
  858.         if (PEAR::isError($error $this->put('AUTH''PLAIN'))) {
  859.             return $error;
  860.         }
  861.         /* 334: Continue authentication request */
  862.         if (PEAR::isError($error $this->parseResponse(334))) {
  863.             /* 503: Error: already authenticated */
  864.             if ($this->code === 503{
  865.                 return true;
  866.             }
  867.             return $error;
  868.         }
  869.  
  870.         $auth_str base64_encode($authz chr(0$uid chr(0$pwd);
  871.  
  872.         if (PEAR::isError($error $this->put($auth_str))) {
  873.             return $error;
  874.         }
  875.  
  876.         /* 235: Authentication successful */
  877.         if (PEAR::isError($error $this->parseResponse(235))) {
  878.             return $error;
  879.         }
  880.  
  881.         return true;
  882.     }
  883.  
  884.      /**
  885.      * Authenticates the user using the GSSAPI method.
  886.      *
  887.      * PHP krb5 extension is required,
  888.      * service principal and credentials cache must be set.
  889.      *
  890.      * @param string $uid   The userid to authenticate as.
  891.      * @param string $pwd   The password to authenticate with.
  892.      * @param string $authz The optional authorization proxy identifier.
  893.      *
  894.      * @return mixed Returns a PEAR_Error with an error message on any
  895.      *                kind of failure, or true on success.
  896.      */
  897.     protected function authGSSAPI($uid$pwd$authz '')
  898.     {
  899.         if (PEAR::isError($error $this->put('AUTH''GSSAPI'))) {
  900.             return $error;
  901.         }
  902.         /* 334: Continue authentication request */
  903.         if (PEAR::isError($error $this->parseResponse(334))) {
  904.             /* 503: Error: already authenticated */
  905.             if ($this->code === 503{
  906.                 return true;
  907.             }
  908.             return $error;
  909.         }
  910.  
  911.         if (!$this->gssapi_principal{
  912.             return PEAR::raiseError('No Kerberos service principal set'2);
  913.         }
  914.  
  915.         if (!empty($this->gssapi_cname)) {
  916.             putenv('KRB5CCNAME=' $this->gssapi_cname);
  917.         }
  918.  
  919.         try {
  920.             $ccache = new KRB5CCache();
  921.             if (!empty($this->gssapi_cname)) {
  922.                 $ccache->open($this->gssapi_cname);
  923.             }
  924.             
  925.             $gssapicontext = new GSSAPIContext();
  926.             $gssapicontext->acquireCredentials($ccache);
  927.  
  928.             $token   '';
  929.             $success $gssapicontext->initSecContext($this->gssapi_principalnullnullnull$token);
  930.             $token   base64_encode($token);
  931.         }
  932.         catch (Exception $e{
  933.             return PEAR::raiseError('GSSAPI authentication failed: ' $e->getMessage());
  934.         }
  935.  
  936.         if (PEAR::isError($error $this->put($token))) {
  937.             return $error;
  938.         }
  939.  
  940.         /* 334: Continue authentication request */
  941.         if (PEAR::isError($error $this->parseResponse(334))) {
  942.             return $error;
  943.         }
  944.  
  945.         $response $this->arguments[0];
  946.  
  947.         try {
  948.             $challenge base64_decode($response);
  949.             $gssapicontext->unwrap($challenge$challenge);
  950.             $gssapicontext->wrap($challenge$challengetrue);
  951.         }
  952.         catch (Exception $e{
  953.             return PEAR::raiseError('GSSAPI authentication failed: ' $e->getMessage());
  954.         }
  955.  
  956.         if (PEAR::isError($error $this->put(base64_encode($challenge)))) {
  957.             return $error;
  958.         }
  959.  
  960.         /* 235: Authentication successful */
  961.         if (PEAR::isError($error $this->parseResponse(235))) {
  962.             return $error;
  963.         }
  964.  
  965.         return true;
  966.     }
  967.  
  968.     /**
  969.      * Send the HELO command.
  970.      *
  971.      * @param string $domain The domain name to say we are.
  972.      *
  973.      * @return mixed Returns a PEAR_Error with an error message on any
  974.      *                kind of failure, or true on success.
  975.      * @since 1.0
  976.      */
  977.     public function helo($domain)
  978.     {
  979.         if (PEAR::isError($error $this->put('HELO'$domain))) {
  980.             return $error;
  981.         }
  982.         if (PEAR::isError($error $this->parseResponse(250))) {
  983.             return $error;
  984.         }
  985.  
  986.         return true;
  987.     }
  988.  
  989.     /**
  990.      * Return the list of SMTP service extensions advertised by the server.
  991.      *
  992.      * @return array The list of SMTP service extensions.
  993.      * @since 1.3
  994.      */
  995.     public function getServiceExtensions()
  996.     {
  997.         return $this->esmtp;
  998.     }
  999.  
  1000.     /**
  1001.      * Send the MAIL FROM: command.
  1002.      *
  1003.      * @param string $sender The sender (reverse path) to set.
  1004.      * @param string $params String containing additional MAIL parameters,
  1005.      *                        such as the NOTIFY flags defined by RFC 1891
  1006.      *                        or the VERP protocol.
  1007.      *
  1008.      *                        If $params is an array, only the 'verp' option
  1009.      *                        is supported.  If 'verp' is true, the XVERP
  1010.      *                        parameter is appended to the MAIL command.
  1011.      *                        If the 'verp' value is a string, the full
  1012.      *                        XVERP=value parameter is appended.
  1013.      *
  1014.      * @return mixed Returns a PEAR_Error with an error message on any
  1015.      *                kind of failure, or true on success.
  1016.      * @since 1.0
  1017.      */
  1018.     public function mailFrom($sender$params = null)
  1019.     {
  1020.         $args = "FROM:<$sender>";
  1021.  
  1022.         /* Support the deprecated array form of $params. */
  1023.         if (is_array($params&& isset($params['verp'])) {
  1024.             if ($params['verp'=== true{
  1025.                 $args .= ' XVERP';
  1026.             elseif (trim($params['verp'])) {
  1027.                 $args .= ' XVERP=' $params['verp'];
  1028.             }
  1029.         elseif (is_string($params&& !empty($params)) {
  1030.             $args .= ' ' $params;
  1031.         }
  1032.  
  1033.         if (PEAR::isError($error $this->put('MAIL'$args))) {
  1034.             return $error;
  1035.         }
  1036.         if (PEAR::isError($error $this->parseResponse(250$this->pipelining))) {
  1037.             return $error;
  1038.         }
  1039.  
  1040.         return true;
  1041.     }
  1042.  
  1043.     /**
  1044.      * Send the RCPT TO: command.
  1045.      *
  1046.      * @param string $recipient The recipient (forward path) to add.
  1047.      * @param string $params    String containing additional RCPT parameters,
  1048.      *                           such as the NOTIFY flags defined by RFC 1891.
  1049.      *
  1050.      * @return mixed Returns a PEAR_Error with an error message on any
  1051.      *                kind of failure, or true on success.
  1052.      *
  1053.      * @since 1.0
  1054.      */
  1055.     public function rcptTo($recipient$params = null)
  1056.     {
  1057.         $args = "TO:<$recipient>";
  1058.         if (is_string($params)) {
  1059.             $args .= ' ' $params;
  1060.         }
  1061.  
  1062.         if (PEAR::isError($error $this->put('RCPT'$args))) {
  1063.             return $error;
  1064.         }
  1065.         if (PEAR::isError($error $this->parseResponse(array(250251)$this->pipelining))) {
  1066.             return $error;
  1067.         }
  1068.  
  1069.         return true;
  1070.     }
  1071.  
  1072.     /**
  1073.      * Quote the data so that it meets SMTP standards.
  1074.      *
  1075.      * This is provided as a separate public function to facilitate
  1076.      * easier overloading for the cases where it is desirable to
  1077.      * customize the quoting behavior.
  1078.      *
  1079.      * @param string &$data The message text to quote. The string must be passed
  1080.      *                       by reference, and the text will be modified in place.
  1081.      *
  1082.      * @since 1.2
  1083.      */
  1084.     public function quotedata(&$data)
  1085.     {
  1086.         /* Because a single leading period (.) signifies an end to the
  1087.          * data, legitimate leading periods need to be "doubled" ('..'). */
  1088.         $data preg_replace('/^\./m''..'$data);
  1089.  
  1090.         /* Change Unix (\n) and Mac (\r) linefeeds into CRLF's (\r\n). */
  1091.         $data preg_replace('/(?:\r\n|\n|\r(?!\n))/'"\r\n"$data);
  1092.     }
  1093.  
  1094.     /**
  1095.      * Send the DATA command.
  1096.      *
  1097.      * @param mixed  $data    The message data, either as a string or an open
  1098.      *                         file resource.
  1099.      * @param string $headers The message headers.  If $headers is provided,
  1100.      *                         $data is assumed to contain only body data.
  1101.      *
  1102.      * @return mixed Returns a PEAR_Error with an error message on any
  1103.      *                kind of failure, or true on success.
  1104.      * @since 1.0
  1105.      */
  1106.     public function data($data$headers = null)
  1107.     {
  1108.         /* Verify that $data is a supported type. */
  1109.         if (!is_string($data&& !is_resource($data)) {
  1110.             return PEAR::raiseError('Expected a string or file resource');
  1111.         }
  1112.  
  1113.         /* Start by considering the size of the optional headers string.  We
  1114.          * also account for the addition 4 character "\r\n\r\n" separator
  1115.          * sequence. */
  1116.         $size $headers_size (is_null($headers)) ? 0 : strlen($headers+ 4;
  1117.  
  1118.         if (is_resource($data)) {
  1119.             $stat fstat($data);
  1120.             if ($stat === false{
  1121.                 return PEAR::raiseError('Failed to get file size');
  1122.             }
  1123.             $size += $stat['size'];
  1124.         else {
  1125.             $size += strlen($data);
  1126.         }
  1127.  
  1128.         /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
  1129.          * that no fixed maximum message size is in force".  Furthermore, it
  1130.          * says that if "the parameter is omitted no information is conveyed
  1131.          * about the server's fixed maximum message size". */
  1132.         $limit (isset($this->esmtp['SIZE'])) $this->esmtp['SIZE': 0;
  1133.         if ($limit > 0 && $size >= $limit{
  1134.             return PEAR::raiseError('Message size exceeds server limit');
  1135.         }
  1136.  
  1137.         /* Initiate the DATA command. */
  1138.         if (PEAR::isError($error $this->put('DATA'))) {
  1139.             return $error;
  1140.         }
  1141.         if (PEAR::isError($error $this->parseResponse(354))) {
  1142.             return $error;
  1143.         }
  1144.  
  1145.         /* If we have a separate headers string, send it first. */
  1146.         if (!is_null($headers)) {
  1147.             $this->quotedata($headers);
  1148.             if (PEAR::isError($result $this->send($headers "\r\n\r\n"))) {
  1149.                 return $result;
  1150.             }
  1151.  
  1152.             /* Subtract the headers size now that they've been sent. */
  1153.             $size -= $headers_size;
  1154.         }
  1155.  
  1156.         /* Now we can send the message body data. */
  1157.         if (is_resource($data)) {
  1158.             /* Stream the contents of the file resource out over our socket
  1159.              * connection, line by line.  Each line must be run through the
  1160.              * quoting routine. */
  1161.             while (strlen($line fread($data8192)) > 0{
  1162.                 /* If the last character is an newline, we need to grab the
  1163.                  * next character to check to see if it is a period. */
  1164.                 while (!feof($data)) {
  1165.                     $char fread($data1);
  1166.                     $line .= $char;
  1167.                     if ($char != "\n"{
  1168.                         break;
  1169.                     }
  1170.                 }
  1171.                 $this->quotedata($line);
  1172.                 if (PEAR::isError($result $this->send($line))) {
  1173.                     return $result;
  1174.                 }
  1175.             }
  1176.  
  1177.              $last $line;
  1178.         else {
  1179.             /*
  1180.              * Break up the data by sending one chunk (up to 512k) at a time.
  1181.              * This approach reduces our peak memory usage.
  1182.              */
  1183.             for ($offset = 0; $offset $size;{
  1184.                 $end $offset + 512000;
  1185.  
  1186.                 /*
  1187.                  * Ensure we don't read beyond our data size or span multiple
  1188.                  * lines.  quotedata() can't properly handle character data
  1189.                  * that's split across two line break boundaries.
  1190.                  */
  1191.                 if ($end >= $size{
  1192.                     $end $size;
  1193.                 else {
  1194.                     for ($end $size$end++{
  1195.                         if ($data[$end!= "\n"{
  1196.                             break;
  1197.                         }
  1198.                     }
  1199.                 }
  1200.  
  1201.                 /* Extract our chunk and run it through the quoting routine. */
  1202.                 $chunk substr($data$offset$end $offset);
  1203.                 $this->quotedata($chunk);
  1204.  
  1205.                 /* If we run into a problem along the way, abort. */
  1206.                 if (PEAR::isError($result $this->send($chunk))) {
  1207.                     return $result;
  1208.                 }
  1209.  
  1210.                 /* Advance the offset to the end of this chunk. */
  1211.                 $offset $end;
  1212.             }
  1213.  
  1214.             $last $chunk;
  1215.         }
  1216.  
  1217.         /* Don't add another CRLF sequence if it's already in the data */
  1218.         $terminator (substr($last-2== "\r\n" '' "\r\n"".\r\n";
  1219.  
  1220.         /* Finally, send the DATA terminator sequence. */
  1221.         if (PEAR::isError($result $this->send($terminator))) {
  1222.             return $result;
  1223.         }
  1224.  
  1225.         /* Verify that the data was successfully received by the server. */
  1226.         if (PEAR::isError($error $this->parseResponse(250$this->pipelining))) {
  1227.             return $error;
  1228.         }
  1229.  
  1230.         return true;
  1231.     }
  1232.  
  1233.     /**
  1234.      * Send the SEND FROM: command.
  1235.      *
  1236.      * @param string $path The reverse path to send.
  1237.      *
  1238.      * @return mixed Returns a PEAR_Error with an error message on any
  1239.      *                kind of failure, or true on success.
  1240.      * @since 1.2.6
  1241.      */
  1242.     public function sendFrom($path)
  1243.     {
  1244.         if (PEAR::isError($error $this->put('SEND'"FROM:<$path>"))) {
  1245.             return $error;
  1246.         }
  1247.         if (PEAR::isError($error $this->parseResponse(250$this->pipelining))) {
  1248.             return $error;
  1249.         }
  1250.  
  1251.         return true;
  1252.     }
  1253.  
  1254.     /**
  1255.      * Send the SOML FROM: command.
  1256.      *
  1257.      * @param string $path The reverse path to send.
  1258.      *
  1259.      * @return mixed Returns a PEAR_Error with an error message on any
  1260.      *                kind of failure, or true on success.
  1261.      * @since 1.2.6
  1262.      */
  1263.     public function somlFrom($path)
  1264.     {
  1265.         if (PEAR::isError($error $this->put('SOML'"FROM:<$path>"))) {
  1266.             return $error;
  1267.         }
  1268.         if (PEAR::isError($error $this->parseResponse(250$this->pipelining))) {
  1269.             return $error;
  1270.         }
  1271.  
  1272.         return true;
  1273.     }
  1274.  
  1275.     /**
  1276.      * Send the SAML FROM: command.
  1277.      *
  1278.      * @param string $path The reverse path to send.
  1279.      *
  1280.      * @return mixed Returns a PEAR_Error with an error message on any
  1281.      *                kind of failure, or true on success.
  1282.      * @since 1.2.6
  1283.      */
  1284.     public function samlFrom($path)
  1285.     {
  1286.         if (PEAR::isError($error $this->put('SAML'"FROM:<$path>"))) {
  1287.             return $error;
  1288.         }
  1289.         if (PEAR::isError($error $this->parseResponse(250$this->pipelining))) {
  1290.             return $error;
  1291.         }
  1292.  
  1293.         return true;
  1294.     }
  1295.  
  1296.     /**
  1297.      * Send the RSET command.
  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.0
  1302.      */
  1303.     public function rset()
  1304.     {
  1305.         if (PEAR::isError($error $this->put('RSET'))) {
  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 VRFY command.
  1317.      *
  1318.      * @param string $string The string to verify
  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.0
  1323.      */
  1324.     public function vrfy($string)
  1325.     {
  1326.         /* Note: 251 is also a valid response code */
  1327.         if (PEAR::isError($error $this->put('VRFY'$string))) {
  1328.             return $error;
  1329.         }
  1330.         if (PEAR::isError($error $this->parseResponse(array(250252)))) {
  1331.             return $error;
  1332.         }
  1333.  
  1334.         return true;
  1335.     }
  1336.  
  1337.     /**
  1338.      * Send the NOOP command.
  1339.      *
  1340.      * @return mixed Returns a PEAR_Error with an error message on any
  1341.      *                kind of failure, or true on success.
  1342.      * @since 1.0
  1343.      */
  1344.     public function noop()
  1345.     {
  1346.         if (PEAR::isError($error $this->put('NOOP'))) {
  1347.             return $error;
  1348.         }
  1349.         if (PEAR::isError($error $this->parseResponse(250))) {
  1350.             return $error;
  1351.         }
  1352.  
  1353.         return true;
  1354.     }
  1355.  
  1356.     /**
  1357.      * Backwards-compatibility method.  identifySender()'s functionality is
  1358.      * now handled internally.
  1359.      *
  1360.      * @return boolean This method always return true.
  1361.      *
  1362.      * @since 1.0
  1363.      */
  1364.     public function identifySender()
  1365.     {
  1366.         return true;
  1367.     }
  1368. }

Documentation generated on Tue, 25 Sep 2018 01:00:02 +0000 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.