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