Project

General

Profile

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

    
9
/*
10
	pfSense_MODULE:	notifications
11
*/
12

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

    
39
	/* private variables - DO NOT ACCESS */
40

    
41
	var $state="Disconnected";
42
	var $connection=0;
43
	var $pending_recipients=0;
44
	var $next_token="";
45
	var $direct_sender="";
46
	var $connected_domain="";
47
	var $result_code;
48
	var $disconnected_error=0;
49

    
50
	/* Private methods - DO NOT CALL */
51

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

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

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

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

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

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

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

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

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

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

    
359
	/* Public methods */
360

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

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

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

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

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

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

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

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

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

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

    
841
};
842

    
843
?>
(38-38/50)