Project

General

Profile

Download (49.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * smtp.php
4
 *
5
 * @(#) $Header: /opt2/ena/metal/smtp/smtp.php,v 1.50 2016/01/19 00:16:06 mlemos Exp $
6
 *
7
 */
8

    
9
/*
10
{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?>
11
<class>
12

    
13
	<package>net.manuellemos.smtp</package>
14

    
15
	<version>@(#) $Id: smtp.php,v 1.50 2016/01/19 00:16:06 mlemos Exp $</version>
16
	<copyright>Copyright (C) Manuel Lemos 1999-2011</copyright>
17
	<title>Sending e-mail messages via SMTP protocol</title>
18
	<author>Manuel Lemos</author>
19
	<authoraddress>mlemos-at-acm.org</authoraddress>
20

    
21
	<documentation>
22
		<idiom>en</idiom>
23
		<purpose>Sending e-mail messages via SMTP protocol</purpose>
24
		<translation>If you are interested in translating the documentation of
25
			this class to your own idiom, please <link>
26
				<data>contact the author</data>
27
				<url>mailto:<getclassproperty>authoraddress</getclassproperty></url>
28
			</link>.</translation>
29
		<support>Technical support for using this class may be obtained in the
30
			<tt>smtpclass</tt> support forum. Just go to the support forum pages
31
			page to browse the forum archives and post support request
32
			messages:<paragraphbreak />
33
			<link>
34
				<data>http://www.phpclasses.org/discuss/package/14/</data>
35
				<url>http://www.phpclasses.org/discuss/package/14/</url>
36
			</link></support>
37
		<usage>To use this class just create a new object, set any variables
38
			to configure its options and call the
39
			<functionlink>SendMessage</functionlink> function to send a
40
			message.<paragraphbreak />It is not recommended that you use this
41
			class alone unless you have deep understanding of Internet mail
42
			standards on how to compose compliant e-mail messages. Instead, use
43
			the <link>
44
				<data>MIME message composing and sending class</data>
45
				<url>http://www.phpclasses.org/mimemessage</url>
46
			</link> and its sub-class SMTP message together with this SMTP class
47
			to properly compose e-mail messages, so your messages are not
48
			discarded for not being correctly composed.</usage>
49
	</documentation>
50

    
51
{/metadocument}
52
*/
53

    
54
class smtp_class
55
{
56
/*
57
{metadocument}
58
	<variable>
59
		<name>user</name>
60
		<type>STRING</type>
61
		<value></value>
62
		<documentation>
63
			<purpose>Define the authorized user when sending messages to a SMTP
64
				server.</purpose>
65
			<usage>Set this variable to the user name when the SMTP server
66
				requires authentication.</usage>
67
		</documentation>
68
	</variable>
69
{/metadocument}
70
*/
71
	var $user="";
72

    
73
/*
74
{metadocument}
75
	<variable>
76
		<name>realm</name>
77
		<type>STRING</type>
78
		<value></value>
79
		<documentation>
80
			<purpose>Define the authentication realm when sending messages to a
81
				SMTP server.</purpose>
82
			<usage>Set this variable when the SMTP server requires
83
				authentication and if more than one authentication realm is
84
				supported.</usage>
85
		</documentation>
86
	</variable>
87
{/metadocument}
88
*/
89
	var $realm="";
90

    
91
/*
92
{metadocument}
93
	<variable>
94
		<name>password</name>
95
		<type>STRING</type>
96
		<value></value>
97
		<documentation>
98
			<purpose>Define the authorized user password when sending messages
99
				to a SMTP server.</purpose>
100
			<usage>Set this variable to the user password when the SMTP server
101
				requires authentication.</usage>
102
		</documentation>
103
	</variable>
104
{/metadocument}
105
*/
106
	var $password="";
107

    
108
/*
109
{metadocument}
110
	<variable>
111
		<name>workstation</name>
112
		<type>STRING</type>
113
		<value></value>
114
		<documentation>
115
			<purpose>Define the client workstation name when sending messages
116
				to a SMTP server.</purpose>
117
			<usage>Set this variable to the client workstation when the SMTP
118
				server requires authentication identifiying the origin workstation
119
				name.</usage>
120
		</documentation>
121
	</variable>
122
{/metadocument}
123
*/
124
	var $workstation="";
125
	
126
/*
127
{metadocument}
128
	<variable>
129
		<name>authentication_mechanism</name>
130
		<type>STRING</type>
131
		<value></value>
132
		<documentation>
133
			<purpose>Force the use of a specific authentication mechanism.</purpose>
134
			<usage>Set it to an empty string to let the class determine the
135
				authentication mechanism to use automatically based on the
136
				supported mechanisms by the server and by the SASL client library
137
				classes.<paragraphbreak />
138
				Set this variable to a specific mechanism name if you want to
139
				override the automatic authentication mechanism selection.</usage>
140
		</documentation>
141
	</variable>
142
{/metadocument}
143
*/
144
	var $authentication_mechanism="";
145

    
146
/*
147
{metadocument}
148
	<variable>
149
		<name>host_name</name>
150
		<type>STRING</type>
151
		<value></value>
152
		<documentation>
153
			<purpose>Define the SMTP server host name.</purpose>
154
			<usage>Set to the host name of the SMTP server to which you want to
155
				relay the messages.</usage>
156
		</documentation>
157
	</variable>
158
{/metadocument}
159
*/
160
	var $host_name="";
161

    
162
/*
163
{metadocument}
164
	<variable>
165
		<name>host_port</name>
166
		<type>INTEGER</type>
167
		<value>25</value>
168
		<documentation>
169
			<purpose>Define the SMTP server host port.</purpose>
170
			<usage>Set to the TCP port of the SMTP server host to connect.</usage>
171
		</documentation>
172
	</variable>
173
{/metadocument}
174
*/
175
	var $host_port=25;
176

    
177
/*
178
{metadocument}
179
	<variable>
180
		<name>socks_host_name</name>
181
		<type>STRING</type>
182
		<value></value>
183
		<documentation>
184
			<purpose>Define the SOCKS server host name.</purpose>
185
			<usage>Set to the SOCKS server host name through which the SMTP
186
				connection should be routed. Leave it empty if you do not want the
187
				connections to be established through a SOCKS server.</usage>
188
		</documentation>
189
	</variable>
190
{/metadocument}
191
*/
192
	var $socks_host_name = '';
193

    
194
/*
195
{metadocument}
196
	<variable>
197
		<name>socks_host_port</name>
198
		<type>INTEGER</type>
199
		<value>1080</value>
200
		<documentation>
201
			<purpose>Define the SOCKS server host port.</purpose>
202
			<usage>Set to the port of the SOCKS server host through which the
203
				the SMTP connection should be routed.</usage>
204
		</documentation>
205
	</variable>
206
{/metadocument}
207
*/
208
	var $socks_host_port=1080;
209

    
210
/*
211
{metadocument}
212
	<variable>
213
		<name>socks_version</name>
214
		<type>STRING</type>
215
		<value>5</value>
216
		<documentation>
217
			<purpose>Set the SOCKS protocol version.</purpose>
218
			<usage>Change this value if SOCKS server you want to use is
219
				listening to a different port.</usage>
220
		</documentation>
221
	</variable>
222
{/metadocument}
223
*/
224
	var $socks_version='5';
225

    
226
/*
227
{metadocument}
228
	<variable>
229
		<name>http_proxy_host_name</name>
230
		<type>STRING</type>
231
		<value></value>
232
		<documentation>
233
			<purpose>Define the HTTP proxy server host name.</purpose>
234
			<usage>Set to the HTTP proxy server host name through which the
235
				SMTP connection should be routed. Leave it empty if you do not
236
				want the connections to be established through an HTTP proxy.</usage>
237
		</documentation>
238
	</variable>
239
{/metadocument}
240
*/
241
	var $http_proxy_host_name = '';
242

    
243
/*
244
{metadocument}
245
	<variable>
246
		<name>http_proxy_host_port</name>
247
		<type>INTEGER</type>
248
		<value>80</value>
249
		<documentation>
250
			<purpose>Define the HTTP proxy server host port.</purpose>
251
			<usage>Set to the port of the HTTP proxy server host through which
252
				the SMTP connection should be routed.</usage>
253
		</documentation>
254
	</variable>
255
{/metadocument}
256
*/
257
	var $http_proxy_host_port=80;
258

    
259
/*
260
{metadocument}
261
	<variable>
262
		<name>user_agent</name>
263
		<type>STRING</type>
264
		<value>SMTP Class (http://www.phpclasses.org/smtpclass $Revision: 1.50 $)</value>
265
		<documentation>
266
			<purpose>Set the user agent used when connecting via an HTTP proxy.</purpose>
267
			<usage>Change this value only if for some reason you want emulate a
268
				certain e-mail client.</usage>
269
		</documentation>
270
	</variable>
271
{/metadocument}
272
*/
273
	var $user_agent='SMTP Class (http://www.phpclasses.org/smtpclass $Revision: 1.50 $)';
274

    
275
/*
276
{metadocument}
277
	<variable>
278
		<name>ssl</name>
279
		<type>BOOLEAN</type>
280
		<value>0</value>
281
		<documentation>
282
			<purpose>Define whether the connection to the SMTP server should be
283
				established securely using SSL protocol.</purpose>
284
			<usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server
285
				requires secure connections using SSL protocol.</usage>
286
		</documentation>
287
	</variable>
288
{/metadocument}
289
*/
290
	var $ssl=0;
291

    
292
/*
293
{metadocument}
294
	<variable>
295
		<name>start_tls</name>
296
		<type>BOOLEAN</type>
297
		<value>0</value>
298
		<documentation>
299
			<purpose>Define whether the connection to the SMTP server should use
300
				encryption after the connection is established using TLS
301
				protocol.</purpose>
302
			<usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server
303
				requires that authentication be done securely starting the TLS
304
				protocol after the connection is established.</usage>
305
		</documentation>
306
	</variable>
307
{/metadocument}
308
*/
309
	var $start_tls = 0;
310

    
311
/*
312
{metadocument}
313
	<variable>
314
		<name>localhost</name>
315
		<type>STRING</type>
316
		<value></value>
317
		<documentation>
318
			<purpose>Name of the local host computer</purpose>
319
			<usage>Set to the name of the computer connecting to the SMTP
320
				server from the local network.</usage>
321
		</documentation>
322
	</variable>
323
{/metadocument}
324
*/
325
	var $localhost="";
326

    
327
/*
328
{metadocument}
329
	<variable>
330
		<name>timeout</name>
331
		<type>INTEGER</type>
332
		<value>0</value>
333
		<documentation>
334
			<purpose>Specify the connection timeout period in seconds.</purpose>
335
			<usage>Leave it set to <integervalue>0</integervalue> if you want
336
				the connection attempts to wait forever. Change this value if for
337
				some reason the timeout period seems insufficient or otherwise it
338
				seems too long.</usage>
339
		</documentation>
340
	</variable>
341
{/metadocument}
342
*/
343
	var $timeout=0;
344

    
345
/*
346
{metadocument}
347
	<variable>
348
		<name>data_timeout</name>
349
		<type>INTEGER</type>
350
		<value>0</value>
351
		<documentation>
352
			<purpose>Specify the timeout period in seconds to wait for data from
353
				the server.</purpose>
354
			<usage>Leave it set to <integervalue>0</integervalue> if you want
355
				to use the same value defined in the
356
				<variablelink>timeout</variablelink> variable. Change this value
357
				if for some reason the default data timeout period seems
358
				insufficient or otherwise it seems too long.</usage>
359
		</documentation>
360
	</variable>
361
{/metadocument}
362
*/
363
	var $data_timeout=0;
364

    
365
/*
366
{metadocument}
367
	<variable>
368
		<name>direct_delivery</name>
369
		<type>BOOLEAN</type>
370
		<value>0</value>
371
		<documentation>
372
			<purpose>Boolean flag that indicates whether the message should be
373
				sent in direct delivery mode, i.e. the message is sent to the SMTP
374
				server associated to the domain of the recipient instead of
375
				relaying to the server specified by the
376
				<variablelink>host_name</variablelink> variable.</purpose>
377
			<usage>Set this to <tt><booleanvalue>1</booleanvalue></tt> if you
378
				want to send urgent messages directly to the recipient domain SMTP
379
				server.</usage>
380
		</documentation>
381
	</variable>
382
{/metadocument}
383
*/
384
	var $direct_delivery=0;
385

    
386
/*
387
{metadocument}
388
	<variable>
389
		<name>error</name>
390
		<type>STRING</type>
391
		<value></value>
392
		<documentation>
393
			<purpose>Message that describes the error when a call to a class
394
				function fails.</purpose>
395
			<usage>Check this variable when an error occurs to understand what
396
				happened.</usage>
397
		</documentation>
398
	</variable>
399
{/metadocument}
400
*/
401
	var $error="";
402

    
403
/*
404
{metadocument}
405
	<variable>
406
		<name>debug</name>
407
		<type>BOOLEAN</type>
408
		<value>0</value>
409
		<documentation>
410
			<purpose>Specify whether it is necessary to output SMTP connection
411
				debug information.</purpose>
412
			<usage>Set this variable to
413
				<tt><booleanvalue>1</booleanvalue></tt> if you need to see
414
				the progress of the SMTP connection and protocol dialog when you
415
				need to understand the reason for delivery problems.</usage>
416
		</documentation>
417
	</variable>
418
{/metadocument}
419
*/
420
	var $debug=0;
421

    
422
/*
423
{metadocument}
424
	<variable>
425
		<name>html_debug</name>
426
		<type>BOOLEAN</type>
427
		<value>0</value>
428
		<documentation>
429
			<purpose>Specify whether the debug information should be outputted in
430
				HTML format.</purpose>
431
			<usage>Set this variable to
432
				<tt><booleanvalue>1</booleanvalue></tt> if you need to see
433
				the debug output in a Web page.</usage>
434
		</documentation>
435
	</variable>
436
{/metadocument}
437
*/
438
	var $html_debug=0;
439

    
440
/*
441
{metadocument}
442
	<variable>
443
		<name>esmtp</name>
444
		<type>BOOLEAN</type>
445
		<value>1</value>
446
		<documentation>
447
			<purpose>Specify whether the class should attempt to use ESMTP
448
				extensions supported by the server.</purpose>
449
			<usage>Set this variable to
450
				<tt><booleanvalue>0</booleanvalue></tt> if for some reason you
451
				want to avoid benefitting from ESMTP extensions.</usage>
452
		</documentation>
453
	</variable>
454
{/metadocument}
455
*/
456
	var $esmtp=1;
457

    
458
/*
459
{metadocument}
460
	<variable>
461
		<name>esmtp_extensions</name>
462
		<type>HASH</type>
463
		<value></value>
464
		<documentation>
465
			<purpose>Associative array with the list of ESMTP extensions
466
				supported by the SMTP server.</purpose>
467
			<usage>Check this variable after connecting to the SMTP server to
468
				determine which ESMTP extensions are supported.</usage>
469
		</documentation>
470
	</variable>
471
{/metadocument}
472
*/
473
	var $esmtp_extensions=array();
474

    
475
/*
476
{metadocument}
477
	<variable>
478
		<name>exclude_address</name>
479
		<type>STRING</type>
480
		<value></value>
481
		<documentation>
482
			<purpose>Specify an address that should be considered invalid
483
				when resolving host name addresses.</purpose>
484
			<usage>In some networks any domain name that does not exist is
485
				resolved as a sub-domain of the default local domain. If the DNS is
486
				configured in such way that it always resolves any sub-domain of
487
				the default local domain to a given address, it is hard to
488
				determine whether a given domain does not exist.<paragraphbreak />
489
				If your network is configured this way, you may set this variable
490
				to the address that all sub-domains of the default local domain
491
				resolves, so the class can assume that such address is invalid.</usage>
492
		</documentation>
493
	</variable>
494
{/metadocument}
495
*/
496
	var $exclude_address="";
497

    
498
/*
499
{metadocument}
500
	<variable>
501
		<name>getmxrr</name>
502
		<type>STRING</type>
503
		<value>getmxrr</value>
504
		<documentation>
505
			<purpose>Specify the name of the function that is called to determine
506
				the SMTP server address of a given domain.</purpose>
507
			<usage>Change this to a working replacement of the PHP
508
				<tt>getmxrr()</tt> function if this is not working in your system
509
					and you want to send messages in direct delivery mode.</usage>
510
		</documentation>
511
	</variable>
512
{/metadocument}
513
*/
514
	var $getmxrr="GetMXRR";
515

    
516
/*
517
{metadocument}
518
	<variable>
519
		<name>pop3_auth_host</name>
520
		<type>STRING</type>
521
		<value></value>
522
		<documentation>
523
			<purpose>Specify the server address for POP3 based authentication.</purpose>
524
			<usage>Set this variable to the address of the POP3 server if the
525
				SMTP server requires POP3 based authentication.</usage>
526
		</documentation>
527
	</variable>
528
{/metadocument}
529
*/
530
	var $pop3_auth_host="";
531

    
532
/*
533
{metadocument}
534
	<variable>
535
		<name>pop3_auth_port</name>
536
		<type>INTEGER</type>
537
		<value>110</value>
538
		<documentation>
539
			<purpose>Specify the server port for POP3 based authentication.</purpose>
540
			<usage>Set this variable to the port of the POP3 server if the
541
				SMTP server requires POP3 based authentication.</usage>
542
		</documentation>
543
	</variable>
544
{/metadocument}
545
*/
546
	var $pop3_auth_port=110;
547

    
548
	/* private variables - DO NOT ACCESS */
549

    
550
	var $state="Disconnected";
551
	var $connection=0;
552
	var $pending_recipients=0;
553
	var $next_token="";
554
	var $direct_sender="";
555
	var $connected_domain="";
556
	var $result_code;
557
	var $disconnected_error=0;
558
	var $esmtp_host="";
559
	var $maximum_piped_recipients=100;
560

    
561
	/* Private methods - DO NOT CALL */
562

    
563
	Function Tokenize($string,$separator="")
564
	{
565
		if(!strcmp($separator,""))
566
		{
567
			$separator=$string;
568
			$string=$this->next_token;
569
		}
570
		for($character=0;$character<strlen($separator);$character++)
571
		{
572
			if(GetType($position=strpos($string,$separator[$character]))=="integer")
573
				$found=(IsSet($found) ? min($found,$position) : $position);
574
		}
575
		if(IsSet($found))
576
		{
577
			$this->next_token=substr($string,$found+1);
578
			return(substr($string,0,$found));
579
		}
580
		else
581
		{
582
			$this->next_token="";
583
			return($string);
584
		}
585
	}
586

    
587
	Function OutputDebug($message)
588
	{
589
		$message.="\n";
590
		if($this->html_debug)
591
			$message=str_replace("\n","<br />\n",HtmlEntities($message));
592
		echo $message;
593
		flush();
594
	}
595

    
596
	Function SetDataAccessError($error)
597
	{
598
		$this->error=$error;
599
		if(function_exists("socket_get_status"))
600
		{
601
			$status=socket_get_status($this->connection);
602
			if($status["timed_out"])
603
				$this->error.=": data access time out";
604
			elseif($status["eof"])
605
			{
606
				$this->error.=": the server disconnected";
607
				$this->disconnected_error=1;
608
			}
609
		}
610
		return($this->error);
611
	}
612

    
613
	Function SetError($error)
614
	{
615
		return($this->error=$error);
616
	}
617

    
618
	Function GetLine()
619
	{
620
		for($line="";;)
621
		{
622
			if(feof($this->connection))
623
			{
624
				$this->error="reached the end of data while reading from the SMTP server conection";
625
				return("");
626
			}
627
			if(GetType($data=@fgets($this->connection,100))!="string"
628
			|| strlen($data)==0)
629
			{
630
				$this->SetDataAccessError("it was not possible to read line from the SMTP server");
631
				return("");
632
			}
633
			$line.=$data;
634
			$length=strlen($line);
635
			if($length>=2
636
			&& substr($line,$length-2,2)=="\r\n")
637
			{
638
				$line=substr($line,0,$length-2);
639
				if($this->debug)
640
					$this->OutputDebug("S $line");
641
				return($line);
642
			}
643
		}
644
	}
645

    
646
	Function PutLine($line)
647
	{
648
		if($this->debug)
649
			$this->OutputDebug("C $line");
650
		if(!@fputs($this->connection,"$line\r\n"))
651
		{
652
			$this->SetDataAccessError("it was not possible to send a line to the SMTP server");
653
			return(0);
654
		}
655
		return(1);
656
	}
657

    
658
	Function PutData(&$data)
659
	{
660
		if(strlen($data))
661
		{
662
			if($this->debug)
663
				$this->OutputDebug("C $data");
664
			if(!@fputs($this->connection,$data))
665
			{
666
				$this->SetDataAccessError("it was not possible to send data to the SMTP server");
667
				return(0);
668
			}
669
		}
670
		return(1);
671
	}
672

    
673
	Function VerifyResultLines($code,&$responses)
674
	{
675
		$responses=array();
676
		Unset($this->result_code);
677
		while(strlen($line=$this->GetLine($this->connection)))
678
		{
679
			if(IsSet($this->result_code))
680
			{
681
				if(strcmp($this->Tokenize($line," -"),$this->result_code))
682
				{
683
					$this->error=$line;
684
					return(0);
685
				}
686
			}
687
			else
688
			{
689
				$this->result_code=$this->Tokenize($line," -");
690
				if(GetType($code)=="array")
691
				{
692
					for($codes=0;$codes<count($code) && strcmp($this->result_code,$code[$codes]);$codes++);
693
					if($codes>=count($code))
694
					{
695
						$this->error=$line;
696
						return(0);
697
					}
698
				}
699
				else
700
				{
701
					if(strcmp($this->result_code,$code))
702
					{
703
						$this->error=$line;
704
						return(0);
705
					}
706
				}
707
			}
708
			$responses[]=$this->Tokenize("");
709
			if(!strcmp($this->result_code,$this->Tokenize($line," ")))
710
				return(1);
711
		}
712
		return(-1);
713
	}
714

    
715
	Function FlushRecipients()
716
	{
717
		if($this->pending_sender)
718
		{
719
			if($this->VerifyResultLines("250",$responses)<=0)
720
				return(0);
721
			$this->pending_sender=0;
722
		}
723
		for(;$this->pending_recipients;$this->pending_recipients--)
724
		{
725
			if($this->VerifyResultLines(array("250","251"),$responses)<=0)
726
				return(0);
727
		}
728
		return(1);
729
	}
730

    
731
	Function Resolve($domain, &$ip, $server_type)
732
	{
733
		if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain))
734
			$ip=$domain;
735
		else
736
		{
737
			if($this->debug)
738
				$this->OutputDebug('Resolving '.$server_type.' server domain "'.$domain.'"...');
739
			if(!strcmp($ip=@gethostbyname($domain),$domain))
740
				$ip="";
741
		}
742
		if(strlen($ip)==0
743
		|| (strlen($this->exclude_address)
744
		&& !strcmp(@gethostbyname($this->exclude_address),$ip)))
745
			return($this->SetError("could not resolve the host domain \"".$domain."\""));
746
		return('');
747
	}
748

    
749
	Function ConnectToHost($domain, $port, $resolve_message)
750
	{
751
		if($this->ssl)
752
		{
753
			$version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
754
			$php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
755
			if($php_version<4003000)
756
				return("establishing SSL connections requires at least PHP version 4.3.0");
757
			if(!function_exists("extension_loaded")
758
			|| !extension_loaded("openssl"))
759
				return("establishing SSL connections requires the OpenSSL extension enabled");
760
		}
761
		if(strlen($this->Resolve($domain, $ip, 'SMTP')))
762
			return($this->error);
763
		if(strlen($this->socks_host_name))
764
		{
765
			switch($this->socks_version)
766
			{
767
				case '4':
768
					$version = 4;
769
					break;
770
				case '5':
771
					$version = 5;
772
					break;
773
				default:
774
					return('it was not specified a supported SOCKS protocol version');
775
					break;
776
			}
777
			$host_ip = $ip;
778
			$host_port = $port;
779
			if(strlen($this->error = $this->Resolve($this->socks_host_name, $ip, 'SOCKS')))
780
				return($this->error);
781
			if($this->ssl)
782
				$ip="ssl://".($socks_host = $this->socks_host_name);
783
			else
784
				$socks_host = $ip;
785
			if($this->debug)
786
				$this->OutputDebug("Connecting to SOCKS server \"".$socks_host."\" port ".$this->http_proxy_host_port."...");
787
			if(($this->connection=($this->timeout ? fsockopen($ip, $this->socks_host_port, $errno, $error, $this->timeout) : fsockopen($ip, $this->socks_host_port, $errno, $error))))
788
			{
789
				$timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
790
				if($timeout
791
				&& function_exists("socket_set_timeout"))
792
					socket_set_timeout($this->connection,$timeout,0);
793
				if(strlen($this->socks_host_name))
794
				{
795
					if($this->debug)
796
						$this->OutputDebug('Connected to the SOCKS server '.$this->socks_host_name);
797
					$send_error = 'it was not possible to send data to the SOCKS server';
798
					$receive_error = 'it was not possible to receive data from the SOCKS server';
799
					switch($version)
800
					{
801
						case 4:
802
							$command = 1;
803
							$user = '';
804
							if(!fputs($this->connection, chr($version).chr($command).pack('nN', $host_port, ip2long($host_ip)).$user.Chr(0)))
805
								$error = $this->SetDataAccessError($send_error);
806
							else
807
							{
808
								$response = fgets($this->connection, 9);
809
								if(strlen($response) != 8)
810
									$error = $this->SetDataAccessError($receive_error);
811
								else
812
								{
813
									$socks_errors = array(
814
										"\x5a"=>'',
815
										"\x5b"=>'request rejected',
816
										"\x5c"=>'request failed because client is not running identd (or not reachable from the server)',
817
										"\x5d"=>'request failed because client\'s identd could not confirm the user ID string in the request',
818
									);
819
									$error_code = $response[1];
820
									$error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
821
									if(strlen($error))
822
										$error = 'SOCKS error: '.$error;
823
								}
824
							}
825
							break;
826
						case 5:
827
							if($this->debug)
828
								$this->OutputDebug('Negotiating the authentication method ...');
829
							$methods = 1;
830
							$method = 0;
831
							if(!fputs($this->connection, chr($version).chr($methods).chr($method)))
832
								$error = $this->SetDataAccessError($send_error);
833
							else
834
							{
835
								$response = fgets($this->connection, 3);
836
								if(strlen($response) != 2)
837
									$error = $this->SetDataAccessError($receive_error);
838
								elseif(Ord($response[1]) != $method)
839
									$error = 'the SOCKS server requires an authentication method that is not yet supported';
840
								else
841
								{
842
									if($this->debug)
843
										$this->OutputDebug('Connecting to SMTP server IP '.$host_ip.' port '.$host_port.'...');
844
									$command = 1;
845
									$address_type = 1;
846
									if(!fputs($this->connection, chr($version).chr($command)."\x00".chr($address_type).pack('Nn', ip2long($host_ip), $host_port)))
847
										$error = $this->SetDataAccessError($send_error);
848
									else
849
									{
850
										$response = fgets($this->connection, 11);
851
										if(strlen($response) != 10)
852
											$error = $this->SetDataAccessError($receive_error);
853
										else
854
										{
855
											$socks_errors = array(
856
												"\x00"=>'',
857
												"\x01"=>'general SOCKS server failure',
858
												"\x02"=>'connection not allowed by ruleset',
859
												"\x03"=>'Network unreachable',
860
												"\x04"=>'Host unreachable',
861
												"\x05"=>'Connection refused',
862
												"\x06"=>'TTL expired',
863
												"\x07"=>'Command not supported',
864
												"\x08"=>'Address type not supported'
865
											);
866
											$error_code = $response[1];
867
											$error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
868
											if(strlen($error))
869
												$error = 'SOCKS error: '.$error;
870
										}
871
									}
872
								}
873
							}
874
							break;
875
						default:
876
							$error = 'support for SOCKS protocol version '.$this->socks_version.' is not yet implemented';
877
							break;
878
					}
879
					if(strlen($this->error = $error))
880
					{
881
						fclose($this->connection);
882
						return($error);
883
					}
884
				}
885
				return('');
886
			}
887
		}
888
		elseif(strlen($this->http_proxy_host_name))
889
		{
890
			if(strlen($error = $this->Resolve($this->http_proxy_host_name, $ip, 'SMTP')))
891
				return($error);
892
			if($this->ssl)
893
				$ip = 'ssl://'.($proxy_host = $this->http_proxy_host_name);
894
			else
895
				$proxy_host = $ip;
896
			if($this->debug)
897
				$this->OutputDebug("Connecting to HTTP proxy server \"".$ip."\" port ".$this->http_proxy_host_port."...");
898
			if(($this->connection=($this->timeout ? @fsockopen($ip, $this->http_proxy_host_port, $errno, $error, $this->timeout) : @fsockopen($ip, $this->http_proxy_host_port, $errno, $error))))
899
			{
900
				if($this->debug)
901
					$this->OutputDebug('Connected to HTTP proxy host "'.$this->http_proxy_host_name.'".');
902
				$timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
903
				if($timeout
904
				&& function_exists("socket_set_timeout"))
905
					socket_set_timeout($this->connection,$timeout,0);
906
				if($this->PutLine('CONNECT '.$domain.':'.$port.' HTTP/1.0')
907
				&& $this->PutLine('User-Agent: '.$this->user_agent)
908
				&& $this->PutLine(''))
909
				{
910
					if(GetType($response = $this->GetLine()) == 'string')
911
					{
912
						if(!preg_match('/^http\\/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)$/i', $response,$matches))
913
							return($this->SetError("3 it was received an unexpected HTTP response status"));
914
						$error = $matches[1];
915
						switch($error)
916
						{
917
							case '200':
918
								for(;;)
919
								{
920
									if(GetType($response = $this->GetLine()) != 'string')
921
										break;
922
									if(strlen($response) == 0)
923
										return('');
924
								}
925
								break;
926
							default:
927
								$this->error = 'the HTTP proxy returned error '.$error.' '.$matches[2];
928
								break;
929
						}
930
					}
931
				}
932
				if($this->debug)
933
					$this->OutputDebug("Disconnected.");
934
				fclose($this->connection);
935
				$this->connection = 0;
936
				return($this->error);
937
			}
938
		}
939
		else
940
		{
941
			if($this->ssl)
942
				$ip = 'ssl://'.($host = $domain);
943
			elseif($this->start_tls)
944
				$ip = $host = $domain;
945
			else
946
				$host = $ip;
947
			if($this->debug)
948
				$this->OutputDebug("Connecting to SMTP server \"".$host."\" port ".$port."...");
949
			if(($this->connection=($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno, $error))))
950
				return("");
951
		}
952
		$error=($this->timeout ? strval($error) : "??");
953
		switch($error)
954
		{
955
			case "-3":
956
				return("-3 socket could not be created");
957
			case "-4":
958
				return("-4 dns lookup on hostname \"".$domain."\" failed");
959
			case "-5":
960
				return("-5 connection refused or timed out");
961
			case "-6":
962
				return("-6 fdopen() call failed");
963
			case "-7":
964
				return("-7 setvbuf() call failed");
965
		}
966
		return("could not connect to the host \"".$domain."\": ".$error);
967
	}
