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
|
?>
|