]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Net/SMTP.php
d632258d6394186932536823d6c027f2daef552c
[quix0rs-gnu-social.git] / extlib / Net / SMTP.php
1 <?php
2 /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2003 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Authors: Chuck Hagenbuch <chuck@horde.org>                           |
17 // |          Jon Parise <jon@php.net>                                    |
18 // |          Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>      |
19 // +----------------------------------------------------------------------+
20 //
21 // $Id: SMTP.php,v 1.63 2008/06/10 05:39:12 jon Exp $
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      * 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 (empty($this->_esmtp['AUTH'])) {
464             if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
465                 if (!isset($this->_esmtp['STARTTLS'])) {
466                     return PEAR::raiseError('SMTP server does not support authentication');
467                 }
468                 if (PEAR::isError($result = $this->_put('STARTTLS'))) {
469                     return $result;
470                 }
471                 if (PEAR::isError($result = $this->_parseResponse(220))) {
472                     return $result;
473                 }
474                 if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
475                     return $result;
476                 } elseif ($result !== true) {
477                     return PEAR::raiseError('STARTTLS failed');
478                 }
479
480                 /* Send EHLO again to recieve the AUTH string from the
481                  * SMTP server. */
482                 $this->_negotiate();
483                 if (empty($this->_esmtp['AUTH'])) {
484                     return PEAR::raiseError('SMTP server does not support authentication');
485                 }
486             } else {
487                 return PEAR::raiseError('SMTP server does not support authentication');
488             }
489         }
490
491         /* If no method has been specified, get the name of the best
492          * supported method advertised by the SMTP server. */
493         if (empty($method)) {
494             if (PEAR::isError($method = $this->_getBestAuthMethod())) {
495                 /* Return the PEAR_Error object from _getBestAuthMethod(). */
496                 return $method;
497             }
498         } else {
499             $method = strtoupper($method);
500             if (!in_array($method, $this->auth_methods)) {
501                 return PEAR::raiseError("$method is not a supported authentication method");
502             }
503         }
504
505         switch ($method) {
506         case 'DIGEST-MD5':
507             $result = $this->_authDigest_MD5($uid, $pwd);
508             break;
509
510         case 'CRAM-MD5':
511             $result = $this->_authCRAM_MD5($uid, $pwd);
512             break;
513
514         case 'LOGIN':
515             $result = $this->_authLogin($uid, $pwd);
516             break;
517
518         case 'PLAIN':
519             $result = $this->_authPlain($uid, $pwd);
520             break;
521
522         default:
523             $result = PEAR::raiseError("$method is not a supported authentication method");
524             break;
525         }
526
527         /* If an error was encountered, return the PEAR_Error object. */
528         if (PEAR::isError($result)) {
529             return $result;
530         }
531
532         return true;
533     }
534
535     /**
536      * Authenticates the user using the DIGEST-MD5 method.
537      *
538      * @param string The userid to authenticate as.
539      * @param string The password to authenticate with.
540      *
541      * @return mixed Returns a PEAR_Error with an error message on any
542      *               kind of failure, or true on success.
543      * @access private
544      * @since  1.1.0
545      */
546     function _authDigest_MD5($uid, $pwd)
547     {
548         if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
549             return $error;
550         }
551         /* 334: Continue authentication request */
552         if (PEAR::isError($error = $this->_parseResponse(334))) {
553             /* 503: Error: already authenticated */
554             if ($this->_code === 503) {
555                 return true;
556             }
557             return $error;
558         }
559
560         $challenge = base64_decode($this->_arguments[0]);
561         $digest = &Auth_SASL::factory('digestmd5');
562         $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
563                                                        $this->host, "smtp"));
564
565         if (PEAR::isError($error = $this->_put($auth_str))) {
566             return $error;
567         }
568         /* 334: Continue authentication request */
569         if (PEAR::isError($error = $this->_parseResponse(334))) {
570             return $error;
571         }
572
573         /* We don't use the protocol's third step because SMTP doesn't
574          * allow subsequent authentication, so we just silently ignore
575          * it. */
576         if (PEAR::isError($error = $this->_put(''))) {
577             return $error;
578         }
579         /* 235: Authentication successful */
580         if (PEAR::isError($error = $this->_parseResponse(235))) {
581             return $error;
582         }
583     }
584
585     /**
586      * Authenticates the user using the CRAM-MD5 method.
587      *
588      * @param string The userid to authenticate as.
589      * @param string The password to authenticate with.
590      *
591      * @return mixed Returns a PEAR_Error with an error message on any
592      *               kind of failure, or true on success.
593      * @access private
594      * @since  1.1.0
595      */
596     function _authCRAM_MD5($uid, $pwd)
597     {
598         if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
599             return $error;
600         }
601         /* 334: Continue authentication request */
602         if (PEAR::isError($error = $this->_parseResponse(334))) {
603             /* 503: Error: already authenticated */
604             if ($this->_code === 503) {
605                 return true;
606             }
607             return $error;
608         }
609
610         $challenge = base64_decode($this->_arguments[0]);
611         $cram = &Auth_SASL::factory('crammd5');
612         $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
613
614         if (PEAR::isError($error = $this->_put($auth_str))) {
615             return $error;
616         }
617
618         /* 235: Authentication successful */
619         if (PEAR::isError($error = $this->_parseResponse(235))) {
620             return $error;
621         }
622     }
623
624     /**
625      * Authenticates the user using the LOGIN method.
626      *
627      * @param string The userid to authenticate as.
628      * @param string The password to authenticate with.
629      *
630      * @return mixed Returns a PEAR_Error with an error message on any
631      *               kind of failure, or true on success.
632      * @access private
633      * @since  1.1.0
634      */
635     function _authLogin($uid, $pwd)
636     {
637         if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
638             return $error;
639         }
640         /* 334: Continue authentication request */
641         if (PEAR::isError($error = $this->_parseResponse(334))) {
642             /* 503: Error: already authenticated */
643             if ($this->_code === 503) {
644                 return true;
645             }
646             return $error;
647         }
648
649         if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
650             return $error;
651         }
652         /* 334: Continue authentication request */
653         if (PEAR::isError($error = $this->_parseResponse(334))) {
654             return $error;
655         }
656
657         if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
658             return $error;
659         }
660
661         /* 235: Authentication successful */
662         if (PEAR::isError($error = $this->_parseResponse(235))) {
663             return $error;
664         }
665
666         return true;
667     }
668
669     /**
670      * Authenticates the user using the PLAIN method.
671      *
672      * @param string The userid to authenticate as.
673      * @param string The password to authenticate with.
674      *
675      * @return mixed Returns a PEAR_Error with an error message on any
676      *               kind of failure, or true on success.
677      * @access private
678      * @since  1.1.0
679      */
680     function _authPlain($uid, $pwd)
681     {
682         if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
683             return $error;
684         }
685         /* 334: Continue authentication request */
686         if (PEAR::isError($error = $this->_parseResponse(334))) {
687             /* 503: Error: already authenticated */
688             if ($this->_code === 503) {
689                 return true;
690             }
691             return $error;
692         }
693
694         $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
695
696         if (PEAR::isError($error = $this->_put($auth_str))) {
697             return $error;
698         }
699
700         /* 235: Authentication successful */
701         if (PEAR::isError($error = $this->_parseResponse(235))) {
702             return $error;
703         }
704
705         return true;
706     }
707
708     /**
709      * Send the HELO command.
710      *
711      * @param string The domain name to say we are.
712      *
713      * @return mixed Returns a PEAR_Error with an error message on any
714      *               kind of failure, or true on success.
715      * @access public
716      * @since  1.0
717      */
718     function helo($domain)
719     {
720         if (PEAR::isError($error = $this->_put('HELO', $domain))) {
721             return $error;
722         }
723         if (PEAR::isError($error = $this->_parseResponse(250))) {
724             return $error;
725         }
726
727         return true;
728     }
729
730     /**
731      * Return the list of SMTP service extensions advertised by the server.
732      *
733      * @return array The list of SMTP service extensions.
734      * @access public
735      * @since 1.3
736      */
737     function getServiceExtensions()
738     {
739         return $this->_esmtp;
740     }
741
742     /**
743      * Send the MAIL FROM: command.
744      *
745      * @param string $sender    The sender (reverse path) to set.
746      * @param string $params    String containing additional MAIL parameters,
747      *                          such as the NOTIFY flags defined by RFC 1891
748      *                          or the VERP protocol.
749      *
750      *                          If $params is an array, only the 'verp' option
751      *                          is supported.  If 'verp' is true, the XVERP
752      *                          parameter is appended to the MAIL command.  If
753      *                          the 'verp' value is a string, the full
754      *                          XVERP=value parameter is appended.
755      *
756      * @return mixed Returns a PEAR_Error with an error message on any
757      *               kind of failure, or true on success.
758      * @access public
759      * @since  1.0
760      */
761     function mailFrom($sender, $params = null)
762     {
763         $args = "FROM:<$sender>";
764
765         /* Support the deprecated array form of $params. */
766         if (is_array($params) && isset($params['verp'])) {
767             /* XVERP */
768             if ($params['verp'] === true) {
769                 $args .= ' XVERP';
770
771             /* XVERP=something */
772             } elseif (trim($params['verp'])) {
773                 $args .= ' XVERP=' . $params['verp'];
774             }
775         } elseif (is_string($params)) {
776             $args .= ' ' . $params;
777         }
778
779         if (PEAR::isError($error = $this->_put('MAIL', $args))) {
780             return $error;
781         }
782         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
783             return $error;
784         }
785
786         return true;
787     }
788
789     /**
790      * Send the RCPT TO: command.
791      *
792      * @param string $recipient The recipient (forward path) to add.
793      * @param string $params    String containing additional RCPT parameters,
794      *                          such as the NOTIFY flags defined by RFC 1891.
795      *
796      * @return mixed Returns a PEAR_Error with an error message on any
797      *               kind of failure, or true on success.
798      *
799      * @access public
800      * @since  1.0
801      */
802     function rcptTo($recipient, $params = null)
803     {
804         $args = "TO:<$recipient>";
805         if (is_string($params)) {
806             $args .= ' ' . $params;
807         }
808
809         if (PEAR::isError($error = $this->_put('RCPT', $args))) {
810             return $error;
811         }
812         if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
813             return $error;
814         }
815
816         return true;
817     }
818
819     /**
820      * Quote the data so that it meets SMTP standards.
821      *
822      * This is provided as a separate public function to facilitate
823      * easier overloading for the cases where it is desirable to
824      * customize the quoting behavior.
825      *
826      * @param string $data  The message text to quote. The string must be passed
827      *                      by reference, and the text will be modified in place.
828      *
829      * @access public
830      * @since  1.2
831      */
832     function quotedata(&$data)
833     {
834         /* Change Unix (\n) and Mac (\r) linefeeds into
835          * Internet-standard CRLF (\r\n) linefeeds. */
836         $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
837
838         /* Because a single leading period (.) signifies an end to the
839          * data, legitimate leading periods need to be "doubled"
840          * (e.g. '..'). */
841         $data = str_replace("\n.", "\n..", $data);
842     }
843
844     /**
845      * Send the DATA command.
846      *
847      * @param string $data  The message body to send.
848      *
849      * @return mixed Returns a PEAR_Error with an error message on any
850      *               kind of failure, or true on success.
851      * @access public
852      * @since  1.0
853      */
854     function data($data)
855     {
856         /* RFC 1870, section 3, subsection 3 states "a value of zero
857          * indicates that no fixed maximum message size is in force".
858          * Furthermore, it says that if "the parameter is omitted no
859          * information is conveyed about the server's fixed maximum
860          * message size". */
861         if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
862             if (strlen($data) >= $this->_esmtp['SIZE']) {
863                 $this->disconnect();
864                 return PEAR::raiseError('Message size excedes the server limit');
865             }
866         }
867
868         /* Quote the data based on the SMTP standards. */
869         $this->quotedata($data);
870
871         if (PEAR::isError($error = $this->_put('DATA'))) {
872             return $error;
873         }
874         if (PEAR::isError($error = $this->_parseResponse(354))) {
875             return $error;
876         }
877
878         if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
879             return $result;
880         }
881         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
882             return $error;
883         }
884
885         return true;
886     }
887
888     /**
889      * Send the SEND FROM: command.
890      *
891      * @param string The reverse path to send.
892      *
893      * @return mixed Returns a PEAR_Error with an error message on any
894      *               kind of failure, or true on success.
895      * @access public
896      * @since  1.2.6
897      */
898     function sendFrom($path)
899     {
900         if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
901             return $error;
902         }
903         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
904             return $error;
905         }
906
907         return true;
908     }
909
910     /**
911      * Backwards-compatibility wrapper for sendFrom().
912      *
913      * @param string The reverse path to send.
914      *
915      * @return mixed Returns a PEAR_Error with an error message on any
916      *               kind of failure, or true on success.
917      *
918      * @access      public
919      * @since       1.0
920      * @deprecated  1.2.6
921      */
922     function send_from($path)
923     {
924         return sendFrom($path);
925     }
926
927     /**
928      * Send the SOML FROM: command.
929      *
930      * @param string The reverse path to send.
931      *
932      * @return mixed Returns a PEAR_Error with an error message on any
933      *               kind of failure, or true on success.
934      * @access public
935      * @since  1.2.6
936      */
937     function somlFrom($path)
938     {
939         if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
940             return $error;
941         }
942         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
943             return $error;
944         }
945
946         return true;
947     }
948
949     /**
950      * Backwards-compatibility wrapper for somlFrom().
951      *
952      * @param string The reverse path to send.
953      *
954      * @return mixed Returns a PEAR_Error with an error message on any
955      *               kind of failure, or true on success.
956      *
957      * @access      public
958      * @since       1.0
959      * @deprecated  1.2.6
960      */
961     function soml_from($path)
962     {
963         return somlFrom($path);
964     }
965
966     /**
967      * Send the SAML FROM: command.
968      *
969      * @param string The reverse path to send.
970      *
971      * @return mixed Returns a PEAR_Error with an error message on any
972      *               kind of failure, or true on success.
973      * @access public
974      * @since  1.2.6
975      */
976     function samlFrom($path)
977     {
978         if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
979             return $error;
980         }
981         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
982             return $error;
983         }
984
985         return true;
986     }
987
988     /**
989      * Backwards-compatibility wrapper for samlFrom().
990      *
991      * @param string The reverse path to send.
992      *
993      * @return mixed Returns a PEAR_Error with an error message on any
994      *               kind of failure, or true on success.
995      *
996      * @access      public
997      * @since       1.0
998      * @deprecated  1.2.6
999      */
1000     function saml_from($path)
1001     {
1002         return samlFrom($path);
1003     }
1004
1005     /**
1006      * Send the RSET command.
1007      *
1008      * @return mixed Returns a PEAR_Error with an error message on any
1009      *               kind of failure, or true on success.
1010      * @access public
1011      * @since  1.0
1012      */
1013     function rset()
1014     {
1015         if (PEAR::isError($error = $this->_put('RSET'))) {
1016             return $error;
1017         }
1018         if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
1019             return $error;
1020         }
1021
1022         return true;
1023     }
1024
1025     /**
1026      * Send the VRFY command.
1027      *
1028      * @param string The string to verify
1029      *
1030      * @return mixed Returns a PEAR_Error with an error message on any
1031      *               kind of failure, or true on success.
1032      * @access public
1033      * @since  1.0
1034      */
1035     function vrfy($string)
1036     {
1037         /* Note: 251 is also a valid response code */
1038         if (PEAR::isError($error = $this->_put('VRFY', $string))) {
1039             return $error;
1040         }
1041         if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
1042             return $error;
1043         }
1044
1045         return true;
1046     }
1047
1048     /**
1049      * Send the NOOP command.
1050      *
1051      * @return mixed Returns a PEAR_Error with an error message on any
1052      *               kind of failure, or true on success.
1053      * @access public
1054      * @since  1.0
1055      */
1056     function noop()
1057     {
1058         if (PEAR::isError($error = $this->_put('NOOP'))) {
1059             return $error;
1060         }
1061         if (PEAR::isError($error = $this->_parseResponse(250))) {
1062             return $error;
1063         }
1064
1065         return true;
1066     }
1067
1068     /**
1069      * Backwards-compatibility method.  identifySender()'s functionality is
1070      * now handled internally.
1071      *
1072      * @return  boolean     This method always return true.
1073      *
1074      * @access  public
1075      * @since   1.0
1076      */
1077     function identifySender()
1078     {
1079         return true;
1080     }
1081
1082 }