968

    
969
	Function SASLAuthenticate($mechanisms, $credentials, &$authenticated, &$mechanism)
970
	{
971
		$authenticated=0;
972
		if(!function_exists("class_exists")
973
		|| !class_exists("sasl_client_class"))
974
		{
975
			$this->error="it is not possible to authenticate using the specified mechanism because the SASL library class is not loaded";
976
			return(0);
977
		}
978
		$sasl=new sasl_client_class;
979
		$sasl->SetCredential("user",$credentials["user"]);
980
		$sasl->SetCredential("password",$credentials["password"]);
981
		if(IsSet($credentials["realm"]))
982
			$sasl->SetCredential("realm",$credentials["realm"]);
983
		if(IsSet($credentials["workstation"]))
984
			$sasl->SetCredential("workstation",$credentials["workstation"]);
985
		if(IsSet($credentials["mode"]))
986
			$sasl->SetCredential("mode",$credentials["mode"]);
987
		do
988
		{
989
			$status=$sasl->Start($mechanisms,$message,$interactions);
990
		}
991
		while($status==SASL_INTERACT);
992
		switch($status)
993
		{
994
			case SASL_CONTINUE:
995
				break;
996
			case SASL_NOMECH:
997
				if(strlen($this->authentication_mechanism))
998
				{
999
					$this->error="authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error;
1000
					return(0);
1001
				}
1002
				break;
1003
			default:
1004
				$this->error="Could not start the SASL authentication client: ".$sasl->error;
1005
				return(0);
1006
		}
1007
		if(strlen($mechanism=$sasl->mechanism))
1008
		{
1009
			if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0)
1010
			{
1011
				$this->error="Could not send the AUTH command";
1012
				return(0);
1013
			}
1014
			if(!$this->VerifyResultLines(array("235","334"),$responses))
1015
				return(0);
1016
			switch($this->result_code)
1017
			{
1018
				case "235":
1019
					$response="";
1020
					$authenticated=1;
1021
					break;
1022
				case "334":
1023
					$response=base64_decode($responses[0]);
1024
					break;
1025
				default:
1026
					$this->error="Authentication error: ".$responses[0];
1027
					return(0);
1028
			}
1029
			for(;!$authenticated;)
1030
			{
1031
				do
1032
				{
1033
					$status=$sasl->Step($response,$message,$interactions);
1034
				}
1035
				while($status==SASL_INTERACT);
1036
				switch($status)
1037
				{
1038
					case SASL_CONTINUE:
1039
						if($this->PutLine(base64_encode($message))==0)
1040
						{
1041
							$this->error="Could not send the authentication step message";
1042
							return(0);
1043
						}
1044
						if(!$this->VerifyResultLines(array("235","334"),$responses))
1045
							return(0);
1046
						switch($this->result_code)
1047
						{
1048
							case "235":
1049
								$response="";
1050
								$authenticated=1;
1051
								break;
1052
							case "334":
1053
								$response=base64_decode($responses[0]);
1054
								break;
1055
							default:
1056
								$this->error="Authentication error: ".$responses[0];
1057
								return(0);
1058
						}
1059
						break;
1060
					default:
1061
						$this->error="Could not process the SASL authentication step: ".$sasl->error;
1062
						return(0);
1063
				}
1064
			}
1065
		}
