Modified pipelined request function by splitting up the the provided queries
[squirrelmail.git] / functions / imap_general.php
CommitLineData
59177427 1<?php
bccadd02 2
35586184 3/**
a6fd80f5 4 * imap_general.php
35586184 5 *
76911253 6 * Copyright (c) 1999-2003 The SquirrelMail Project Team
15e6162e 7 * Licensed under the GNU GPL. For full terms see the file COPYING.
35586184 8 *
15e6162e 9 * This implements all functions that do general imap functions.
35586184 10 *
15e6162e 11 * $Id$
35586184 12 */
13
b68edc75 14require_once(SM_PATH . 'functions/page_header.php');
47a29326 15require_once(SM_PATH . 'functions/auth.php');
16
35586184 17
41600f7d 18global $sqimap_session_id;
19$sqimap_session_id = 1;
71257f8b 20
3411d4ec 21/* Sets an unique session id in order to avoid simultanous sessions crash. */
487daa81 22function sqimap_session_id($unique_id = false) {
41600f7d 23 global $data_dir, $username, $sqimap_session_id;
487daa81 24 if (!$unique_id) {
098ea084 25 return( sprintf("A%03d", $sqimap_session_id++) );
487daa81 26 } else {
098ea084 27 return( sprintf("A%03d", $sqimap_session_id++) . ' UID' );
487daa81 28 }
9c737111 29}
30
3411d4ec 31/*
32 * Both send a command and accept the result from the command.
33 * This is to allow proper session number handling.
34 */
487daa81 35function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id = false) {
c5809184 36 if ($imap_stream) {
098ea084 37 $sid = sqimap_session_id($unique_id);
38 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
bc78cc6e 39 $tag_uid_a = explode(' ',trim($sid));
40 $tag = $tag_uid_a[0];
41 $read = sqimap_read_data_list ($imap_stream, $tag, $handle_errors, $response, $message, $query );
42 /* get the response and the message */
43 $message = $message[$tag];
44 $response = $response[$tag];
45 return $read[$tag];
c5809184 46 } else {
47 global $squirrelmail_language, $color;
48 set_up_language($squirrelmail_language);
49 require_once(SM_PATH . 'functions/display_messages.php');
50 $string = "<b><font color=$color[2]>\n" .
51 _("ERROR : No available imapstream.") .
52 "</b></font>\n";
53 error_box($string,$color);
098ea084 54 return false;
c5809184 55 }
1c72b151 56}
57
7c7b74b3 58function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
59 &$message, $unique_id = false,$filter=false,
60 $outputstream=false,$no_return=false) {
c5809184 61 if ($imap_stream) {
62 $sid = sqimap_session_id($unique_id);
bc78cc6e 63 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
64 $tag_uid_a = explode(' ',trim($sid));
65 $tag = $tag_uid_a[0];
66
67 $read = sqimap_read_data ($imap_stream, $tag, $handle_errors, $response,
7c7b74b3 68 $message, $query,$filter,$outputstream,$no_return);
bc78cc6e 69 /* retrieve the response and the message */
70 $response = $response[$tag];
71 $message = $message[$tag];
6b6c2e06 72
bc78cc6e 73 if (!empty($read[$tag])) {
74 return $read[$tag][0];
75 } else {
76 return $read[$tag];
77 }
c5809184 78 } else {
79 global $squirrelmail_language, $color;
80 set_up_language($squirrelmail_language);
81 require_once(SM_PATH . 'functions/display_messages.php');
82 $string = "<b><font color=$color[2]>\n" .
83 _("ERROR : No available imapstream.") .
84 "</b></font>\n";
85 error_box($string,$color);
098ea084 86 return false;
bc78cc6e 87 }
88}
89function sqimap_prepare_pipelined_query($new_query,&$tag,&$aQuery,$unique_id) {
90 $sid = sqimap_session_id($unique_id);
91 $tag_uid_a = explode(' ',trim($sid));
92 $tag = $tag_uid_a[0];
93 $query = $sid . ' '.$new_query."\n";
94 $aQuery[$tag] = $query;
42a07ac1 95}
96
52c8b585 97function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
bc78cc6e 98 &$aServerResponse, &$aServerMessage, $unique_id = false,
99 $filter=false,$outputstream=false,$no_return=false) {
52c8b585 100
bc78cc6e 101 $aResponse = false;
52c8b585 102 /*
103 Do not fire all calls at once to the imap-server but split the calls up
104 in portions of $iChunkSize. If we do not do that I think we misbehave as
105 IMAP client or should handle BYE calls if the IMAP-server drops the
106 connection because the number of queries is to large. This isn't tested
107 but a wild guess how it could work in the field.
108 */
109 $iQueryCount = count($aQueryList);
110 $iChunkSize = 32;
111 // array_chunk would also do the job but it's supported from php > 4.2
112 $aQueryChunks = array();
113 $iLoops = floor($iQueryCount / $iChunkSize);
114
115 if ($iLoops * $iChunkSize !== $iQueryCount) ++$iLoops;
116
117 if (!function_exists('array_chunk')) { // arraychunk replacement
118 reset($aQueryList);
119 for($i=0;$i<$iLoops;++$i) {
120 for($j=0;$j<$iChunkSize;++$j) {
121 $key = key($aQueryList);
122 $aTmp[$key] = $aQueryList[$key];
123 if (next($aQueryList) === false) break;
124 }
125 $aQueryChunks[] = $aTmp;
126 }
127 } else {
128 $aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
bc78cc6e 129 }
52c8b585 130
131 for ($i=0;$i<$iLoops;++$i) {
132 $aQuery = $aQueryChunks[$i];
133 foreach($aQuery as $tag => $query) {
134 fputs($imap_stream,$query);
135 $aResults[$tag] = false;
136 }
bc78cc6e 137
52c8b585 138 foreach($aQuery as $tag => $query) {
139 if (!$aResults[$tag]) {
140 $aReturnedResponse = sqimap_read_data_list ($imap_stream, $tag,
bc78cc6e 141 $handle_errors, $response, $message, $query,
142 $filter,$outputstream,$no_return);
52c8b585 143 foreach ($aReturnedResponse as $returned_tag => $aResponse) {
144 if (!empty($aResponse)) {
145 $aResults[$returned_tag] = $aResponse[0];
146 } else {
147 $aResults[$returned_tag] = $aResponse;
148 }
149 $aServerResponse[$returned_tag] = $response[$returned_tag];
150 $aServerMessage[$returned_tag] = $message[$returned_tag];
151 }
bc78cc6e 152 }
153 }
154 }
b7277e4f 155 return $aResults;
bc78cc6e 156}
9c737111 157
b8c285ab 158/*
159 * custom fgets function. gets a line from IMAP
160 * no matter how big it may be
161 */
162
163function sqimap_fgets($imap_stream) {
164 $read = '';
165 $buffer = 4096;
166 $results = '';
c41daf03 167 $offset = 0;
168 while (strpos($results, "\r\n", $offset) === false) {
b8c285ab 169 if (!($read = fgets($imap_stream, $buffer))) {
329a7ca5 170 /* this happens in case of an error */
171 /* reset $results because it's useless */
172 $results = false;
b8c285ab 173 break;
174 }
c41daf03 175 if ( $results != '' ) {
176 $offset = strlen($results) - 1;
177 }
b8c285ab 178 $results .= $read;
179 }
180 return $results;
181}
182
7c7b74b3 183function sqimap_fread($imap_stream,$iSize,$filter=false,
184 $outputstream=false, $no_return=false) {
185 if (!$filter || !$outputstream) {
186 $iBufferSize = $iSize;
187 } else {
bc78cc6e 188 $iBufferSize = 62400; // multiple of 78 in case of base64 decoding.
7c7b74b3 189 }
190 $iRet = $iSize - $iBufferSize;
5eba0be2 191 $iRetrieved = 0;
7c7b74b3 192 $i = 0;
bc78cc6e 193 $results = $sReadRem = '';
194 $bFinished = $bBufferSizeAdapted = $bBufferIsOk = false;
5eba0be2 195 while (($iRetrieved < ($iSize - $iBufferSize))) {
7c7b74b3 196 $sRead = fread($imap_stream,$iBufferSize);
bc78cc6e 197 if (!$sRead) {
7c7b74b3 198 $results = false;
199 break;
200 }
5eba0be2 201 $iRetrieved += $iBufferSize;
7c7b74b3 202 if ($filter) {
bc78cc6e 203 // in case line-endings do not appear at position 78 we adapt the buffersize so we can base64 decode on the fly
204 if (!$bBufferSizeAdapted) {
205 $i = strpos($sRead,"\n");
206 if ($i) {
207 ++$i;
208 $iFragments = floor($iBufferSize / $i);
209 $iNewBufferSize = $iFragments * $i;
210 $iRemainder = $iNewBufferSize + $i - $iBufferSize;
211 if ($iNewBufferSize == $iBufferSize) {
212 $bBufferIsOk = true;
213 $iRemainder = 0;
214 $iNewBufferSize = $iBufferSize;
215 $bBufferSizeAdapted = true;
216 }
217 if (!$bBufferIsOk && ($iRemainder + $iBufferSize) < $iSize) {
218 $sReadRem = fread($imap_stream,$iRemainder);
219 } else if (!$bBufferIsOk) {
220 $sReadRem = fread($imap_stream,$iSize - $iBufferSize);
221 $bFinished = true;
222 }
223 if (!$sReadRem && $sReadRem !== '') {
224 $results = false;
225 break;
226 }
227 $iBufferSize = $iNewBufferSize;
228 $bBufferSizeAdapted = true;
229 } else {
230 $sReadRem = fread($imap_stream,$iSize - $iBufferSize);
231 $bFinished = true;
232 if (!$sReadRem) {
233 $results = false;
234 break;
235 }
236 }
237 $sRead .= $sReadRem;
238 $iRetrieved += $iRemainder;
239 unset($sReadRem);
240 }
7c7b74b3 241 $filter($sRead);
242 }
243 if ($outputstream) {
244 if (is_resource($outputstream)) {
245 fwrite($outputstream,$sRead);
246 } else if ($outputstream == 'php://stdout') {
247 echo $sRead;
248 }
249 }
250 if ($no_return) {
251 $sRead = '';
252 }
253 $results .= $sRead;
254 }
bc78cc6e 255 if (!$results && !$bFinished) {
5eba0be2 256 $sRead = fread($imap_stream,($iSize - ($iRetrieved)));
7c7b74b3 257 if ($filter) {
258 $filter($sRead);
259 }
260 if ($outputstream) {
261 if (is_resource($outputstream)) {
262 fwrite($outputstream,$sRead);
263 } else if ($outputstream == 'php://stdout') { // FIXME
264 echo $sRead;
265 }
266 }
267 if ($no_return) {
268 $sRead = '';
269 }
270 $results .= $sRead;
271 }
272 return $results;
273}
274
275
276
bee165ef 277/*
3411d4ec 278 * Reads the output from the IMAP stream. If handle_errors is set to true,
279 * this will also handle all errors that are received. If it is not set,
280 * the errors will be sent back through $response and $message
bee165ef 281 */
b8c285ab 282
bc78cc6e 283function sqimap_read_data_list ($imap_stream, $tag, $handle_errors,
7c7b74b3 284 &$response, &$message, $query = '',
285 $filter = false, $outputstream = false, $no_return = false) {
9c737111 286 global $color, $squirrelmail_language;
9c737111 287 $read = '';
bc78cc6e 288 if (!is_array($message)) $message = array();
289 if (!is_array($response)) $response = array();
b8c285ab 290 $resultlist = array();
291 $data = array();
292 $read = sqimap_fgets($imap_stream);
ba8f367a 293 $i = 0;
bea3eb1e 294 while ($read) {
295 $char = $read{0};
296 switch ($char)
297 {
15bc7f66 298 case '+':
299 default:
300 $read = sqimap_fgets($imap_stream);
301 break;
302
303 case $tag{0}:
304 {
bea3eb1e 305 /* get the command */
306 $arg = '';
307 $i = strlen($tag)+1;
308 $s = substr($read,$i);
309 if (($j = strpos($s,' ')) || ($j = strpos($s,"\n"))) {
310 $arg = substr($s,0,$j);
311 }
312 $found_tag = substr($read,0,$i-1);
313 if ($arg && $found_tag==$tag) {
314 switch ($arg)
315 {
15bc7f66 316 case 'OK':
317 case 'BAD':
318 case 'NO':
319 case 'BYE':
320 case 'PREAUTH':
bc78cc6e 321 $response[$found_tag] = $arg;
322 $message[$found_tag] = trim(substr($read,$i+strlen($arg)));
323 if (!empty($data)) {
324 $resultlist[] = $data;
325 }
326 $aResponse[$tag] = $resultlist;
c0b23345 327 break 3; /* switch switch while */
329a7ca5 328 default:
bea3eb1e 329 /* this shouldn't happen */
bc78cc6e 330 $response[$found_tag] = $arg;
331 $message[$found_tag] = trim(substr($read,$i+strlen($arg)));
332 if (!empty($data)) {
333 $resultlist[] = $data;
334 }
335 $aResponse[$found_tag] = $resultlist;
c0b23345 336 break 3; /* switch switch while */
bea3eb1e 337 }
338 } elseif($found_tag !== $tag) {
bc78cc6e 339 /* not the tag we are looking for, continue */
340 if (!empty($data)) {
341 $resultlist[] = $data;
342 }
343 $aResponse[$found_tag] = $resultlist;
344 $resultlist = $data = array();
bea3eb1e 345 $read = sqimap_fgets($imap_stream);
bc78cc6e 346 if ($read === false) { /* error */
347 break 3; /* switch switch while */
348 }
bea3eb1e 349 break;
350 }
15bc7f66 351 } // end case $tag{0}
352
353 case '*':
354 {
bea3eb1e 355 if (preg_match('/^\*\s\d+\sFETCH/',$read)) {
356 /* check for literal */
357 $s = substr($read,-3);
358 $fetch_data = array();
359 do { /* outer loop, continue until next untagged fetch
360 or tagged reponse */
361 do { /* innerloop for fetching literals. with this loop
362 we prohibid that literal responses appear in the
363 outer loop so we can trust the untagged and
364 tagged info provided by $read */
365 if ($s === "}\r\n") {
366 $j = strrpos($read,'{');
367 $iLit = substr($read,$j+1,-3);
368 $fetch_data[] = $read;
7c7b74b3 369 $sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
329a7ca5 370 if ($sLiteral === false) { /* error */
371 break 4; /* while while switch while */
372 }
bea3eb1e 373 /* backwards compattibility */
374 $aLiteral = explode("\n", $sLiteral);
375 /* release not neaded data */
376 unset($sLiteral);
377 foreach ($aLiteral as $line) {
378 $fetch_data[] = $line ."\n";
379 }
380 /* release not neaded data */
381 unset($aLiteral);
382 /* next fgets belongs to this fetch because
383 we just got the exact literalsize and there
384 must follow data to complete the response */
329a7ca5 385 $read = sqimap_fgets($imap_stream);
386 if ($read === false) { /* error */
387 break 4; /* while while switch while */
388 }
c0b23345 389 $fetch_data[] = $read;
bea3eb1e 390 } else {
329a7ca5 391 $fetch_data[] = $read;
bea3eb1e 392 }
393 /* retrieve next line and check in the while
394 statements if it belongs to this fetch response */
395 $read = sqimap_fgets($imap_stream);
329a7ca5 396 if ($read === false) { /* error */
397 break 4; /* while while switch while */
398 }
bea3eb1e 399 /* check for next untagged reponse and break */
400 if ($read{0} == '*') break 2;
401 $s = substr($read,-3);
402 } while ($s === "}\r\n");
403 $s = substr($read,-3);
404 } while ($read{0} !== '*' &&
106d4087 405 substr($read,0,strlen($tag)) !== $tag);
329a7ca5 406 $resultlist[] = $fetch_data;
bea3eb1e 407 /* release not neaded data */
408 unset ($fetch_data);
409 } else {
410 $s = substr($read,-3);
411 do {
412 if ($s === "}\r\n") {
413 $j = strrpos($read,'{');
414 $iLit = substr($read,$j+1,-3);
415 $data[] = $read;
329a7ca5 416 $sLiteral = fread($imap_stream,$iLit);
c0b23345 417 if ($sLiteral === false) { /* error */
329a7ca5 418 $read = false;
419 break 3; /* while switch while */
420 }
c0b23345 421 $data[] = $sLiteral;
b7277e4f 422 $data[] = sqimap_fgets($imap_stream);
bea3eb1e 423 } else {
329a7ca5 424 $data[] = $read;
bea3eb1e 425 }
426 $read = sqimap_fgets($imap_stream);
329a7ca5 427 if ($read === false) {
428 break 3; /* while switch while */
429 } else if ($read{0} == '*') {
106d4087 430 break;
431 }
bea3eb1e 432 $s = substr($read,-3);
433 } while ($s === "}\r\n");
434 break 1;
435 }
436 break;
15bc7f66 437 } // end case '*'
438 } // end switch
c0b23345 439 } // end while
440
441 /* error processing in case $read is false */
442 if ($read === false) {
443 unset($data);
444 set_up_language($squirrelmail_language);
445 require_once(SM_PATH . 'functions/display_messages.php');
446 $string = "<b><font color=$color[2]>\n" .
329a7ca5 447 _("ERROR : Connection dropped by imap-server.") .
448 "</b><br>\n" .
449 _("Query:") . ' '.
450 htmlspecialchars($query) . '<br>' . "</font><br>\n";
c0b23345 451 error_box($string,$color);
452 exit;
b8c285ab 453 }
c0b23345 454
15bc7f66 455 /* Set $resultlist array */
b8c285ab 456 if (!empty($data)) {
bc78cc6e 457 //$resultlist[] = $data;
9c737111 458 }
b8c285ab 459 elseif (empty($resultlist)) {
460 $resultlist[] = array();
461 }
15bc7f66 462
463 /* Return result or handle errors */
bee165ef 464 if ($handle_errors == false) {
bc78cc6e 465 return $aResponse;
bee165ef 466 return( $resultlist );
dd381002 467 }
bc78cc6e 468 switch ($response[$tag])
dd381002 469 {
470 case 'OK':
bc78cc6e 471 return $aResponse;
329a7ca5 472 break;
dd381002 473 case 'NO':
474 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
6b6c2e06 475 if (strstr($message[$tag], 'command resulted in') === false) {
441f2d33 476 set_up_language($squirrelmail_language);
098ea084 477 require_once(SM_PATH . 'functions/display_messages.php');
478 $string = "<b><font color=$color[2]>\n" .
b8c285ab 479 _("ERROR : Could not complete request.") .
480 "</b><br>\n" .
9b761dbd 481 _("Query:") . ' ' .
482 htmlspecialchars($query) . '<br>' .
b8c285ab 483 _("Reason Given: ") .
bc78cc6e 484 htmlspecialchars($message[$tag]) . "</font><br>\n";
098ea084 485 error_box($string,$color);
329a7ca5 486 echo '</body></html>';
052e0c26 487 exit;
9c737111 488 }
329a7ca5 489 break;
dd381002 490 case 'BAD':
9c737111 491 set_up_language($squirrelmail_language);
098ea084 492 require_once(SM_PATH . 'functions/display_messages.php');
bef6629c 493 $string = "<b><font color=$color[2]>\n" .
b8c285ab 494 _("ERROR : Bad or malformed request.") .
495 "</b><br>\n" .
9b761dbd 496 _("Query:") . ' '.
497 htmlspecialchars($query) . '<br>' .
b8c285ab 498 _("Server responded: ") .
bc78cc6e 499 htmlspecialchars($message[$tag]) . "</font><br>\n";
dd381002 500 error_box($string,$color);
329a7ca5 501 echo '</body></html>';
dd381002 502 exit;
503 case 'BYE':
504 set_up_language($squirrelmail_language);
505 require_once(SM_PATH . 'functions/display_messages.php');
506 $string = "<b><font color=$color[2]>\n" .
507 _("ERROR : Imap server closed the connection.") .
508 "</b><br>\n" .
509 _("Query:") . ' '.
510 htmlspecialchars($query) . '<br>' .
511 _("Server responded: ") .
bc78cc6e 512 htmlspecialchars($message[$tag]) . "</font><br>\n";
dd381002 513 error_box($string,$color);
329a7ca5 514 echo '</body></html>';
9c737111 515 exit;
dd381002 516 default:
517 set_up_language($squirrelmail_language);
518 require_once(SM_PATH . 'functions/display_messages.php');
519 $string = "<b><font color=$color[2]>\n" .
520 _("ERROR : Unknown imap response.") .
521 "</b><br>\n" .
522 _("Query:") . ' '.
523 htmlspecialchars($query) . '<br>' .
524 _("Server responded: ") .
bc78cc6e 525 htmlspecialchars($message[$tag]) . "</font><br>\n";
dd381002 526 error_box($string,$color);
329a7ca5 527 /* the error is displayed but because we don't know the reponse we
528 return the result anyway */
bc78cc6e 529 return $aResponse;
329a7ca5 530 break;
9c737111 531 }
9c737111 532}
533
7c7b74b3 534function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
535 &$response, &$message, $query = '',
536 $filter=false,$outputstream=false,$no_return=false) {
bc78cc6e 537
538 $tag_uid_a = explode(' ',trim($tag_uid));
539 $tag = $tag_uid_a[0];
540
541 $res = sqimap_read_data_list($imap_stream, $tag, $handle_errors,
7c7b74b3 542 $response, $message, $query,$filter,$outputstream,$no_return);
863936bb 543 /* sqimap_read_data should be called for one response
544 but since it just calls sqimap_read_data_list which
545 handles multiple responses we need to check for that
546 and merge the $res array IF they are seperated and
547 IF it was a FETCH response. */
548
ba8f367a 549// if (isset($res[1]) && is_array($res[1]) && isset($res[1][0])
550// && preg_match('/^\* \d+ FETCH/', $res[1][0])) {
551// $result = array();
552// foreach($res as $index=>$value) {
553// $result = array_merge($result, $res["$index"]);
554// }
555// }
863936bb 556 if (isset($result)) {
bc78cc6e 557 return $result[$tag];
56afb33f 558 }
56afb33f 559 else {
bc78cc6e 560 return $res;
56afb33f 561 }
9c737111 562}
563
3411d4ec 564/*
565 * Logs the user into the imap server. If $hide is set, no error messages
566 * will be displayed. This function returns the imap connection handle.
567 */
9c737111 568function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
47a29326 569 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech;
85fc999e 570
eb2f6102 571 if (!isset($onetimepad) || empty($onetimepad)) {
572 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
573 }
bd9829d7 574 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
098ea084 575 $host=$imap_server_address;
576
577 if (($use_imap_tls == true) and (check_php_version(4,3)) and (extension_loaded('openssl'))) {
578 /* Use TLS by prefixing "tls://" to the hostname */
579 $imap_server_address = 'tls://' . $imap_server_address;
580 }
47a29326 581
3411d4ec 582 $imap_stream = fsockopen ( $imap_server_address, $imap_port, $error_number, $error_string, 15);
85fc999e 583
3411d4ec 584 /* Do some error correction */
9c737111 585 if (!$imap_stream) {
586 if (!$hide) {
441f2d33 587 set_up_language($squirrelmail_language, true);
098ea084 588 require_once(SM_PATH . 'functions/display_messages.php');
589 $string = sprintf (_("Error connecting to IMAP server: %s.") .
590 "<br>\r\n", $imap_server_address) .
1f720b34 591 "$error_number : $error_string<br>\r\n";
098ea084 592 logout_error($string,$color);
9c737111 593 }
594 exit;
595 }
052e0c26 596
2f1f7a12 597 $server_info = fgets ($imap_stream, 1024);
598
599 /* Decrypt the password */
600 $password = OneTimePadDecrypt($password, $onetimepad);
601
098ea084 602 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
fe0b18b3 603 // We're using some sort of authentication OTHER than plain or login
098ea084 604 $tag=sqimap_session_id(false);
605 if ($imap_auth_mech == 'digest-md5') {
606 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
607 } elseif ($imap_auth_mech == 'cram-md5') {
608 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
609 }
610 fputs($imap_stream,$query);
611 $answer=sqimap_fgets($imap_stream);
612 // Trim the "+ " off the front
613 $response=explode(" ",$answer,3);
614 if ($response[0] == '+') {
615 // Got a challenge back
616 $challenge=$response[1];
617 if ($imap_auth_mech == 'digest-md5') {
618 $reply = digest_md5_response($username,$password,$challenge,'imap',$host);
619 } elseif ($imap_auth_mech == 'cram-md5') {
620 $reply = cram_md5_response($username,$password,$challenge);
621 }
622 fputs($imap_stream,$reply);
623 $read=sqimap_fgets($imap_stream);
624 if ($imap_auth_mech == 'digest-md5') {
625 // DIGEST-MD5 has an extra step..
626 if (substr($read,0,1) == '+') { // OK so far..
627 fputs($imap_stream,"\r\n");
628 $read=sqimap_fgets($imap_stream);
629 }
630 }
631 $results=explode(" ",$read,3);
632 $response=$results[1];
633 $message=$results[2];
47a29326 634 } else {
098ea084 635 // Fake the response, so the error trap at the bottom will work
636 $response="BAD";
637 $message='IMAP server does not appear to support the authentication method selected.';
638 $message .= ' Please contact your system administrator.';
47a29326 639 }
fe0b18b3 640 } elseif ($imap_auth_mech == 'login') {
098ea084 641 // Original IMAP login code
fbb76d0e 642 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
47a29326 643 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
1e7fc1cb 644 } elseif ($imap_auth_mech == 'plain') {
098ea084 645 /* Replace this with SASL PLAIN if it ever gets implemented */
646 $response="BAD";
647 $message='SquirrelMail does not support SASL PLAIN yet. Rerun conf.pl and use login instead.';
648 } else {
649 $response="BAD";
650 $message="Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
651 }
47a29326 652
098ea084 653 /* If the connection was not successful, lets see why */
9c737111 654 if ($response != 'OK') {
655 if (!$hide) {
74424a43 656 if ($response != 'NO') {
3411d4ec 657 /* "BAD" and anything else gets reported here. */
098ea084 658 $message = htmlspecialchars($message);
9c737111 659 set_up_language($squirrelmail_language, true);
098ea084 660 require_once(SM_PATH . 'functions/display_messages.php');
9c737111 661 if ($response == 'BAD') {
bea3eb1e 662 $string = sprintf (_("Bad request: %s")."<br>\r\n", $message);
9c737111 663 } else {
bea3eb1e 664 $string = sprintf (_("Unknown error: %s") . "<br>\n", $message);
9c737111 665 }
1e7fc1cb 666 if (isset($read) && is_array($read)) {
bea3eb1e 667 $string .= '<br>' . _("Read data:") . "<br>\n";
9c737111 668 foreach ($read as $line) {
1f720b34 669 $string .= htmlspecialchars($line) . "<br>\n";
9c737111 670 }
671 }
098ea084 672 error_box($string,$color);
9c737111 673 exit;
165e24a7 674 } else {
3411d4ec 675 /*
676 * If the user does not log in with the correct
1c72b151 677 * username and password it is not possible to get the
678 * correct locale from the user's preferences.
679 * Therefore, apply the same hack as on the login
680 * screen.
3411d4ec 681 *
682 * $squirrelmail_language is set by a cookie when
1c72b151 683 * the user selects language and logs out
bee165ef 684 */
9be8198d 685
9c737111 686 set_up_language($squirrelmail_language, true);
bd9c880b 687 include_once(SM_PATH . 'functions/display_messages.php' );
098ea084 688 sqsession_destroy();
bd9c880b 689 logout_error( _("Unknown user or password incorrect.") );
9c737111 690 exit;
052e0c26 691 }
9c737111 692 } else {
052e0c26 693 exit;
9c737111 694 }
695 }
9c737111 696 return $imap_stream;
697}
f1e6f580 698
3411d4ec 699/* Simply logs out the IMAP session */
9c737111 700function sqimap_logout ($imap_stream) {
8d936b0c 701 /* Logout is not valid until the server returns 'BYE'
702 * If we don't have an imap_ stream we're already logged out */
26a2cc8b 703 if(isset($imap_stream) && $imap_stream)
8d936b0c 704 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
9c737111 705}
706
487daa81 707function sqimap_capability($imap_stream, $capability='') {
9c737111 708 global $sqimap_capabilities;
9c737111 709 if (!is_array($sqimap_capabilities)) {
1c72b151 710 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
711
9c737111 712 $c = explode(' ', $read[0]);
713 for ($i=2; $i < count($c); $i++) {
714 $cap_list = explode('=', $c[$i]);
3411d4ec 715 if (isset($cap_list[1])) {
9c737111 716 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
3411d4ec 717 } else {
9c737111 718 $sqimap_capabilities[$cap_list[0]] = TRUE;
3411d4ec 719 }
f1e6f580 720 }
9c737111 721 }
487daa81 722 if ($capability) {
098ea084 723 if (isset($sqimap_capabilities[$capability])) {
724 return $sqimap_capabilities[$capability];
725 } else {
726 return false;
727 }
f1e6f580 728 }
487daa81 729 return $sqimap_capabilities;
9c737111 730}
731
3411d4ec 732/* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test */
9c737111 733function sqimap_get_delimiter ($imap_stream = false) {
3411d4ec 734 global $sqimap_delimiter, $optional_delimiter;
85fc999e 735
9c737111 736 /* Use configured delimiter if set */
737 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
738 return $optional_delimiter;
739 }
85fc999e 740
9c737111 741 /* Do some caching here */
742 if (!$sqimap_delimiter) {
743 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
3411d4ec 744 /*
745 * According to something that I can't find, this is supposed to work on all systems
746 * OS: This won't work in Courier IMAP.
747 * OS: According to rfc2342 response from NAMESPACE command is:
748 * OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
749 * OS: We want to lookup all personal NAMESPACES...
750 */
1c72b151 751 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
f1e6f580 752 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
9c737111 753 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
f1e6f580 754 $pn = $data2[1];
9c737111 755 }
f1e6f580 756 $pna = explode(')(', $pn);
9c737111 757 while (list($k, $v) = each($pna)) {
862ff2d3 758 $lst = explode('"', $v);
759 if (isset($lst[3])) {
760 $pn[$lst[1]] = $lst[3];
761 } else {
74424a43 762 $pn[$lst[1]] = '';
862ff2d3 763 }
f1e6f580 764 }
765 }
766 $sqimap_delimiter = $pn[0];
767 } else {
768 fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
769 $read = sqimap_read_data($imap_stream, '.', true, $a, $b);
770 $quote_position = strpos ($read[0], '"');
771 $sqimap_delimiter = substr ($read[0], $quote_position+1, 1);
772 }
773 }
774 return $sqimap_delimiter;
9c737111 775}
052e0c26 776
777
3411d4ec 778/* Gets the number of messages in the current mailbox. */
9c737111 779function sqimap_get_num_messages ($imap_stream, $mailbox) {
1f720b34 780 $read_ary = sqimap_run_command ($imap_stream, "EXAMINE \"$mailbox\"", false, $result, $message);
9c737111 781 for ($i = 0; $i < count($read_ary); $i++) {
85fc999e 782 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
783 return $regs[1];
784 }
9c737111 785 }
1f720b34 786 return false; //"BUG! Couldn't get number of messages in $mailbox!";
9c737111 787}
788
cfe1a81e 789
91688e5f 790function parseAddress($address, $max=0) {
791 $aTokens = array();
792 $aAddress = array();
793 $iCnt = strlen($address);
794 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
795 $aReplace = array(' (',' <',' ,',' ;',' :');
796 $address = str_replace($aSpecials,$aReplace,$address);
bc78cc6e 797 $i = $iAddrFound = $bGroup = 0;
91688e5f 798 while ($i < $iCnt) {
799 $cChar = $address{$i};
800 switch($cChar)
329a7ca5 801 {
802 case '<':
803 $iEnd = strpos($address,'>',$i+1);
804 if (!$iEnd) {
805 $sToken = substr($address,$i);
806 $i = $iCnt;
807 } else {
808 $sToken = substr($address,$i,$iEnd - $i +1);
809 $i = $iEnd;
810 }
811 $sToken = str_replace($aReplace, $aSpecials,$sToken);
812 $aTokens[] = $sToken;
813 break;
814 case '"':
815 $iEnd = strpos($address,$cChar,$i+1);
da1e42a1 816 if ($iEnd) {
817 // skip escaped quotes
818 $prev_char = $address{$iEnd-1};
819 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
820 $iEnd = strpos($address,$cChar,$iEnd+1);
821 if ($iEnd) {
822 $prev_char = $address{$iEnd-1};
823 } else {
824 $prev_char = false;
825 }
826 }
827 }
329a7ca5 828 if (!$iEnd) {
829 $sToken = substr($address,$i);
830 $i = $iCnt;
831 } else {
832 // also remove the surrounding quotes
833 $sToken = substr($address,$i+1,$iEnd - $i -1);
834 $i = $iEnd;
835 }
836 $sToken = str_replace($aReplace, $aSpecials,$sToken);
76c3ee28 837 if ($sToken) $aTokens[] = $sToken;
329a7ca5 838 break;
839 case '(':
840 $iEnd = strpos($address,')',$i);
841 if (!$iEnd) {
842 $sToken = substr($address,$i);
843 $i = $iCnt;
844 } else {
845 $sToken = substr($address,$i,$iEnd - $i + 1);
846 $i = $iEnd;
847 }
848 $sToken = str_replace($aReplace, $aSpecials,$sToken);
849 $aTokens[] = $sToken;
850 break;
851 case ',':
bc78cc6e 852 ++$iAddrFound;
329a7ca5 853 case ';':
bc78cc6e 854 if (!$bGroup) {
855 ++$iAddrFound;
856 } else {
857 $bGroup = false;
858 }
859 if ($max && $max == $iAddrFound) {
860 break 2;
861 } else {
862 $aTokens[] = $cChar;
863 break;
864 }
865 case ':':
866 $bGroup = true;
329a7ca5 867 case ' ':
868 $aTokens[] = $cChar;
869 break;
91688e5f 870 default:
329a7ca5 871 $iEnd = strpos($address,' ',$i+1);
872 if ($iEnd) {
873 $sToken = trim(substr($address,$i,$iEnd - $i));
874 $i = $iEnd-1;
91688e5f 875 } else {
329a7ca5 876 $sToken = trim(substr($address,$i));
877 $i = $iCnt;
91688e5f 878 }
329a7ca5 879 if ($sToken) $aTokens[] = $sToken;
91688e5f 880 }
329a7ca5 881 ++$i;
9c737111 882 }
91688e5f 883 $sPersonal = $sEmail = $sComment = $sGroup = '';
884 $aStack = $aComment = array();
885 foreach ($aTokens as $sToken) {
886 if ($max && $max == count($aAddress)) {
329a7ca5 887 return $aAddress;
092d4f2c 888 }
329a7ca5 889 $cChar = $sToken{0};
91688e5f 890 switch ($cChar)
329a7ca5 891 {
892 case '=':
893 case '"':
894 case ' ':
895 $aStack[] = $sToken;
896 break;
897 case '(':
898 $aComment[] = substr($sToken,1,-1);
899 break;
900 case ';':
901 if ($sGroup) {
902 $sEmail = trim(implode(' ',$aStack));
903 $aAddress[] = array($sGroup,$sEmail);
904 $aStack = $aComment = array();
905 $sGroup = '';
906 break;
907 }
908 case ',':
909 if (!$sEmail) {
910 while (count($aStack) && !$sEmail) {
911 $sEmail = trim(array_pop($aStack));
912 }
913 }
914 if (count($aStack)) {
915 $sPersonal = trim(implode('',$aStack));
916 } else {
917 $sPersonal = '';
bea3eb1e 918 }
329a7ca5 919 if (!$sPersonal && count($aComment)) {
920 $sComment = implode(' ',$aComment);
921 $sPersonal .= $sComment;
922 }
923 $aAddress[] = array($sEmail,$sPersonal);
924 $sPersonal = $sComment = $sEmail = '';
925 $aStack = $aComment = array();
926 break;
927 case ':':
928 $sGroup = implode(' ',$aStack); break;
929 $aStack = array();
930 break;
931 case '<':
932 $sEmail = trim(substr($sToken,1,-1));
933 break;
934 case '>':
935 /* skip */
936 break;
937 default: $aStack[] = $sToken; break;
33565ec4 938 }
7e3de682 939 }
91688e5f 940 /* now do the action again for the last address */
941 if (!$sEmail) {
942 while (count($aStack) && !$sEmail) {
329a7ca5 943 $sEmail = trim(array_pop($aStack));
91688e5f 944 }
7e3de682 945 }
91688e5f 946 if (count($aStack)) {
329a7ca5 947 $sPersonal = trim(implode('',$aStack));
098ea084 948 } else {
91688e5f 949 $sPersonal = '';
9c737111 950 }
329a7ca5 951 if (!$sPersonal && count($aComment)) {
91688e5f 952 $sComment = implode(' ',$aComment);
329a7ca5 953 $sPersonal .= $sComment;
91688e5f 954 }
955 $aAddress[] = array($sEmail,$sPersonal);
956 return $aAddress;
957}
958
959
85fc999e 960
9c737111 961/*
3411d4ec 962 * Returns the number of unseen messages in this folder
963 */
9c737111 964function sqimap_unseen_messages ($imap_stream, $mailbox) {
1f720b34 965 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (UNSEEN)", false, $result, $message);
ea7ff111 966 $i = 0;
1f720b34 967 $regs = array(false, false);
ea7ff111 968 while (isset($read_ary[$i])) {
969 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
970 break;
971 }
972 $i++;
973 }
9c737111 974 return $regs[1];
975}
976
1f720b34 977/*
978 * Returns the number of unseen/total messages in this folder
979 */
980function sqimap_status_messages ($imap_stream, $mailbox) {
b8ec18ef 981 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (MESSAGES UNSEEN RECENT)", false, $result, $message);
1f720b34 982 $i = 0;
b8ec18ef 983 $messages = $unseen = $recent = false;
1f720b34 984 $regs = array(false,false);
985 while (isset($read_ary[$i])) {
986 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 987 $unseen = $regs[1];
988 }
1f720b34 989 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 990 $messages = $regs[1];
991 }
b8ec18ef 992 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 993 $recent = $regs[1];
994 }
1f720b34 995 $i++;
996 }
b8ec18ef 997 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
1f720b34 998}
999
9c737111 1000
1001/*
3411d4ec 1002 * Saves a message to a given folder -- used for saving sent messages
1003 */
9c737111 1004function sqimap_append ($imap_stream, $sent_folder, $length) {
1005 fputs ($imap_stream, sqimap_session_id() . " APPEND \"$sent_folder\" (\\Seen) \{$length}\r\n");
85fc999e 1006 $tmp = fgets ($imap_stream, 1024);
9c737111 1007}
1008
8813fb15 1009function sqimap_append_done ($imap_stream, $folder='') {
1f720b34 1010 global $squirrelmail_language, $color;
9c737111 1011 fputs ($imap_stream, "\r\n");
1012 $tmp = fgets ($imap_stream, 1024);
69146537 1013 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
1014 set_up_language($squirrelmail_language);
1f720b34 1015 require_once(SM_PATH . 'functions/display_messages.php');
098ea084 1016 $reason = $regs[3];
1017 if ($regs[2] == 'NO') {
1018 $string = "<b><font color=$color[2]>\n" .
1019 _("ERROR : Could not append message to") ." $folder." .
1020 "</b><br>\n" .
1021 _("Server responded: ") .
1022 $reason . "<br>\n";
1023 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
1024 $string .= _("Solution: ") .
1025 _("Remove unneccessary messages from your folder and start with your Trash folder.")
1026 ."<br>\n";
1027 }
1028 $string .= "</font>\n";
1029 error_box($string,$color);
1030 } else {
8813fb15 1031 $string = "<b><font color=$color[2]>\n" .
098ea084 1032 _("ERROR : Bad or malformed request.") .
1033 "</b><br>\n" .
1034 _("Server responded: ") .
1035 $tmp . "</font><br>\n";
1036 error_box($string,$color);
8813fb15 1037 exit;
098ea084 1038 }
69146537 1039 }
9c737111 1040}
85fc999e 1041
bd9829d7 1042function sqimap_get_user_server ($imap_server, $username) {
bd9829d7 1043 if (substr($imap_server, 0, 4) != "map:") {
1044 return $imap_server;
1045 }
bd9829d7 1046 $function = substr($imap_server, 4);
1047 return $function($username);
1048}
1049
1050/* This is an example that gets imapservers from yellowpages (NIS).
1051 * you can simple put map:map_yp_alias in your $imap_server_address
1052 * in config.php use your own function instead map_yp_alias to map your
1053 * LDAP whatever way to find the users imapserver. */
1054
1055function map_yp_alias($username) {
1056 $yp = `ypmatch $username aliases`;
1057 return chop(substr($yp, strlen($username)+1));
1058}
1059
15e6162e 1060?>