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