1066
		return(1);
1067
	}
1068
	
1069
	Function StartSMTP($localhost)
1070
	{
1071
		$success = 1;
1072
		$this->esmtp_extensions = array();
1073
		$fallback=1;
1074
		if($this->esmtp
1075
		|| strlen($this->user))
1076
		{
1077
			if($this->PutLine('EHLO '.$localhost))
1078
			{
1079
				if(($success_code=$this->VerifyResultLines('250',$responses))>0)
1080
				{
1081
					$this->esmtp_host=$this->Tokenize($responses[0]," ");
1082
					for($response=1;$response<count($responses);$response++)
1083
					{
1084
						$extension=strtoupper($this->Tokenize($responses[$response]," "));
1085
						$this->esmtp_extensions[$extension]=$this->Tokenize("");
1086
					}
1087
					$success=1;
1088
					$fallback=0;
1089
				}
1090
				else
1091
				{
1092
					if($success_code==0)
1093
					{
1094
						$code=$this->Tokenize($this->error," -");
1095
						switch($code)
1096
						{
1097
							case "421":
1098
								$fallback=0;
1099
								break;
1100
						}
1101
					}
1102
				}
1103
			}
1104
			else
1105
				$fallback=0;
1106
		}
1107
		if($fallback)
1108
		{
1109
			if($this->PutLine("HELO $localhost")
1110
			&& $this->VerifyResultLines("250",$responses)>0)
1111
				$success=1;
1112
		}
1113
		return($success);
1114
	}
