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