]>
Commit | Line | Data |
---|---|---|
6dd01fce | 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 | ?> |