1115

    
1116
	/* Public methods */
1117

    
1118
/*
1119
{metadocument}
1120
	<function>
1121
		<name>Connect</name>
1122
		<type>BOOLEAN</type>
1123
		<documentation>
1124
			<purpose>Connect to an SMTP server.</purpose>
1125
			<usage>Call this function as first step to send e-mail messages.</usage>
1126
			<returnvalue>The function returns
1127
				<tt><booleanvalue>1</booleanvalue></tt> if the connection is
1128
					successfully established.</returnvalue>
1129
		</documentation>
1130
		<argument>
1131
			<name>domain</name>
1132
			<type>STRING</type>
1133
			<defaultvalue></defaultvalue>
1134
			<documentation>
1135
				<purpose>Specify the domain of the recipient when using the direct
1136
					delivery mode.</purpose>
1137
			</documentation>
1138
		</argument>
1139
		<do>
1140
{/metadocument}
1141
*/
1142
	Function Connect($domain="")
1143
	{
1144
		if(strcmp($this->state,"Disconnected"))
1145
		{
1146
			$this->error="connection is already established";
1147
			return(0);
1148
		}
1149
		$this->disconnected_error=0;
1150
		$this->error=$error="";
1151
		$this->esmtp_host="";
1152
		$this->esmtp_extensions=array();
1153
		$hosts=array();
1154
		if($this->direct_delivery)
1155
		{
1156
			if(strlen($domain)==0)
1157
				return(1);
1158
			$hosts=$weights=$mxhosts=array();
1159
			$getmxrr=$this->getmxrr;
1160
			if(function_exists($getmxrr)
1161
			&& $getmxrr($domain,$hosts,$weights))
1162
			{
1163
				for($host=0;$host<count($hosts);$host++)
1164
					$mxhosts[$weights[$host]]=$hosts[$host];
1165
				KSort($mxhosts);
1166
				for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++)
1167
					$hosts[$host]=$mxhosts[Key($mxhosts)];
1168
			}
1169
			else
1170
			{
1171
				if(strcmp(@gethostbyname($domain),$domain)!=0)
1172
					$hosts[]=$domain;
1173
			}
1174
		}
