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