Wordpress 3.6
[autoinstalls/wordpress.git] / wp-includes / class-smtp.php
1 <?php
2 /*~ class.smtp.php
3 .---------------------------------------------------------------------------.
4 |  Software: PHPMailer - PHP email class                                    |
5 |   Version: 5.2.4                                                          |
6 |      Site: https://code.google.com/a/apache-extras.org/p/phpmailer/       |
7 | ------------------------------------------------------------------------- |
8 |     Admin: Jim Jagielski (project admininistrator)                        |
9 |   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
10 |          : Marcus Bointon (coolbru) coolbru@users.sourceforge.net         |
11 |          : Jim Jagielski (jimjag) jimjag@gmail.com                        |
12 |   Founder: Brent R. Matzelle (original founder)                           |
13 | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
15 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
16 | ------------------------------------------------------------------------- |
17 |   License: Distributed under the Lesser General Public License (LGPL)     |
18 |            http://www.gnu.org/copyleft/lesser.html                        |
19 | This program is distributed in the hope that it will be useful - WITHOUT  |
20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
21 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
22 '---------------------------------------------------------------------------'
23 */
24
25 /**
26  * PHPMailer - PHP SMTP email transport class
27  * NOTE: Designed for use with PHP version 5 and up
28  * @package PHPMailer
29  * @author Andy Prevost
30  * @author Marcus Bointon
31  * @copyright 2004 - 2008 Andy Prevost
32  * @author Jim Jagielski
33  * @copyright 2010 - 2012 Jim Jagielski
34  * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
35  */
36
37 /**
38  * PHP RFC821 SMTP client
39  *
40  * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
41  * SMTP also provides some utility methods for sending mail to an SMTP server.
42  * @author Chris Ryan
43  * @package PHPMailer
44  */
45
46 class SMTP {
47   /**
48    *  SMTP server port
49    *  @var int
50    */
51   public $SMTP_PORT = 25;
52
53   /**
54    *  SMTP reply line ending (don't change)
55    *  @var string
56    */
57   public $CRLF = "\r\n";
58
59   /**
60    *  Sets whether debugging is turned on
61    *  @var bool
62    */
63   public $do_debug;       // the level of debug to perform
64
65   /**
66    * Sets the function/method to use for debugging output.
67    * Right now we only honor "echo" or "error_log"
68    * @var string
69    */
70   public $Debugoutput     = "echo";
71
72   /**
73    *  Sets VERP use on/off (default is off)
74    *  @var bool
75    */
76   public $do_verp = false;
77
78   /**
79    * Sets the SMTP timeout value for reads, in seconds
80    * @var int
81    */
82   public $Timeout         = 15;
83
84   /**
85    * Sets the SMTP timelimit value for reads, in seconds
86    * @var int
87    */
88   public $Timelimit       = 30;
89
90   /**
91    * Sets the SMTP PHPMailer Version number
92    * @var string
93    */
94   public $Version         = '5.2.4';
95
96   /////////////////////////////////////////////////
97   // PROPERTIES, PRIVATE AND PROTECTED
98   /////////////////////////////////////////////////
99
100   /**
101    * @var resource The socket to the server
102    */
103   private $smtp_conn;
104   /**
105    * @var string Error message, if any, for the last call
106    */
107   private $error;
108   /**
109    * @var string The reply the server sent to us for HELO
110    */
111   private $helo_rply;
112
113   /**
114    * Outputs debugging info via user-defined method
115    * @param string $str
116    */
117   private function edebug($str) {
118     if ($this->Debugoutput == "error_log") {
119         error_log($str);
120     } else {
121         echo $str;
122     }
123   }
124
125   /**
126    * Initialize the class so that the data is in a known state.
127    * @access public
128    * @return SMTP
129    */
130   public function __construct() {
131     $this->smtp_conn = 0;
132     $this->error = null;
133     $this->helo_rply = null;
134
135     $this->do_debug = 0;
136   }
137
138   /////////////////////////////////////////////////
139   // CONNECTION FUNCTIONS
140   /////////////////////////////////////////////////
141
142   /**
143    * Connect to the server specified on the port specified.
144    * If the port is not specified use the default SMTP_PORT.
145    * If tval is specified then a connection will try and be
146    * established with the server for that number of seconds.
147    * If tval is not specified the default is 30 seconds to
148    * try on the connection.
149    *
150    * SMTP CODE SUCCESS: 220
151    * SMTP CODE FAILURE: 421
152    * @access public
153    * @param string $host
154    * @param int $port
155    * @param int $tval
156    * @return bool
157    */
158   public function Connect($host, $port = 0, $tval = 30) {
159     // set the error val to null so there is no confusion
160     $this->error = null;
161
162     // make sure we are __not__ connected
163     if($this->connected()) {
164       // already connected, generate error
165       $this->error = array("error" => "Already connected to a server");
166       return false;
167     }
168
169     if(empty($port)) {
170       $port = $this->SMTP_PORT;
171     }
172
173     // connect to the smtp server
174     $this->smtp_conn = @fsockopen($host,    // the host of the server
175                                  $port,    // the port to use
176                                  $errno,   // error number if any
177                                  $errstr,  // error message if any
178                                  $tval);   // give up after ? secs
179     // verify we connected properly
180     if(empty($this->smtp_conn)) {
181       $this->error = array("error" => "Failed to connect to server",
182                            "errno" => $errno,
183                            "errstr" => $errstr);
184       if($this->do_debug >= 1) {
185         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />');
186       }
187       return false;
188     }
189
190     // SMTP server can take longer to respond, give longer timeout for first read
191     // Windows does not have support for this timeout function
192     if(substr(PHP_OS, 0, 3) != "WIN") {
193      $max = ini_get('max_execution_time');
194      if ($max != 0 && $tval > $max) { // don't bother if unlimited
195       @set_time_limit($tval);
196      }
197      stream_set_timeout($this->smtp_conn, $tval, 0);
198     }
199
200     // get any announcement
201     $announce = $this->get_lines();
202
203     if($this->do_debug >= 2) {
204       $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />');
205     }
206
207     return true;
208   }
209
210   /**
211    * Initiate a TLS communication with the server.
212    *
213    * SMTP CODE 220 Ready to start TLS
214    * SMTP CODE 501 Syntax error (no parameters allowed)
215    * SMTP CODE 454 TLS not available due to temporary reason
216    * @access public
217    * @return bool success
218    */
219   public function StartTLS() {
220     $this->error = null; # to avoid confusion
221
222     if(!$this->connected()) {
223       $this->error = array("error" => "Called StartTLS() without being connected");
224       return false;
225     }
226
227     fputs($this->smtp_conn,"STARTTLS" . $this->CRLF);
228
229     $rply = $this->get_lines();
230     $code = substr($rply,0,3);
231
232     if($this->do_debug >= 2) {
233       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
234     }
235
236     if($code != 220) {
237       $this->error =
238          array("error"     => "STARTTLS not accepted from server",
239                "smtp_code" => $code,
240                "smtp_msg"  => substr($rply,4));
241       if($this->do_debug >= 1) {
242         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
243       }
244       return false;
245     }
246
247     // Begin encrypted connection
248     if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
249       return false;
250     }
251
252     return true;
253   }
254
255   /**
256    * Performs SMTP authentication.  Must be run after running the
257    * Hello() method.  Returns true if successfully authenticated.
258    * @access public
259    * @param string $username
260    * @param string $password
261    * @param string $authtype
262    * @param string $realm
263    * @param string $workstation
264    * @return bool
265    */
266   public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') {
267     if (empty($authtype)) {
268       $authtype = 'LOGIN';
269     }
270
271     switch ($authtype) {
272       case 'PLAIN':
273         // Start authentication
274         fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF);
275     
276         $rply = $this->get_lines();
277         $code = substr($rply,0,3);
278     
279         if($code != 334) {
280           $this->error =
281             array("error" => "AUTH not accepted from server",
282                   "smtp_code" => $code,
283                   "smtp_msg" => substr($rply,4));
284           if($this->do_debug >= 1) {
285             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
286           }
287           return false;
288         }
289         // Send encoded username and password
290         fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF);
291
292         $rply = $this->get_lines();
293         $code = substr($rply,0,3);
294     
295         if($code != 235) {
296           $this->error =
297             array("error" => "Authentication not accepted from server",
298                   "smtp_code" => $code,
299                   "smtp_msg" => substr($rply,4));
300           if($this->do_debug >= 1) {
301             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
302           }
303           return false;
304         }
305         break;
306       case 'LOGIN':
307         // Start authentication
308         fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
309     
310         $rply = $this->get_lines();
311         $code = substr($rply,0,3);
312     
313         if($code != 334) {
314           $this->error =
315             array("error" => "AUTH not accepted from server",
316                   "smtp_code" => $code,
317                   "smtp_msg" => substr($rply,4));
318           if($this->do_debug >= 1) {
319             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
320           }
321           return false;
322         }
323     
324         // Send encoded username
325         fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
326     
327         $rply = $this->get_lines();
328         $code = substr($rply,0,3);
329     
330         if($code != 334) {
331           $this->error =
332             array("error" => "Username not accepted from server",
333                   "smtp_code" => $code,
334                   "smtp_msg" => substr($rply,4));
335           if($this->do_debug >= 1) {
336             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
337           }
338           return false;
339         }
340     
341         // Send encoded password
342         fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
343     
344         $rply = $this->get_lines();
345         $code = substr($rply,0,3);
346     
347         if($code != 235) {
348           $this->error =
349             array("error" => "Password not accepted from server",
350                   "smtp_code" => $code,
351                   "smtp_msg" => substr($rply,4));
352           if($this->do_debug >= 1) {
353             $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
354           }
355           return false;
356         }
357         break;
358       case 'NTLM':
359         /*
360          * ntlm_sasl_client.php
361          ** Bundled with Permission
362          **
363          ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
364          ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
365          */
366         require_once('ntlm_sasl_client.php');
367         $temp = new stdClass();
368         $ntlm_client = new ntlm_sasl_client_class;
369         if(! $ntlm_client->Initialize($temp)){//let's test if every function its available
370             $this->error = array("error" => $temp->error);
371             if($this->do_debug >= 1) {
372                 $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF);
373             }
374             return false;
375         }
376         $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1
377         
378         fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF);
379
380         $rply = $this->get_lines();
381         $code = substr($rply,0,3);
382         
383
384         if($code != 334) {
385             $this->error =
386                 array("error" => "AUTH not accepted from server",
387                       "smtp_code" => $code,
388                       "smtp_msg" => substr($rply,4));
389             if($this->do_debug >= 1) {
390                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
391             }
392             return false;
393         }
394         
395         $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2
396         $challange = base64_decode($challange);
397         $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password);
398         $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3
399         // Send encoded username
400         fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF);
401
402         $rply = $this->get_lines();
403         $code = substr($rply,0,3);
404
405         if($code != 235) {
406             $this->error =
407                 array("error" => "Could not authenticate",
408                       "smtp_code" => $code,
409                       "smtp_msg" => substr($rply,4));
410             if($this->do_debug >= 1) {
411                 $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF);
412             }
413             return false;
414         }
415         break;
416     }
417     return true;
418   }
419
420   /**
421    * Returns true if connected to a server otherwise false
422    * @access public
423    * @return bool
424    */
425   public function Connected() {
426     if(!empty($this->smtp_conn)) {
427       $sock_status = socket_get_status($this->smtp_conn);
428       if($sock_status["eof"]) {
429         // the socket is valid but we are not connected
430         if($this->do_debug >= 1) {
431             $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected");
432         }
433         $this->Close();
434         return false;
435       }
436       return true; // everything looks good
437     }
438     return false;
439   }
440
441   /**
442    * Closes the socket and cleans up the state of the class.
443    * It is not considered good to use this function without
444    * first trying to use QUIT.
445    * @access public
446    * @return void
447    */
448   public function Close() {
449     $this->error = null; // so there is no confusion
450     $this->helo_rply = null;
451     if(!empty($this->smtp_conn)) {
452       // close the connection and cleanup
453       fclose($this->smtp_conn);
454       $this->smtp_conn = 0;
455     }
456   }
457
458   /////////////////////////////////////////////////
459   // SMTP COMMANDS
460   /////////////////////////////////////////////////
461
462   /**
463    * Issues a data command and sends the msg_data to the server
464    * finializing the mail transaction. $msg_data is the message
465    * that is to be send with the headers. Each header needs to be
466    * on a single line followed by a <CRLF> with the message headers
467    * and the message body being seperated by and additional <CRLF>.
468    *
469    * Implements rfc 821: DATA <CRLF>
470    *
471    * SMTP CODE INTERMEDIATE: 354
472    *     [data]
473    *     <CRLF>.<CRLF>
474    *     SMTP CODE SUCCESS: 250
475    *     SMTP CODE FAILURE: 552,554,451,452
476    * SMTP CODE FAILURE: 451,554
477    * SMTP CODE ERROR  : 500,501,503,421
478    * @access public
479    * @param string $msg_data
480    * @return bool
481    */
482   public function Data($msg_data) {
483     $this->error = null; // so no confusion is caused
484
485     if(!$this->connected()) {
486       $this->error = array(
487               "error" => "Called Data() without being connected");
488       return false;
489     }
490
491     fputs($this->smtp_conn,"DATA" . $this->CRLF);
492
493     $rply = $this->get_lines();
494     $code = substr($rply,0,3);
495
496     if($this->do_debug >= 2) {
497       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
498     }
499
500     if($code != 354) {
501       $this->error =
502         array("error" => "DATA command not accepted from server",
503               "smtp_code" => $code,
504               "smtp_msg" => substr($rply,4));
505       if($this->do_debug >= 1) {
506         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
507       }
508       return false;
509     }
510
511     /* the server is ready to accept data!
512      * according to rfc 821 we should not send more than 1000
513      * including the CRLF
514      * characters on a single line so we will break the data up
515      * into lines by \r and/or \n then if needed we will break
516      * each of those into smaller lines to fit within the limit.
517      * in addition we will be looking for lines that start with
518      * a period '.' and append and additional period '.' to that
519      * line. NOTE: this does not count towards limit.
520      */
521
522     // normalize the line breaks so we know the explode works
523     $msg_data = str_replace("\r\n","\n",$msg_data);
524     $msg_data = str_replace("\r","\n",$msg_data);
525     $lines = explode("\n",$msg_data);
526
527     /* we need to find a good way to determine is headers are
528      * in the msg_data or if it is a straight msg body
529      * currently I am assuming rfc 822 definitions of msg headers
530      * and if the first field of the first line (':' sperated)
531      * does not contain a space then it _should_ be a header
532      * and we can process all lines before a blank "" line as
533      * headers.
534      */
535
536     $field = substr($lines[0],0,strpos($lines[0],":"));
537     $in_headers = false;
538     if(!empty($field) && !strstr($field," ")) {
539       $in_headers = true;
540     }
541
542     $max_line_length = 998; // used below; set here for ease in change
543
544     while(list(,$line) = @each($lines)) {
545       $lines_out = null;
546       if($line == "" && $in_headers) {
547         $in_headers = false;
548       }
549       // ok we need to break this line up into several smaller lines
550       while(strlen($line) > $max_line_length) {
551         $pos = strrpos(substr($line,0,$max_line_length)," ");
552
553         // Patch to fix DOS attack
554         if(!$pos) {
555           $pos = $max_line_length - 1;
556           $lines_out[] = substr($line,0,$pos);
557           $line = substr($line,$pos);
558         } else {
559           $lines_out[] = substr($line,0,$pos);
560           $line = substr($line,$pos + 1);
561         }
562
563         /* if processing headers add a LWSP-char to the front of new line
564          * rfc 822 on long msg headers
565          */
566         if($in_headers) {
567           $line = "\t" . $line;
568         }
569       }
570       $lines_out[] = $line;
571
572       // send the lines to the server
573       while(list(,$line_out) = @each($lines_out)) {
574         if(strlen($line_out) > 0)
575         {
576           if(substr($line_out, 0, 1) == ".") {
577             $line_out = "." . $line_out;
578           }
579         }
580         fputs($this->smtp_conn,$line_out . $this->CRLF);
581       }
582     }
583
584     // message data has been sent
585     fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
586
587     $rply = $this->get_lines();
588     $code = substr($rply,0,3);
589
590     if($this->do_debug >= 2) {
591       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
592     }
593
594     if($code != 250) {
595       $this->error =
596         array("error" => "DATA not accepted from server",
597               "smtp_code" => $code,
598               "smtp_msg" => substr($rply,4));
599       if($this->do_debug >= 1) {
600         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
601       }
602       return false;
603     }
604     return true;
605   }
606
607   /**
608    * Sends the HELO command to the smtp server.
609    * This makes sure that we and the server are in
610    * the same known state.
611    *
612    * Implements from rfc 821: HELO <SP> <domain> <CRLF>
613    *
614    * SMTP CODE SUCCESS: 250
615    * SMTP CODE ERROR  : 500, 501, 504, 421
616    * @access public
617    * @param string $host
618    * @return bool
619    */
620   public function Hello($host = '') {
621     $this->error = null; // so no confusion is caused
622
623     if(!$this->connected()) {
624       $this->error = array(
625             "error" => "Called Hello() without being connected");
626       return false;
627     }
628
629     // if hostname for HELO was not specified send default
630     if(empty($host)) {
631       // determine appropriate default to send to server
632       $host = "localhost";
633     }
634
635     // Send extended hello first (RFC 2821)
636     if(!$this->SendHello("EHLO", $host)) {
637       if(!$this->SendHello("HELO", $host)) {
638         return false;
639       }
640     }
641
642     return true;
643   }
644
645   /**
646    * Sends a HELO/EHLO command.
647    * @access private
648    * @param string $hello
649    * @param string $host
650    * @return bool
651    */
652   private function SendHello($hello, $host) {
653     fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
654
655     $rply = $this->get_lines();
656     $code = substr($rply,0,3);
657
658     if($this->do_debug >= 2) {
659       $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '<br />');
660     }
661
662     if($code != 250) {
663       $this->error =
664         array("error" => $hello . " not accepted from server",
665               "smtp_code" => $code,
666               "smtp_msg" => substr($rply,4));
667       if($this->do_debug >= 1) {
668         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
669       }
670       return false;
671     }
672
673     $this->helo_rply = $rply;
674
675     return true;
676   }
677
678   /**
679    * Starts a mail transaction from the email address specified in
680    * $from. Returns true if successful or false otherwise. If True
681    * the mail transaction is started and then one or more Recipient
682    * commands may be called followed by a Data command.
683    *
684    * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
685    *
686    * SMTP CODE SUCCESS: 250
687    * SMTP CODE SUCCESS: 552,451,452
688    * SMTP CODE SUCCESS: 500,501,421
689    * @access public
690    * @param string $from
691    * @return bool
692    */
693   public function Mail($from) {
694     $this->error = null; // so no confusion is caused
695
696     if(!$this->connected()) {
697       $this->error = array(
698               "error" => "Called Mail() without being connected");
699       return false;
700     }
701
702     $useVerp = ($this->do_verp ? " XVERP" : "");
703     fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);
704
705     $rply = $this->get_lines();
706     $code = substr($rply,0,3);
707
708     if($this->do_debug >= 2) {
709       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
710     }
711
712     if($code != 250) {
713       $this->error =
714         array("error" => "MAIL not accepted from server",
715               "smtp_code" => $code,
716               "smtp_msg" => substr($rply,4));
717       if($this->do_debug >= 1) {
718         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
719       }
720       return false;
721     }
722     return true;
723   }
724
725   /**
726    * Sends the quit command to the server and then closes the socket
727    * if there is no error or the $close_on_error argument is true.
728    *
729    * Implements from rfc 821: QUIT <CRLF>
730    *
731    * SMTP CODE SUCCESS: 221
732    * SMTP CODE ERROR  : 500
733    * @access public
734    * @param bool $close_on_error
735    * @return bool
736    */
737   public function Quit($close_on_error = true) {
738     $this->error = null; // so there is no confusion
739
740     if(!$this->connected()) {
741       $this->error = array(
742               "error" => "Called Quit() without being connected");
743       return false;
744     }
745
746     // send the quit command to the server
747     fputs($this->smtp_conn,"quit" . $this->CRLF);
748
749     // get any good-bye messages
750     $byemsg = $this->get_lines();
751
752     if($this->do_debug >= 2) {
753       $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '<br />');
754     }
755
756     $rval = true;
757     $e = null;
758
759     $code = substr($byemsg,0,3);
760     if($code != 221) {
761       // use e as a tmp var cause Close will overwrite $this->error
762       $e = array("error" => "SMTP server rejected quit command",
763                  "smtp_code" => $code,
764                  "smtp_rply" => substr($byemsg,4));
765       $rval = false;
766       if($this->do_debug >= 1) {
767         $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '<br />');
768       }
769     }
770
771     if(empty($e) || $close_on_error) {
772       $this->Close();
773     }
774
775     return $rval;
776   }
777
778   /**
779    * Sends the command RCPT to the SMTP server with the TO: argument of $to.
780    * Returns true if the recipient was accepted false if it was rejected.
781    *
782    * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
783    *
784    * SMTP CODE SUCCESS: 250,251
785    * SMTP CODE FAILURE: 550,551,552,553,450,451,452
786    * SMTP CODE ERROR  : 500,501,503,421
787    * @access public
788    * @param string $to
789    * @return bool
790    */
791   public function Recipient($to) {
792     $this->error = null; // so no confusion is caused
793
794     if(!$this->connected()) {
795       $this->error = array(
796               "error" => "Called Recipient() without being connected");
797       return false;
798     }
799
800     fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
801
802     $rply = $this->get_lines();
803     $code = substr($rply,0,3);
804
805     if($this->do_debug >= 2) {
806       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
807     }
808
809     if($code != 250 && $code != 251) {
810       $this->error =
811         array("error" => "RCPT not accepted from server",
812               "smtp_code" => $code,
813               "smtp_msg" => substr($rply,4));
814       if($this->do_debug >= 1) {
815         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
816       }
817       return false;
818     }
819     return true;
820   }
821
822   /**
823    * Sends the RSET command to abort and transaction that is
824    * currently in progress. Returns true if successful false
825    * otherwise.
826    *
827    * Implements rfc 821: RSET <CRLF>
828    *
829    * SMTP CODE SUCCESS: 250
830    * SMTP CODE ERROR  : 500,501,504,421
831    * @access public
832    * @return bool
833    */
834   public function Reset() {
835     $this->error = null; // so no confusion is caused
836
837     if(!$this->connected()) {
838       $this->error = array(
839               "error" => "Called Reset() without being connected");
840       return false;
841     }
842
843     fputs($this->smtp_conn,"RSET" . $this->CRLF);
844
845     $rply = $this->get_lines();
846     $code = substr($rply,0,3);
847
848     if($this->do_debug >= 2) {
849       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
850     }
851
852     if($code != 250) {
853       $this->error =
854         array("error" => "RSET failed",
855               "smtp_code" => $code,
856               "smtp_msg" => substr($rply,4));
857       if($this->do_debug >= 1) {
858         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
859       }
860       return false;
861     }
862
863     return true;
864   }
865
866   /**
867    * Starts a mail transaction from the email address specified in
868    * $from. Returns true if successful or false otherwise. If True
869    * the mail transaction is started and then one or more Recipient
870    * commands may be called followed by a Data command. This command
871    * will send the message to the users terminal if they are logged
872    * in and send them an email.
873    *
874    * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
875    *
876    * SMTP CODE SUCCESS: 250
877    * SMTP CODE SUCCESS: 552,451,452
878    * SMTP CODE SUCCESS: 500,501,502,421
879    * @access public
880    * @param string $from
881    * @return bool
882    */
883   public function SendAndMail($from) {
884     $this->error = null; // so no confusion is caused
885
886     if(!$this->connected()) {
887       $this->error = array(
888           "error" => "Called SendAndMail() without being connected");
889       return false;
890     }
891
892     fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
893
894     $rply = $this->get_lines();
895     $code = substr($rply,0,3);
896
897     if($this->do_debug >= 2) {
898       $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '<br />');
899     }
900
901     if($code != 250) {
902       $this->error =
903         array("error" => "SAML not accepted from server",
904               "smtp_code" => $code,
905               "smtp_msg" => substr($rply,4));
906       if($this->do_debug >= 1) {
907         $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '<br />');
908       }
909       return false;
910     }
911     return true;
912   }
913
914   /**
915    * This is an optional command for SMTP that this class does not
916    * support. This method is here to make the RFC821 Definition
917    * complete for this class and __may__ be implimented in the future
918    *
919    * Implements from rfc 821: TURN <CRLF>
920    *
921    * SMTP CODE SUCCESS: 250
922    * SMTP CODE FAILURE: 502
923    * SMTP CODE ERROR  : 500, 503
924    * @access public
925    * @return bool
926    */
927   public function Turn() {
928     $this->error = array("error" => "This method, TURN, of the SMTP ".
929                                     "is not implemented");
930     if($this->do_debug >= 1) {
931       $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '<br />');
932     }
933     return false;
934   }
935
936   /**
937   * Get the current error
938   * @access public
939   * @return array
940   */
941   public function getError() {
942     return $this->error;
943   }
944
945   /////////////////////////////////////////////////
946   // INTERNAL FUNCTIONS
947   /////////////////////////////////////////////////
948
949   /**
950    * Read in as many lines as possible
951    * either before eof or socket timeout occurs on the operation.
952    * With SMTP we can tell if we have more lines to read if the
953    * 4th character is '-' symbol. If it is a space then we don't
954    * need to read anything else.
955    * @access private
956    * @return string
957    */
958   private function get_lines() {
959     $data = "";
960     $endtime = 0;
961     /* If for some reason the fp is bad, don't inf loop */
962     if (!is_resource($this->smtp_conn)) {
963       return $data;
964     }
965     stream_set_timeout($this->smtp_conn, $this->Timeout);
966     if ($this->Timelimit > 0) {
967       $endtime = time() + $this->Timelimit;
968     }
969     while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
970       $str = @fgets($this->smtp_conn,515);
971       if($this->do_debug >= 4) {
972         $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />');
973         $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />');
974       }
975       $data .= $str;
976       if($this->do_debug >= 4) {
977         $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />');
978       }
979       // if 4th character is a space, we are done reading, break the loop
980       if(substr($str,3,1) == " ") { break; }
981       // Timed-out? Log and break
982       $info = stream_get_meta_data($this->smtp_conn);
983       if ($info['timed_out']) {
984         if($this->do_debug >= 4) {
985           $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds) <br />");
986         }
987         break;
988       }
989       // Now check if reads took too long
990       if ($endtime) {
991         if (time() > $endtime) {
992           if($this->do_debug >= 4) {
993             $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds) <br />");
994           }
995           break;
996         }
997       }
998     }
999     return $data;
1000   }
1001
1002 }
1003 ?>