Project

General

Profile

Download (21.8 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 $tls=0;
20
	var $localhost="";
21
	var $timeout=0;
22
	var $data_timeout=0;
23
	var $direct_delivery=0;
24
	var $error="";
25
	var $debug=0;
26
	var $html_debug=0;
27
	var $esmtp=1;
28
	var $esmtp_host="";
29
	var $esmtp_extensions=array();
30
	var $maximum_piped_recipients=100;
31
	var $exclude_address="";
32
	var $getmxrr="GetMXRR";
33
	var $pop3_auth_host="";
34
	var $pop3_auth_port=110;
35

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

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

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

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

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

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

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

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

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

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

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

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

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

    
356
	/* Public methods */
357

    
358
	Function Connect($domain="")
359
	{
360
		if(strcmp($this->state,"Disconnected"))
361
		{
362
			$this->error=gettext("connection is already established");
363
			return(0);
364
		}
365
		$this->disconnected_error=0;
366
		$this->error=$error="";
367
		$this->esmtp_host="";
368
		$this->esmtp_extensions=array();
369
		$hosts=array();
370
		if($this->direct_delivery)
371
		{
372
			if(strlen($domain)==0)
373
				return(1);
374
			$hosts=$weights=$mxhosts=array();
375
			$getmxrr=$this->getmxrr;
376
			if(function_exists($getmxrr)
377
			&& $getmxrr($domain,$hosts,$weights))
378
			{
379
				for($host=0;$host<count($hosts);$host++)
380
					$mxhosts[$weights[$host]]=$hosts[$host];
381
				KSort($mxhosts);
382
				for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++)
383
					$hosts[$host]=$mxhosts[Key($mxhosts)];
384
			}
385
			else
386
			{
387
				if(strcmp(@gethostbyname($domain),$domain)!=0)
388
					$hosts[]=$domain;
389
			}
390
		}
391
		else
392
		{
393
			if(strlen($this->host_name))
394
				$hosts[]=$this->host_name;
395
			if(strlen($this->pop3_auth_host))
396
			{
397
				$user=$this->user;
398
				if(strlen($user)==0)
399
				{
400
					$this->error=gettext("it was not specified the POP3 authentication user");
401
					return(0);
402
				}
403
				$password=$this->password;
404
				if(strlen($password)==0)
405
				{
406
					$this->error=gettext("it was not specified the POP3 authentication password");
407
					return(0);
408
				}
409
				$domain=$this->pop3_auth_host;
410
				$this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, sprintf(gettext("Resolving POP3 authentication host \"%s\"..."), $domain));
411
				if(strlen($this->error))
412
					return(0);
413
				if(strlen($response=$this->GetLine())==0)
414
					return(0);
415
				if(strcmp($this->Tokenize($response," "),"+OK"))
416
				{
417
					$this->error=gettext("POP3 authentication server greeting was not found");
418
					return(0);
419
				}
420
				if(!$this->PutLine("USER ".$this->user)
421
				|| strlen($response=$this->GetLine())==0)
422
					return(0);
423
				if(strcmp($this->Tokenize($response," "),"+OK"))
424
				{
425
					$this->error=gettext("POP3 authentication user was not accepted:") . " ".$this->Tokenize("\r\n");
426
					return(0);
427
				}
428
				if(!$this->PutLine("PASS ".$password)
429
				|| strlen($response=$this->GetLine())==0)
430
					return(0);
431
				if(strcmp($this->Tokenize($response," "),"+OK"))
432
				{
433
					$this->error=gettext("POP3 authentication password was not accepted:") . " ".$this->Tokenize("\r\n");
434
					return(0);
435
				}
436
				fclose($this->connection);
437
				$this->connection=0;
438
			}
439
		}
440
		if(count($hosts)==0)
441
		{
442
			$this->error=gettext("could not determine the SMTP to connect");
443
			return(0);
444
		}
445
		for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++)
446
		{
447
			$domain=$hosts[$host];
448
			$error=$this->ConnectToHost($domain, $this->host_port, sprintf(gettext("Resolving SMTP server domain \"%s\"..."), $domain));
449
		}
450
		if(strlen($error))
451
		{
452
			$this->error=$error;
453
			return(0);
454
		}
455
		$timeout=($this->data_timeout ? $this->data_timeout : $this->timeout);
456
		if($timeout
457
		&& function_exists("socket_set_timeout"))
458
			socket_set_timeout($this->connection,$timeout,0);
459
		if($this->debug)
460
			$this->OutputDebug(sprintf(gettext("Connected to SMTP server \"%s\"."), $domain));
461
		if($this->VerifyResultLines("220",$responses)>0)
