Ticket #535: class.smtp.php

File class.smtp.php, 32.9 KB (added by amuller, 14 years ago)

Classe smtp sem explode e implodes

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