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