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