]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/phpmailer/class.smtp.php
Release 6.5.0
[Github/sugarcrm.git] / include / phpmailer / class.smtp.php
1 <?php
2
3 /*
4
5 Modification information for LGPL compliance
6
7 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
8
9 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
10
11 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
12
13 r54045 - 2010-01-26 12:25:05 -0800 (Tue, 26 Jan 2010) - roger - merge from Kobe rev: 53336 - 54021
14
15 r53116 - 2009-12-09 17:24:37 -0800 (Wed, 09 Dec 2009) - mitani - Merge Kobe into Windex Revision 51633 to 53087
16
17 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
18
19 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
20
21 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
22
23 r43691 - 2009-01-29 15:25:53 -0800 (Thu, 29 Jan 2009) - faissah - 27521  : Update to phpmailer version 2.3.
24
25 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
26
27 r39785 - 2008-09-12 15:49:45 -0700 (Fri, 12 Sep 2008) - faissah - Update to PHPmailer 2.2.1
28
29 r24320 - 2007-07-13 16:42:12 -0700 (Fri, 13 Jul 2007) - chris - Email 2.0 - enabling Gmail SMTP over SSL sends.
30
31 r11652 - 2006-02-21 18:24:06 -0800 (Tue, 21 Feb 2006) - chris - Bug 4719: updating PHPMailer classes for security (DDoS)
32 Touched:
33 include/phpmailer (everything)
34 include/SugarPHPMailer.php (adding our constructor)
35 modules/Email/Email.php (to use the new constructor)
36
37 r2414 - 2005-01-14 18:30:52 -0800 (Fri, 14 Jan 2005) - clint - Change Issues to Bugs.  --Clint
38
39 r915 - 2004-10-08 15:31:10 -0700 (Fri, 08 Oct 2004) - julian - E-mail notification feature + new admin console
40
41
42 */
43
44
45 /*~ class.smtp.php
46 .---------------------------------------------------------------------------.
47 |  Software: PHPMailer - PHP email class                                    |
48 |   Version: 2.3                                                            |
49 |   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
50 |      Info: http://phpmailer.sourceforge.net                               |
51 |   Support: http://sourceforge.net/projects/phpmailer/                     |
52 | ------------------------------------------------------------------------- |
53 |    Author: Andy Prevost (project admininistrator)                         |
54 |    Author: Brent R. Matzelle (original founder)                           |
55 | Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
56 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
57 | ------------------------------------------------------------------------- |
58 |   License: Distributed under the Lesser General Public License (LGPL)     |
59 |            http://www.gnu.org/copyleft/lesser.html                        |
60 | This program is distributed in the hope that it will be useful - WITHOUT  |
61 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
62 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
63 | ------------------------------------------------------------------------- |
64 | We offer a number of paid services (www.codeworxtech.com):                |
65 | - Web Hosting on highly optimized fast and secure servers                 |
66 | - Technology Consulting                                                   |
67 | - Oursourcing (highly qualified programmers and graphic designers)        |
68 '---------------------------------------------------------------------------'
69
70 /**
71  * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
72  * commands except TURN which will always return a not implemented
73  * error. SMTP also provides some utility methods for sending mail
74  * to an SMTP server.
75  * @package PHPMailer
76  * @author Chris Ryan
77  */
78
79 class SMTP {
80   /**
81    *  SMTP server port
82    *  @var int
83    */
84   public $SMTP_PORT = 25;
85
86   /**
87    *  SMTP reply line ending
88    *  @var string
89    */
90   public $CRLF = "\r\n";
91
92   /**
93    *  Sets whether debugging is turned on
94    *  @var bool
95    */
96   public $do_debug;       // the level of debug to perform
97
98   /**
99    *  Sets VERP use on/off (default is off)
100    *  @var bool
101    */
102   public $do_verp = false;
103
104   /**#@+
105    * @access private
106    */
107   private $smtp_conn;      // the socket to the server
108   private $error;          // error if any on the last call
109   private $helo_rply;      // the reply the server sent to us for HELO
110   /**#@-*/
111
112   /**
113    * Initialize the class so that the data is in a known state.
114    * @access public
115    * @return void
116    */
117   public function __construct() {
118     $this->smtp_conn = 0;
119     $this->error = null;
120     $this->helo_rply = null;
121
122     $this->do_debug = 0;
123   }
124
125   /*************************************************************
126    *                    CONNECTION FUNCTIONS                  *
127    ***********************************************************/
128
129   /**
130    * Connect to the server specified on the port specified.
131    * If the port is not specified use the default SMTP_PORT.
132    * If tval is specified then a connection will try and be
133    * established with the server for that number of seconds.
134    * If tval is not specified the default is 30 seconds to
135    * try on the connection.
136    *
137    * SMTP CODE SUCCESS: 220
138    * SMTP CODE FAILURE: 421
139    * @access public
140    * @return bool
141    */
142   public function Connect($host,$port=0,$tval=30) {
143     /* set the error val to null so there is no confusion */
144     $this->error = null;
145
146     /* make sure we are __not__ connected */
147     if($this->connected()) {
148       /* ok we are connected! what should we do?
149        * for now we will just give an error saying we
150        * are already connected
151        */
152       $this->error = array("error" => "Already connected to a server");
153       return false;
154     }
155
156     if(empty($port)) {
157       $port = $this->SMTP_PORT;
158     }
159
160     /* connect to the smtp server */
161     $this->smtp_conn = fsockopen($host,    // the host of the server
162                                  $port,    // the port to use
163                                  $errno,   // error number if any
164                                  $errstr,  // error message if any
165                                  $tval);   // give up after ? secs
166     /* verify we connected properly */
167     if(empty($this->smtp_conn)) 
168     {
169       $GLOBALS['log']->fatal("SMTP -> ERROR: Failed to connect to server. Code: $errno Reply: $errstr ");
170       return false;
171     }
172
173     /* sometimes the SMTP server takes a little longer to respond
174      * so we will give it a longer timeout for the first read
175      * - Windows still does not have support for this timeout function
176      */
177     if(substr(PHP_OS, 0, 3) != "WIN")
178      socket_set_timeout($this->smtp_conn, $tval, 0);
179
180     /* get any announcement stuff */
181     $announce = $this->get_lines();
182
183     /* set the timeout  of any socket functions at 1/10 of a second */
184     //if(function_exists("socket_set_timeout"))
185     //   socket_set_timeout($this->smtp_conn, 0, 100000);
186
187     if($this->do_debug >= 2) {
188       echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
189     }
190
191     return true;
192   }
193
194   /**
195    * Initiate a TSL communication with the server.
196    *
197    * SMTP CODE 220 Ready to start TLS
198    * SMTP CODE 501 Syntax error (no parameters allowed)
199    * SMTP CODE 454 TLS not available due to temporary reason
200    * @access public
201    * @return bool success
202    */
203   public function StartTLS() {
204     $this->error = null; # to avoid confusion
205
206     if(!$this->connected()) {
207       $this->error = array("error" => "Called StartTLS() without being connected");
208       return false;
209     }
210
211     fputs($this->smtp_conn,"STARTTLS" . $this->CRLF);
212
213     $rply = $this->get_lines();
214     $code = substr($rply,0,3);
215
216     if($this->do_debug >= 2) {
217       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
218     }
219
220     if($code != 220) 
221     {
222       $GLOBALS['log']->fatal("SMTP -> ERROR: STARTTLS not accepted from server. Code: $code Reply: $rply ");
223       return false;
224     }
225
226     //Begin encrypted connection
227     if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
228       return false;
229     }
230
231     return true;
232   }
233
234   /**
235    * Performs SMTP authentication.  Must be run after running the
236    * Hello() method.  Returns true if successfully authenticated.
237    * @access public
238    * @return bool
239    */
240   public function Authenticate($username, $password) {
241     // Start authentication
242     fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
243
244     $rply = $this->get_lines();
245     $code = substr($rply,0,3);
246
247     if($code != 334) 
248     {
249       $GLOBALS['log']->fatal("SMTP -> ERROR:AUTH not accepted from server. Code: $code Reply: $rply");
250       return false;
251     }
252
253     // Send encoded username
254     fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
255
256     $rply = $this->get_lines();
257     $code = substr($rply,0,3);
258
259     if($code != 334) 
260     {
261       $GLOBALS['log']->fatal("SMTP -> ERROR:Username not accepted from server. Code: $code Reply: $rply ");
262       return false;
263     }
264     
265     $password = from_html($password);
266     // Send encoded password
267     fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
268
269     $rply = $this->get_lines();
270     $code = substr($rply,0,3);
271
272     if($code != 235) 
273     {
274       $GLOBALS['log']->fatal("SMTP -> ERROR:Password not accepted from server. Code: $code Reply: $rply ");
275       return false;
276     }
277
278     return true;
279   }
280
281   /**
282    * Returns true if connected to a server otherwise false
283    * @access public
284    * @return bool
285    */
286   public function Connected() {
287     if(!empty($this->smtp_conn)) {
288       $sock_status = socket_get_status($this->smtp_conn);
289       if($sock_status["eof"]) {
290         // hmm this is an odd situation... the socket is
291         // valid but we are not connected anymore
292         if($this->do_debug >= 1) {
293             echo "SMTP -> NOTICE:" . $this->CRLF .
294                  "EOF caught while checking if connected";
295         }
296         $this->Close();
297         return false;
298       }
299       return true; // everything looks good
300     }
301     return false;
302   }
303
304   /**
305    * Closes the socket and cleans up the state of the class.
306    * It is not considered good to use this function without
307    * first trying to use QUIT.
308    * @access public
309    * @return void
310    */
311   public function Close() {
312     $this->error = null; // so there is no confusion
313     $this->helo_rply = null;
314     if(!empty($this->smtp_conn)) {
315       // close the connection and cleanup
316       fclose($this->smtp_conn);
317       $this->smtp_conn = 0;
318     }
319   }
320
321   /***************************************************************
322    *                        SMTP COMMANDS                       *
323    *************************************************************/
324
325   /**
326    * Issues a data command and sends the msg_data to the server
327    * finializing the mail transaction. $msg_data is the message
328    * that is to be send with the headers. Each header needs to be
329    * on a single line followed by a <CRLF> with the message headers
330    * and the message body being seperated by and additional <CRLF>.
331    *
332    * Implements rfc 821: DATA <CRLF>
333    *
334    * SMTP CODE INTERMEDIATE: 354
335    *     [data]
336    *     <CRLF>.<CRLF>
337    *     SMTP CODE SUCCESS: 250
338    *     SMTP CODE FAILURE: 552,554,451,452
339    * SMTP CODE FAILURE: 451,554
340    * SMTP CODE ERROR  : 500,501,503,421
341    * @access public
342    * @return bool
343    */
344   public function Data($msg_data) {
345     $this->error = null; // so no confusion is caused
346
347     if(!$this->connected()) {
348       $this->error = array(
349               "error" => "Called Data() without being connected");
350       return false;
351     }
352
353     fputs($this->smtp_conn,"DATA" . $this->CRLF);
354
355     $rply = $this->get_lines();
356     $code = substr($rply,0,3);
357
358     if($this->do_debug >= 2) {
359       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
360     }
361
362     if($code != 354) {
363       $this->error =
364         array("error" => "DATA command not accepted from server",
365               "smtp_code" => $code,
366               "smtp_msg" => substr($rply,4));
367       if($this->do_debug >= 1) {
368         echo "SMTP -> ERROR: " . $this->error["error"] .
369                  ": " . $rply . $this->CRLF;
370       }
371       return false;
372     }
373
374     /* the server is ready to accept data!
375      * according to rfc 821 we should not send more than 1000
376      * including the CRLF
377      * characters on a single line so we will break the data up
378      * into lines by \r and/or \n then if needed we will break
379      * each of those into smaller lines to fit within the limit.
380      * in addition we will be looking for lines that start with
381      * a period '.' and append and additional period '.' to that
382      * line. NOTE: this does not count towards are limit.
383      */
384
385     // normalize the line breaks so we know the explode works
386     $msg_data = str_replace("\r\n","\n",$msg_data);
387     $msg_data = str_replace("\r","\n",$msg_data);
388     $lines = explode("\n",$msg_data);
389
390     /* we need to find a good way to determine is headers are
391      * in the msg_data or if it is a straight msg body
392      * currently I am assuming rfc 822 definitions of msg headers
393      * and if the first field of the first line (':' sperated)
394      * does not contain a space then it _should_ be a header
395      * and we can process all lines before a blank "" line as
396      * headers.
397      */
398     $field = substr($lines[0],0,strpos($lines[0],":"));
399     $in_headers = false;
400     if(!empty($field) && !strstr($field," ")) {
401       $in_headers = true;
402     }
403
404     $max_line_length = 998; // used below; set here for ease in change
405
406     while(list(,$line) = @each($lines)) {
407       $lines_out = null;
408       if($line == "" && $in_headers) {
409         $in_headers = false;
410       }
411       // ok we need to break this line up into several smaller lines
412       while(strlen($line) > $max_line_length) {
413         $pos = strrpos(substr($line,0,$max_line_length)," ");
414
415         // Patch to fix DOS attack
416         if(!$pos) {
417           $pos = $max_line_length - 1;
418           $lines_out[] = substr($line,0,$pos);
419           $line = substr($line,$pos);
420         } else {
421           $lines_out[] = substr($line,0,$pos);
422           $line = substr($line,$pos + 1);
423         }
424
425         /* if we are processing headers we need to
426          * add a LWSP-char to the front of the new line
427          * rfc 822 on long msg headers
428          */
429         if($in_headers) {
430           $line = "\t" . $line;
431         }
432       }
433       $lines_out[] = $line;
434
435       // now send the lines to the server
436       while(list(,$line_out) = @each($lines_out)) {
437         if(strlen($line_out) > 0)
438         {
439           if(substr($line_out, 0, 1) == ".") {
440             $line_out = "." . $line_out;
441           }
442         }
443         fputs($this->smtp_conn,$line_out . $this->CRLF);
444       }
445     }
446
447     // ok all the message data has been sent so lets get this
448     // over with aleady
449     fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
450
451     $rply = $this->get_lines();
452     $code = substr($rply,0,3);
453
454     if($this->do_debug >= 2) {
455       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
456     }
457
458     if($code != 250) {
459       $this->error =
460         array("error" => "DATA not accepted from server",
461               "smtp_code" => $code,
462               "smtp_msg" => substr($rply,4));
463       if($this->do_debug >= 1) {
464         echo "SMTP -> ERROR: " . $this->error["error"] .
465                  ": " . $rply . $this->CRLF;
466       }
467       return false;
468     }
469     return true;
470   }
471
472   /**
473    * Expand takes the name and asks the server to list all the
474    * people who are members of the _list_. Expand will return
475    * back and array of the result or false if an error occurs.
476    * Each value in the array returned has the format of:
477    *     [ <full-name> <sp> ] <path>
478    * The definition of <path> is defined in rfc 821
479    *
480    * Implements rfc 821: EXPN <SP> <string> <CRLF>
481    *
482    * SMTP CODE SUCCESS: 250
483    * SMTP CODE FAILURE: 550
484    * SMTP CODE ERROR  : 500,501,502,504,421
485    * @access public
486    * @return string array
487    */
488   public function Expand($name) {
489     $this->error = null; // so no confusion is caused
490
491     if(!$this->connected()) {
492       $this->error = array(
493             "error" => "Called Expand() without being connected");
494       return false;
495     }
496
497     fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
498
499     $rply = $this->get_lines();
500     $code = substr($rply,0,3);
501
502     if($this->do_debug >= 2) {
503       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
504     }
505
506     if($code != 250) {
507       $this->error =
508         array("error" => "EXPN not accepted from server",
509               "smtp_code" => $code,
510               "smtp_msg" => substr($rply,4));
511       if($this->do_debug >= 1) {
512         echo "SMTP -> ERROR: " . $this->error["error"] .
513                  ": " . $rply . $this->CRLF;
514       }
515       return false;
516     }
517
518     // parse the reply and place in our array to return to user
519     $entries = explode($this->CRLF,$rply);
520     while(list(,$l) = @each($entries)) {
521       $list[] = substr($l,4);
522     }
523
524     return $list;
525   }
526
527   /**
528    * Sends the HELO command to the smtp server.
529    * This makes sure that we and the server are in
530    * the same known state.
531    *
532    * Implements from rfc 821: HELO <SP> <domain> <CRLF>
533    *
534    * SMTP CODE SUCCESS: 250
535    * SMTP CODE ERROR  : 500, 501, 504, 421
536    * @access public
537    * @return bool
538    */
539   public function Hello($host="") {
540     $this->error = null; // so no confusion is caused
541
542     if(!$this->connected()) {
543       $this->error = array(
544             "error" => "Called Hello() without being connected");
545       return false;
546     }
547
548     // if a hostname for the HELO was not specified determine
549     //a suitable one to send
550     if(empty($host)) {
551       // we need to determine some sort of appopiate default
552       // to send to the server
553       $host = "localhost";
554     }
555
556     // Send extended hello first (RFC 2821)
557     if(!$this->SendHello("EHLO", $host))
558     {
559       if(!$this->SendHello("HELO", $host))
560           return false;
561     }
562
563     return true;
564   }
565
566   /**
567    * Sends a HELO/EHLO command.
568    * @access private
569    * @return bool
570    */
571   private function SendHello($hello, $host) {
572     fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
573
574     $rply = $this->get_lines();
575     $code = substr($rply,0,3);
576
577     if($this->do_debug >= 2) {
578       echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
579     }
580
581     if($code != 250) 
582     {
583       $GLOBALS['log']->fatal("SMTP -> ERROR: $hello not accepted from server. Code: $code, Reply: $rply");
584       return false;
585     }
586
587     $this->helo_rply = $rply;
588
589     return true;
590   }
591
592   /**
593    * Gets help information on the keyword specified. If the keyword
594    * is not specified then returns generic help, ussually contianing
595    * A list of keywords that help is available on. This function
596    * returns the results back to the user. It is up to the user to
597    * handle the returned data. If an error occurs then false is
598    * returned with $this->error set appropiately.
599    *
600    * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
601    *
602    * SMTP CODE SUCCESS: 211,214
603    * SMTP CODE ERROR  : 500,501,502,504,421
604    * @access public
605    * @return string
606    */
607   public function Help($keyword="") {
608     $this->error = null; // to avoid confusion
609
610     if(!$this->connected()) {
611       $this->error = array(
612               "error" => "Called Help() without being connected");
613       return false;
614     }
615
616     $extra = "";
617     if(!empty($keyword)) {
618       $extra = " " . $keyword;
619     }
620
621     fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
622
623     $rply = $this->get_lines();
624     $code = substr($rply,0,3);
625
626     if($this->do_debug >= 2) {
627       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
628     }
629
630     if($code != 211 && $code != 214) {
631       $this->error =
632         array("error" => "HELP not accepted from server",
633               "smtp_code" => $code,
634               "smtp_msg" => substr($rply,4));
635       if($this->do_debug >= 1) {
636         echo "SMTP -> ERROR: " . $this->error["error"] .
637                  ": " . $rply . $this->CRLF;
638       }
639       return false;
640     }
641
642     return $rply;
643   }
644
645   /**
646    * Starts a mail transaction from the email address specified in
647    * $from. Returns true if successful or false otherwise. If True
648    * the mail transaction is started and then one or more Recipient
649    * commands may be called followed by a Data command.
650    *
651    * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
652    *
653    * SMTP CODE SUCCESS: 250
654    * SMTP CODE SUCCESS: 552,451,452
655    * SMTP CODE SUCCESS: 500,501,421
656    * @access public
657    * @return bool
658    */
659   public function Mail($from) {
660     $this->error = null; // so no confusion is caused
661
662     if(!$this->connected()) {
663       $this->error = array(
664               "error" => "Called Mail() without being connected");
665       return false;
666     }
667
668     $useVerp = ($this->do_verp ? "XVERP" : "");
669     fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);
670
671     $rply = $this->get_lines();
672     $code = substr($rply,0,3);
673
674     if($this->do_debug >= 2) {
675       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
676     }
677
678     if($code != 250) 
679     {
680       $GLOBALS['log']->fatal("SMTP -> ERROR: MAIL not accepted from server. Code: $code Reply: $rply ");
681       return false;
682     }
683     return true;
684   }
685
686   /**
687    * Sends the command NOOP to the SMTP server.
688    *
689    * Implements from rfc 821: NOOP <CRLF>
690    *
691    * SMTP CODE SUCCESS: 250
692    * SMTP CODE ERROR  : 500, 421
693    * @access public
694    * @return bool
695    */
696   public function Noop() {
697     $this->error = null; // so no confusion is caused
698
699     if(!$this->connected()) {
700       $this->error = array(
701               "error" => "Called Noop() without being connected");
702       return false;
703     }
704
705     fputs($this->smtp_conn,"NOOP" . $this->CRLF);
706
707     $rply = $this->get_lines();
708     $code = substr($rply,0,3);
709
710     if($this->do_debug >= 2) {
711       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
712     }
713
714     if($code != 250) {
715       $this->error =
716         array("error" => "NOOP not accepted from server",
717               "smtp_code" => $code,
718               "smtp_msg" => substr($rply,4));
719       if($this->do_debug >= 1) {
720         echo "SMTP -> ERROR: " . $this->error["error"] .
721                  ": " . $rply . $this->CRLF;
722       }
723       return false;
724     }
725     return true;
726   }
727
728   /**
729    * Sends the quit command to the server and then closes the socket
730    * if there is no error or the $close_on_error argument is true.
731    *
732    * Implements from rfc 821: QUIT <CRLF>
733    *
734    * SMTP CODE SUCCESS: 221
735    * SMTP CODE ERROR  : 500
736    * @access public
737    * @return bool
738    */
739   public function Quit($close_on_error=true) {
740     $this->error = null; // so there is no confusion
741
742     if(!$this->connected()) {
743       $this->error = array(
744               "error" => "Called Quit() without being connected");
745       return false;
746     }
747
748     // send the quit command to the server
749     fputs($this->smtp_conn,"quit" . $this->CRLF);
750
751     // get any good-bye messages
752     $byemsg = $this->get_lines();
753
754     if($this->do_debug >= 2) {
755       echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
756     }
757
758     $rval = true;
759     $e = null;
760
761     $code = substr($byemsg,0,3);
762     if($code != 221) {
763       // use e as a tmp var cause Close will overwrite $this->error
764       $e = array("error" => "SMTP server rejected quit command",
765                  "smtp_code" => $code,
766                  "smtp_rply" => substr($byemsg,4));
767       $rval = false;
768       if($this->do_debug >= 1) {
769         echo "SMTP -> ERROR: " . $e["error"] . ": " .
770                  $byemsg . $this->CRLF;
771       }
772     }
773
774     if(empty($e) || $close_on_error) {
775       $this->Close();
776     }
777
778     return $rval;
779   }
780
781   /**
782    * Sends the command RCPT to the SMTP server with the TO: argument of $to.
783    * Returns true if the recipient was accepted false if it was rejected.
784    *
785    * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
786    *
787    * SMTP CODE SUCCESS: 250,251
788    * SMTP CODE FAILURE: 550,551,552,553,450,451,452
789    * SMTP CODE ERROR  : 500,501,503,421
790    * @access public
791    * @return bool
792    */
793   public function Recipient($to) {
794     $this->error = null; // so no confusion is caused
795
796     if(!$this->connected()) {
797       $this->error = array(
798               "error" => "Called Recipient() without being connected");
799       return false;
800     }
801
802     fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
803
804     $rply = $this->get_lines();
805     $code = substr($rply,0,3);
806
807     if($this->do_debug >= 2) {
808       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
809     }
810
811     if($code != 250 && $code != 251) 
812     {
813       $GLOBALS['log']->fatal("SMTP -> ERROR: RCPT not accepted from server. Code: $code Reply: $rply ");
814       return false;
815     }
816     return true;
817   }
818
819   /**
820    * Sends the RSET command to abort and transaction that is
821    * currently in progress. Returns true if successful false
822    * otherwise.
823    *
824    * Implements rfc 821: RSET <CRLF>
825    *
826    * SMTP CODE SUCCESS: 250
827    * SMTP CODE ERROR  : 500,501,504,421
828    * @access public
829    * @return bool
830    */
831   public function Reset() {
832     $this->error = null; // so no confusion is caused
833
834     if(!$this->connected()) {
835       $this->error = array(
836               "error" => "Called Reset() without being connected");
837       return false;
838     }
839
840     fputs($this->smtp_conn,"RSET" . $this->CRLF);
841
842     $rply = $this->get_lines();
843     $code = substr($rply,0,3);
844
845     if($this->do_debug >= 2) {
846       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
847     }
848
849     if($code != 250) {
850       $this->error =
851         array("error" => "RSET failed",
852               "smtp_code" => $code,
853               "smtp_msg" => substr($rply,4));
854       if($this->do_debug >= 1) {
855         echo "SMTP -> ERROR: " . $this->error["error"] .
856                  ": " . $rply . $this->CRLF;
857       }
858       return false;
859     }
860
861     return true;
862   }
863
864   /**
865    * Starts a mail transaction from the email address specified in
866    * $from. Returns true if successful or false otherwise. If True
867    * the mail transaction is started and then one or more Recipient
868    * commands may be called followed by a Data command. This command
869    * will send the message to the users terminal if they are logged
870    * in.
871    *
872    * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
873    *
874    * SMTP CODE SUCCESS: 250
875    * SMTP CODE SUCCESS: 552,451,452
876    * SMTP CODE SUCCESS: 500,501,502,421
877    * @access public
878    * @return bool
879    */
880   public function Send($from) {
881     $this->error = null; // so no confusion is caused
882
883     if(!$this->connected()) {
884       $this->error = array(
885               "error" => "Called Send() without being connected");
886       return false;
887     }
888
889     fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
890
891     $rply = $this->get_lines();
892     $code = substr($rply,0,3);
893
894     if($this->do_debug >= 2) {
895       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
896     }
897
898     if($code != 250) 
899     {
900       $GLOBALS['log']->fatal("SMTP -> ERROR:SEND not accepted from server. Code:$code Reply:$rply");      
901       return false;
902     }
903     return true;
904   }
905
906   /**
907    * Starts a mail transaction from the email address specified in
908    * $from. Returns true if successful or false otherwise. If True
909    * the mail transaction is started and then one or more Recipient
910    * commands may be called followed by a Data command. This command
911    * will send the message to the users terminal if they are logged
912    * in and send them an email.
913    *
914    * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
915    *
916    * SMTP CODE SUCCESS: 250
917    * SMTP CODE SUCCESS: 552,451,452
918    * SMTP CODE SUCCESS: 500,501,502,421
919    * @access public
920    * @return bool
921    */
922   public function SendAndMail($from) {
923     $this->error = null; // so no confusion is caused
924
925     if(!$this->connected()) {
926       $this->error = array(
927           "error" => "Called SendAndMail() without being connected");
928       return false;
929     }
930
931     fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
932
933     $rply = $this->get_lines();
934     $code = substr($rply,0,3);
935
936     if($this->do_debug >= 2) {
937       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
938     }
939
940     if($code != 250) {
941       $this->error =
942         array("error" => "SAML not accepted from server",
943               "smtp_code" => $code,
944               "smtp_msg" => substr($rply,4));
945       if($this->do_debug >= 1) {
946         echo "SMTP -> ERROR: " . $this->error["error"] .
947                  ": " . $rply . $this->CRLF;
948       }
949       return false;
950     }
951     return true;
952   }
953
954   /**
955    * Starts a mail transaction from the email address specified in
956    * $from. Returns true if successful or false otherwise. If True
957    * the mail transaction is started and then one or more Recipient
958    * commands may be called followed by a Data command. This command
959    * will send the message to the users terminal if they are logged
960    * in or mail it to them if they are not.
961    *
962    * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
963    *
964    * SMTP CODE SUCCESS: 250
965    * SMTP CODE SUCCESS: 552,451,452
966    * SMTP CODE SUCCESS: 500,501,502,421
967    * @access public
968    * @return bool
969    */
970   public function SendOrMail($from) {
971     $this->error = null; // so no confusion is caused
972
973     if(!$this->connected()) {
974       $this->error = array(
975           "error" => "Called SendOrMail() without being connected");
976       return false;
977     }
978
979     fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
980
981     $rply = $this->get_lines();
982     $code = substr($rply,0,3);
983
984     if($this->do_debug >= 2) {
985       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
986     }
987
988     if($code != 250) {
989       $this->error =
990         array("error" => "SOML not accepted from server",
991               "smtp_code" => $code,
992               "smtp_msg" => substr($rply,4));
993       if($this->do_debug >= 1) {
994         echo "SMTP -> ERROR: " . $this->error["error"] .
995                  ": " . $rply . $this->CRLF;
996       }
997       return false;
998     }
999     return true;
1000   }
1001
1002   /**
1003    * This is an optional command for SMTP that this class does not
1004    * support. This method is here to make the RFC821 Definition
1005    * complete for this class and __may__ be implimented in the future
1006    *
1007    * Implements from rfc 821: TURN <CRLF>
1008    *
1009    * SMTP CODE SUCCESS: 250
1010    * SMTP CODE FAILURE: 502
1011    * SMTP CODE ERROR  : 500, 503
1012    * @access public
1013    * @return bool
1014    */
1015   public function Turn() {
1016     $this->error = array("error" => "This method, TURN, of the SMTP ".
1017                                     "is not implemented");
1018     if($this->do_debug >= 1) {
1019       echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
1020     }
1021     return false;
1022   }
1023
1024   /**
1025    * Verifies that the name is recognized by the server.
1026    * Returns false if the name could not be verified otherwise
1027    * the response from the server is returned.
1028    *
1029    * Implements rfc 821: VRFY <SP> <string> <CRLF>
1030    *
1031    * SMTP CODE SUCCESS: 250,251
1032    * SMTP CODE FAILURE: 550,551,553
1033    * SMTP CODE ERROR  : 500,501,502,421
1034    * @access public
1035    * @return int
1036    */
1037   public function Verify($name) {
1038     $this->error = null; // so no confusion is caused
1039
1040     if(!$this->connected()) {
1041       $this->error = array(
1042               "error" => "Called Verify() without being connected");
1043       return false;
1044     }
1045
1046     fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
1047
1048     $rply = $this->get_lines();
1049     $code = substr($rply,0,3);
1050
1051     if($this->do_debug >= 2) {
1052       echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
1053     }
1054
1055     if($code != 250 && $code != 251) {
1056       $this->error =
1057         array("error" => "VRFY failed on name '$name'",
1058               "smtp_code" => $code,
1059               "smtp_msg" => substr($rply,4));
1060       if($this->do_debug >= 1) {
1061         echo "SMTP -> ERROR: " . $this->error["error"] .
1062                  ": " . $rply . $this->CRLF;
1063       }
1064       return false;
1065     }
1066     return $rply;
1067   }
1068
1069   /*******************************************************************
1070    *                       INTERNAL FUNCTIONS                       *
1071    ******************************************************************/
1072
1073   /**
1074    * Read in as many lines as possible
1075    * either before eof or socket timeout occurs on the operation.
1076    * With SMTP we can tell if we have more lines to read if the
1077    * 4th character is '-' symbol. If it is a space then we don't
1078    * need to read anything else.
1079    * @access private
1080    * @return string
1081    */
1082   private function get_lines() {
1083     $data = "";
1084     while($str = @fgets($this->smtp_conn,515)) {
1085       if($this->do_debug >= 4) {
1086         echo "SMTP -> get_lines(): \$data was \"$data\"" .
1087                  $this->CRLF;
1088         echo "SMTP -> get_lines(): \$str is \"$str\"" .
1089                  $this->CRLF;
1090       }
1091       $data .= $str;
1092       if($this->do_debug >= 4) {
1093         echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
1094       }
1095       // if the 4th character is a space then we are done reading
1096       // so just break the loop
1097       if(substr($str,3,1) == " ") { break; }
1098     }
1099     return $data;
1100   }
1101
1102 }
1103
1104
1105  ?>