1175
		else
1176
		{
1177
			if(strlen($this->host_name))
1178
				$hosts[]=$this->host_name;
1179
			if(strlen($this->pop3_auth_host))
1180
			{
1181
				$user=$this->user;
1182
				if(strlen($user)==0)
1183
				{
1184
					$this->error="it was not specified the POP3 authentication user";
1185
					return(0);
1186
				}
1187
				$password=$this->password;
1188
				if(strlen($password)==0)
1189
				{
1190
					$this->error="it was not specified the POP3 authentication password";
1191
					return(0);
1192
				}
1193
				$domain=$this->pop3_auth_host;
1194
				$this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, "Resolving POP3 authentication host \"".$domain."\"...");
1195
				if(strlen($this->error))
1196
					return(0);
1197
				if(strlen($response=$this->GetLine())==0)
1198
					return(0);
1199
				if(strcmp($this->Tokenize($response," "),"+OK"))
1200
				{
1201
					$this->error="POP3 authentication server greeting was not found";
1202
					return(0);
1203
				}
1204
				if(!$this->PutLine("USER ".$this->user)
1205
				|| strlen($response=$this->GetLine())==0)
1206
					return(0);
1207
				if(strcmp($this->Tokenize($response," "),"+OK"))
1208
				{
1209
					$this->error="POP3 authentication user was not accepted: ".$this->Tokenize("\r\n");
1210
					return(0);
1211
				}
1212
				if(!$this->PutLine("PASS ".$password)
1213
				|| strlen($response=$this->GetLine())==0)
1214
					return(0);
1215
				if(strcmp($this->Tokenize($response," "),"+OK"))
1216
				{
1217
					$this->error="POP3 authentication password was not accepted: ".$this->Tokenize("\r\n");
1218
					return(0);
1219
				}
1220
				fclose($this->connection);
1221
				$this->connection=0;
1222
			}
