Project

General

Profile

Download (20.7 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * smtp.php
4
 *
5
 * @(#) $Header$
6
 *
7
 */
8

    
9
class smtp_class
10
{
11
	var $user="";
12
	var $realm="";
13
	var $password="";
14
	var $workstation="";
15
	var $authentication_mechanism="";
16
	var $host_name="";
17
	var $host_port=25;
18
	var $ssl=0;
19
	var $localhost="";
20
	var $timeout=0;
21
	var $data_timeout=0;
22
	var $direct_delivery=0;
23
	var $error="";
24
	var $debug=0;
25
	var $html_debug=0;
26
	var $esmtp=1;
27
	var $esmtp_host="";
28
	var $esmtp_extensions=array();
29
	var $maximum_piped_recipients=100;
30
	var $exclude_address="";
31
	var $getmxrr="GetMXRR";
32
	var $pop3_auth_host="";
33
	var $pop3_auth_port=110;
34

    
35
	/* private variables - DO NOT ACCESS */
36

    
37
	var $state="Disconnected";
38
	var $connection=0;
39
	var $pending_recipients=0;
40
	var $next_token="";
41
	var $direct_sender="";
42
	var $connected_domain="";
43
	var $result_code;
44
	var $disconnected_error=0;
45

    
46
	/* Private methods - DO NOT CALL */
47

    
48
	Function Tokenize($string,$separator="")
49
	{
50
		if(!strcmp($separator,""))
51
		{
52
			$separator=$string;
53
			$string=$this->next_token;
54
		}
55
		for($character=0;$character<strlen($separator);$character++)
56
		{
57
			if(GetType($position=strpos($string,$separator[$character]))=="integer")
58
				$found=(IsSet($found) ? min($found,$position) : $position);
59
		}
60
		if(IsSet($found))
61
		{
62
			$this->next_token=substr($string,$found+1);
63
			return(substr($string,0,$found));
64
		}
65
		else
66
		{
67
			$this->next_token="";
68
			return($string);
69
		}
70
	}
71

    
72
	Function OutputDebug($message)
73
	{
74
		$message.="\n";
75
		if($this->html_debug)
76
			$message=str_replace("\n","<br />\n",HtmlEntities($message));
77
		echo $message;
78
		flush();
79
	}
80

    
81
	Function SetDataAccessError($error)
82
	{
83
		$this->error=$error;
84
		if(function_exists("socket_get_status"))
85
		{
86
			$status=socket_get_status($this->connection);
87
			if($status["timed_out"])
88
				$this->error.=": data access time out";
89
			elseif($status["eof"])
90
			{
91
				$this->error.=": the server disconnected";
92
				$this->disconnected_error=1;
93
			}
94
		}
95
	}
96

    
97
	Function GetLine()
98
	{
99
		for($line="";;)
100
		{
101
			if(feof($this->connection))
102
			{
103
				$this->error="reached the end of data while reading from the SMTP server conection";
104
				return("");
105
			}
106
			if(GetType($data=@fgets($this->connection,100))!="string"
107
			|| strlen($data)==0)
108
			{
109
				$this->SetDataAccessError("it was not possible to read line from the SMTP server");
110
				return("");
111
			}
112
			$line.=$data;
113
			$length=strlen($line);
114
			if($length>=2
115
			&& substr($line,$length-2,2)=="\r\n")
116
			{
117
				$line=substr($line,0,$length-2);
118
				if($this->debug)
119
					$this->OutputDebug("S $line");
120
				return($line);
121
			}
122
		}
123
	}
124

    
125
	Function PutLine($line)
126
	{
127
		if($this->debug)
128
			$this->OutputDebug("C $line");
129
		if(!@fputs($this->connection,"$line\r\n"))
130
		{
131
			$this->SetDataAccessError("it was not possible to send a line to the SMTP server");
132
			return(0);
133
		}
134
		return(1);
135
	}
136

    
137
	Function PutData(&$data)
138
	{
139
		if(strlen($data))
140
		{
141
			if($this->debug)
142
				$this->OutputDebug("C $data");
143
			if(!@fputs($this->connection,$data))
144
			{
145
				$this->SetDataAccessError("it was not possible to send data to the SMTP server");
146
				return(0);
147
			}
148
		}
149
		return(1);
150
	}
151

    
152
	Function VerifyResultLines($code,&$responses)
153
	{
154
		$responses=array();
155
		Unset($this->result_code);
156
		while(strlen($line=$this->GetLine($this->connection)))
157
		{
158
			if(IsSet($this->result_code))
159
			{
160
				if(strcmp($this->Tokenize($line," -"),$this->result_code))
161
				{
162
					$this->error=$line;
163
					return(0);
164
				}
165
			}
166
			else
167
			{
168
				$this->result_code=$this->Tokenize($line," -");
169
				if(GetType($code)=="array")
170
				{
171
					for($codes=0;$codes<count($code) && strcmp($this->result_code,$code[$codes]);$codes++);
172
					if($codes>=count($code))
173
					{
174
						$this->error=$line;
175
						return(0);
176
					}
177
				}
178
				else
179
				{
180
					if(strcmp($this->result_code,$code))
181
					{
182
						$this->error=$line;
183
						return(0);
184
					}
185
				}
186
			}
187
			$responses[]=$this->Tokenize("");
188
			if(!strcmp($this->result_code,$this->Tokenize($line," ")))
189
				return(1);
190
		}
191
		return(-1);
192
	}
193

    
194
	Function FlushRecipients()
195
	{
196
		if($this->pending_sender)
197
		{
198
			if($this->VerifyResultLines("250",$responses)<=0)
199
				return(0);
200
			$this->pending_sender=0;
201
		}
202
		for(;$this->pending_recipients;$this->pending_recipients--)
203
		{
204
			if($this->VerifyResultLines(array("250","251"),$responses)<=0)
205
				return(0);
206
		}
207
		return(1);
208
	}
209

    
210
	Function ConnectToHost($domain, $port, $resolve_message)
211
	{
212
		if($this->ssl)
213
		{
214
			$version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
215
			$php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
216
			if($php_version<4003000)
217
				return("establishing SSL connections requires at least PHP version 4.3.0");
218
			if(!function_exists("extension_loaded")
219
			|| !extension_loaded("openssl"))
220
				return("establishing SSL connections requires the OpenSSL extension enabled");
221
		}
222
		if(ereg('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$',$domain))
223
			$ip=$domain;
224
		else
225
		{
226
			if($this->debug)
227
				$this->OutputDebug($resolve_message);
228
			if(!strcmp($ip=@gethostbyname($domain),$domain))
229
				return("could not resolve host \"".$domain."\"");
230
		}
231
		if(strlen($this->exclude_address)
232
		&& !strcmp(@gethostbyname($this->exclude_address),$ip))
233
			return("domain \"".$domain."\" resolved to an address excluded to be valid");
234
		if($this->debug)
235
			$this->OutputDebug("Connecting to host address \"".$ip."\" port ".$port."...");
236
		if(($this->connection=($this->timeout ? @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port,$errno,$error,$this->timeout) : @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port))))
237
			return("");
238
		$error=($this->timeout ? strval($error) : "??");
239
		switch($error)
240
		{
241
			case "-3":
242
				return("-3 socket could not be created");
243
			case "-4":
244
				return("-4 dns lookup on hostname \"".$domain."\" failed");
245
			case "-5":
246
				return("-5 connection refused or timed out");
247
			case "-6":
248
				return("-6 fdopen() call failed");
249
			case "-7":
250
				return("-7 setvbuf() call failed");
251
		}
252
		return("could not connect to the host \"".$domain."\": ".$error);
253
	}
254

    
255
	Function SASLAuthenticate($mechanisms, $credentials, &$authenticated, &$mechanism)
256
	{
257
		$authenticated=0;
258
		if(!function_exists("class_exists")
259
		|| !class_exists("sasl_client_class"))
260
		{
261
			$this->error="it is not possible to authenticate using the specified mechanism because the SASL library class is not loaded";
262
			return(0);
263
		}
264
		$sasl=new sasl_client_class;
265
		$sasl->SetCredential("user",$credentials["user"]);
266
		$sasl->SetCredential("password",$credentials["password"]);
267
		if(IsSet($credentials["realm"]))
268
			$sasl->SetCredential("realm",$credentials["realm"]);
269
		if(IsSet($credentials["workstation"]))
270
			$sasl->SetCredential("workstation",$credentials["workstation"]);
271
		if(IsSet($credentials["mode"]))
272
			$sasl->SetCredential("mode",$credentials["mode"]);
273
		do
274
		{
275
			$status=$sasl->Start($mechanisms,$message,$interactions);
276
		}
277
		while($status==SASL_INTERACT);
278
		switch($status)
279
		{
280
			case SASL_CONTINUE:
281
				break;
282
			case SASL_NOMECH:
283
				if(strlen($this->authentication_mechanism))
284
				{
285
					$this->error="authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error;
286
					return(0);
287
				}
288
				break;
289
			default:
290
				$this->error="Could not start the SASL authentication client: ".$sasl->error;
291
				return(0);
292
		}
293
		if(strlen($mechanism=$sasl->mechanism))
294
		{
295
			if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0)
296
			{
297
				$this->error="Could not send the AUTH command";
298
				return(0);
299
			}
300
			if(!$this->VerifyResultLines(array("235","334"),$responses))
301
				return(0);
302
			switch($this->result_code)
303
			{
304
				case "235":
305
					$response="";
306
					$authenticated=1;
307
					break;
308
				case "334":
309
					$response=base64_decode($responses[0]);
310
					break;
311
				default:
312
					$this->error="Authentication error: ".$responses[0];
313
					return(0);
314
			}
315
			for(;!$authenticated;)
316
			{
317
				do
318
				{
319
					$status=$sasl->Step($response,$message,$interactions);
320
				}
321
				while($status==SASL_INTERACT);
322
				switch($status)
323
				{
324
					case SASL_CONTINUE:
325
						if($this->PutLine(base64_encode($message))==0)
326
						{
327
							$this->error="Could not send the authentication step message";
328
							return(0);
329
						}
330
						if(!$this->VerifyResultLines(array("235","334"),$responses))
331
							return(0);
332
						switch($this->result_code)
333
						{
334
							case "235":
335
								$response="";
336
								$authenticated=1;
337
								break;
338
							case "334":
339
								$response=base64_decode($responses[0]);
340
								break;
341
							default:
342
								$this->error="Authentication error: ".$responses[0];
343
								return(0);
344
						}
345
						break;
346
					default:
347
						$this->error="Could not process the SASL authentication step: ".$sasl->error;
348
						return(0);
349
				}
350
			}
351
		}
352
		return(1);
353
	}
