squirrelmail-webmail  1.4.22
About: SquirrelMail is a standards-based webmail package with strong MIME support, address books, and folder manipulation (written in PHP4).
  Fossies Dox: squirrelmail-webmail-1.4.22.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

Deliver_SMTP.class.php
Go to the documentation of this file.
1 <?php
2 
15 require_once(SM_PATH . 'class/deliver/Deliver.class.php');
16 
21 class Deliver_SMTP extends Deliver {
22 
23  function preWriteToStream(&$s) {
24  if ($s) {
25  if ($s{0} == '.') $s = '.' . $s;
26  $s = str_replace("\n.","\n..",$s);
27  }
28  }
29 
30  function initStream($message, $domain, $length=0, $host='', $port='', $user='', $pass='', $authpop=false, $pop_host='') {
32 
33  if ($authpop) {
34  $this->authPop($pop_host, '', $user, $pass);
35  }
36 
37  $rfc822_header = $message->rfc822_header;
38 
39  $from = $rfc822_header->from[0];
40  $to = $rfc822_header->to;
41  $cc = $rfc822_header->cc;
42  $bcc = $rfc822_header->bcc;
43  $content_type = $rfc822_header->content_type;
44 
45  // MAIL FROM: <from address> MUST be empty in cae of MDN (RFC2298)
46  if ($content_type->type0 == 'multipart' &&
47  $content_type->type1 == 'report' &&
48  isset($content_type->properties['report-type']) &&
49  $content_type->properties['report-type']=='disposition-notification') {
50  // reinitialize the from object because otherwise the from header somehow
51  // is affected. This $from var is used for smtp command MAIL FROM which
52  // is not the same as what we put in the rfc822 header.
53  $from = new AddressStructure();
54  $from->host = '';
55  $from->mailbox = '';
56  }
57 
58  if (($use_smtp_tls == true) and (check_php_version(4,3)) and (extension_loaded('openssl'))) {
59  $stream = @fsockopen('tls://' . $host, $port, $errorNumber, $errorString);
60  } else {
61  $stream = @fsockopen($host, $port, $errorNumber, $errorString);
62  }
63 
64  if (!$stream) {
65  $this->dlv_msg = $errorString;
66  $this->dlv_ret_nr = $errorNumber;
67  $this->dlv_server_msg = _("Can't open SMTP stream.");
68  return(0);
69  }
70  $tmp = fgets($stream, 1024);
71  if ($this->errorCheck($tmp, $stream)) {
72  return(0);
73  }
74 
75  /*
76  * If $_SERVER['HTTP_HOST'] is set, use that in our HELO to the SMTP
77  * server. This should fix the DNS issues some people have had
78  */
79  if (sqgetGlobalVar('HTTP_HOST', $HTTP_HOST, SQ_SERVER)) { // HTTP_HOST is set
80  // optionally trim off port number
81  if($p = strrpos($HTTP_HOST, ':')) {
82  $HTTP_HOST = substr($HTTP_HOST, 0, $p);
83  }
84  $helohost = $HTTP_HOST;
85  } else { // For some reason, HTTP_HOST is not set - revert to old behavior
86  $helohost = $domain;
87  }
88 
89  // if the host is an IPv4 address, enclose it in brackets
90  //
91  if (preg_match('/^\d+\.\d+\.\d+\.\d+$/', $helohost))
92  $helohost = '[' . $helohost . ']';
93 
94  /* Lets introduce ourselves */
95  fputs($stream, "EHLO $helohost\r\n");
96  $tmp = fgets($stream,1024);
97  if ($this->errorCheck($tmp,$stream)) {
98  // fall back to HELO if EHLO is not supported (error 5xx)
99  if ($this->dlv_ret_nr{0} == '5') {
100  fputs($stream, "HELO $helohost\r\n");
101  $tmp = fgets($stream,1024);
102  if ($this->errorCheck($tmp,$stream)) {
103  return(0);
104  }
105  } else {
106  return(0);
107  }
108  }
109 
110  // Try authentication by a plugin
111  $smtp_auth_args = array(
112  'auth_mech' => $smtp_auth_mech,
113  'user' => $user,
114  'pass' => $pass,
115  'host' => $host,
116  'port' => $port,
117  'stream' => $stream,
118  );
119  if (boolean_hook_function('smtp_auth', $smtp_auth_args, 1)) {
120  // authentication succeeded
121  } else if (( $smtp_auth_mech == 'cram-md5') or ( $smtp_auth_mech == 'digest-md5' )) {
122  // Doing some form of non-plain auth
123  if ($smtp_auth_mech == 'cram-md5') {
124  fputs($stream, "AUTH CRAM-MD5\r\n");
125  } elseif ($smtp_auth_mech == 'digest-md5') {
126  fputs($stream, "AUTH DIGEST-MD5\r\n");
127  }
128 
129  $tmp = fgets($stream,1024);
130 
131  if ($this->errorCheck($tmp,$stream)) {
132  return(0);
133  }
134 
135  // At this point, $tmp should hold "334 <challenge string>"
136  $chall = substr($tmp,4);
137  // Depending on mechanism, generate response string
138  if ($smtp_auth_mech == 'cram-md5') {
139  $response = cram_md5_response($user,$pass,$chall);
140  } elseif ($smtp_auth_mech == 'digest-md5') {
141  $response = digest_md5_response($user,$pass,$chall,'smtp',$host);
142  }
143  fputs($stream, $response);
144 
145  // Let's see what the server had to say about that
146  $tmp = fgets($stream,1024);
147  if ($this->errorCheck($tmp,$stream)) {
148  return(0);
149  }
150 
151  // CRAM-MD5 is done at this point. If DIGEST-MD5, there's a bit more to go
152  if ($smtp_auth_mech == 'digest-md5') {
153  // $tmp contains rspauth, but I don't store that yet. (No need yet)
154  fputs($stream,"\r\n");
155  $tmp = fgets($stream,1024);
156 
157  if ($this->errorCheck($tmp,$stream)) {
158  return(0);
159  }
160  }
161  // CRAM-MD5 and DIGEST-MD5 code ends here
162  } elseif ($smtp_auth_mech == 'none') {
163  // No auth at all, just send helo and then send the mail
164  // We already said hi earlier, nothing more is needed.
165  } elseif ($smtp_auth_mech == 'login') {
166  // The LOGIN method
167  fputs($stream, "AUTH LOGIN\r\n");
168  $tmp = fgets($stream, 1024);
169 
170  if ($this->errorCheck($tmp, $stream)) {
171  return(0);
172  }
173  fputs($stream, base64_encode ($user) . "\r\n");
174  $tmp = fgets($stream, 1024);
175  if ($this->errorCheck($tmp, $stream)) {
176  return(0);
177  }
178 
179  fputs($stream, base64_encode($pass) . "\r\n");
180  $tmp = fgets($stream, 1024);
181  if ($this->errorCheck($tmp, $stream)) {
182  return(0);
183  }
184  } elseif ($smtp_auth_mech == "plain") {
185  /* SASL Plain */
186  $auth = base64_encode("$user\0$user\0$pass");
187 
188  $query = "AUTH PLAIN\r\n";
189  fputs($stream, $query);
190  $read=fgets($stream, 1024);
191 
192  if (substr($read,0,3) == '334') { // OK so far..
193  fputs($stream, "$auth\r\n");
194  $read = fgets($stream, 1024);
195  }
196 
197  $results=explode(" ",$read,3);
198  $response=$results[1];
199  $message=$results[2];
200  } else {
201  /* Right here, they've reached an unsupported auth mechanism.
202  This is the ugliest hack I've ever done, but it'll do till I can fix
203  things up better tomorrow. So tired... */
204  if ($this->errorCheck("535 Unable to use this auth type",$stream)) {
205  return(0);
206  }
207  }
208 
209  /* Ok, who is sending the message? */
210  $fromaddress = (strlen($from->mailbox) && $from->host) ?
211  $from->mailbox.'@'.$from->host : '';
212  fputs($stream, 'MAIL FROM:<'.$fromaddress.">\r\n");
213  $tmp = fgets($stream, 1024);
214  if ($this->errorCheck($tmp, $stream)) {
215  return(0);
216  }
217 
218  /* send who the recipients are */
219  for ($i = 0, $cnt = count($to); $i < $cnt; $i++) {
220  if (!$to[$i]->host) $to[$i]->host = $domain;
221  if (strlen($to[$i]->mailbox)) {
222  fputs($stream, 'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host.">\r\n");
223  $tmp = fgets($stream, 1024);
224  if ($this->errorCheck($tmp, $stream)) {
225  return(0);
226  }
227  }
228  }
229 
230  for ($i = 0, $cnt = count($cc); $i < $cnt; $i++) {
231  if (!$cc[$i]->host) $cc[$i]->host = $domain;
232  if (strlen($cc[$i]->mailbox)) {
233  fputs($stream, 'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host.">\r\n");
234  $tmp = fgets($stream, 1024);
235  if ($this->errorCheck($tmp, $stream)) {
236  return(0);
237  }
238  }
239  }
240 
241  for ($i = 0, $cnt = count($bcc); $i < $cnt; $i++) {
242  if (!$bcc[$i]->host) $bcc[$i]->host = $domain;
243  if (strlen($bcc[$i]->mailbox)) {
244  fputs($stream, 'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host.">\r\n");
245  $tmp = fgets($stream, 1024);
246  if ($this->errorCheck($tmp, $stream)) {
247  return(0);
248  }
249  }
250  }
251  /* Lets start sending the actual message */
252  fputs($stream, "DATA\r\n");
253  $tmp = fgets($stream, 1024);
254  if ($this->errorCheck($tmp, $stream)) {
255  return(0);
256  }
257  return $stream;
258  }
259 
261  fputs($stream, "\r\n.\r\n"); /* end the DATA part */
262  $tmp = fgets($stream, 1024);
263  $this->errorCheck($tmp, $stream);
264  if ($this->dlv_ret_nr != 250) {
265  return(0);
266  }
267  fputs($stream, "QUIT\r\n"); /* log off */
268  fclose($stream);
269  return true;
270  }
271 
272  /* check if an SMTP reply is an error and set an error message) */
273  function errorCheck($line, $smtpConnection) {
274 
275  $err_num = substr($line, 0, 3);
276  $this->dlv_ret_nr = $err_num;
277  $server_msg = substr($line, 4);
278 
279  while(substr($line, 0, 4) == ($err_num.'-')) {
280  $line = fgets($smtpConnection, 1024);
281  $server_msg .= substr($line, 4);
282  }
283 
284  if ( ((int) $err_num{0}) < 4) {
285  return false;
286  }
287 
288  switch ($err_num) {
289  case '421': $message = _("Service not available, closing channel");
290  break;
291  case '432': $message = _("A password transition is needed");
292  break;
293  case '450': $message = _("Requested mail action not taken: mailbox unavailable");
294  break;
295  case '451': $message = _("Requested action aborted: error in processing");
296  break;
297  case '452': $message = _("Requested action not taken: insufficient system storage");
298  break;
299  case '454': $message = _("Temporary authentication failure");
300  break;
301  case '500': $message = _("Syntax error; command not recognized");
302  break;
303  case '501': $message = _("Syntax error in parameters or arguments");
304  break;
305  case '502': $message = _("Command not implemented");
306  break;
307  case '503': $message = _("Bad sequence of commands");
308  break;
309  case '504': $message = _("Command parameter not implemented");
310  break;
311  case '530': $message = _("Authentication required");
312  break;
313  case '534': $message = _("Authentication mechanism is too weak");
314  break;
315  case '535': $message = _("Authentication failed");
316  break;
317  case '538': $message = _("Encryption required for requested authentication mechanism");
318  break;
319  case '550': $message = _("Requested action not taken: mailbox unavailable");
320  break;
321  case '551': $message = _("User not local; please try forwarding");
322  break;
323  case '552': $message = _("Requested mail action aborted: exceeding storage allocation");
324  break;
325  case '553': $message = _("Requested action not taken: mailbox name not allowed");
326  break;
327  case '554': $message = _("Transaction failed");
328  break;
329  default: $message = _("Unknown response");
330  break;
331  }
332 
333  $this->dlv_msg = $message;
334  $this->dlv_server_msg = nl2br(htmlspecialchars($server_msg));
335 
336  return true;
337  }
338 
339  function authPop($pop_server='', $pop_port='', $user, $pass) {
340  if (!$pop_port) {
341  $pop_port = 110;
342  }
343  if (!$pop_server) {
344  $pop_server = 'localhost';
345  }
346  $popConnection = @fsockopen($pop_server, $pop_port, $err_no, $err_str);
347  if (!$popConnection) {
348  error_log("Error connecting to POP Server ($pop_server:$pop_port)"
349  . " $err_no : $err_str");
350  } else {
351  $tmp = fgets($popConnection, 1024); /* banner */
352  if (substr($tmp, 0, 3) != '+OK') {
353  return(0);
354  }
355  fputs($popConnection, "USER $user\r\n");
356  $tmp = fgets($popConnection, 1024);
357  if (substr($tmp, 0, 3) != '+OK') {
358  return(0);
359  }
360  fputs($popConnection, 'PASS ' . $pass . "\r\n");
361  $tmp = fgets($popConnection, 1024);
362  if (substr($tmp, 0, 3) != '+OK') {
363  return(0);
364  }
365  fputs($popConnection, "QUIT\r\n"); /* log off */
366  fclose($popConnection);
367  }
368  }
369 }
370 
AddressStructure
Definition: AddressStructure.class.php:23
elseif
if(! sqgetGlobalVar('sound', $sound, SQ_GET)) elseif($sound=='(none)')
Definition: testsound.php:25
sqgetGlobalVar
sqgetGlobalVar($name, &$value, $search=SQ_INORDER)
Definition: global.php:344
Deliver_SMTP
Definition: Deliver_SMTP.class.php:21
$cnt
$cnt
Definition: options_identities.php:86
Deliver_SMTP\errorCheck
errorCheck($line, $smtpConnection)
Definition: Deliver_SMTP.class.php:273
$from
$from
Definition: printer_friendly_bottom.php:60
$rfc822_header
$rfc822_header
Definition: compose.php:362
Deliver_SMTP\authPop
authPop($pop_server='', $pop_port='', $user, $pass)
Definition: Deliver_SMTP.class.php:339
$message
$message
Definition: download.php:54
Deliver
Definition: Deliver.class.php:29
Deliver_SMTP\finalizeStream
finalizeStream($stream)
Definition: Deliver_SMTP.class.php:260
$use_smtp_tls
$use_smtp_tls
Definition: config_default.php:249
check_php_version
if(function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) check_php_version($a='0', $b='0', $c='0')
Definition: global.php:194
SQ_SERVER
const SQ_SERVER
Definition: global.php:20
SM_PATH
const SM_PATH
Definition: decrypt_headers.php:16
_
_($str)
Definition: gettext.php:160
$read
$read
Definition: message_details_bottom.php:75
$smtp_auth_mech
$smtp_auth_mech
Definition: config_default.php:257
$to
$to
Definition: printer_friendly_bottom.php:66
Deliver_SMTP\preWriteToStream
preWriteToStream(&$s)
Definition: Deliver_SMTP.class.php:23
$cc
$cc
Definition: printer_friendly_bottom.php:65
$stream
$stream
Definition: configtest.php:429
Deliver_SMTP\initStream
initStream($message, $domain, $length=0, $host='', $port='', $user='', $pass='', $authpop=false, $pop_host='')
Definition: Deliver_SMTP.class.php:30
cram_md5_response
cram_md5_response($username, $password, $challenge)
Definition: auth.php:141
boolean_hook_function
boolean_hook_function($name, $parm=NULL, $priority=0, $tie=false)
Definition: plugin.php:128
$domain
$domain
Definition: config_default.php:118
digest_md5_response
digest_md5_response($username, $password, $challenge, $service, $host)
Definition: auth.php:163