Project

General

Profile

Download (21.3 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.=gettext(": data access time out");
93
			elseif($status["eof"])
94
			{
95
				$this->error.=gettext(": 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=gettext("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(gettext("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(gettext("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(gettext("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(gettext("establishing SSL connections requires at least PHP version 4.3.0"));
222
			if(!function_exists("extension_loaded")
223
			|| !extension_loaded("openssl"))
224
				return(gettext("establishing SSL connections requires the OpenSSL extension enabled"));
225
		}
226
		if(preg_match('/^[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(sprintf(gettext("could not resolve host \"%s\""), $domain));
234
		}
235
		if(strlen($this->exclude_address)
236
		&& !strcmp(@gethostbyname($this->exclude_address),$ip))
237
			return(sprintf(gettext("domain \"%s\" resolved to an address excluded to be valid"), $domain));
238
		if($this->debug)
239
			$this->OutputDebug(sprintf(gettext('Connecting to host address "%1$s" port %2$s...'), $ip, $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(gettext("-3 socket could not be created"));
247
			case "-4":
248
				return(sprintf(gettext("-4 dns lookup on hostname \"%s\" failed"), $domain));
249
			case "-5":
250
				return(gettext("-5 connection refused or timed out"));
251
			case "-6":
252
				return(gettext("-6 fdopen() call failed"));
253
			case "-7":
254
				return(gettext("-7 setvbuf() call failed"));
255
		}
256
		return(sprintf(gettext('could not connect to the host "%1$s": %2$s'), $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=gettext("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=printf(gettext('authenticated mechanism %1$s may not be used: %2$s'), $this->authentication_mechanism, $sasl->error);
290
					return(0);
291
				}
292
				break;
293
			default:
294
				$this->error=gettext("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=gettext("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=gettext("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=gettext("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=gettext("Authentication error:") . " ".$responses[0];
347
								return(0);
348
						}
349
						break;
350
					default:
351
						$this->error=gettext("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=gettext("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=gettext("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=gettext("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, sprintf(gettext("Resolving POP3 authentication host \"%s\"..."), $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=gettext("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=gettext("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=gettext("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=gettext("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, sprintf(gettext("Resolving SMTP server domain \"%s\"..."), $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(sprintf(gettext("Connected to SMTP server \"%s\"."), $domain));
464
		if(!strcmp($localhost=$this->localhost,"")
465
		&& !strcmp($localhost=getenv("SERVER_NAME"),"")
466
		&& !strcmp($localhost=getenv("HOST"),"")
467
		&& !strcmp($localhost=getenv("HOSTNAME"),"")
468
		&& !strcmp($localhost=exec("/bin/hostname"),""))
469
			$localhost="localhost";
470
		$success=0;
471
		if($this->VerifyResultLines("220",$responses)>0)
472
		{
473
			$fallback=1;
474
			if($this->esmtp
475
			|| strlen($this->user))
476
			{
477
				if($this->PutLine("EHLO $localhost"))
478
				{
479
					if(($success_code=$this->VerifyResultLines("250",$responses))>0)
480
					{
481
						$this->esmtp_host=$this->Tokenize($responses[0]," ");
482
						for($response=1;$response<count($responses);$response++)
483
						{
484
							$extension=strtoupper($this->Tokenize($responses[$response]," "));
485
							$this->esmtp_extensions[$extension]=$this->Tokenize("");
486
						}
487
						$success=1;
488
						$fallback=0;
489
					}
490
					else
491
					{
492
						if($success_code==0)
493
						{
494
							$code=$this->Tokenize($this->error," -");
495
							switch($code)
496
							{
497
								case "421":
498
									$fallback=0;
499
									break;
500
							}
501
						}
502
					}
503
				}
504
				else
505
					$fallback=0;
506
			}
507
			if($fallback)
508
			{
509
				if($this->PutLine("HELO $localhost")
510
				&& $this->VerifyResultLines("250",$responses)>0)
511
					$success=1;
512
			}
513
			if($success
514
			&& strlen($this->user)
515
			&& strlen($this->pop3_auth_host)==0)
516
			{
517
				if(!IsSet($this->esmtp_extensions["AUTH"]))
518
				{
519
					$this->error=gettext("server does not require authentication");
520
					$success=0;
521
				}
522
				else
523
				{
524
					if(strlen($this->authentication_mechanism))
525
						$mechanisms=array($this->authentication_mechanism);
526
					else
527
					{
528
						$mechanisms=array();
529
						for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
530
							$mechanisms[]=$authentication;
531
					}
532
					$credentials=array(
533
						"user"=>$this->user,
534
						"password"=>$this->password
535
					);
536
					if(strlen($this->realm))
537
						$credentials["realm"]=$this->realm;
538
					if(strlen($this->workstation))
539
						$credentials["workstation"]=$this->workstation;
540
					$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
541
					if(!$success
542
					&& !strcmp($mechanism,"PLAIN"))
543
					{
544
						/*
545
						 * Author:  Russell Robinson, 25 May 2003, http://www.tectite.com/
546
						 * Purpose: Try various AUTH PLAIN authentication methods.
547
						 */
548
						$mechanisms=array("PLAIN");
549
						$credentials=array(
550
							"user"=>$this->user,
551
							"password"=>$this->password
552
						);
553
						if(strlen($this->realm))
554
						{
555
							/*
556
							 * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method
557
							 * some sendmails won't accept the realm, so try again without it
558
							 */
559
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
560
						}
561
						if(!$success)
562
						{
563
							/*
564
							 * It was seen an EXIM configuration like this:
565
							 * user^password^unused
566
							 */
567
							$credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE;
568
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
569
						}
570
						if(!$success)
571
						{
572
							/*
573
							 * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html
574
							 * specifies: ^user^password
575
							 */
576
							$credentials["mode"]=SASL_PLAIN_EXIM_MODE;
577
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
578
						}
579
					}
580
					if($success
581
					&& strlen($mechanism)==0)
582
					{
583
						$this->error=gettext("it is not supported any of the authentication mechanisms required by the server");
584
						$success=0;
585
					}
586
				}
587
			}
588
		}
589
		if($success)
590
		{
591
			$this->state="Connected";
592
			$this->connected_domain=$domain;
593
		}
594
		else
595
		{
596
			fclose($this->connection);
597
			$this->connection=0;
598
		}
599
		return($success);
600
	}
601

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

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

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

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

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

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

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

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

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

    
843
};
844

    
845
?>
(51-51/66)