354

    
355
	/* Public methods */
356

    
357
	Function Connect($domain="")
358
	{
359
		if(strcmp($this->state,"Disconnected"))
360
		{
361
			$this->error="connection is already established";
362
			return(0);
363
		}
364
		$this->disconnected_error=0;
365
		$this->error=$error="";
366
		$this->esmtp_host="";
367
		$this->esmtp_extensions=array();
368
		$hosts=array();
369
		if($this->direct_delivery)
370
		{
371
			if(strlen($domain)==0)
372
				return(1);
373
			$hosts=$weights=$mxhosts=array();
374
			$getmxrr=$this->getmxrr;
375
			if(function_exists($getmxrr)
376
			&& $getmxrr($domain,$hosts,$weights))
377
			{
378
				for($host=0;$host<count($hosts);$host++)
379
					$mxhosts[$weights[$host]]=$hosts[$host];
380
				KSort($mxhosts);
381
				for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++)
382
					$hosts[$host]=$mxhosts[Key($mxhosts)];
383
			}
384
			else
385
			{
386
				if(strcmp(@gethostbyname($domain),$domain)!=0)
387
					$hosts[]=$domain;
388
			}
389
		}
390
		else
391
		{
392
			if(strlen($this->host_name))
393
				$hosts[]=$this->host_name;
394
			if(strlen($this->pop3_auth_host))
395
			{
396
				$user=$this->user;
397
				if(strlen($user)==0)
398
				{
399
					$this->error="it was not specified the POP3 authentication user";
400
					return(0);
401
				}
402
				$password=$this->password;
403
				if(strlen($password)==0)
404
				{
405
					$this->error="it was not specified the POP3 authentication password";
406
					return(0);
407
				}
408
				$domain=$this->pop3_auth_host;
409
				$this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, "Resolving POP3 authentication host \"".$domain."\"...");
410
				if(strlen($this->error))
411
					return(0);
412
				if(strlen($response=$this->GetLine())==0)
413
					return(0);
414
				if(strcmp($this->Tokenize($response," "),"+OK"))
415
				{
416
					$this->error="POP3 authentication server greeting was not found";
417
					return(0);
418
				}
419
				if(!$this->PutLine("USER ".$this->user)
420
				|| strlen($response=$this->GetLine())==0)
421
					return(0);
422
				if(strcmp($this->Tokenize($response," "),"+OK"))
423
				{
424
					$this->error="POP3 authentication user was not accepted: ".$this->Tokenize("\r\n");
425
					return(0);
426
				}
427
				if(!$this->PutLine("PASS ".$password)
428
				|| strlen($response=$this->GetLine())==0)
429
					return(0);
430
				if(strcmp($this->Tokenize($response," "),"+OK"))
431
				{
432
					$this->error="POP3 authentication password was not accepted: ".$this->Tokenize("\r\n");
433
					return(0);
434
				}
435
				fclose($this->connection);
436
				$this->connection=0;
437
			}