1223
		}
1224
		if(count($hosts)==0)
1225
		{
1226
			$this->error="could not determine the SMTP to connect";
1227
			return(0);
1228
		}
1229
		for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++)
1230
		{
1231
			$domain=$hosts[$host];
1232
			$error=$this->ConnectToHost($domain, $this->host_port, "Resolving SMTP server domain \"$domain\"...");
1233
		}
1234
		if(strlen($error))
1235
		{
1236
			$this->error=$error;
1237
			return(0);
1238
		}
1239
		$timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
1240
		if($timeout
1241
		&& function_exists("socket_set_timeout"))
1242
			socket_set_timeout($this->connection,$timeout,0);
1243
		if($this->debug)
1244
			$this->OutputDebug("Connected to SMTP server \"".$domain."\".");
1245
		if(!strcmp($localhost=$this->localhost,"")
1246
		&& !strcmp($localhost=getenv("HTTP_HOST"),"")
1247
		&& !strcmp($localhost=getenv("HOST"),""))
1248
			$localhost="localhost";
1249
		$success=0;
1250
		if($this->VerifyResultLines("220",$responses)>0)
1251
		{
1252
			$success = $this->StartSMTP($localhost);
1253
			if($this->start_tls)
1254
			{
1255
				if(!IsSet($this->esmtp_extensions["STARTTLS"]))
1256
				{
1257
					$this->error="server does not support starting TLS";
1258
					$success=0;
1259
				}
1260
				elseif(!function_exists('stream_socket_enable_crypto'))
1261
				{
1262
					$this->error="this PHP installation or version does not support starting TLS";
1263
					$success=0;
1264
				}
1265
				elseif($success = ($this->PutLine('STARTTLS')
1266
				&& $this->VerifyResultLines('220',$responses)>0))
1267
				{
1268
					if($this->debug)
1269
						$this->OutputDebug('Starting TLS cryptograpic protocol');
1270
					if(!($success = @stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_TLS_CLIENT)))
1271
						$this->error = 'could not start TLS connection encryption protocol';
1272
					else
1273
					{
1274
						if($this->debug)
1275
							$this->OutputDebug('TLS started');
1276
						$success = $this->StartSMTP($localhost);
1277
					}
1278
				}
1279
			}
1280
			if($success
1281
			&& strlen($this->user)
1282
			&& strlen($this->pop3_auth_host)==0)
1283
			{
1284
				if(!IsSet($this->esmtp_extensions["AUTH"]))
1285
				{
1286
					$this->error="server does not require authentication";
1287
					if(IsSet($this->esmtp_extensions["STARTTLS"]))
1288
						$this->error .= ', it probably requires starting TLS';
1289
					$success=0;
1290
				}
1291
				else
1292
				{
1293
					if(strlen($this->authentication_mechanism))
1294
						$mechanisms=array($this->authentication_mechanism);
1295
					else
1296
					{
1297
						$mechanisms=array();
1298
						for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
1299
							$mechanisms[]=$authentication;
1300
					}
1301
					$credentials=array(
1302
						"user"=>$this->user,
1303
						"password"=>$this->password
1304
					);
1305
					if(strlen($this->realm))
1306
						$credentials["realm"]=$this->realm;
1307
					if(strlen($this->workstation))
1308
						$credentials["workstation"]=$this->workstation;
1309
					$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
1310
					if(!$success
1311
					&& !strcmp($mechanism,"PLAIN"))
1312
					{
1313
						/*
1314
						 * Author:  Russell Robinson, 25 May 2003, http://www.tectite.com/
1315
						 * Purpose: Try various AUTH PLAIN authentication methods.
1316
						 */
1317
						$mechanisms=array("PLAIN");
1318
						$credentials=array(
1319
							"user"=>$this->user,
1320
							"password"=>$this->password
1321
						);
1322
						if(strlen($this->realm))
1323
						{
1324
							/*
1325
							 * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method
1326
							 * some sendmails won't accept the realm, so try again without it
1327
							 */
1328
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
1329
						}
1330
						if(!$success)
1331
						{
1332
							/*
1333
							 * It was seen an EXIM configuration like this:
1334
							 * user^password^unused
1335
							 */
1336
							$credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE;
1337
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
1338
						}
1339
						if(!$success)
1340
						{
1341
							/*
1342
							 * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html
1343
							 * specifies: ^user^password
1344
							 */
1345
							$credentials["mode"]=SASL_PLAIN_EXIM_MODE;
1346
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
1347
						}
1348
					}
1349
					if($success
1350
					&& strlen($mechanism)==0)
1351
					{
1352
						$this->error="it is not supported any of the authentication mechanisms required by the server";
1353
						$success=0;
1354
					}
1355
				}
1356
			}
1357
		}
1358
		if($success)
1359
		{
1360
			$this->state="Connected";
1361
			$this->connected_domain=$domain;
1362
		}
1363
		else
1364
		{
1365
			fclose($this->connection);
1366
			$this->connection=0;
1367
		}
1368
		return($success);
1369
	}
1370
/*
1371
{metadocument}
1372
		</do>
1373
	</function>
1374
{/metadocument}
1375
*/
1376

    
1377
/*
1378
{metadocument}
1379
	<function>
1380
		<name>MailFrom</name>
1381
		<type>BOOLEAN</type>
1382
		<documentation>
1383
			<purpose>Set the address of the message sender.</purpose>
1384
			<usage>Call this function right after establishing a connection with
1385
				the <functionlink>Connect</functionlink> function.</usage>
1386
			<returnvalue>The function returns
1387
				<tt><booleanvalue>1</booleanvalue></tt> if the sender address is
1388
					successfully set.</returnvalue>
1389
		</documentation>
1390
		<argument>
1391
			<name>sender</name>
1392
			<type>STRING</type>
1393
			<documentation>
1394
				<purpose>E-mail address of the sender.</purpose>
1395
			</documentation>
1396
		</argument>
1397
		<do>
1398
{/metadocument}
1399
*/
1400
	Function MailFrom($sender)
