handle escaped quotes again
[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 // skip escaped quotes
779 $prev_char = $address{$iEnd-1};
780 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
781 $iEnd = strpos($address,$cChar,$iEnd+1);
782 if ($iEnd) {
783 $prev_char = $address{$iEnd-1};
784 } else {
785 $prev_char = false;
786 }
787 }
788 }
789 if (!$iEnd) {
790 $sToken = substr($address,$i);
791 $i = $iCnt;
792 } else {
793 // also remove the surrounding quotes
794 $sToken = substr($address,$i+1,$iEnd - $i -1);
795 $i = $iEnd;
796 }
797 $sToken = str_replace($aReplace, $aSpecials,$sToken);
798 if ($sToken) $aTokens[] = $sToken;
799 break;
800 case '(':
801 $iEnd = strpos($address,')',$i);
802 if (!$iEnd) {
803 $sToken = substr($address,$i);
804 $i = $iCnt;
805 } else {
806 $sToken = substr($address,$i,$iEnd - $i + 1);
807 $i = $iEnd;
808 }
809 $sToken = str_replace($aReplace, $aSpecials,$sToken);
810 $aTokens[] = $sToken;
811 break;
812 case ',':
813 ++$iAddrFound;
814 case ';':
815 if (!$bGroup) {
816 ++$iAddrFound;
817 } else {
818 $bGroup = false;
819 }
820 if ($max && $max == $iAddrFound) {
821 break 2;
822 } else {
823 $aTokens[] = $cChar;
824 break;
825 }
826 case ':':
827 $bGroup = true;
828 case ' ':
829 $aTokens[] = $cChar;
830 break;
831 default:
832 $iEnd = strpos($address,' ',$i+1);
833 if ($iEnd) {
834 $sToken = trim(substr($address,$i,$iEnd - $i));
835 $i = $iEnd-1;
836 } else {
837 $sToken = trim(substr($address,$i));
838 $i = $iCnt;
839 }
840 if ($sToken) $aTokens[] = $sToken;
841 }
842 ++$i;
843 }
844 $sPersonal = $sEmail = $sComment = $sGroup = '';
845 $aStack = $aComment = array();
846 foreach ($aTokens as $sToken) {
847 if ($max && $max == count($aAddress)) {
848 return $aAddress;
849 }
850 $cChar = $sToken{0};
851 switch ($cChar)
852 {
853 case '=':
854 case '"':
855 case ' ':
856 $aStack[] = $sToken;
857 break;
858 case '(':
859 $aComment[] = substr($sToken,1,-1);
860 break;
861 case ';':
862 if ($sGroup) {
863 $sEmail = trim(implode(' ',$aStack));
864 $aAddress[] = array($sGroup,$sEmail);
865 $aStack = $aComment = array();
866 $sGroup = '';
867 break;
868 }
869 case ',':
870 if (!$sEmail) {
871 while (count($aStack) && !$sEmail) {
872 $sEmail = trim(array_pop($aStack));
873 }
874 }
875 if (count($aStack)) {
876 $sPersonal = trim(implode('',$aStack));
877 } else {
878 $sPersonal = '';
879 }
880 if (!$sPersonal && count($aComment)) {
881 $sComment = implode(' ',$aComment);
882 $sPersonal .= $sComment;
883 }
884 $aAddress[] = array($sEmail,$sPersonal);
885 $sPersonal = $sComment = $sEmail = '';
886 $aStack = $aComment = array();
887 break;
888 case ':':
889 $sGroup = implode(' ',$aStack); break;
890 $aStack = array();
891 break;
892 case '<':
893 $sEmail = trim(substr($sToken,1,-1));
894 break;
895 case '>':
896 /* skip */
897 break;
898 default: $aStack[] = $sToken; break;
899 }
900 }
901 /* now do the action again for the last address */
902 if (!$sEmail) {
903 while (count($aStack) && !$sEmail) {
904 $sEmail = trim(array_pop($aStack));
905 }
906 }
907 if (count($aStack)) {
908 $sPersonal = trim(implode('',$aStack));
909 } else {
910 $sPersonal = '';
911 }
912 if (!$sPersonal && count($aComment)) {
913 $sComment = implode(' ',$aComment);
914 $sPersonal .= $sComment;
915 }
916 $aAddress[] = array($sEmail,$sPersonal);
917 return $aAddress;
918 }
919
920
921
922 /*
923 * Returns the number of unseen messages in this folder
924 */
925 function sqimap_unseen_messages ($imap_stream, $mailbox) {
926 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (UNSEEN)", false, $result, $message);
927 $i = 0;
928 $regs = array(false, false);
929 while (isset($read_ary[$i])) {
930 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
931 break;
932 }
933 $i++;
934 }
935 return $regs[1];
936 }
937
938 /*
939 * Returns the number of unseen/total messages in this folder
940 */
941 function sqimap_status_messages ($imap_stream, $mailbox) {
942 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (MESSAGES UNSEEN RECENT)", false, $result, $message);
943 $i = 0;
944 $messages = $unseen = $recent = false;
945 $regs = array(false,false);
946 while (isset($read_ary[$i])) {
947 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
948 $unseen = $regs[1];
949 }
950 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
951 $messages = $regs[1];
952 }
953 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
954 $recent = $regs[1];
955 }
956 $i++;
957 }
958 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
959 }
960
961
962 /*
963 * Saves a message to a given folder -- used for saving sent messages
964 */
965 function sqimap_append ($imap_stream, $sent_folder, $length) {
966 fputs ($imap_stream, sqimap_session_id() . " APPEND \"$sent_folder\" (\\Seen) \{$length}\r\n");
967 $tmp = fgets ($imap_stream, 1024);
968 }
969
970 function sqimap_append_done ($imap_stream, $folder='') {
971 global $squirrelmail_language, $color;
972 fputs ($imap_stream, "\r\n");
973 $tmp = fgets ($imap_stream, 1024);
974 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
975 set_up_language($squirrelmail_language);
976 require_once(SM_PATH . 'functions/display_messages.php');
977 $reason = $regs[3];
978 if ($regs[2] == 'NO') {
979 $string = "<b><font color=$color[2]>\n" .
980 _("ERROR : Could not append message to") ." $folder." .
981 "</b><br>\n" .
982 _("Server responded: ") .
983 $reason . "<br>\n";
984 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
985 $string .= _("Solution: ") .
986 _("Remove unneccessary messages from your folder and start with your Trash folder.")
987 ."<br>\n";
988 }
989 $string .= "</font>\n";
990 error_box($string,$color);
991 } else {
992 $string = "<b><font color=$color[2]>\n" .
993 _("ERROR : Bad or malformed request.") .
994 "</b><br>\n" .
995 _("Server responded: ") .
996 $tmp . "</font><br>\n";
997 error_box($string,$color);
998 exit;
999 }
1000 }
1001 }
1002
1003 function sqimap_get_user_server ($imap_server, $username) {
1004 if (substr($imap_server, 0, 4) != "map:") {
1005 return $imap_server;
1006 }
1007 $function = substr($imap_server, 4);
1008 return $function($username);
1009 }
1010
1011 /* This is an example that gets imapservers from yellowpages (NIS).
1012 * you can simple put map:map_yp_alias in your $imap_server_address
1013 * in config.php use your own function instead map_yp_alias to map your
1014 * LDAP whatever way to find the users imapserver. */
1015
1016 function map_yp_alias($username) {
1017 $yp = `ypmatch $username aliases`;
1018 return chop(substr($yp, strlen($username)+1));
1019 }
1020
1021 ?>