source: subversion/trunk/roundcubemail/program/lib/Net/SMTP.php @ 2180

Last change on this file since 2180 was 2180, checked in by alec, 4 years ago

updated bundled Net_SMTP to 1.3.1

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