1401
	{
1402
		if($this->direct_delivery)
1403
		{
1404
			switch($this->state)
1405
			{
1406
				case "Disconnected":
1407
					$this->direct_sender=$sender;
1408
					return(1);
1409
				case "Connected":
1410
					$sender=$this->direct_sender;
1411
					break;
1412
				default:
1413
					$this->error="direct delivery connection is already established and sender is already set";
1414
					return(0);
1415
			}
1416
		}
1417
		else
1418
		{
1419
			if(strcmp($this->state,"Connected"))
1420
			{
1421
				$this->error="connection is not in the initial state";
1422
				return(0);
1423
			}
1424
		}
1425
		$this->error="";
1426
		if(!$this->PutLine("MAIL FROM:<$sender>"))
1427
			return(0);
1428
		if(!IsSet($this->esmtp_extensions["PIPELINING"])
1429
		&& $this->VerifyResultLines("250",$responses)<=0)
1430
			return(0);
1431
		$this->state="SenderSet";
1432
		if(IsSet($this->esmtp_extensions["PIPELINING"]))
1433
			$this->pending_sender=1;
1434
		$this->pending_recipients=0;
1435
		return(1);
1436
	}
1437
/*
1438
{metadocument}
1439
		</do>
1440
	</function>
1441
{/metadocument}
1442
*/
1443

    
1444
/*
1445
{metadocument}
1446
	<function>
1447
		<name>SetRecipient</name>
1448
		<type>BOOLEAN</type>
1449
		<documentation>
1450
			<purpose>Set the address of a message recipient.</purpose>
1451
			<usage>Call this function repeatedly for each recipient right after
1452
				setting the message sender with the
1453
				<functionlink>MailFrom</functionlink> function.</usage>
1454
			<returnvalue>The function returns
1455
				<tt><booleanvalue>1</booleanvalue></tt> if the recipient address is
1456
					successfully set.</returnvalue>
1457
		</documentation>
1458
		<argument>
1459
			<name>recipient</name>
1460
			<type>STRING</type>
1461
			<documentation>
1462
				<purpose>E-mail address of a recipient.</purpose>
1463
			</documentation>
1464
		</argument>
1465
		<do>
1466
{/metadocument}
1467
*/
1468
	Function SetRecipient($recipient)
1469
	{
1470
		if($this->direct_delivery)
1471
		{
1472
			if(GetType($at=strrpos($recipient,"@"))!="integer")
1473
				return("it was not specified a valid direct recipient");
1474
			$domain=substr($recipient,$at+1);
1475
			switch($this->state)
1476
			{
1477
				case "Disconnected":
1478
					if(!$this->Connect($domain))
1479
						return(0);
1480
					if(!$this->MailFrom(""))
1481
					{
1482
						$error=$this->error;
1483
						$this->Disconnect();
1484
						$this->error=$error;
1485
						return(0);
1486
					}
1487
					break;
1488
				case "SenderSet":
1489
				case "RecipientSet":
1490
					if(strcmp($this->connected_domain,$domain))
1491
					{
1492
						$this->error="it is not possible to deliver directly to recipients of different domains";
1493
						return(0);
1494
					}
1495
					break;
1496
				default:
1497
					$this->error="connection is already established and the recipient is already set";
1498
					return(0);
1499
			}
1500
		}
1501
		else
1502
		{
1503
			switch($this->state)
1504
			{
1505
				case "SenderSet":
1506
				case "RecipientSet":
1507
					break;
1508
				default:
1509
					$this->error="connection is not in the recipient setting state";
1510
					return(0);
1511
			}
1512
		}
1513
		$this->error="";
1514
		if(!$this->PutLine("RCPT TO:<$recipient>"))
1515
			return(0);
1516
		if(IsSet($this->esmtp_extensions["PIPELINING"]))
1517
		{
1518
			$this->pending_recipients++;
1519
			if($this->pending_recipients>=$this->maximum_piped_recipients)
1520
			{
1521
				if(!$this->FlushRecipients())
1522
					return(0);
1523
			}
1524
		}
1525
		else
1526
		{
1527
			if($this->VerifyResultLines(array("250","251"),$responses)<=0)
1528
				return(0);
1529
		}
1530
		$this->state="RecipientSet";
1531
		return(1);
1532
	}
1533
/*
1534
{metadocument}
1535
		</do>
1536
	</function>
1537
{/metadocument}
1538
*/
1539

    
1540
/*
1541
{metadocument}
1542
	<function>
1543
		<name>StartData</name>
1544
		<type>BOOLEAN</type>
1545
		<documentation>
1546
			<purpose>Tell the SMTP server that the message data will start being
1547
				sent.</purpose>
1548
			<usage>Call this function right after you are done setting all the
1549
				message recipients with the
1550
				<functionlink>SetRecipient</functionlink> function.</usage>
1551
			<returnvalue>The function returns
1552
				<tt><booleanvalue>1</booleanvalue></tt> if the server is ready to
1553
				start receiving the message data.</returnvalue>
1554
		</documentation>
1555
		<do>
1556
{/metadocument}
1557
*/
1558
	Function StartData()
1559
	{
1560
		if(strcmp($this->state,"RecipientSet"))
1561
		{
1562
			$this->error="connection is not in the start sending data state";
1563
			return(0);
1564
		}
1565
		$this->error="";
1566
		if(!$this->PutLine("DATA"))
1567
			return(0);
1568
		if($this->pending_recipients)
1569
		{
1570
			if(!$this->FlushRecipients())
1571
				return(0);
1572
		}
1573
		if($this->VerifyResultLines("354",$responses)<=0)
1574
			return(0);
1575
		$this->state="SendingData";
1576
		return(1);
1577
	}
1578
/*
1579
{metadocument}
1580
		</do>
1581
	</function>
1582
{/metadocument}
1583
*/
1584

    
1585
/*
1586
{metadocument}
1587
	<function>
1588
		<name>PrepareData</name>
1589
		<type>STRING</type>
1590
		<documentation>
1591
			<purpose>Prepare message data to normalize line breaks and escaping
1592
				lines that contain single dots.</purpose>
1593
			<usage>Call this function if the message data you want to send may
1594
				contain line breaks that are not the
1595
				<stringvalue>&#13;&#10;</stringvalue> sequence or it may contain
1596
				lines that just have a single dot.</usage>
1597
			<returnvalue>Resulting normalized messages data.</returnvalue>
1598
		</documentation>
1599
		<argument>
1600
			<name>data</name>
1601
			<type>STRING</type>
1602
			<documentation>
1603
				<purpose>Message data to be prepared.</purpose>
1604
			</documentation>
1605
		</argument>
1606
		<do>
1607
{/metadocument}
1608
*/
1609
	Function PrepareData($data)
1610
	{
1611
		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));
1612
	}
1613
/*
1614
{metadocument}
1615
		</do>
1616
	</function>
1617
{/metadocument}
1618
*/
1619

    
1620
/*
1621
{metadocument}
1622
	<function>
1623
		<name>SendData</name>
1624
		<type>BOOLEAN</type>
1625
		<documentation>
1626
			<purpose>Send message data.</purpose>
1627
			<usage>Call this function repeatedly for all message data blocks
1628
				to be sent right after	start sending message data with the
1629
				<functionlink>StartData</functionlink> function.</usage>
1630
			<returnvalue>The function returns
1631
				<tt><booleanvalue>1</booleanvalue></tt> if the message data was
1632
				sent to the SMTP server successfully.</returnvalue>
1633
		</documentation>
1634
		<argument>
1635
			<name>data</name>
1636
			<type>STRING</type>
1637
			<documentation>
1638
				<purpose>Message data to be sent.</purpose>
1639
			</documentation>
1640
		</argument>
1641
		<do>
1642
{/metadocument}
1643
*/
1644
	Function SendData($data)