438
		}
439
		if(count($hosts)==0)
440
		{
441
			$this->error="could not determine the SMTP to connect";
442
			return(0);
443
		}
444
		for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++)
445
		{
446
			$domain=$hosts[$host];
447
			$error=$this->ConnectToHost($domain, $this->host_port, "Resolving SMTP server domain \"$domain\"...");
448
		}
449
		if(strlen($error))
450
		{
451
			$this->error=$error;
452
			return(0);
453
		}
454
		$timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
455
		if($timeout
456
		&& function_exists("socket_set_timeout"))
457
			socket_set_timeout($this->connection,$timeout,0);
458
		if($this->debug)
459
			$this->OutputDebug("Connected to SMTP server \"".$domain."\".");
460
		if(!strcmp($localhost=$this->localhost,"")
461
		&& !strcmp($localhost=getenv("SERVER_NAME"),"")
462
		&& !strcmp($localhost=getenv("HOST"),""))
463
			$localhost="localhost";
464
		$success=0;
465
		if($this->VerifyResultLines("220",$responses)>0)
466
		{
467
			$fallback=1;
468
			if($this->esmtp
469
			|| strlen($this->user))
470
			{
471
				if($this->PutLine("EHLO $localhost"))
472
				{
473
					if(($success_code=$this->VerifyResultLines("250",$responses))>0)
474
					{
475
						$this->esmtp_host=$this->Tokenize($responses[0]," ");
476
						for($response=1;$response<count($responses);$response++)
477
						{
478
							$extension=strtoupper($this->Tokenize($responses[$response]," "));
479
							$this->esmtp_extensions[$extension]=$this->Tokenize("");
480
						}
481
						$success=1;
482
						$fallback=0;
483
					}
484
					else
485
					{
486
						if($success_code==0)
487
						{
488
							$code=$this->Tokenize($this->error," -");
489
							switch($code)
490
							{
491
								case "421":
492
									$fallback=0;
493
									break;
494
							}
495
						}
496
					}
497
				}
498
				else
499
					$fallback=0;
500
			}
501
			if($fallback)
502
			{
503
				if($this->PutLine("HELO $localhost")
504
				&& $this->VerifyResultLines("250",$responses)>0)
505
					$success=1;
506
			}
507
			if($success
508
			&& strlen($this->user)
509
			&& strlen($this->pop3_auth_host)==0)
510
			{
511
				if(!IsSet($this->esmtp_extensions["AUTH"]))
512
				{
513
					$this->error="server does not require authentication";
514
					$success=0;
515
				}
516
				else
517
				{
518
					if(strlen($this->authentication_mechanism))
519
						$mechanisms=array($this->authentication_mechanism);
520
					else
521
					{
522
						$mechanisms=array();
523
						for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
524
							$mechanisms[]=$authentication;
525
					}
526
					$credentials=array(
527
						"user"=>$this->user,
528
						"password"=>$this->password
529
					);
530
					if(strlen($this->realm))
531
						$credentials["realm"]=$this->realm;
532
					if(strlen($this->workstation))
533
						$credentials["workstation"]=$this->workstation;
534
					$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
535
					if(!$success
536
					&& !strcmp($mechanism,"PLAIN"))
537
					{
538
						/*
539
						 * Author:  Russell Robinson, 25 May 2003, http://www.tectite.com/
540
						 * Purpose: Try various AUTH PLAIN authentication methods.
541
						 */
542
						$mechanisms=array("PLAIN");
543
						$credentials=array(
544
							"user"=>$this->user,
545
							"password"=>$this->password
546
						);
547
						if(strlen($this->realm))
548
						{
549
							/*
550
							 * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method
551
							 * some sendmails won't accept the realm, so try again without it
552
							 */
553
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
554
						}
555
						if(!$success)
556
						{
557
							/*
558
							 * It was seen an EXIM configuration like this:
559
							 * user^password^unused
560
							 */
561
							$credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE;
562
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
563
						}
564
						if(!$success)
565
						{
566
							/*
567
							 * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html
568
							 * specifies: ^user^password
569
							 */
570
							$credentials["mode"]=SASL_PLAIN_EXIM_MODE;
571
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
572
						}
573
					}
574
					if($success
575
					&& strlen($mechanism)==0)
576
					{
577
						$this->error="it is not supported any of the authentication mechanisms required by the server";
578
						$success=0;
579
					}
580
				}
581
			}
582
		}
