PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / site / class / smtp.php

smtp.php in VikBooking Hotel Booking Engine & PMS trunk, at site/class/smtp.php

1,555 lines 39.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 class smtp_class
14 {
15 /*
16 {metadocument}
17 <variable>
18 <name>user</name>
19 <type>STRING</type>
20 <value></value>
21 <documentation>
22 <purpose>Define the authorized user when sending messages to a SMTP
23 server.</purpose>
24 <usage>Set this variable to the user name when the SMTP server
25 requires authentication.</usage>
26 </documentation>
27 </variable>
28 {/metadocument}
29 */
30 var $user="";
31
32 /*
33 {metadocument}
34 <variable>
35 <name>realm</name>
36 <type>STRING</type>
37 <value></value>
38 <documentation>
39 <purpose>Define the authentication realm when sending messages to a
40 SMTP server.</purpose>
41 <usage>Set this variable when the SMTP server requires
42 authentication and if more than one authentication realm is
43 supported.</usage>
44 </documentation>
45 </variable>
46 {/metadocument}
47 */
48 var $realm="";
49
50 /*
51 {metadocument}
52 <variable>
53 <name>password</name>
54 <type>STRING</type>
55 <value></value>
56 <documentation>
57 <purpose>Define the authorized user password when sending messages
58 to a SMTP server.</purpose>
59 <usage>Set this variable to the user password when the SMTP server
60 requires authentication.</usage>
61 </documentation>
62 </variable>
63 {/metadocument}
64 */
65 var $password="";
66
67 /*
68 {metadocument}
69 <variable>
70 <name>workstation</name>
71 <type>STRING</type>
72 <value></value>
73 <documentation>
74 <purpose>Define the client workstation name when sending messages
75 to a SMTP server.</purpose>
76 <usage>Set this variable to the client workstation when the SMTP
77 server requires authentication identifiying the origin workstation
78 name.</usage>
79 </documentation>
80 </variable>
81 {/metadocument}
82 */
83 var $workstation="";
84
85 /*
86 {metadocument}
87 <variable>
88 <name>authentication_mechanism</name>
89 <type>STRING</type>
90 <value></value>
91 <documentation>
92 <purpose>Force the use of a specific authentication mechanism.</purpose>
93 <usage>Set it to an empty string to let the class determine the
94 authentication mechanism to use automatically based on the
95 supported mechanisms by the server and by the SASL client library
96 classes.<paragraphbreak />
97 Set this variable to a specific mechanism name if you want to
98 override the automatic authentication mechanism selection.</usage>
99 </documentation>
100 </variable>
101 {/metadocument}
102 */
103 var $authentication_mechanism="";
104
105 /*
106 {metadocument}
107 <variable>
108 <name>host_name</name>
109 <type>STRING</type>
110 <value></value>
111 <documentation>
112 <purpose>Define the SMTP server host name.</purpose>
113 <usage>Set to the host name of the SMTP server to which you want to
114 relay the messages.</usage>
115 </documentation>
116 </variable>
117 {/metadocument}
118 */
119 var $host_name="";
120
121 /*
122 {metadocument}
123 <variable>
124 <name>host_post</name>
125 <type>INTEGER</type>
126 <value>25</value>
127 <documentation>
128 <purpose>Define the SMTP server host port.</purpose>
129 <usage>Set to the TCP port of the SMTP server host to connect.</usage>
130 </documentation>
131 </variable>
132 {/metadocument}
133 */
134 var $host_port=25;
135
136 /*
137 {metadocument}
138 <variable>
139 <name>ssl</name>
140 <type>BOOLEAN</type>
141 <value>0</value>
142 <documentation>
143 <purpose>Define whether the connection to the SMTP server should be
144 established securely using SSL protocol.</purpose>
145 <usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server
146 requires secure connections using SSL protocol.</usage>
147 </documentation>
148 </variable>
149 {/metadocument}
150 */
151 var $ssl=0;
152
153 /*
154 {metadocument}
155 <variable>
156 <name>start_tls</name>
157 <type>BOOLEAN</type>
158 <value>0</value>
159 <documentation>
160 <purpose>Define whether the connection to the SMTP server should use
161 encryption after the connection is established using TLS
162 protocol.</purpose>
163 <usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server
164 requires that authentication be done securely starting the TLS
165 protocol after the connection is established.</usage>
166 </documentation>
167 </variable>
168 {/metadocument}
169 */
170 var $start_tls = 0;
171
172 /*
173 {metadocument}
174 <variable>
175 <name>localhost</name>
176 <type>STRING</type>
177 <value></value>
178 <documentation>
179 <purpose>Name of the local host computer</purpose>
180 <usage>Set to the name of the computer connecting to the SMTP
181 server from the local network.</usage>
182 </documentation>
183 </variable>
184 {/metadocument}
185 */
186 var $localhost="";
187
188 /*
189 {metadocument}
190 <variable>
191 <name>timeout</name>
192 <type>INTEGER</type>
193 <value>0</value>
194 <documentation>
195 <purpose>Specify the connection timeout period in seconds.</purpose>
196 <usage>Leave it set to <integervalue>0</integervalue> if you want
197 the connection attempts to wait forever. Change this value if for
198 some reason the timeout period seems insufficient or otherwise it
199 seems too long.</usage>
200 </documentation>
201 </variable>
202 {/metadocument}
203 */
204 var $timeout=0;
205
206 /*
207 {metadocument}
208 <variable>
209 <name>data_timeout</name>
210 <type>INTEGER</type>
211 <value>0</value>
212 <documentation>
213 <purpose>Specify the timeout period in seconds to wait for data from
214 the server.</purpose>
215 <usage>Leave it set to <integervalue>0</integervalue> if you want
216 to use the same value defined in the
217 <variablelink>timeout</variablelink> variable. Change this value
218 if for some reason the default data timeout period seems
219 insufficient or otherwise it seems too long.</usage>
220 </documentation>
221 </variable>
222 {/metadocument}
223 */
224 var $data_timeout=0;
225
226 /*
227 {metadocument}
228 <variable>
229 <name>direct_delivery</name>
230 <type>BOOLEAN</type>
231 <value>0</value>
232 <documentation>
233 <purpose>Boolean flag that indicates whether the message should be
234 sent in direct delivery mode, i.e. the message is sent to the SMTP
235 server associated to the domain of the recipient instead of
236 relaying to the server specified by the
237 <variablelink>host_name</variablelink> variable.</purpose>
238 <usage>Set this to <tt><booleanvalue>1</booleanvalue></tt> if you
239 want to send urgent messages directly to the recipient domain SMTP
240 server.</usage>
241 </documentation>
242 </variable>
243 {/metadocument}
244 */
245 var $direct_delivery=0;
246
247 /*
248 {metadocument}
249 <variable>
250 <name>error</name>
251 <type>STRING</type>
252 <value></value>
253 <documentation>
254 <purpose>Message that describes the error when a call to a class
255 function fails.</purpose>
256 <usage>Check this variable when an error occurs to understand what
257 happened.</usage>
258 </documentation>
259 </variable>
260 {/metadocument}
261 */
262 var $error="";
263
264 /*
265 {metadocument}
266 <variable>
267 <name>debug</name>
268 <type>BOOLEAN</type>
269 <value>0</value>
270 <documentation>
271 <purpose>Specify whether it is necessary to output SMTP connection
272 debug information.</purpose>
273 <usage>Set this variable to
274 <tt><booleanvalue>1</booleanvalue></tt> if you need to see
275 the progress of the SMTP connection and protocol dialog when you
276 need to understand the reason for delivery problems.</usage>
277 </documentation>
278 </variable>
279 {/metadocument}
280 */
281 var $debug=0;
282
283 /*
284 {metadocument}
285 <variable>
286 <name>html_debug</name>
287 <type>BOOLEAN</type>
288 <value>0</value>
289 <documentation>
290 <purpose>Specify whether the debug information should be outputted in
291 HTML format.</purpose>
292 <usage>Set this variable to
293 <tt><booleanvalue>1</booleanvalue></tt> if you need to see
294 the debug output in a Web page.</usage>
295 </documentation>
296 </variable>
297 {/metadocument}
298 */
299 var $html_debug=0;
300
301 /*
302 {metadocument}
303 <variable>
304 <name>esmtp</name>
305 <type>BOOLEAN</type>
306 <value>1</value>
307 <documentation>
308 <purpose>Specify whether the class should attempt to use ESMTP
309 extensions supported by the server.</purpose>
310 <usage>Set this variable to
311 <tt><booleanvalue>0</booleanvalue></tt> if for some reason you
312 want to avoid benefitting from ESMTP extensions.</usage>
313 </documentation>
314 </variable>
315 {/metadocument}
316 */
317 var $esmtp=1;
318
319 /*
320 {metadocument}
321 <variable>
322 <name>esmtp_extensions</name>
323 <type>HASH</type>
324 <value></value>
325 <documentation>
326 <purpose>Associative array with the list of ESMTP extensions
327 supported by the SMTP server.</purpose>
328 <usage>Check this variable after connecting to the SMTP server to
329 determine which ESMTP extensions are supported.</usage>
330 </documentation>
331 </variable>
332 {/metadocument}
333 */
334 var $esmtp_extensions=array();
335
336 /*
337 {metadocument}
338 <variable>
339 <name>exclude_address</name>
340 <type>STRING</type>
341 <value></value>
342 <documentation>
343 <purpose>Specify an address that should be considered invalid
344 when resolving host name addresses.</purpose>
345 <usage>In some networks any domain name that does not exist is
346 resolved as a sub-domain of the default local domain. If the DNS is
347 configured in such way that it always resolves any sub-domain of
348 the default local domain to a given address, it is hard to
349 determine whether a given domain does not exist.<paragraphbreak />
350 If your network is configured this way, you may set this variable
351 to the address that all sub-domains of the default local domain
352 resolves, so the class can assume that such address is invalid.</usage>
353 </documentation>
354 </variable>
355 {/metadocument}
356 */
357 var $exclude_address="";
358
359 /*
360 {metadocument}
361 <variable>
362 <name>getmxrr</name>
363 <type>STRING</type>
364 <value>getmxrr</value>
365 <documentation>
366 <purpose>Specify the name of the function that is called to determine
367 the SMTP server address of a given domain.</purpose>
368 <usage>Change this to a working replacement of the PHP
369 <tt>getmxrr()</tt> function if this is not working in your system
370 and you want to send messages in direct delivery mode.</usage>
371 </documentation>
372 </variable>
373 {/metadocument}
374 */
375 var $getmxrr="GetMXRR";
376
377 /*
378 {metadocument}
379 <variable>
380 <name>pop3_auth_host</name>
381 <type>STRING</type>
382 <value></value>
383 <documentation>
384 <purpose>Specify the server address for POP3 based authentication.</purpose>
385 <usage>Set this variable to the address of the POP3 server if the
386 SMTP server requires POP3 based authentication.</usage>
387 </documentation>
388 </variable>
389 {/metadocument}
390 */
391 var $pop3_auth_host="";
392
393 /*
394 {metadocument}
395 <variable>
396 <name>pop3_auth_port</name>
397 <type>INTEGER</type>
398 <value>110</value>
399 <documentation>
400 <purpose>Specify the server port for POP3 based authentication.</purpose>
401 <usage>Set this variable to the port of the POP3 server if the
402 SMTP server requires POP3 based authentication.</usage>
403 </documentation>
404 </variable>
405 {/metadocument}
406 */
407 var $pop3_auth_port=110;
408
409 /* private variables - DO NOT ACCESS */
410
411 var $state="Disconnected";
412 var $connection=0;
413 var $pending_recipients=0;
414 var $next_token="";
415 var $direct_sender="";
416 var $connected_domain="";
417 var $result_code;
418 var $disconnected_error=0;
419 var $esmtp_host="";
420 var $maximum_piped_recipients=100;
421
422 /* Private methods - DO NOT CALL */
423
424 Function Tokenize($string,$separator="")
425 {
426 if(!strcmp($separator,""))
427 {
428 $separator=$string;
429 $string=$this->next_token;
430 }
431 for($character=0;$character<strlen($separator);$character++)
432 {
433 if(GetType($position=strpos($string,$separator[$character]))=="integer")
434 $found=(IsSet($found) ? min($found,$position) : $position);
435 }
436 if(IsSet($found))
437 {
438 $this->next_token=substr($string,$found+1);
439 return(substr($string,0,$found));
440 }
441 else
442 {
443 $this->next_token="";
444 return($string);
445 }
446 }
447
448 Function OutputDebug($message)
449 {
450 $message.="\n";
451 if($this->html_debug)
452 $message=str_replace("\n","<br />\n",HtmlEntities($message));
453 echo $message;
454 flush();
455 }
456
457 Function SetDataAccessError($error)
458 {
459 $this->error=$error;
460 if(function_exists("socket_get_status"))
461 {
462 $status=socket_get_status($this->connection);
463 if($status["timed_out"])
464 $this->error.=": data access time out";
465 elseif($status["eof"])
466 {
467 $this->error.=": the server disconnected";
468 $this->disconnected_error=1;
469 }
470 }
471 }
472
473 Function GetLine()
474 {
475 for($line="";;)
476 {
477 if(feof($this->connection))
478 {
479 $this->error="reached the end of data while reading from the SMTP server conection";
480 return("");
481 }
482 if(GetType($data=@fgets($this->connection,100))!="string"
483 || strlen($data)==0)
484 {
485 $this->SetDataAccessError("it was not possible to read line from the SMTP server");
486 return("");
487 }
488 $line.=$data;
489 $length=strlen($line);
490 if($length>=2
491 && substr($line,$length-2,2)=="\r\n")
492 {
493 $line=substr($line,0,$length-2);
494 if($this->debug)
495 $this->OutputDebug("S $line");
496 return($line);
497 }
498 }
499 }
500
501 Function PutLine($line)
502 {
503 if($this->debug)
504 $this->OutputDebug("C $line");
505 if(!@fputs($this->connection,"$line\r\n"))
506 {
507 $this->SetDataAccessError("it was not possible to send a line to the SMTP server");
508 return(0);
509 }
510 return(1);
511 }
512
513 Function PutData(&$data)
514 {
515 if(strlen($data))
516 {
517 if($this->debug)
518 $this->OutputDebug("C $data");
519 if(!@fputs($this->connection,$data))
520 {
521 $this->SetDataAccessError("it was not possible to send data to the SMTP server");
522 return(0);
523 }
524 }
525 return(1);
526 }
527
528 Function VerifyResultLines($code,&$responses)
529 {
530 $responses=array();
531 Unset($this->result_code);
532 while(strlen($line=$this->GetLine($this->connection)))
533 {
534 if(IsSet($this->result_code))
535 {
536 if(strcmp($this->Tokenize($line," -"),$this->result_code))
537 {
538 $this->error=$line;
539 return(0);
540 }
541 }
542 else
543 {
544 $this->result_code=$this->Tokenize($line," -");
545 if(GetType($code)=="array")
546 {
547 for($codes=0;$codes<count($code) && strcmp($this->result_code,$code[$codes]);$codes++);
548 if($codes>=count($code))
549 {
550 $this->error=$line;
551 return(0);
552 }
553 }
554 else
555 {
556 if(strcmp($this->result_code,$code))
557 {
558 $this->error=$line;
559 return(0);
560 }
561 }
562 }
563 $responses[]=$this->Tokenize("");
564 if(!strcmp($this->result_code,$this->Tokenize($line," ")))
565 return(1);
566 }
567 return(-1);
568 }
569
570 Function FlushRecipients()
571 {
572 if($this->pending_sender)
573 {
574 if($this->VerifyResultLines("250",$responses)<=0)
575 return(0);
576 $this->pending_sender=0;
577 }
578 for(;$this->pending_recipients;$this->pending_recipients--)
579 {
580 if($this->VerifyResultLines(array("250","251"),$responses)<=0)
581 return(0);
582 }
583 return(1);
584 }
585
586 Function ConnectToHost($domain, $port, $resolve_message)
587 {
588 if($this->ssl)
589 {
590 $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
591 $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
592 if($php_version<4003000)
593 return("establishing SSL connections requires at least PHP version 4.3.0");
594 if(!function_exists("extension_loaded")
595 || !extension_loaded("openssl"))
596 return("establishing SSL connections requires the OpenSSL extension enabled");
597 }
598 if(function_exists('preg_match') ? preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $domain) : ereg('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$',$domain))
599 $ip=$domain;
600 else
601 {
602 if($this->debug)
603 $this->OutputDebug($resolve_message);
604 if(!strcmp($ip=@gethostbyname($domain),$domain))
605 return("could not resolve host \"".$domain."\"");
606 }
607 if(strlen($this->exclude_address)
608 && !strcmp(@gethostbyname($this->exclude_address),$ip))
609 return("domain \"".$domain."\" resolved to an address excluded to be valid");
610 if($this->debug)
611 $this->OutputDebug("Connecting to host address \"".$ip."\" port ".$port."...");
612 if(($this->connection=($this->timeout ? @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port,$errno,$error,$this->timeout) : @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port))))
613 return("");
614 $error=($this->timeout ? strval($error) : "??");
615 switch($error)
616 {
617 case "-3":
618 return("-3 socket could not be created");
619 case "-4":
620 return("-4 dns lookup on hostname \"".$domain."\" failed");
621 case "-5":
622 return("-5 connection refused or timed out");
623 case "-6":
624 return("-6 fdopen() call failed");
625 case "-7":
626 return("-7 setvbuf() call failed");
627 }
628 return("could not connect to the host \"".$domain."\": ".$error);
629 }
630
631 Function SASLAuthenticate($mechanisms, $credentials, &$authenticated, &$mechanism)
632 {
633 $authenticated=0;
634 if(!function_exists("class_exists")
635 || !class_exists("sasl_client_class"))
636 {
637 $this->error="it is not possible to authenticate using the specified mechanism because the SASL library class is not loaded";
638 return(0);
639 }
640 $sasl=new sasl_client_class;
641 $sasl->SetCredential("user",$credentials["user"]);
642 $sasl->SetCredential("password",$credentials["password"]);
643 if(IsSet($credentials["realm"]))
644 $sasl->SetCredential("realm",$credentials["realm"]);
645 if(IsSet($credentials["workstation"]))
646 $sasl->SetCredential("workstation",$credentials["workstation"]);
647 if(IsSet($credentials["mode"]))
648 $sasl->SetCredential("mode",$credentials["mode"]);
649 do
650 {
651 $status=$sasl->Start($mechanisms,$message,$interactions);
652 }
653 while($status==SASL_INTERACT);
654 switch($status)
655 {
656 case SASL_CONTINUE:
657 break;
658 case SASL_NOMECH:
659 if(strlen($this->authentication_mechanism))
660 {
661 $this->error="authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error;
662 return(0);
663 }
664 break;
665 default:
666 $this->error="Could not start the SASL authentication client: ".$sasl->error;
667 return(0);
668 }
669 if(strlen($mechanism=$sasl->mechanism))
670 {
671 if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0)
672 {
673 $this->error="Could not send the AUTH command";
674 return(0);
675 }
676 if(!$this->VerifyResultLines(array("235","334"),$responses))
677 return(0);
678 switch($this->result_code)
679 {
680 case "235":
681 $response="";
682 $authenticated=1;
683 break;
684 case "334":
685 $response=base64_decode($responses[0]);
686 break;
687 default:
688 $this->error="Authentication error: ".$responses[0];
689 return(0);
690 }
691 for(;!$authenticated;)
692 {
693 do
694 {
695 $status=$sasl->Step($response,$message,$interactions);
696 }
697 while($status==SASL_INTERACT);
698 switch($status)
699 {
700 case SASL_CONTINUE:
701 if($this->PutLine(base64_encode($message))==0)
702 {
703 $this->error="Could not send the authentication step message";
704 return(0);
705 }
706 if(!$this->VerifyResultLines(array("235","334"),$responses))
707 return(0);
708 switch($this->result_code)
709 {
710 case "235":
711 $response="";
712 $authenticated=1;
713 break;
714 case "334":
715 $response=base64_decode($responses[0]);
716 break;
717 default:
718 $this->error="Authentication error: ".$responses[0];
719 return(0);
720 }
721 break;
722 default:
723 $this->error="Could not process the SASL authentication step: ".$sasl->error;
724 return(0);
725 }
726 }
727 }
728 return(1);
729 }
730
731 Function StartSMTP($localhost)
732 {
733 $success = 1;
734 $this->esmtp_extensions = array();
735 $fallback=1;
736 if($this->esmtp
737 || strlen($this->user))
738 {
739 if($this->PutLine('EHLO '.$localhost))
740 {
741 if(($success_code=$this->VerifyResultLines('250',$responses))>0)
742 {
743 $this->esmtp_host=$this->Tokenize($responses[0]," ");
744 for($response=1;$response<count($responses);$response++)
745 {
746 $extension=strtoupper($this->Tokenize($responses[$response]," "));
747 $this->esmtp_extensions[$extension]=$this->Tokenize("");
748 }
749 $success=1;
750 $fallback=0;
751 }
752 else
753 {
754 if($success_code==0)
755 {
756 $code=$this->Tokenize($this->error," -");
757 switch($code)
758 {
759 case "421":
760 $fallback=0;
761 break;
762 }
763 }
764 }
765 }
766 else
767 $fallback=0;
768 }
769 if($fallback)
770 {
771 if($this->PutLine("HELO $localhost")
772 && $this->VerifyResultLines("250",$responses)>0)
773 $success=1;
774 }
775 return($success);
776 }
777
778 /* Public methods */
779
780 /*
781 {metadocument}
782 <function>
783 <name>Connect</name>
784 <type>BOOLEAN</type>
785 <documentation>
786 <purpose>Connect to an SMTP server.</purpose>
787 <usage>Call this function as first step to send e-mail messages.</usage>
788 <returnvalue>The function returns
789 <tt><booleanvalue>1</booleanvalue></tt> if the connection is
790 successfully established.</returnvalue>
791 </documentation>
792 <argument>
793 <name>domain</name>
794 <type>STRING</type>
795 <defaultvalue></defaultvalue>
796 <documentation>
797 <purpose>Specify the domain of the recipient when using the direct
798 delivery mode.</purpose>
799 </documentation>
800 </argument>
801 <do>
802 {/metadocument}
803 */
804 Function Connect($domain="")
805 {
806 if(strcmp($this->state,"Disconnected"))
807 {
808 $this->error="connection is already established";
809 return(0);
810 }
811 $this->disconnected_error=0;
812 $this->error=$error="";
813 $this->esmtp_host="";
814 $this->esmtp_extensions=array();
815 $hosts=array();
816 if($this->direct_delivery)
817 {
818 if(strlen($domain)==0)
819 return(1);
820 $hosts=$weights=$mxhosts=array();
821 $getmxrr=$this->getmxrr;
822 if(function_exists($getmxrr)
823 && $getmxrr($domain,$hosts,$weights))
824 {
825 for($host=0;$host<count($hosts);$host++)
826 $mxhosts[$weights[$host]]=$hosts[$host];
827 KSort($mxhosts);
828 for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++)
829 $hosts[$host]=$mxhosts[Key($mxhosts)];
830 }
831 else
832 {
833 if(strcmp(@gethostbyname($domain),$domain)!=0)
834 $hosts[]=$domain;
835 }
836 }
837 else
838 {
839 if(strlen($this->host_name))
840 $hosts[]=$this->host_name;
841 if(strlen($this->pop3_auth_host))
842 {
843 $user=$this->user;
844 if(strlen($user)==0)
845 {
846 $this->error="it was not specified the POP3 authentication user";
847 return(0);
848 }
849 $password=$this->password;
850 if(strlen($password)==0)
851 {
852 $this->error="it was not specified the POP3 authentication password";
853 return(0);
854 }
855 $domain=$this->pop3_auth_host;
856 $this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, "Resolving POP3 authentication host \"".$domain."\"...");
857 if(strlen($this->error))
858 return(0);
859 if(strlen($response=$this->GetLine())==0)
860 return(0);
861 if(strcmp($this->Tokenize($response," "),"+OK"))
862 {
863 $this->error="POP3 authentication server greeting was not found";
864 return(0);
865 }
866 if(!$this->PutLine("USER ".$this->user)
867 || strlen($response=$this->GetLine())==0)
868 return(0);
869 if(strcmp($this->Tokenize($response," "),"+OK"))
870 {
871 $this->error="POP3 authentication user was not accepted: ".$this->Tokenize("\r\n");
872 return(0);
873 }
874 if(!$this->PutLine("PASS ".$password)
875 || strlen($response=$this->GetLine())==0)
876 return(0);
877 if(strcmp($this->Tokenize($response," "),"+OK"))
878 {
879 $this->error="POP3 authentication password was not accepted: ".$this->Tokenize("\r\n");
880 return(0);
881 }
882 fclose($this->connection);
883 $this->connection=0;
884 }
885 }
886 if(count($hosts)==0)
887 {
888 $this->error="could not determine the SMTP to connect";
889 return(0);
890 }
891 for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++)
892 {
893 $domain=$hosts[$host];
894 $error=$this->ConnectToHost($domain, $this->host_port, "Resolving SMTP server domain \"$domain\"...");
895 }
896 if(strlen($error))
897 {
898 $this->error=$error;
899 return(0);
900 }
901 $timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
902 if($timeout
903 && function_exists("socket_set_timeout"))
904 socket_set_timeout($this->connection,$timeout,0);
905 if($this->debug)
906 $this->OutputDebug("Connected to SMTP server \"".$domain."\".");
907 if(!strcmp($localhost=$this->localhost,"")
908 && !strcmp($localhost=getenv("SERVER_NAME"),"")
909 && !strcmp($localhost=getenv("HOST"),""))
910 $localhost="localhost";
911 $success=0;
912 if($this->VerifyResultLines("220",$responses)>0)
913 {
914 $success = $this->StartSMTP($localhost);
915 if($this->start_tls)
916 {
917 if(!IsSet($this->esmtp_extensions["STARTTLS"]))
918 {
919 $this->error="server does not support starting TLS";
920 $success=0;
921 }
922 elseif(!function_exists('stream_socket_enable_crypto'))
923 {
924 $this->error="this PHP installation or version does not support starting TLS";
925 $success=0;
926 }
927 elseif($success = ($this->PutLine('STARTTLS')
928 && $this->VerifyResultLines('220',$responses)>0))
929 {
930 $this->OutputDebug('Starting TLS cryptograpic protocol');
931 if(!($success = stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_TLS_CLIENT)))
932 $this->error = 'could not start TLS connection encryption protocol';
933 else
934 {
935 $this->OutputDebug('TLS started');
936 $success = $this->StartSMTP($localhost);
937 }
938 }
939 }
940 if($success
941 && strlen($this->user)
942 && strlen($this->pop3_auth_host)==0)
943 {
944 if(!IsSet($this->esmtp_extensions["AUTH"]))
945 {
946 $this->error="server does not require authentication";
947 if(IsSet($this->esmtp_extensions["STARTTLS"]))
948 $this->error .= ', it probably requires starting TLS';
949 $success=0;
950 }
951 else
952 {
953 if(strlen($this->authentication_mechanism))
954 $mechanisms=array($this->authentication_mechanism);
955 else
956 {
957 $mechanisms=array();
958 for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
959 $mechanisms[]=$authentication;
960 }
961 $credentials=array(
962 "user"=>$this->user,
963 "password"=>$this->password
964 );
965 if(strlen($this->realm))
966 $credentials["realm"]=$this->realm;
967 if(strlen($this->workstation))
968 $credentials["workstation"]=$this->workstation;
969 $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
970 if(!$success
971 && !strcmp($mechanism,"PLAIN"))
972 {
973 /*
974 * Author: Russell Robinson, 25 May 2003, http://www.tectite.com/
975 * Purpose: Try various AUTH PLAIN authentication methods.
976 */
977 $mechanisms=array("PLAIN");
978 $credentials=array(
979 "user"=>$this->user,
980 "password"=>$this->password
981 );
982 if(strlen($this->realm))
983 {
984 /*
985 * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method
986 * some sendmails won't accept the realm, so try again without it
987 */
988 $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
989 }
990 if(!$success)
991 {
992 /*
993 * It was seen an EXIM configuration like this:
994 * user^password^unused
995 */
996 $credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE;
997 $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
998 }
999 if(!$success)
1000 {
1001 /*
1002 * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html
1003 * specifies: ^user^password
1004 */
1005 $credentials["mode"]=SASL_PLAIN_EXIM_MODE;
1006 $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
1007 }
1008 }
1009 if($success
1010 && strlen($mechanism)==0)
1011 {
1012 $this->error="it is not supported any of the authentication mechanisms required by the server";
1013 $success=0;
1014 }
1015 }
1016 }
1017 }
1018 if($success)
1019 {
1020 $this->state="Connected";
1021 $this->connected_domain=$domain;
1022 }
1023 else
1024 {
1025 fclose($this->connection);
1026 $this->connection=0;
1027 }
1028 return($success);
1029 }
1030 /*
1031 {metadocument}
1032 </do>
1033 </function>
1034 {/metadocument}
1035 */
1036
1037 /*
1038 {metadocument}
1039 <function>
1040 <name>MailFrom</name>
1041 <type>BOOLEAN</type>
1042 <documentation>
1043 <purpose>Set the address of the message sender.</purpose>
1044 <usage>Call this function right after establishing a connection with
1045 the <functionlink>Connect</functionlink> function.</usage>
1046 <returnvalue>The function returns
1047 <tt><booleanvalue>1</booleanvalue></tt> if the sender address is
1048 successfully set.</returnvalue>
1049 </documentation>
1050 <argument>
1051 <name>sender</name>
1052 <type>STRING</type>
1053 <documentation>
1054 <purpose>E-mail address of the sender.</purpose>
1055 </documentation>
1056 </argument>
1057 <do>
1058 {/metadocument}
1059 */
1060 Function MailFrom($sender)
1061 {
1062 if($this->direct_delivery)
1063 {
1064 switch($this->state)
1065 {
1066 case "Disconnected":
1067 $this->direct_sender=$sender;
1068 return(1);
1069 case "Connected":
1070 $sender=$this->direct_sender;
1071 break;
1072 default:
1073 $this->error="direct delivery connection is already established and sender is already set";
1074 return(0);
1075 }
1076 }
1077 else
1078 {
1079 if(strcmp($this->state,"Connected"))
1080 {
1081 $this->error="connection is not in the initial state";
1082 return(0);
1083 }
1084 }
1085 $this->error="";
1086 if(!$this->PutLine("MAIL FROM:<$sender>"))
1087 return(0);
1088 if(!IsSet($this->esmtp_extensions["PIPELINING"])
1089 && $this->VerifyResultLines("250",$responses)<=0)
1090 return(0);
1091 $this->state="SenderSet";
1092 if(IsSet($this->esmtp_extensions["PIPELINING"]))
1093 $this->pending_sender=1;
1094 $this->pending_recipients=0;
1095 return(1);
1096 }
1097 /*
1098 {metadocument}
1099 </do>
1100 </function>
1101 {/metadocument}
1102 */
1103
1104 /*
1105 {metadocument}
1106 <function>
1107 <name>SetRecipient</name>
1108 <type>BOOLEAN</type>
1109 <documentation>
1110 <purpose>Set the address of a message recipient.</purpose>
1111 <usage>Call this function repeatedly for each recipient right after
1112 setting the message sender with the
1113 <functionlink>MailFrom</functionlink> function.</usage>
1114 <returnvalue>The function returns
1115 <tt><booleanvalue>1</booleanvalue></tt> if the recipient address is
1116 successfully set.</returnvalue>
1117 </documentation>
1118 <argument>
1119 <name>recipient</name>
1120 <type>STRING</type>
1121 <documentation>
1122 <purpose>E-mail address of a recipient.</purpose>
1123 </documentation>
1124 </argument>
1125 <do>
1126 {/metadocument}
1127 */
1128 Function SetRecipient($recipient)
1129 {
1130 if($this->direct_delivery)
1131 {
1132 if(GetType($at=strrpos($recipient,"@"))!="integer")
1133 return("it was not specified a valid direct recipient");
1134 $domain=substr($recipient,$at+1);
1135 switch($this->state)
1136 {
1137 case "Disconnected":
1138 if(!$this->Connect($domain))
1139 return(0);
1140 if(!$this->MailFrom(""))
1141 {
1142 $error=$this->error;
1143 $this->Disconnect();
1144 $this->error=$error;
1145 return(0);
1146 }
1147 break;
1148 case "SenderSet":
1149 case "RecipientSet":
1150 if(strcmp($this->connected_domain,$domain))
1151 {
1152 $this->error="it is not possible to deliver directly to recipients of different domains";
1153 return(0);
1154 }
1155 break;
1156 default:
1157 $this->error="connection is already established and the recipient is already set";
1158 return(0);
1159 }
1160 }
1161 else
1162 {
1163 switch($this->state)
1164 {
1165 case "SenderSet":
1166 case "RecipientSet":
1167 break;
1168 default:
1169 $this->error="connection is not in the recipient setting state";
1170 return(0);
1171 }
1172 }
1173 $this->error="";
1174 if(!$this->PutLine("RCPT TO:<$recipient>"))
1175 return(0);
1176 if(IsSet($this->esmtp_extensions["PIPELINING"]))
1177 {
1178 $this->pending_recipients++;
1179 if($this->pending_recipients>=$this->maximum_piped_recipients)
1180 {
1181 if(!$this->FlushRecipients())
1182 return(0);
1183 }
1184 }
1185 else
1186 {
1187 if($this->VerifyResultLines(array("250","251"),$responses)<=0)
1188 return(0);
1189 }
1190 $this->state="RecipientSet";
1191 return(1);
1192 }
1193 /*
1194 {metadocument}
1195 </do>
1196 </function>
1197 {/metadocument}
1198 */
1199
1200 /*
1201 {metadocument}
1202 <function>
1203 <name>StartData</name>
1204 <type>BOOLEAN</type>
1205 <documentation>
1206 <purpose>Tell the SMTP server that the message data will start being
1207 sent.</purpose>
1208 <usage>Call this function right after you are done setting all the
1209 message recipients with the
1210 <functionlink>SetRecipient</functionlink> function.</usage>
1211 <returnvalue>The function returns
1212 <tt><booleanvalue>1</booleanvalue></tt> if the server is ready to
1213 start receiving the message data.</returnvalue>
1214 </documentation>
1215 <do>
1216 {/metadocument}
1217 */
1218 Function StartData()
1219 {
1220 if(strcmp($this->state,"RecipientSet"))
1221 {
1222 $this->error="connection is not in the start sending data state";
1223 return(0);
1224 }
1225 $this->error="";
1226 if(!$this->PutLine("DATA"))
1227 return(0);
1228 if($this->pending_recipients)
1229 {
1230 if(!$this->FlushRecipients())
1231 return(0);
1232 }
1233 if($this->VerifyResultLines("354",$responses)<=0)
1234 return(0);
1235 $this->state="SendingData";
1236 return(1);
1237 }
1238 /*
1239 {metadocument}
1240 </do>
1241 </function>
1242 {/metadocument}
1243 */
1244
1245 /*
1246 {metadocument}
1247 <function>
1248 <name>PrepareData</name>
1249 <type>STRING</type>
1250 <documentation>
1251 <purpose>Prepare message data to normalize line breaks and escaping
1252 lines that contain single dots.</purpose>
1253 <usage>Call this function if the message data you want to send may
1254 contain line breaks that are not the
1255 <stringvalue>&#13;&#10;</stringvalue> sequence or it may contain
1256 lines that just have a single dot.</usage>
1257 <returnvalue>Resulting normalized messages data.</returnvalue>
1258 </documentation>
1259 <argument>
1260 <name>data</name>
1261 <type>STRING</type>
1262 <documentation>
1263 <purpose>Message data to be prepared.</purpose>
1264 </documentation>
1265 </argument>
1266 <do>
1267 {/metadocument}
1268 */
1269 Function PrepareData($data)
1270 {
1271 if(function_exists("preg_replace"))
1272 return(preg_replace(array("/\n\n|\r\r/","/(^|[^\r])\n/","/\r([^\n]|\$)/D","/(^|\n)\\./"),array("\r\n\r\n","\\1\r\n","\r\n\\1","\\1.."),$data));
1273 else
1274 return(ereg_replace("(^|\n)\\.","\\1..",ereg_replace("\r([^\n]|\$)","\r\n\\1",ereg_replace("(^|[^\r])\n","\\1\r\n",ereg_replace("\n\n|\r\r","\r\n\r\n",$data)))));
1275 }
1276 /*
1277 {metadocument}
1278 </do>
1279 </function>
1280 {/metadocument}
1281 */
1282
1283 /*
1284 {metadocument}
1285 <function>
1286 <name>SendData</name>
1287 <type>BOOLEAN</type>
1288 <documentation>
1289 <purpose>Send message data.</purpose>
1290 <usage>Call this function repeatedly for all message data blocks
1291 to be sent right after start sending message data with the
1292 <functionlink>StartData</functionlink> function.</usage>
1293 <returnvalue>The function returns
1294 <tt><booleanvalue>1</booleanvalue></tt> if the message data was
1295 sent to the SMTP server successfully.</returnvalue>
1296 </documentation>
1297 <argument>
1298 <name>data</name>
1299 <type>STRING</type>
1300 <documentation>
1301 <purpose>Message data to be sent.</purpose>
1302 </documentation>
1303 </argument>
1304 <do>
1305 {/metadocument}
1306 */
1307 Function SendData($data)
1308 {
1309 if(strcmp($this->state,"SendingData"))
1310 {
1311 $this->error="connection is not in the sending data state";
1312 return(0);
1313 }
1314 $this->error="";
1315 return($this->PutData($data));
1316 }
1317 /*
1318 {metadocument}
1319 </do>
1320 </function>
1321 {/metadocument}
1322 */
1323
1324 /*
1325 {metadocument}
1326 <function>
1327 <name>EndSendingData</name>
1328 <type>BOOLEAN</type>
1329 <documentation>
1330 <purpose>Tell the server that all the message data was sent.</purpose>
1331 <usage>Call this function when you are done with sending the message
1332 data with the <functionlink>SendData</functionlink> function.</usage>
1333 <returnvalue>The function returns
1334 <tt><booleanvalue>1</booleanvalue></tt> if the server accepted the
1335 message.</returnvalue>
1336 </documentation>
1337 <do>
1338 {/metadocument}
1339 */
1340 Function EndSendingData()
1341 {
1342 if(strcmp($this->state,"SendingData"))
1343 {
1344 $this->error="connection is not in the sending data state";
1345 return(0);
1346 }
1347 $this->error="";
1348 if(!$this->PutLine("\r\n.")
1349 || $this->VerifyResultLines("250",$responses)<=0)
1350 return(0);
1351 $this->state="Connected";
1352 return(1);
1353 }
1354 /*
1355 {metadocument}
1356 </do>
1357 </function>
1358 {/metadocument}
1359 */
1360
1361 /*
1362 {metadocument}
1363 <function>
1364 <name>ResetConnection</name>
1365 <type>BOOLEAN</type>
1366 <documentation>
1367 <purpose>Reset an already established SMTP connection to the initial
1368 state.</purpose>
1369 <usage>Call this function when there was an error sending a message
1370 and you need to skip to sending another message without
1371 disconnecting.</usage>
1372 <returnvalue>The function returns
1373 <tt><booleanvalue>1</booleanvalue></tt> if the connection was
1374 resetted successfully.</returnvalue>
1375 </documentation>
1376 <do>
1377 {/metadocument}
1378 */
1379 Function ResetConnection()
1380 {
1381 switch($this->state)
1382 {
1383 case "Connected":
1384 return(1);
1385 case "SendingData":
1386 $this->error="can not reset the connection while sending data";
1387 return(0);
1388 case "Disconnected":
1389 $this->error="can not reset the connection before it is established";
1390 return(0);
1391 }
1392 $this->error="";
1393 if(!$this->PutLine("RSET")
1394 || $this->VerifyResultLines("250",$responses)<=0)
1395 return(0);
1396 $this->state="Connected";
1397 return(1);
1398 }
1399 /*
1400 {metadocument}
1401 </do>
1402 </function>
1403 {/metadocument}
1404 */
1405
1406 /*
1407 {metadocument}
1408 <function>
1409 <name>Disconnect</name>
1410 <type>BOOLEAN</type>
1411 <documentation>
1412 <purpose>Terminate a previously opened connection.</purpose>
1413 <usage>Call this function after you are done sending your
1414 messages.</usage>
1415 <returnvalue>The function returns
1416 <tt><booleanvalue>1</booleanvalue></tt> if the connection was
1417 successfully closed.</returnvalue>
1418 </documentation>
1419 <argument>
1420 <name>quit</name>
1421 <type>BOOLEAN</type>
1422 <defaultvalue>1</defaultvalue>
1423 <documentation>
1424 <purpose>Boolean option that tells whether the class should
1425 perform the final connection quit handshake, or just close the
1426 connection without waiting.</purpose>
1427 </documentation>
1428 </argument>
1429 <do>
1430 {/metadocument}
1431 */
1432 Function Disconnect($quit=1)
1433 {
1434 if(!strcmp($this->state,"Disconnected"))
1435 {
1436 $this->error="it was not previously established a SMTP connection";
1437 return(0);
1438 }
1439 $this->error="";
1440 if(!strcmp($this->state,"Connected")
1441 && $quit
1442 && (!$this->PutLine("QUIT")
1443 || ($this->VerifyResultLines("221",$responses)<=0
1444 && !$this->disconnected_error)))
1445 return(0);
1446 if($this->disconnected_error)
1447 $this->disconnected_error=0;
1448 else
1449 fclose($this->connection);
1450 $this->connection=0;
1451 $this->state="Disconnected";
1452 if($this->debug)
1453 $this->OutputDebug("Disconnected.");
1454 return(1);
1455 }
1456 /*
1457 {metadocument}
1458 </do>
1459 </function>
1460 {/metadocument}
1461 */
1462
1463 /*
1464 {metadocument}
1465 <function>
1466 <name>SendMessage</name>
1467 <type>BOOLEAN</type>
1468 <documentation>
1469 <purpose>Send a message in a single call.</purpose>
1470 <usage>Call this function if you want to send a single messages to a
1471 small number of recipients in a single call.</usage>
1472 <returnvalue>The function returns
1473 <tt><booleanvalue>1</booleanvalue></tt> if the message was sent
1474 successfully.</returnvalue>
1475 </documentation>
1476 <argument>
1477 <name>sender</name>
1478 <type>STRING</type>
1479 <documentation>
1480 <purpose>E-mail address of the sender.</purpose>
1481 </documentation>
1482 </argument>
1483 <argument>
1484 <name>recipients</name>
1485 <type>STRING</type>
1486 <documentation>
1487 <purpose>Array with a list of the e-mail addresses of the
1488 recipients of the message.</purpose>
1489 </documentation>
1490 </argument>
1491 <argument>
1492 <name>headers</name>
1493 <type>ARRAY</type>
1494 <documentation>
1495 <purpose>Array with a list of the header lines of the message.</purpose>
1496 </documentation>
1497 </argument>
1498 <argument>
1499 <name>body</name>
1500 <type>STRING</type>
1501 <documentation>
1502 <purpose>Body data of the message.</purpose>
1503 </documentation>
1504 </argument>
1505 <do>
1506 {/metadocument}
1507 */
1508 Function SendMessage($sender,$recipients,$headers,$body)
1509 {
1510 if(($success=$this->Connect()))
1511 {
1512 if(($success=$this->MailFrom($sender)))
1513 {
1514 for($recipient=0;$recipient<count($recipients);$recipient++)
1515 {
1516 if(!($success=$this->SetRecipient($recipients[$recipient])))
1517 break;
1518 }
1519 if($success
1520 && ($success=$this->StartData()))
1521 {
1522 for($header_data="",$header=0;$header<count($headers);$header++)
1523 $header_data.=$headers[$header]."\r\n";
1524 $success=($this->SendData($header_data."\r\n")
1525 && $this->SendData($this->PrepareData($body))
1526 && $this->EndSendingData());
1527 }
1528 }
1529 $error=$this->error;
1530 $disconnect_success=$this->Disconnect($success);
1531 if($success)
1532 $success=$disconnect_success;
1533 else
1534 $this->error=$error;
1535 }
1536 return($success);
1537 }
1538 /*
1539 {metadocument}
1540 </do>
1541 </function>
1542 {/metadocument}
1543 */
1544
1545 };
1546
1547 /*
1548
1549 {metadocument}
1550 </class>
1551 {/metadocument}
1552
1553 */
1554
1555 ?>