6 * Copyright (c) 1999-2005 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
9 * This implements all functions that do general imap functions.
12 * @package squirrelmail
17 require_once(SM_PATH
. 'functions/page_header.php');
18 require_once(SM_PATH
. 'functions/auth.php');
22 * Generates a new session ID by incrementing the last one used;
23 * this ensures that each command has a unique ID.
24 * @param bool $unique_id (since 1.3.0) controls use of unique
25 * identifiers/message sequence numbers in IMAP commands. See IMAP
26 * rfc 'UID command' chapter.
27 * @return string IMAP session id of the form 'A000'.
30 function sqimap_session_id($unique_id = FALSE) {
31 static $sqimap_session_id = 1;
34 return( sprintf("A%03d", $sqimap_session_id++
) );
36 return( sprintf("A%03d", $sqimap_session_id++
) . ' UID' );
41 * Both send a command and accept the result from the command.
42 * This is to allow proper session number handling.
43 * @param resource $imap_stream imap connection resource
44 * @param string $query imap command
45 * @param boolean $handle_errors see sqimap_retrieve_imap_response()
46 * @param mixed $response
47 * @param mixed $message
48 * @param boolean $unique_id (since 1.3.0) controls use of unique
49 * identifiers/message sequence numbers in IMAP commands. See IMAP
50 * rfc 'UID command' chapter.
51 * @return mixed returns false on imap error. displays error message
52 * if imap stream is not available.
55 function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id = false) {
57 $sid = sqimap_session_id($unique_id);
58 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
59 $tag_uid_a = explode(' ',trim($sid));
61 $read = sqimap_retrieve_imap_response ($imap_stream, $tag, $handle_errors, $response, $message, $query );
62 /* get the response and the message */
63 $message = $message[$tag];
64 $response = $response[$tag];
67 global $squirrelmail_language, $color;
68 set_up_language($squirrelmail_language);
69 require_once(SM_PATH
. 'functions/display_messages.php');
70 $string = "<b><font color=\"$color[2]\">\n" .
71 _("ERROR : No available imapstream.") .
73 error_box($string,$color);
79 * @param resource $imap_stream imap connection resource
80 * @param string $query imap command
81 * @param boolean $handle_errors see sqimap_retrieve_imap_response()
82 * @param mixed $response
83 * @param mixed $message
84 * @param boolean $unique_id (since 1.3.0) controls use of unique
85 * identifiers/message sequence numbers in IMAP commands. See IMAP
86 * rfc 'UID command' chapter.
87 * @param mixed $filter (since 1.4.1 and 1.5.0)
88 * @param mixed $outputstream (since 1.4.1 and 1.5.0)
89 * @param mixed $no_return (since 1.4.1 and 1.5.0)
90 * @return mixed returns false on imap error. displays error message
91 * if imap stream is not available.
94 function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
95 &$message, $unique_id = false,$filter=false,
96 $outputstream=false,$no_return=false) {
98 $sid = sqimap_session_id($unique_id);
99 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
100 $tag_uid_a = explode(' ',trim($sid));
101 $tag = $tag_uid_a[0];
103 $read = sqimap_read_data ($imap_stream, $tag, $handle_errors, $response,
104 $message, $query,$filter,$outputstream,$no_return);
105 if (empty($read)) { //Imap server dropped its connection
110 /* retrieve the response and the message */
111 $response = $response[$tag];
112 $message = $message[$tag];
114 if (!empty($read[$tag])) {
115 return $read[$tag][0];
120 global $squirrelmail_language, $color;
121 set_up_language($squirrelmail_language);
122 require_once(SM_PATH
. 'functions/display_messages.php');
123 $string = "<b><font color=\"$color[2]\">\n" .
124 _("ERROR : No available imapstream.") .
126 error_box($string,$color);
131 function sqimap_prepare_pipelined_query($new_query,&$tag,&$aQuery,$unique_id) {
132 $sid = sqimap_session_id($unique_id);
133 $tag_uid_a = explode(' ',trim($sid));
134 $tag = $tag_uid_a[0];
135 $query = $sid . ' '.$new_query."\r\n";
136 $aQuery[$tag] = $query;
139 function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
140 &$aServerResponse, &$aServerMessage, $unique_id = false,
141 $filter=false,$outputstream=false,$no_return=false) {
145 Do not fire all calls at once to the imap-server but split the calls up
146 in portions of $iChunkSize. If we do not do that I think we misbehave as
147 IMAP client or should handle BYE calls if the IMAP-server drops the
148 connection because the number of queries is to large. This isn't tested
149 but a wild guess how it could work in the field.
151 After testing it on Exchange 2000 we discovered that a chunksize of 32
152 was quicker then when we raised it to 128.
154 $iQueryCount = count($aQueryList);
156 // array_chunk would also do the job but it's supported from php > 4.2
157 $aQueryChunks = array();
158 $iLoops = floor($iQueryCount / $iChunkSize);
160 if ($iLoops * $iChunkSize != $iQueryCount) ++
$iLoops;
162 if (!function_exists('array_chunk')) { // arraychunk replacement
164 for($i=0;$i<$iLoops;++
$i) {
165 for($j=0;$j<$iChunkSize;++
$j) {
166 $key = key($aQueryList);
167 $aTmp[$key] = $aQueryList[$key];
168 if (next($aQueryList) === false) break;
170 $aQueryChunks[] = $aTmp;
173 $aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
176 for ($i=0;$i<$iLoops;++
$i) {
177 $aQuery = $aQueryChunks[$i];
178 foreach($aQuery as $tag => $query) {
179 fputs($imap_stream,$query);
180 $aResults[$tag] = false;
182 foreach($aQuery as $tag => $query) {
183 if ($aResults[$tag] == false) {
184 $aReturnedResponse = sqimap_retrieve_imap_response ($imap_stream, $tag,
185 $handle_errors, $response, $message, $query,
186 $filter,$outputstream,$no_return);
187 foreach ($aReturnedResponse as $returned_tag => $aResponse) {
188 if (!empty($aResponse)) {
189 $aResults[$returned_tag] = $aResponse[0];
191 $aResults[$returned_tag] = $aResponse;
193 $aServerResponse[$returned_tag] = $response[$returned_tag];
194 $aServerMessage[$returned_tag] = $message[$returned_tag];
203 * Custom fgets function: gets a line from the IMAP-server,
204 * no matter how big it may be.
205 * @param stream imap_stream the stream to read from
206 * @return string a line
208 function sqimap_fgets($imap_stream) {
213 while (strpos($results, "\r\n", $offset) === false) {
214 if (!($read = fgets($imap_stream, $buffer))) {
215 /* this happens in case of an error */
216 /* reset $results because it's useless */
220 if ( $results != '' ) {
221 $offset = strlen($results) - 1;
228 function sqimap_fread($imap_stream,$iSize,$filter=false,
229 $outputstream=false, $no_return=false) {
230 if (!$filter ||
!$outputstream) {
231 $iBufferSize = $iSize;
233 // see php bug 24033. They changed fread behaviour %$^&$%
234 $iBufferSize = 7800; // multiple of 78 in case of base64 decoding.
236 if ($iSize < $iBufferSize) {
237 $iBufferSize = $iSize;
242 $sRead = $sReadRem = '';
243 // NB: fread can also stop at end of a packet on sockets.
244 while ($iRetrieved < $iSize) {
245 $sRead = fread($imap_stream,$iBufferSize);
246 $iLength = strlen($sRead);
247 $iRetrieved +
= $iLength ;
248 $iRemaining = $iSize - $iRetrieved;
249 if ($iRemaining < $iBufferSize) {
250 $iBufferSize = $iRemaining;
256 if ($sReadRem != '') {
257 $sRead = $sReadRem . $sRead;
261 if ($filter && $sRead != '') {
262 // in case the filter is base64 decoding we return a remainder
263 $sReadRem = $filter($sRead);
265 if ($outputstream && $sRead != '') {
266 if (is_resource($outputstream)) {
267 fwrite($outputstream,$sRead);
268 } else if ($outputstream == 'php://stdout') {
283 * Obsolete function, inform plugins that use it
285 * @deprecated (since 1.5.0) use sqimap_run_command or sqimap_run_command_list instead
287 function sqimap_read_data_list($imap_stream, $tag, $handle_errors,
288 &$response, &$message, $query = '') {
289 global $color, $squirrelmail_language;
290 set_up_language($squirrelmail_language);
291 require_once(SM_PATH
. 'functions/display_messages.php');
292 $string = "<b><font color=\"$color[2]\">\n" .
293 _("ERROR : Bad function call.") .
296 'There is a plugin installed which make use of the <br />' .
297 'SquirrelMail internal function sqimap_read_data_list.<br />'.
298 'Please adapt the installed plugin and let it use<br />'.
299 'sqimap_run_command or sqimap_run_command_list instead<br /><br />'.
300 'The following query was issued:<br />'.
301 htmlspecialchars($query) . '<br />' . "</font><br />\n";
302 error_box($string,$color);
303 echo '</body></html>';
308 * Function to display an error related to an IMAP-query.
309 * @param string title the caption of the error box
310 * @param string query the query that went wrong
311 * @param string message_title optional message title
312 * @param string message optional error message
313 * @param string $link an optional link to try again
316 function sqimap_error_box($title, $query = '', $message_title = '', $message = '', $link = '')
318 global $color, $squirrelmail_language;
320 set_up_language($squirrelmail_language);
321 require_once(SM_PATH
. 'functions/display_messages.php');
322 $string = "<font color=\"$color[2]\"><b>\n" . $title . "</b><br />\n";
323 $cmd = explode(' ',$query);
324 $cmd= strtolower($cmd[0]);
326 if ($query != '' && $cmd != 'login')
327 $string .= _("Query:") . ' ' . htmlspecialchars($query) . '<br />';
328 if ($message_title != '')
329 $string .= $message_title;
331 $string .= htmlspecialchars($message);
332 $string .= "</font><br />\n";
335 error_box($string,$color);
339 * Reads the output from the IMAP stream. If handle_errors is set to true,
340 * this will also handle all errors that are received. If it is not set,
341 * the errors will be sent back through $response and $message.
343 function sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
344 &$response, &$message, $query = '',
345 $filter = false, $outputstream = false, $no_return = false) {
346 global $color, $squirrelmail_language;
348 if (!is_array($message)) $message = array();
349 if (!is_array($response)) $response = array();
351 $resultlist = array();
353 $read = sqimap_fgets($imap_stream);
361 $read = sqimap_fgets($imap_stream);
366 /* get the command */
369 $s = substr($read,$i);
370 if (($j = strpos($s,' ')) ||
($j = strpos($s,"\n"))) {
371 $arg = substr($s,0,$j);
373 $found_tag = substr($read,0,$i-1);
382 $response[$found_tag] = $arg;
383 $message[$found_tag] = trim(substr($read,$i+
strlen($arg)));
385 $resultlist[] = $data;
387 $aResponse[$found_tag] = $resultlist;
388 $data = $resultlist = array();
389 if ($found_tag == $tag) {
390 break 3; /* switch switch while */
394 /* this shouldn't happen */
395 $response[$found_tag] = $arg;
396 $message[$found_tag] = trim(substr($read,$i+
strlen($arg)));
398 $resultlist[] = $data;
400 $aResponse[$found_tag] = $resultlist;
401 $data = $resultlist = array();
402 if ($found_tag == $tag) {
403 break 3; /* switch switch while */
407 $read = sqimap_fgets($imap_stream);
408 if ($read === false) { /* error */
409 break 2; /* switch while */
412 } // end case $tag{0}
416 if (preg_match('/^\*\s\d+\sFETCH/',$read)) {
417 /* check for literal */
418 $s = substr($read,-3);
419 $fetch_data = array();
420 do { /* outer loop, continue until next untagged fetch
422 do { /* innerloop for fetching literals. with this loop
423 we prohibid that literal responses appear in the
424 outer loop so we can trust the untagged and
425 tagged info provided by $read */
426 if ($s === "}\r\n") {
427 $j = strrpos($read,'{');
428 $iLit = substr($read,$j+
1,-3);
429 $fetch_data[] = $read;
430 $sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
431 if ($sLiteral === false) { /* error */
432 break 4; /* while while switch while */
434 /* backwards compattibility */
435 $aLiteral = explode("\n", $sLiteral);
436 /* release not neaded data */
438 foreach ($aLiteral as $line) {
439 $fetch_data[] = $line ."\n";
441 /* release not neaded data */
443 /* next fgets belongs to this fetch because
444 we just got the exact literalsize and there
445 must follow data to complete the response */
446 $read = sqimap_fgets($imap_stream);
447 if ($read === false) { /* error */
448 break 4; /* while while switch while */
450 $fetch_data[] = $read;
452 $fetch_data[] = $read;
454 /* retrieve next line and check in the while
455 statements if it belongs to this fetch response */
456 $read = sqimap_fgets($imap_stream);
457 if ($read === false) { /* error */
458 break 4; /* while while switch while */
460 /* check for next untagged reponse and break */
461 if ($read{0} == '*') break 2;
462 $s = substr($read,-3);
463 } while ($s === "}\r\n");
464 $s = substr($read,-3);
465 } while ($read{0} !== '*' &&
466 substr($read,0,strlen($tag)) !== $tag);
467 $resultlist[] = $fetch_data;
468 /* release not neaded data */
471 $s = substr($read,-3);
473 if ($s === "}\r\n") {
474 $j = strrpos($read,'{');
475 $iLit = substr($read,$j+
1,-3);
477 $sLiteral = fread($imap_stream,$iLit);
478 if ($sLiteral === false) { /* error */
480 break 3; /* while switch while */
483 $data[] = sqimap_fgets($imap_stream);
487 $read = sqimap_fgets($imap_stream);
488 if ($read === false) {
489 break 3; /* while switch while */
490 } else if ($read{0} == '*') {
493 $s = substr($read,-3);
494 } while ($s === "}\r\n");
502 /* error processing in case $read is false */
503 if ($read === false) {
504 // try to retrieve an untagged bye respons from the results
505 $sResponse = array_pop($data);
506 if ($sResponse !== NULL && strpos($sResponse,'* BYE') !== false) {
507 if (!$handle_errors) {
510 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded:"),$sResponse);
511 echo '</body></html>';
513 } else if ($handle_errors) {
515 sqimap_error_box(_("ERROR : Connection dropped by imap-server."), $query);
520 /* Set $resultlist array */
522 //$resultlist[] = $data;
524 elseif (empty($resultlist)) {
525 $resultlist[] = array();
528 /* Return result or handle errors */
529 if ($handle_errors == false) {
532 switch ($response[$tag]) {
537 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
538 if (strstr($message[$tag], 'command resulted in') === false) {
539 sqimap_error_box(_("ERROR : Could not complete request."), $query, _("Reason Given: "), $message[$tag]);
540 echo '</body></html>';
545 sqimap_error_box(_("ERROR : Bad or malformed request."), $query, _("Server responded: "), $message[$tag]);
546 echo '</body></html>';
549 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded: "), $message[$tag]);
550 echo '</body></html>';
553 sqimap_error_box(_("ERROR : Unknown imap response."), $query, _("Server responded: "), $message[$tag]);
554 /* the error is displayed but because we don't know the reponse we
555 return the result anyway */
561 function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
562 &$response, &$message, $query = '',
563 $filter=false,$outputstream=false,$no_return=false) {
565 $tag_uid_a = explode(' ',trim($tag_uid));
566 $tag = $tag_uid_a[0];
568 $res = sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
569 $response, $message, $query,$filter,$outputstream,$no_return);
574 * Connects to the IMAP server and returns a resource identifier for use with
575 * the other SquirrelMail IMAP functions. Does NOT login!
576 * @param string server hostname of IMAP server
577 * @param int port port number to connect to
578 * @param bool tls whether to use TLS when connecting.
579 * @return imap-stream resource identifier
581 function sqimap_create_stream($server,$port,$tls=false) {
582 global $squirrelmail_language;
585 if ((check_php_version(4,3)) and (extension_loaded('openssl'))) {
586 /* Use TLS by prefixing "tls://" to the hostname */
587 $server = 'tls://' . $server;
589 require_once(SM_PATH
. 'functions/display_messages.php');
590 logout_error( sprintf(_("Error connecting to IMAP server: %s."), $server).
592 _("TLS is enabled, but this version of PHP does not support TLS sockets, or is missing the openssl extension.").
594 _("Please contact your system administrator and report this error.") );
598 $imap_stream = @fsockopen
($server, $port, $error_number, $error_string, 15);
600 /* Do some error correction */
602 set_up_language($squirrelmail_language, true);
603 require_once(SM_PATH
. 'functions/display_messages.php');
604 logout_error( sprintf(_("Error connecting to IMAP server: %s."), $server).
605 "<br />\r\n$error_number : $error_string<br />\r\n" );
608 $server_info = fgets ($imap_stream, 1024);
613 * Logs the user into the imap server. If $hide is set, no error messages
614 * will be displayed. This function returns the imap connection handle.
616 function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
617 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls,
618 $imap_auth_mech, $sqimap_capabilities;
620 if (!isset($onetimepad) ||
empty($onetimepad)) {
621 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION
);
623 if (!isset($sqimap_capabilities)) {
624 sqgetglobalvar('sqimap_capabilities' , $capability , SQ_SESSION
);
627 $host = $imap_server_address;
628 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
630 $imap_stream = sqimap_create_stream($imap_server_address,$imap_port,$use_imap_tls);
632 /* Decrypt the password */
633 $password = OneTimePadDecrypt($password, $onetimepad);
635 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
636 // We're using some sort of authentication OTHER than plain or login
637 $tag=sqimap_session_id(false);
638 if ($imap_auth_mech == 'digest-md5') {
639 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
640 } elseif ($imap_auth_mech == 'cram-md5') {
641 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
643 fputs($imap_stream,$query);
644 $answer=sqimap_fgets($imap_stream);
645 // Trim the "+ " off the front
646 $response=explode(" ",$answer,3);
647 if ($response[0] == '+') {
648 // Got a challenge back
649 $challenge=$response[1];
650 if ($imap_auth_mech == 'digest-md5') {
651 $reply = digest_md5_response($username,$password,$challenge,'imap',$host);
652 } elseif ($imap_auth_mech == 'cram-md5') {
653 $reply = cram_md5_response($username,$password,$challenge);
655 fputs($imap_stream,$reply);
656 $read=sqimap_fgets($imap_stream);
657 if ($imap_auth_mech == 'digest-md5') {
658 // DIGEST-MD5 has an extra step..
659 if (substr($read,0,1) == '+') { // OK so far..
660 fputs($imap_stream,"\r\n");
661 $read=sqimap_fgets($imap_stream);
664 $results=explode(" ",$read,3);
665 $response=$results[1];
666 $message=$results[2];
668 // Fake the response, so the error trap at the bottom will work
670 $message='IMAP server does not appear to support the authentication method selected.';
671 $message .= ' Please contact your system administrator.';
673 } elseif ($imap_auth_mech == 'login') {
674 // Original IMAP login code
675 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
676 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
677 } elseif ($imap_auth_mech == 'plain') {
683 * The mechanism consists of a single message from the client to the
684 * server. The client sends the authorization identity (identity to
685 * login as), followed by a US-ASCII NUL character, followed by the
686 * authentication identity (identity whose password will be used),
687 * followed by a US-ASCII NUL character, followed by the clear-text
688 * password. The client may leave the authorization identity empty to
689 * indicate that it is the same as the authentication identity.
692 $tag=sqimap_session_id(false);
693 $sasl = (isset($capability['SASL-IR']) && $capability['SASL-IR']) ?
true : false;
694 $auth = base64_encode("$username\0$username\0$password");
696 // IMAP Extension for SASL Initial Client Response
697 // <draft-siemborski-imap-sasl-initial-response-01b.txt>
698 $query = $tag . " AUTHENTICATE PLAIN $auth\r\n";
699 fputs($imap_stream, $query);
700 $read = sqimap_fgets($imap_stream);
702 $query = $tag . " AUTHENTICATE PLAIN\r\n";
703 fputs($imap_stream, $query);
704 $read=sqimap_fgets($imap_stream);
705 if (substr($read,0,1) == '+') { // OK so far..
706 fputs($imap_stream, "$auth\r\n");
707 $read = sqimap_fgets($imap_stream);
710 $results=explode(" ",$read,3);
711 $response=$results[1];
712 $message=$results[2];
715 $message="Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
718 /* If the connection was not successful, lets see why */
719 if ($response != 'OK') {
721 if ($response != 'NO') {
722 /* "BAD" and anything else gets reported here. */
723 $message = htmlspecialchars($message);
724 set_up_language($squirrelmail_language, true);
725 require_once(SM_PATH
. 'functions/display_messages.php');
726 if ($response == 'BAD') {
727 $string = sprintf (_("Bad request: %s")."<br />\r\n", $message);
729 $string = sprintf (_("Unknown error: %s") . "<br />\n", $message);
731 if (isset($read) && is_array($read)) {
732 $string .= '<br />' . _("Read data:") . "<br />\n";
733 foreach ($read as $line) {
734 $string .= htmlspecialchars($line) . "<br />\n";
737 error_box($string,$color);
741 * If the user does not log in with the correct
742 * username and password it is not possible to get the
743 * correct locale from the user's preferences.
744 * Therefore, apply the same hack as on the login
747 * $squirrelmail_language is set by a cookie when
748 * the user selects language and logs out
751 set_up_language($squirrelmail_language, true);
752 include_once(SM_PATH
. 'functions/display_messages.php' );
754 /* terminate the session nicely */
755 sqimap_logout($imap_stream);
756 logout_error( _("Unknown user or password incorrect.") );
767 * Simply logs out the IMAP session
768 * @param stream imap_stream the IMAP connection to log out.
771 function sqimap_logout ($imap_stream) {
772 /* Logout is not valid until the server returns 'BYE'
773 * If we don't have an imap_ stream we're already logged out */
774 if(isset($imap_stream) && $imap_stream)
775 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
779 * Retreive the CAPABILITY string from the IMAP server.
780 * If capability is set, returns only that specific capability,
781 * else returns array of all capabilities.
783 function sqimap_capability($imap_stream, $capability='') {
784 global $sqimap_capabilities;
785 if (!is_array($sqimap_capabilities)) {
786 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
788 $c = explode(' ', $read[0]);
789 for ($i=2; $i < count($c); $i++
) {
790 $cap_list = explode('=', $c[$i]);
791 if (isset($cap_list[1])) {
792 // FIX ME. capabilities can occure multiple times.
793 // THREAD=REFERENCES THREAD=ORDEREDSUBJECT
794 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
796 $sqimap_capabilities[$cap_list[0]] = TRUE;
801 if (isset($sqimap_capabilities[$capability])) {
802 return $sqimap_capabilities[$capability];
807 return $sqimap_capabilities;
811 * Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test
813 function sqimap_get_delimiter ($imap_stream = false) {
814 global $sqimap_delimiter, $optional_delimiter;
816 /* Use configured delimiter if set */
817 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
818 return $optional_delimiter;
821 /* Do some caching here */
822 if (!$sqimap_delimiter) {
823 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
825 * According to something that I can't find, this is supposed to work on all systems
826 * OS: This won't work in Courier IMAP.
827 * OS: According to rfc2342 response from NAMESPACE command is:
828 * OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
829 * OS: We want to lookup all personal NAMESPACES...
831 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
832 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
833 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
836 $pna = explode(')(', $pn);
837 while (list($k, $v) = each($pna)) {
838 $lst = explode('"', $v);
839 if (isset($lst[3])) {
840 $pn[$lst[1]] = $lst[3];
846 $sqimap_delimiter = $pn[0];
848 fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
849 $read = sqimap_read_data($imap_stream, '.', true, $a, $b);
850 $read = $read['.'][0]; //sqimap_read_data() now returns a tag array of response array
851 $quote_position = strpos ($read[0], '"');
852 $sqimap_delimiter = substr ($read[0], $quote_position+
1, 1);
855 return $sqimap_delimiter;
859 * This encodes a mailbox name for use in IMAP commands.
860 * @param string what the mailbox to encode
861 * @return string the encoded mailbox string
863 function sqimap_encode_mailbox_name($what)
865 if (ereg("[\"\\\r\n]", $what))
866 return '{' . strlen($what) . "}\r\n" . $what; /* 4.3 literal form */
867 return '"' . $what . '"'; /* 4.3 quoted string form */
871 * Gets the number of messages in the current mailbox.
873 * OBSOLETE use sqimap_status_messages instead.
875 function sqimap_get_num_messages ($imap_stream, $mailbox) {
876 $read_ary = sqimap_run_command ($imap_stream, 'EXAMINE ' . sqimap_encode_mailbox_name($mailbox), false, $result, $message);
877 for ($i = 0; $i < count($read_ary); $i++
) {
878 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
882 return false; //"BUG! Couldn't get number of messages in $mailbox!";
884 include_once(SM_PATH
. 'functions/rfc822address.php');
887 * OBSOLETE FUNCTION should be removed after mailbox_display,
888 * printMessage function is adapted
890 function parseAddress($address, $max=0) {
891 $aAddress = parseRFC822Address($address,array('limit'=> $max));
893 * Because the expected format of the array element is changed we adapt it now.
894 * This also implies that this function is obsolete and should be removed after the
895 * rest of the source is adapted. See Rfc822Address.php for the new function.
897 array_walk($aAddress, '_adaptAddress');
902 * OBSOLETE FUNCTION should be removed after mailbox_display,
903 * printMessage function is adapted
905 function _adaptAddress(&$aAddr,$k) {
906 $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL
]) && $aAddr[SQM_ADDR_PERSONAL
]) ?
907 $aAddr[SQM_ADDR_PERSONAL
] : '';
908 $sEmail = ($aAddr[SQM_ADDR_HOST
]) ?
909 $aAddr[SQM_ADDR_MAILBOX
] . '@'.$aAddr[SQM_ADDR_HOST
] :
910 $aAddr[SQM_ADDR_MAILBOX
];
911 $aAddr = array($sEmail,$sPersonal);
915 * Returns the number of unseen messages in this folder.
916 * obsoleted by sqimap_status_messages !
918 function sqimap_unseen_messages ($imap_stream, $mailbox) {
919 $aStatus = sqimap_status_messages($imap_stream,$mailbox,array('UNSEEN'));
920 return $aStatus['UNSEEN'];
924 * Returns the status items of a mailbox.
925 * Default it returns MESSAGES,UNSEEN and RECENT
926 * Supported status items are MESSAGES, UNSEEN, RECENT, UIDNEXT and UIDVALIDITY
928 function sqimap_status_messages ($imap_stream, $mailbox,
929 $aStatusItems = array('MESSAGES','UNSEEN','RECENT')) {
931 $aStatusItems = implode(' ',$aStatusItems);
932 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) .
933 " ($aStatusItems)", false, $result, $message);
935 $messages = $unseen = $recent = $uidnext = $uidvalidity = false;
936 $regs = array(false,false);
937 while (isset($read_ary[$i])) {
938 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
941 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
942 $messages = $regs[1];
944 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
947 if (preg_match('/UIDNEXT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
950 if (preg_match('/UIDVALIDITY\s+([0-9]+)/i', $read_ary[$i], $regs)) {
951 $uidvalidity = $regs[1];
955 return array('MESSAGES' => $messages,
958 'UIDNEXT' => $uidnext,
959 'UIDVALIDITY' => $uidvalidity);
964 * Saves a message to a given folder -- used for saving sent messages
966 function sqimap_append ($imap_stream, $sent_folder, $length) {
967 fputs ($imap_stream, sqimap_session_id() . ' APPEND ' . sqimap_encode_mailbox_name($sent_folder) . " (\\Seen) \{$length}\r\n");
968 $tmp = fgets ($imap_stream, 1024);
969 sqimap_append_checkresponse($tmp, $sent_folder);
972 function sqimap_append_done ($imap_stream, $folder='') {
973 fputs ($imap_stream, "\r\n");
974 $tmp = fgets ($imap_stream, 1024);
975 sqimap_append_checkresponse($tmp, $folder);
978 function sqimap_append_checkresponse($response, $folder) {
980 if (preg_match("/(.*)(BAD|NO)(.*)$/", $response, $regs)) {
981 global $squirrelmail_language, $color;
982 set_up_language($squirrelmail_language);
983 require_once(SM_PATH
. 'functions/display_messages.php');
986 if ($regs[2] == 'NO') {
987 $string = "<b><font color=\"$color[2]\">\n" .
988 _("ERROR : Could not append message to") ." $folder." .
990 _("Server responded: ") .
991 $reason . "<br />\n";
992 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
993 $string .= _("Solution: ") .
994 _("Remove unneccessary messages from your folder and start with your Trash folder.")
997 $string .= "</font>\n";
998 error_box($string,$color);
1000 $string = "<b><font color=\"$color[2]\">\n" .
1001 _("ERROR : Bad or malformed request.") .
1003 _("Server responded: ") .
1004 $reason . "</font><br />\n";
1005 error_box($string,$color);
1011 function sqimap_get_user_server ($imap_server, $username) {
1012 if (substr($imap_server, 0, 4) != "map:") {
1013 return $imap_server;
1015 $function = substr($imap_server, 4);
1016 return $function($username);
1020 * This is an example that gets imapservers from yellowpages (NIS).
1021 * you can simple put map:map_yp_alias in your $imap_server_address
1022 * in config.php use your own function instead map_yp_alias to map your
1023 * LDAP whatever way to find the users imapserver.
1025 function map_yp_alias($username) {
1026 $yp = `ypmatch
$username aliases`
;
1027 return chop(substr($yp, strlen($username)+
1));