583
		if($success)
584
		{
585
			$this->state="Connected";
586
			$this->connected_domain=$domain;
587
		}
588
		else
589
		{
590
			fclose($this->connection);
591
			$this->connection=0;
592
		}
593
		return($success);
594
	}
595

    
596
	Function MailFrom($sender)
597
	{
598
		if($this->direct_delivery)
599
		{
600
			switch($this->state)
601
			{
602
				case "Disconnected":
603
					$this->direct_sender=$sender;
604
					return(1);
605
				case "Connected":
606
					$sender=$this->direct_sender;
607
					break;
608
				default:
609
					$this->error="direct delivery connection is already established and sender is already set";
610
					return(0);
611
			}
612
		}
613
		else
614
		{
615
			if(strcmp($this->state,"Connected"))
616
			{
617
				$this->error="connection is not in the initial state";
618
				return(0);
619
			}
620
		}
621
		$this->error="";
622
		if(!$this->PutLine("MAIL FROM:<$sender>"))
623
			return(0);
624
		if(!IsSet($this->esmtp_extensions["PIPELINING"])
625
		&& $this->VerifyResultLines("250",$responses)<=0)
626
			return(0);
627
		$this->state="SenderSet";
628
		if(IsSet($this->esmtp_extensions["PIPELINING"]))
629
			$this->pending_sender=1;
630
		$this->pending_recipients=0;
631
		return(1);
632
	}