462
		{
463
			// Send our HELLO
464
			$success = $this->hello($this->hostname());
465
			if ($this->tls)
466
				$success = $this->startTLS();
467

    
468
			if($success
469
			&& strlen($this->user)
470
			&& strlen($this->pop3_auth_host)==0)
471
			{
472
				if(!IsSet($this->esmtp_extensions["AUTH"]))
473
				{
474
					$this->error = gettext("server does not require authentication");
475
					$success=0;
476
				}
477
				else
478
				{
479
					if(strlen($this->authentication_mechanism))
480
						$mechanisms=array($this->authentication_mechanism);
481
					else
482
					{
483
						$mechanisms=array();
484
						for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
485
							$mechanisms[]=$authentication;
486
					}
487
					$credentials=array(
488
						"user"=>$this->user,
489
						"password"=>$this->password
490
					);
491
					if(strlen($this->realm))
492
						$credentials["realm"]=$this->realm;
493
					if(strlen($this->workstation))
494
						$credentials["workstation"]=$this->workstation;
495
					$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
496
					if(!$success
497
					&& !strcmp($mechanism,"PLAIN"))
498
					{
499
						/*
500
						 * Author:  Russell Robinson, 25 May 2003, http://www.tectite.com/
501
						 * Purpose: Try various AUTH PLAIN authentication methods.
502
						 */
503
						$mechanisms=array("PLAIN");
504
						$credentials=array(
505
							"user"=>$this->user,
506
							"password"=>$this->password
507
						);
508
						if(strlen($this->realm))
509
						{
510
							/*
511
							 * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method
512
							 * some sendmails won't accept the realm, so try again without it
513
							 */
514
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
515
						}
516
						if(!$success)
517
						{
518
							/*
519
							 * It was seen an EXIM configuration like this:
520
							 * user^password^unused
521
							 */
522
							$credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE;
523
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
524
						}
525
						if(!$success)
526
						{
527
							/*
528
							 * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html
529
							 * specifies: ^user^password
530
							 */
531
							$credentials["mode"]=SASL_PLAIN_EXIM_MODE;
532
							$success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism);
533
						}
534
					}
535
					if($success
536
					&& strlen($mechanism)==0)
537
					{
538
						$this->error=gettext("it is not supported any of the authentication mechanisms required by the server");
539
						$success=0;
540
					}
541
				}
542
			}
543
		}
544
		if($success)
545
		{
546
			$this->state="Connected";
547
			$this->connected_domain=$domain;
548
		}
549
		else
550
		{
551
			fclose($this->connection);
552
			$this->connection=0;
553
		}
554
		return($success);
555
	}
556

    
557
	Function hostname() {
558
		if(!strcmp($localhost=$this->localhost,"")
559
		&& !strcmp($localhost=getenv("SERVER_NAME"),"")
560
		&& !strcmp($localhost=getenv("HOST"),"")
561
		&& !strcmp($localhost=getenv("HOSTNAME"),"")
562
		&& !strcmp($localhost=gethostname(),""))
563
			$localhost="localhost";
564

    
565
		return $localhost;
566
	}
567

    
568
	Function hello()
569
	{
570
		$success = 0;
571
		$fallback = 1;
572
		if ($this->esmtp || strlen($this->user)) {
573
			if ($this->PutLine("EHLO ".$this->hostname())) {
574
				if (($success_code = $this->VerifyResultLines("250",$responses)) > 0) {
575
					$this->esmtp_host = $this->Tokenize($responses[0]," ");
576
					for($response=1;$response<count($responses);$response++) {
577
						$extension = strtoupper($this->Tokenize($responses[$response]," "));
578
						$this->esmtp_extensions[$extension]=$this->Tokenize("");
579
					}
580
					$success = 1;
581
					$fallback = 0;
582
				} else {
583
					if ($success_code == 0) {
584
						$code = $this->Tokenize($this->error," -");
585
						switch($code) {
586
							case "421":
587
								$fallback=0;
588
								break;
589
						}
590
					}
591
				}
592
			} else
593
				$fallback=0;
594
		}
595

    
596
		if ($fallback) {
597
			if ($this->PutLine("HELO $localhost") && $this->VerifyResultLines("250",$responses)>0)
598
				$success=1;
599
		}
600
		return $success;
601
	}
