Imap error management and 8bit chars encoding ;)
[squirrelmail.git] / functions / imap_general.php
... / ...
CommitLineData
1<?php
2
3/**
4 * imap_general.php
5 *
6 * Copyright (c) 1999-2003 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 * This implements all functions that do general imap functions.
10 *
11 * $Id$
12 */
13
14require_once(SM_PATH . 'functions/page_header.php');
15require_once(SM_PATH . 'functions/auth.php');
16
17
18global $sqimap_session_id;
19$sqimap_session_id = 1;
20
21/* Sets an unique session id in order to avoid simultanous sessions crash. */
22function sqimap_session_id($unique_id = false) {
23 global $data_dir, $username, $sqimap_session_id;
24 if (!$unique_id) {
25 return( sprintf("A%03d", $sqimap_session_id++) );
26 } else {
27 return( sprintf("A%03d", $sqimap_session_id++) . ' UID' );
28 }
29}
30
31/*
32 * Both send a command and accept the result from the command.
33 * This is to allow proper session number handling.
34 */
35function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id = false) {
36 if ($imap_stream) {
37 $sid = sqimap_session_id($unique_id);
38 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
39 $tag_uid_a = explode(' ',trim($sid));
40 $tag = $tag_uid_a[0];
41 $read = sqimap_retrieve_imap_response ($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];
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);
54 return false;
55 }
56}
57
58function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
59 &$message, $unique_id = false,$filter=false,
60 $outputstream=false,$no_return=false) {
61 if ($imap_stream) {
62 $sid = sqimap_session_id($unique_id);
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,
68 $message, $query,$filter,$outputstream,$no_return);
69 /* retrieve the response and the message */
70 $response = $response[$tag];
71 $message = $message[$tag];
72
73 if (!empty($read[$tag])) {
74 return $read[$tag][0];
75 } else {
76 return $read[$tag];
77 }
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);
86 return false;
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."\r\n";
94 $aQuery[$tag] = $query;
95}
96
97function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
98 &$aServerResponse, &$aServerMessage, $unique_id = false,
99 $filter=false,$outputstream=false,$no_return=false) {
100 $aResponse = false;
101
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 After testing it on Exchange 2000 we discovered that a chunksize of 32
110 was quicker then when we raised it to 128.
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
118 if ($iLoops * $iChunkSize != $iQueryCount) ++$iLoops;
119
120 if (!function_exists('array_chunk')) { // arraychunk replacement
121 reset($aQueryList);
122 for($i=0;$i<$iLoops;++$i) {
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 }
130 } else {
131 $aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
132 }
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 }
140 foreach($aQuery as $tag => $query) {
141 if ($aResults[$tag] == false) {
142 $aReturnedResponse = sqimap_retrieve_imap_response ($imap_stream, $tag,
143 $handle_errors, $response, $message, $query,
144 $filter,$outputstream,$no_return);
145 foreach ($aReturnedResponse as $returned_tag => $aResponse) {
146 if (!empty($aResponse)) {
147 $aResults[$returned_tag] = $aResponse[0];
148 } else {
149 $aResults[$returned_tag] = $aResponse;
150 }
151 $aServerResponse[$returned_tag] = $response[$returned_tag];
152 $aServerMessage[$returned_tag] = $message[$returned_tag];
153 }
154 }
155 }
156 }
157 return $aResults;
158}
159
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 = '';
169 $offset = 0;
170 while (strpos($results, "\r\n", $offset) === false) {
171 if (!($read = fgets($imap_stream, $buffer))) {
172 /* this happens in case of an error */
173 /* reset $results because it's useless */
174 $results = false;
175 break;
176 }
177 if ( $results != '' ) {
178 $offset = strlen($results) - 1;
179 }
180 $results .= $read;
181 }
182 return $results;
183}
184
185function sqimap_fread($imap_stream,$iSize,$filter=false,
186 $outputstream=false, $no_return=false) {
187 if (!$filter || !$outputstream) {
188 $iBufferSize = $iSize;
189 } else {
190 $iBufferSize = 62400; // multiple of 78 in case of base64 decoding.
191 }
192 $iRet = $iSize - $iBufferSize;
193 $iRetrieved = 0;
194 $i = 0;
195 $results = $sReadRem = '';
196 $bFinished = $bBufferSizeAdapted = $bBufferIsOk = false;
197 while (($iRetrieved < ($iSize - $iBufferSize))) {
198 $sRead = fread($imap_stream,$iBufferSize);
199 if (!$sRead) {
200 $results = false;
201 break;
202 }
203 $iRetrieved += $iBufferSize;
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 }
215 if ($filter) {
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 }
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 }
268 if (!$results && !$bFinished) {
269 $sRead = fread($imap_stream,($iSize - ($iRetrieved)));
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}
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>'.
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>'.
302 htmlspecialchars($query) . '<br>' . "</font><br>\n";
303 error_box($string,$color);
304 echo '</body></html>';
305 exit;
306}
307
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
325/*
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
329 */
330
331function sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
332 &$response, &$message, $query = '',
333 $filter = false, $outputstream = false, $no_return = false) {
334 global $color, $squirrelmail_language;
335 $read = '';
336 if (!is_array($message)) $message = array();
337 if (!is_array($response)) $response = array();
338 $resultlist = array();
339 $data = array();
340 $read = sqimap_fgets($imap_stream);
341 $i = $k = 0;
342 while ($read) {
343 $char = $read{0};
344 switch ($char)
345 {
346 case '+':
347 default:
348 $read = sqimap_fgets($imap_stream);
349 break;
350
351 case $tag{0}:
352 {
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);
361 if ($found_tag) {
362 switch ($arg)
363 {
364 case 'OK':
365 case 'BAD':
366 case 'NO':
367 case 'BYE':
368 case 'PREAUTH':
369 $response[$found_tag] = $arg;
370 $message[$found_tag] = trim(substr($read,$i+strlen($arg)));
371 if (!empty($data)) {
372 $resultlist[] = $data;
373 }
374 $aResponse[$found_tag] = $resultlist;
375 $data = $resultlist = array();
376 if ($found_tag == $tag) {
377 break 3; /* switch switch while */
378 }
379 break;
380 default:
381 /* this shouldn't happen */
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;
388 $data = $resultlist = array();
389 if ($found_tag == $tag) {
390 break 3; /* switch switch while */
391 }
392 }
393 }
394 $read = sqimap_fgets($imap_stream);
395 if ($read === false) { /* error */
396 break 3; /* switch switch while */
397 }
398 break;
399 } // end case $tag{0}
400
401 case '*':
402 {
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;
417 $sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
418 if ($sLiteral === false) { /* error */
419 break 4; /* while while switch while */
420 }
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 */
433 $read = sqimap_fgets($imap_stream);
434 if ($read === false) { /* error */
435 break 4; /* while while switch while */
436 }
437 $fetch_data[] = $read;
438 } else {
439 $fetch_data[] = $read;
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);
444 if ($read === false) { /* error */
445 break 4; /* while while switch while */
446 }
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} !== '*' &&
453 substr($read,0,strlen($tag)) !== $tag);
454 $resultlist[] = $fetch_data;
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;
464 $sLiteral = fread($imap_stream,$iLit);
465 if ($sLiteral === false) { /* error */
466 $read = false;
467 break 3; /* while switch while */
468 }
469 $data[] = $sLiteral;
470 $data[] = sqimap_fgets($imap_stream);
471 } else {
472 $data[] = $read;
473 }
474 $read = sqimap_fgets($imap_stream);
475 if ($read === false) {
476 break 3; /* while switch while */
477 } else if ($read{0} == '*') {
478 break;
479 }
480 $s = substr($read,-3);
481 } while ($s === "}\r\n");
482 break 1;
483 }
484 break;
485 } // end case '*'
486 } // end switch
487 } // end while
488
489 /* error processing in case $read is false */
490 if ($read === false) {
491 unset($data);
492 sqimap_error_box(_("ERROR : Connection dropped by imap-server."), $query);
493 exit;
494 }
495
496 /* Set $resultlist array */
497 if (!empty($data)) {
498 //$resultlist[] = $data;
499 }
500 elseif (empty($resultlist)) {
501 $resultlist[] = array();
502 }
503
504 /* Return result or handle errors */
505 if ($handle_errors == false) {
506 return $aResponse;
507 return( $resultlist ); //?? Why this?
508 }
509 switch ($response[$tag]) {
510 case 'OK':
511 return $aResponse;
512 break;
513 case 'NO':
514 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
515 if (strstr($message[$tag], 'command resulted in') === false) {
516 sqimap_error_box(_("ERROR : Could not complete request."), $query, _("Reason Given: "), $message[$tag]);
517 echo '</body></html>';
518 exit;
519 }
520 break;
521 case 'BAD':
522 sqimap_error_box(_("ERROR : Bad or malformed request."), $query, _("Server responded: "), $message[$tag]);
523 echo '</body></html>';
524 exit;
525 case 'BYE':
526 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded: "), $message[$tag]);
527 echo '</body></html>';
528 exit;
529 default:
530 sqimap_error_box(_("ERROR : Unknown imap response."), $query, _("Server responded: "), $message[$tag]);
531 /* the error is displayed but because we don't know the reponse we
532 return the result anyway */
533 return $aResponse;
534 break;
535 }
536}
537
538function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
539 &$response, &$message, $query = '',
540 $filter=false,$outputstream=false,$no_return=false) {
541
542 $tag_uid_a = explode(' ',trim($tag_uid));
543 $tag = $tag_uid_a[0];
544
545 $res = sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
546 $response, $message, $query,$filter,$outputstream,$no_return);
547 /* sqimap_read_data should be called for one response
548 but since it just calls sqimap_retrieve_imap_response which
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
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// }
560 if (isset($result)) {
561 return $result[$tag];
562 }
563 else {
564 return $res;
565 }
566}
567
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 */
572function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
573 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech;
574
575 if (!isset($onetimepad) || empty($onetimepad)) {
576 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
577 }
578 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
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 }
585
586 $imap_stream = fsockopen ( $imap_server_address, $imap_port, $error_number, $error_string, 15);
587
588 /* Do some error correction */
589 if (!$imap_stream) {
590 if (!$hide) {
591 set_up_language($squirrelmail_language, true);
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) .
595 "$error_number : $error_string<br>\r\n";
596 logout_error($string,$color);
597 }
598 exit;
599 }
600
601 $server_info = fgets ($imap_stream, 1024);
602
603 /* Decrypt the password */
604 $password = OneTimePadDecrypt($password, $onetimepad);
605
606 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
607 // We're using some sort of authentication OTHER than plain or login
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];
638 } else {
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.';
643 }
644 } elseif ($imap_auth_mech == 'login') {
645 // Original IMAP login code
646 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
647 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
648 } elseif ($imap_auth_mech == 'plain') {
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 }
656
657 /* If the connection was not successful, lets see why */
658 if ($response != 'OK') {
659 if (!$hide) {
660 if ($response != 'NO') {
661 /* "BAD" and anything else gets reported here. */
662 $message = htmlspecialchars($message);
663 set_up_language($squirrelmail_language, true);
664 require_once(SM_PATH . 'functions/display_messages.php');
665 if ($response == 'BAD') {
666 $string = sprintf (_("Bad request: %s")."<br>\r\n", $message);
667 } else {
668 $string = sprintf (_("Unknown error: %s") . "<br>\n", $message);
669 }
670 if (isset($read) && is_array($read)) {
671 $string .= '<br>' . _("Read data:") . "<br>\n";
672 foreach ($read as $line) {
673 $string .= htmlspecialchars($line) . "<br>\n";
674 }
675 }
676 error_box($string,$color);
677 exit;
678 } else {
679 /*
680 * If the user does not log in with the correct
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.
685 *
686 * $squirrelmail_language is set by a cookie when
687 * the user selects language and logs out
688 */
689
690 set_up_language($squirrelmail_language, true);
691 include_once(SM_PATH . 'functions/display_messages.php' );
692 sqsession_destroy();
693 logout_error( _("Unknown user or password incorrect.") );
694 exit;
695 }
696 } else {
697 exit;
698 }
699 }
700 return $imap_stream;
701}
702
703/* Simply logs out the IMAP session */
704function sqimap_logout ($imap_stream) {
705 /* Logout is not valid until the server returns 'BYE'
706 * If we don't have an imap_ stream we're already logged out */
707 if(isset($imap_stream) && $imap_stream)
708 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
709}
710
711function sqimap_capability($imap_stream, $capability='') {
712 global $sqimap_capabilities;
713 if (!is_array($sqimap_capabilities)) {
714 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
715
716 $c = explode(' ', $read[0]);
717 for ($i=2; $i < count($c); $i++) {
718 $cap_list = explode('=', $c[$i]);
719 if (isset($cap_list[1])) {
720 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
721 } else {
722 $sqimap_capabilities[$cap_list[0]] = TRUE;
723 }
724 }
725 }
726 if ($capability) {
727 if (isset($sqimap_capabilities[$capability])) {
728 return $sqimap_capabilities[$capability];
729 } else {
730 return false;
731 }
732 }
733 return $sqimap_capabilities;
734}
735
736/* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test */
737function sqimap_get_delimiter ($imap_stream = false) {
738 global $sqimap_delimiter, $optional_delimiter;
739
740 /* Use configured delimiter if set */
741 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
742 return $optional_delimiter;
743 }
744
745 /* Do some caching here */
746 if (!$sqimap_delimiter) {
747 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
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 */
755 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
756 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
757 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
758 $pn = $data2[1];
759 }
760 $pna = explode(')(', $pn);
761 while (list($k, $v) = each($pna)) {
762 $lst = explode('"', $v);
763 if (isset($lst[3])) {
764 $pn[$lst[1]] = $lst[3];
765 } else {
766 $pn[$lst[1]] = '';
767 }
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;
779}
780
781
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
790/* Gets the number of messages in the current mailbox. */
791function sqimap_get_num_messages ($imap_stream, $mailbox) {
792 $read_ary = sqimap_run_command ($imap_stream, 'EXAMINE ' . sqimap_encode_mailbox_name($mailbox), false, $result, $message);
793 for ($i = 0; $i < count($read_ary); $i++) {
794 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
795 return $regs[1];
796 }
797 }
798 return false; //"BUG! Couldn't get number of messages in $mailbox!";
799}
800
801
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);
809 $i = $iAddrFound = $bGroup = 0;
810 while ($i < $iCnt) {
811 $cChar = $address{$i};
812 switch($cChar)
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);
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 }
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);
849 if ($sToken) $aTokens[] = $sToken;
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 ',':
864 ++$iAddrFound;
865 case ';':
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;
879 case ' ':
880 $aTokens[] = $cChar;
881 break;
882 default:
883 $iEnd = strpos($address,' ',$i+1);
884 if ($iEnd) {
885 $sToken = trim(substr($address,$i,$iEnd - $i));
886 $i = $iEnd-1;
887 } else {
888 $sToken = trim(substr($address,$i));
889 $i = $iCnt;
890 }
891 if ($sToken) $aTokens[] = $sToken;
892 }
893 ++$i;
894 }
895 $sPersonal = $sEmail = $sComment = $sGroup = '';
896 $aStack = $aComment = array();
897 foreach ($aTokens as $sToken) {
898 if ($max && $max == count($aAddress)) {
899 return $aAddress;
900 }
901 $cChar = $sToken{0};
902 switch ($cChar)
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 = '';
930 }
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;
950 }
951 }
952 /* now do the action again for the last address */
953 if (!$sEmail) {
954 while (count($aStack) && !$sEmail) {
955 $sEmail = trim(array_pop($aStack));
956 }
957 }
958 if (count($aStack)) {
959 $sPersonal = trim(implode('',$aStack));
960 } else {
961 $sPersonal = '';
962 }
963 if (!$sPersonal && count($aComment)) {
964 $sComment = implode(' ',$aComment);
965 $sPersonal .= $sComment;
966 }
967 $aAddress[] = array($sEmail,$sPersonal);
968 return $aAddress;
969}
970
971
972
973/*
974 * Returns the number of unseen messages in this folder
975 */
976function sqimap_unseen_messages ($imap_stream, $mailbox) {
977 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (UNSEEN)', false, $result, $message);
978 $i = 0;
979 $regs = array(false, false);
980 while (isset($read_ary[$i])) {
981 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
982 break;
983 }
984 $i++;
985 }
986 return $regs[1];
987}
988
989/*
990 * Returns the number of total/unseen/recent messages in this folder
991 */
992function sqimap_status_messages ($imap_stream, $mailbox) {
993 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (MESSAGES UNSEEN RECENT)', false, $result, $message);
994 $i = 0;
995 $messages = $unseen = $recent = false;
996 $regs = array(false,false);
997 while (isset($read_ary[$i])) {
998 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
999 $unseen = $regs[1];
1000 }
1001 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
1002 $messages = $regs[1];
1003 }
1004 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
1005 $recent = $regs[1];
1006 }
1007 $i++;
1008 }
1009 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
1010}
1011
1012
1013/*
1014 * Saves a message to a given folder -- used for saving sent messages
1015 */
1016function sqimap_append ($imap_stream, $sent_folder, $length) {
1017 fputs ($imap_stream, sqimap_session_id() . ' APPEND ' . sqimap_encode_mailbox_name($sent_folder) . " (\\Seen) \{$length}\r\n");
1018 $tmp = fgets ($imap_stream, 1024);
1019}
1020
1021function sqimap_append_done ($imap_stream, $folder='') {
1022 global $squirrelmail_language, $color;
1023 fputs ($imap_stream, "\r\n");
1024 $tmp = fgets ($imap_stream, 1024);
1025 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
1026 set_up_language($squirrelmail_language);
1027 require_once(SM_PATH . 'functions/display_messages.php');
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 {
1043 $string = "<b><font color=$color[2]>\n" .
1044 _("ERROR : Bad or malformed request.") .
1045 "</b><br>\n" .
1046 _("Server responded: ") .
1047 $tmp . "</font><br>\n";
1048 error_box($string,$color);
1049 exit;
1050 }
1051 }
1052}
1053
1054function sqimap_get_user_server ($imap_server, $username) {
1055 if (substr($imap_server, 0, 4) != "map:") {
1056 return $imap_server;
1057 }
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
1072?>