633

    
634
	Function SetRecipient($recipient)
635
	{
636
		if($this->direct_delivery)
637
		{
638
			if(GetType($at=strrpos($recipient,"@"))!="integer")
639
				return("it was not specified a valid direct recipient");
640
			$domain=substr($recipient,$at+1);
641
			switch($this->state)
642
			{
643
				case "Disconnected":
644
					if(!$this->Connect($domain))
645
						return(0);
646
					if(!$this->MailFrom(""))
647
					{
648
						$error=$this->error;
649
						$this->Disconnect();
650
						$this->error=$error;
651
						return(0);
652
					}
653
					break;
654
				case "SenderSet":
655
				case "RecipientSet":
656
					if(strcmp($this->connected_domain,$domain))
657
					{
658
						$this->error="it is not possible to deliver directly to recipients of different domains";
659
						return(0);
660
					}
661
					break;
662
				default:
663
					$this->error="connection is already established and the recipient is already set";
664
					return(0);
665
			}
666
		}
667
		else
668
		{
669
			switch($this->state)
670
			{
671
				case "SenderSet":
672
				case "RecipientSet":
673
					break;
674
				default:
675
					$this->error="connection is not in the recipient setting state";
676
					return(0);
677
			}
678
		}
679
		$this->error="";
680
		if(!$this->PutLine("RCPT TO:<$recipient>"))
681
			return(0);
682
		if(IsSet($this->esmtp_extensions["PIPELINING"]))
683
		{
684
			$this->pending_recipients++;
685
			if($this->pending_recipients>=$this->maximum_piped_recipients)
686
			{
687
				if(!$this->FlushRecipients())
688
					return(0);
689
			}
690
		}
691
		else
692
		{
693
			if($this->VerifyResultLines(array("250","251"),$responses)<=0)
694
				return(0);
695
		}
696
		$this->state="RecipientSet";
697
		return(1);
698
	}