602

    
603
	Function startTLS() {
604
		if ($this->PutLine("STARTTLS") && $this->VerifyResultLines("220",$responses)>0) {
605
			if (!stream_socket_enable_crypto($this->connection,true,STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
606
				return false;
607
			} else {
608
				// Resend HELO since session has been reset
609
				return $this->hello($this->hostname);
610
			}
611
		} else
612
			return false;
613
	}
614

    
615
	Function MailFrom($sender)
616
	{
617
		if($this->direct_delivery)
618
		{
619
			switch($this->state)
620
			{
621
				case "Disconnected":
622
					$this->direct_sender=$sender;
623
					return(1);
624
				case "Connected":
625
					$sender=$this->direct_sender;
626
					break;
627
				default:
628
					$this->error=gettext("direct delivery connection is already established and sender is already set");
629
					return(0);
630
			}
631
		}
632
		else
633
		{
634
			if(strcmp($this->state,"Connected"))
635
			{
636
				$this->error=gettext("connection is not in the initial state");
637
				return(0);
638
			}
639
		}
640
		$this->error="";
641
		if(!$this->PutLine("MAIL FROM:<$sender>"))
642
			return(0);
643
		if(!IsSet($this->esmtp_extensions["PIPELINING"])
644
		&& $this->VerifyResultLines("250",$responses)<=0)
645
			return(0);
646
		$this->state="SenderSet";
647
		if(IsSet($this->esmtp_extensions["PIPELINING"]))
648
			$this->pending_sender=1;
649
		$this->pending_recipients=0;
650
		return(1);
651
	}
652

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

    
719
	Function StartData()
720
	{
721
		if(strcmp($this->state,"RecipientSet"))
722
		{
723
			$this->error=gettext("connection is not in the start sending data state");
724
			return(0);
725
		}
726
		$this->error="";
727
		if(!$this->PutLine("DATA"))
728
			return(0);
729
		if($this->pending_recipients)
730
		{
731
			if(!$this->FlushRecipients())
732
				return(0);
733
		}
734
		if($this->VerifyResultLines("354",$responses)<=0)
735
			return(0);
736
		$this->state="SendingData";
737
		return(1);
738
	}
739

    
740
	Function PrepareData(&$data,&$output,$preg=1)
741
	{
742
		if($preg
743
		&& function_exists("preg_replace"))
744
			$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);
745
		else
746
			$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))));
747
	}
748

    
749
	Function SendData($data)
750
	{
751
		if(strcmp($this->state,"SendingData"))
752
		{
753
			$this->error=gettext("connection is not in the sending data state");
754
			return(0);
755
		}
756
		$this->error="";
757
		return($this->PutData($data));
758
	}
759

    
760
	Function EndSendingData()
761
	{
762
		if(strcmp($this->state,"SendingData"))
763
		{
764
			$this->error=gettext("connection is not in the sending data state");
765
			return(0);
766
		}
767
		$this->error="";
768
		if(!$this->PutLine("\r\n.")
769
		|| $this->VerifyResultLines("250",$responses)<=0)
770
			return(0);
771
		$this->state="Connected";
772
		return(1);
773
	}
774

    
775
	Function ResetConnection()
776
	{
777
		switch($this->state)
778
		{
779
			case "Connected":
780
				return(1);
781
			case "SendingData":
782
				$this->error="can not reset the connection while sending data";
783
				return(0);
784
			case "Disconnected":
785
				$this->error="can not reset the connection before it is established";
786
				return(0);
787
		}
788
		$this->error="";
789
		if(!$this->PutLine("RSET")
790
		|| $this->VerifyResultLines("250",$responses)<=0)
791
			return(0);
792
		$this->state="Connected";
793
		return(1);
794
	}
795

    
796
	Function Disconnect($quit=1)
797
	{
798
		if(!strcmp($this->state,"Disconnected"))
799
		{
800
			$this->error=gettext("it was not previously established a SMTP connection");
801
			return(0);
802
		}
803
		$this->error="";
804
		if(!strcmp($this->state,"Connected")
805
		&& $quit
806
		&& (!$this->PutLine("QUIT")
807
		|| ($this->VerifyResultLines("221",$responses)<=0
808
		&& !$this->disconnected_error)))
809
			return(0);
810
		if($this->disconnected_error)
811
			$this->disconnected_error=0;
812
		else
813
			fclose($this->connection);
814
		$this->connection=0;
815
		$this->state="Disconnected";
816
		if($this->debug)
817
			$this->OutputDebug("Disconnected.");
818
		return(1);
819
	}
820

    
821
	Function SendMessage($sender,$recipients,$headers,$body)
822
	{
823
		if(($success=$this->Connect()))
824
		{
825
			if(($success=$this->MailFrom($sender)))
826
			{
827
				for($recipient=0;$recipient<count($recipients);$recipient++)
828
				{
829
					if(!($success=$this->SetRecipient($recipients[$recipient])))
830
						break;
831
				}
832
				if($success
833
				&& ($success=$this->StartData()))
834
				{
835
					for($header_data="",$header=0;$header<count($headers);$header++)
836
						$header_data.=$headers[$header]."\r\n";
837
					if(($success=$this->SendData($header_data."\r\n")))
838
					{
839
						$this->PrepareData($body,$body_data);
840
						$success=$this->SendData($body_data);
841
					}
842
					if($success)
843
						$success=$this->EndSendingData();
844
				}
845
			}
846
			$error=$this->error;
847
			$disconnect_success=$this->Disconnect($success);
848
			if($success)
849
				$success=$disconnect_success;
850
			else
851
				$this->error=$error;
852
		}
853
		return($success);
854
	}
855

    
856
};
857

    
858
?>
(51-51/65)