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