1645
	{
1646
		if(strcmp($this->state,"SendingData"))
1647
		{
1648
			$this->error="connection is not in the sending data state";
1649
			return(0);
1650
		}
1651
		$this->error="";
1652
		return($this->PutData($data));
1653
	}
1654
/*
1655
{metadocument}
1656
		</do>
1657
	</function>
1658
{/metadocument}
1659
*/
1660

    
1661
/*
1662
{metadocument}
1663
	<function>
1664
		<name>EndSendingData</name>
1665
		<type>BOOLEAN</type>
1666
		<documentation>
1667
			<purpose>Tell the server that all the message data was sent.</purpose>
1668
			<usage>Call this function when you are done with sending the message
1669
				data with the	<functionlink>SendData</functionlink> function.</usage>
1670
			<returnvalue>The function returns
1671
				<tt><booleanvalue>1</booleanvalue></tt> if the server accepted the
1672
				message.</returnvalue>
1673
		</documentation>
1674
		<do>
1675
{/metadocument}
1676
*/
1677
	Function EndSendingData()
1678
	{
1679
		if(strcmp($this->state,"SendingData"))
1680
		{
1681
			$this->error="connection is not in the sending data state";
1682
			return(0);
1683
		}
1684
		$this->error="";
1685
		if(!$this->PutLine("\r\n.")
1686
		|| $this->VerifyResultLines("250",$responses)<=0)
1687
			return(0);
1688
		$this->state="Connected";
1689
		return(1);
1690
	}
1691
/*
1692
{metadocument}
1693
		</do>
1694
	</function>
1695
{/metadocument}
1696
*/
1697

    
1698
/*
1699
{metadocument}
1700
	<function>
1701
		<name>ResetConnection</name>
1702
		<type>BOOLEAN</type>
1703
		<documentation>
1704
			<purpose>Reset an already established SMTP connection to the initial
1705
				state.</purpose>
1706
			<usage>Call this function when there was an error sending a message
1707
				and you need to skip to sending another message without
1708
				disconnecting.</usage>
1709
			<returnvalue>The function returns
1710
				<tt><booleanvalue>1</booleanvalue></tt> if the connection was
1711
				resetted successfully.</returnvalue>
1712
		</documentation>
1713
		<do>
1714
{/metadocument}
1715
*/
1716
	Function ResetConnection()
1717
	{
1718
		switch($this->state)
1719
		{
1720
			case "Connected":
1721
				return(1);
1722
			case "SendingData":
1723
				$this->error="can not reset the connection while sending data";
1724
				return(0);
1725
			case "Disconnected":
1726
				$this->error="can not reset the connection before it is established";
1727
				return(0);
1728
		}
1729
		$this->error="";
1730
		if(!$this->PutLine("RSET")
1731
		|| $this->VerifyResultLines("250",$responses)<=0)
1732
			return(0);
1733
		$this->state="Connected";
1734
		return(1);
1735
	}
1736
/*
1737
{metadocument}
1738
		</do>
1739
	</function>
1740
{/metadocument}
1741
*/
1742

    
1743
/*
1744
{metadocument}
1745
	<function>
1746
		<name>Disconnect</name>
1747
		<type>BOOLEAN</type>
1748
		<documentation>
1749
			<purpose>Terminate a previously opened connection.</purpose>
1750
			<usage>Call this function after you are done sending your
1751
				messages.</usage>
1752
			<returnvalue>The function returns
1753
				<tt><booleanvalue>1</booleanvalue></tt> if the connection was
1754
					successfully closed.</returnvalue>
1755
		</documentation>
1756
		<argument>
1757
			<name>quit</name>
1758
			<type>BOOLEAN</type>
1759
			<defaultvalue>1</defaultvalue>
1760
			<documentation>
1761
				<purpose>Boolean option that tells whether the class should
1762
					perform the final connection quit handshake, or just close the
1763
					connection without waiting.</purpose>
1764
			</documentation>
1765
		</argument>
1766
		<do>
1767
{/metadocument}
1768
*/
1769
	Function Disconnect($quit=1)
1770
	{
1771
		if(!strcmp($this->state,"Disconnected"))
1772
		{
1773
			$this->error="it was not previously established a SMTP connection";
1774
			return(0);
1775
		}
1776
		$this->error="";
1777
		if(!strcmp($this->state,"Connected")
1778
		&& $quit
1779
		&& (!$this->PutLine("QUIT")
1780
		|| ($this->VerifyResultLines("221",$responses)<=0
1781
		&& !$this->disconnected_error)))
1782
			return(0);
1783
		if($this->disconnected_error)
1784
			$this->disconnected_error=0;
1785
		else
1786
			fclose($this->connection);
1787
		$this->connection=0;
1788
		$this->state="Disconnected";
1789
		if($this->debug)
1790
			$this->OutputDebug("Disconnected.");
1791
		return(1);
1792
	}
1793
/*
1794
{metadocument}
1795
		</do>
1796
	</function>
1797
{/metadocument}
1798
*/
1799

    
1800
/*
1801
{metadocument}
1802
	<function>
1803
		<name>SendMessage</name>
1804
		<type>BOOLEAN</type>
1805
		<documentation>
1806
			<purpose>Send a message in a single call.</purpose>
1807
			<usage>Call this function if you want to send a single messages to a
1808
				small number of recipients in a single call.</usage>
1809
			<returnvalue>The function returns
1810
				<tt><booleanvalue>1</booleanvalue></tt> if the message was sent
1811
				successfully.</returnvalue>
1812
		</documentation>
1813
		<argument>
1814
			<name>sender</name>
1815
			<type>STRING</type>
1816
			<documentation>
1817
				<purpose>E-mail address of the sender.</purpose>
1818
			</documentation>
1819
		</argument>
1820
		<argument>
1821
			<name>recipients</name>
1822
			<type>STRING</type>
1823
			<documentation>
1824
				<purpose>Array with a list of the e-mail addresses of the
1825
					recipients of the message.</purpose>
1826
			</documentation>
1827
		</argument>
1828
		<argument>
1829
			<name>headers</name>
1830
			<type>ARRAY</type>
1831
			<documentation>
1832
				<purpose>Array with a list of the header lines of the message.</purpose>
1833
			</documentation>
1834
		</argument>
1835
		<argument>
1836
			<name>body</name>
1837
			<type>STRING</type>
1838
			<documentation>
1839
				<purpose>Body data of the message.</purpose>
1840
			</documentation>
1841
		</argument>
1842
		<do>
1843
{/metadocument}
1844
*/
1845
	Function SendMessage($sender,$recipients,$headers,$body)
1846
	{
1847
		if(($success=$this->Connect()))
1848
		{
1849
			if(($success=$this->MailFrom($sender)))
1850
			{
1851
				for($recipient=0;$recipient<count($recipients);$recipient++)
1852
				{
1853
					if(!($success=$this->SetRecipient($recipients[$recipient])))
1854
						break;
1855
				}
1856
				if($success
1857
				&& ($success=$this->StartData()))
1858
				{
1859
					for($header_data="",$header=0;$header<count($headers);$header++)
1860
						$header_data.=$headers[$header]."\r\n";
1861
					$success=($this->SendData($header_data."\r\n")
1862
						&& $this->SendData($this->PrepareData($body))
1863
						&& $this->EndSendingData());
1864
				}
1865
			}
1866
			$error=$this->error;
1867
			$disconnect_success=$this->Disconnect($success);
1868
			if($success)
1869
				$success=$disconnect_success;
1870
			else
1871
				$this->error=$error;
1872
		}
1873
		return($success);
1874
	}
1875
/*
1876
{metadocument}
1877
		</do>
1878
	</function>
1879
{/metadocument}
1880
*/
1881

    
1882
};
1883

    
1884
/*
1885

    
1886
{metadocument}
1887
</class>
1888
{/metadocument}
1889

    
1890
*/
1891

    
1892
?>
(53-53/67)