699

    
700
	Function StartData()
701
	{
702
		if(strcmp($this->state,"RecipientSet"))
703
		{
704
			$this->error="connection is not in the start sending data state";
705
			return(0);
706
		}
707
		$this->error="";
708
		if(!$this->PutLine("DATA"))
709
			return(0);
710
		if($this->pending_recipients)
711
		{
712
			if(!$this->FlushRecipients())
713
				return(0);
714
		}
715
		if($this->VerifyResultLines("354",$responses)<=0)
716
			return(0);
717
		$this->state="SendingData";
718
		return(1);
719
	}
720

    
721
	Function PrepareData(&$data,&$output,$preg=1)
722
	{
723
		if($preg
724
		&& function_exists("preg_replace"))
725
			$output=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);
726
		else
727
			$output=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))));
728
	}
729

    
730
	Function SendData($data)
731
	{
732
		if(strcmp($this->state,"SendingData"))
733
		{
734
			$this->error="connection is not in the sending data state";
735
			return(0);
736
		}
737
		$this->error="";
738
		return($this->PutData($data));
739
	}
740

    
741
	Function EndSendingData()
742
	{
743
		if(strcmp($this->state,"SendingData"))
744
		{
745
			$this->error="connection is not in the sending data state";
746
			return(0);
747
		}
748
		$this->error="";
749
		if(!$this->PutLine("\r\n.")
750
		|| $this->VerifyResultLines("250",$responses)<=0)
751
			return(0);
752
		$this->state="Connected";
753
		return(1);
754
	}
755

    
756
	Function ResetConnection()
757
	{
758
		switch($this->state)
759
		{
760
			case "Connected":
761
				return(1);
762
			case "SendingData":
763
				$this->error="can not reset the connection while sending data";
764
				return(0);
765
			case "Disconnected":
766
				$this->error="can not reset the connection before it is established";
767
				return(0);
768
		}
769
		$this->error="";
770
		if(!$this->PutLine("RSET")
771
		|| $this->VerifyResultLines("250",$responses)<=0)
772
			return(0);
773
		$this->state="Connected";
774
		return(1);
775
	}
776

    
777
	Function Disconnect($quit=1)
778
	{
779
		if(!strcmp($this->state,"Disconnected"))
780
		{
781
			$this->error="it was not previously established a SMTP connection";
782
			return(0);
783
		}
784
		$this->error="";
785
		if(!strcmp($this->state,"Connected")
786
		&& $quit
787
		&& (!$this->PutLine("QUIT")
788
		|| ($this->VerifyResultLines("221",$responses)<=0
789
		&& !$this->disconnected_error)))
790
			return(0);
791
		if($this->disconnected_error)
792
			$this->disconnected_error=0;
793
		else
794
			fclose($this->connection);
795
		$this->connection=0;
796
		$this->state="Disconnected";
797
		if($this->debug)
798
			$this->OutputDebug("Disconnected.");
799
		return(1);
800
	}
801

    
802
	Function SendMessage($sender,$recipients,$headers,$body)
803
	{
804
		if(($success=$this->Connect()))
805
		{
806
			if(($success=$this->MailFrom($sender)))
807
			{
808
				for($recipient=0;$recipient<count($recipients);$recipient++)
809
				{
810
					if(!($success=$this->SetRecipient($recipients[$recipient])))
811
						break;
812
				}
813
				if($success
814
				&& ($success=$this->StartData()))
815
				{
816
					for($header_data="",$header=0;$header<count($headers);$header++)
817
						$header_data.=$headers[$header]."\r\n";
818
					if(($success=$this->SendData($header_data."\r\n")))
819
					{
820
						$this->PrepareData($body,$body_data);
821
						$success=$this->SendData($body_data);
822
					}
823
					if($success)
824
						$success=$this->EndSendingData();
825
				}
826
			}
827
			$error=$this->error;
828
			$disconnect_success=$this->Disconnect($success);
829
			if($success)
830
				$success=$disconnect_success;
831
			else
832
				$this->error=$error;
833
		}
834
		return($success);
835
	}
836

    
837
};
838

    
839
?>
(33-33/43)