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