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