Today Cyrus 2.2.2-BETA with SASL Initial Client response was released so it
[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) {
fe55c7c7 572 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls,
573 $imap_auth_mech, $sqimap_capabilities;
85fc999e 574
eb2f6102 575 if (!isset($onetimepad) || empty($onetimepad)) {
576 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
577 }
fe55c7c7 578 if (!isset($sqimap_capabilities)) {
579 sqgetglobalvar('sqimap_capabilities' , $capability , SQ_SESSION );
580 }
581
b98732e8 582 $host = $imap_server_address;
bd9829d7 583 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
fe55c7c7 584
b98732e8 585 $imap_stream = sqimap_create_stream($imap_server_address,$imap_port,$use_imap_tls);
fe55c7c7 586
2f1f7a12 587 /* Decrypt the password */
588 $password = OneTimePadDecrypt($password, $onetimepad);
589
b98732e8 590 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
591 // We're using some sort of authentication OTHER than plain or login
592 $tag=sqimap_session_id(false);
593 if ($imap_auth_mech == 'digest-md5') {
098ea084 594 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
b98732e8 595 } elseif ($imap_auth_mech == 'cram-md5') {
098ea084 596 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
b98732e8 597 }
598 fputs($imap_stream,$query);
599 $answer=sqimap_fgets($imap_stream);
600 // Trim the "+ " off the front
601 $response=explode(" ",$answer,3);
602 if ($response[0] == '+') {
098ea084 603 // Got a challenge back
b98732e8 604 $challenge=$response[1];
605 if ($imap_auth_mech == 'digest-md5') {
606 $reply = digest_md5_response($username,$password,$challenge,'imap',$host);
607 } elseif ($imap_auth_mech == 'cram-md5') {
608 $reply = cram_md5_response($username,$password,$challenge);
609 }
610 fputs($imap_stream,$reply);
611 $read=sqimap_fgets($imap_stream);
612 if ($imap_auth_mech == 'digest-md5') {
613 // DIGEST-MD5 has an extra step..
614 if (substr($read,0,1) == '+') { // OK so far..
098ea084 615 fputs($imap_stream,"\r\n");
616 $read=sqimap_fgets($imap_stream);
098ea084 617 }
b98732e8 618 }
619 $results=explode(" ",$read,3);
620 $response=$results[1];
621 $message=$results[2];
622 } else {
623 // Fake the response, so the error trap at the bottom will work
624 $response="BAD";
625 $message='IMAP server does not appear to support the authentication method selected.';
626 $message .= ' Please contact your system administrator.';
627 }
fe0b18b3 628 } elseif ($imap_auth_mech == 'login') {
b98732e8 629 // Original IMAP login code
630 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
631 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
1e7fc1cb 632 } elseif ($imap_auth_mech == 'plain') {
fe55c7c7 633 /***
634 * SASL PLAIN
635 *
636 * RFC 2595 Chapter 6
637 *
638 * The mechanism consists of a single message from the client to the
639 * server. The client sends the authorization identity (identity to
640 * login as), followed by a US-ASCII NUL character, followed by the
641 * authentication identity (identity whose password will be used),
642 * followed by a US-ASCII NUL character, followed by the clear-text
643 * password. The client may leave the authorization identity empty to
644 * indicate that it is the same as the authentication identity.
645 *
646 **/
b98732e8 647 $tag=sqimap_session_id(false);
fe55c7c7 648 $sasl = (isset($capability['SASL']) && $capability['SASL']) ? true : false;
b98732e8 649 $auth = base64_encode("$username\0$username\0$password");
fe55c7c7 650 if ($sasl) {
651 // IMAP Extension for SASL Initial Client Response
652 // <draft-siemborski-imap-sasl-initial-response-00.txt>
653 $query = $tag . " AUTHENTICATE PLAIN $auth\r\n";
654 fputs($imap_stream, $query);
b98732e8 655 $read = sqimap_fgets($imap_stream);
fe55c7c7 656 } else {
657 $query = $tag . " AUTHENTICATE PLAIN\r\n";
658 fputs($imap_stream, $query);
659 $read=sqimap_fgets($imap_stream);
660 if (substr($read,0,1) == '+') { // OK so far..
661 fputs($imap_stream, "$auth\r\n");
662 $read = sqimap_fgets($imap_stream);
663 }
098ea084 664 }
b98732e8 665 $results=explode(" ",$read,3);
666 $response=$results[1];
667 $message=$results[2];
668 } else {
669 $response="BAD";
670 $message="Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
671 }
fe55c7c7 672
b98732e8 673 /* If the connection was not successful, lets see why */
9c737111 674 if ($response != 'OK') {
675 if (!$hide) {
74424a43 676 if ($response != 'NO') {
3411d4ec 677 /* "BAD" and anything else gets reported here. */
098ea084 678 $message = htmlspecialchars($message);
9c737111 679 set_up_language($squirrelmail_language, true);
098ea084 680 require_once(SM_PATH . 'functions/display_messages.php');
9c737111 681 if ($response == 'BAD') {
bea3eb1e 682 $string = sprintf (_("Bad request: %s")."<br>\r\n", $message);
9c737111 683 } else {
bea3eb1e 684 $string = sprintf (_("Unknown error: %s") . "<br>\n", $message);
9c737111 685 }
1e7fc1cb 686 if (isset($read) && is_array($read)) {
bea3eb1e 687 $string .= '<br>' . _("Read data:") . "<br>\n";
9c737111 688 foreach ($read as $line) {
1f720b34 689 $string .= htmlspecialchars($line) . "<br>\n";
9c737111 690 }
691 }
098ea084 692 error_box($string,$color);
9c737111 693 exit;
165e24a7 694 } else {
3411d4ec 695 /*
696 * If the user does not log in with the correct
1c72b151 697 * username and password it is not possible to get the
698 * correct locale from the user's preferences.
699 * Therefore, apply the same hack as on the login
700 * screen.
3411d4ec 701 *
702 * $squirrelmail_language is set by a cookie when
1c72b151 703 * the user selects language and logs out
bee165ef 704 */
fe55c7c7 705
9c737111 706 set_up_language($squirrelmail_language, true);
bd9c880b 707 include_once(SM_PATH . 'functions/display_messages.php' );
098ea084 708 sqsession_destroy();
bd9c880b 709 logout_error( _("Unknown user or password incorrect.") );
9c737111 710 exit;
052e0c26 711 }
9c737111 712 } else {
052e0c26 713 exit;
9c737111 714 }
715 }
9c737111 716 return $imap_stream;
717}
f1e6f580 718
3411d4ec 719/* Simply logs out the IMAP session */
9c737111 720function sqimap_logout ($imap_stream) {
8d936b0c 721 /* Logout is not valid until the server returns 'BYE'
722 * If we don't have an imap_ stream we're already logged out */
26a2cc8b 723 if(isset($imap_stream) && $imap_stream)
8d936b0c 724 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
9c737111 725}
726
487daa81 727function sqimap_capability($imap_stream, $capability='') {
9c737111 728 global $sqimap_capabilities;
9c737111 729 if (!is_array($sqimap_capabilities)) {
1c72b151 730 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
731
9c737111 732 $c = explode(' ', $read[0]);
733 for ($i=2; $i < count($c); $i++) {
734 $cap_list = explode('=', $c[$i]);
3411d4ec 735 if (isset($cap_list[1])) {
9c737111 736 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
3411d4ec 737 } else {
9c737111 738 $sqimap_capabilities[$cap_list[0]] = TRUE;
3411d4ec 739 }
f1e6f580 740 }
9c737111 741 }
487daa81 742 if ($capability) {
098ea084 743 if (isset($sqimap_capabilities[$capability])) {
744 return $sqimap_capabilities[$capability];
745 } else {
746 return false;
747 }
f1e6f580 748 }
487daa81 749 return $sqimap_capabilities;
9c737111 750}
751
3411d4ec 752/* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test */
9c737111 753function sqimap_get_delimiter ($imap_stream = false) {
3411d4ec 754 global $sqimap_delimiter, $optional_delimiter;
85fc999e 755
9c737111 756 /* Use configured delimiter if set */
757 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
758 return $optional_delimiter;
759 }
85fc999e 760
9c737111 761 /* Do some caching here */
762 if (!$sqimap_delimiter) {
763 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
3411d4ec 764 /*
765 * According to something that I can't find, this is supposed to work on all systems
766 * OS: This won't work in Courier IMAP.
767 * OS: According to rfc2342 response from NAMESPACE command is:
768 * OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
769 * OS: We want to lookup all personal NAMESPACES...
770 */
1c72b151 771 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
f1e6f580 772 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
9c737111 773 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
f1e6f580 774 $pn = $data2[1];
9c737111 775 }
f1e6f580 776 $pna = explode(')(', $pn);
9c737111 777 while (list($k, $v) = each($pna)) {
862ff2d3 778 $lst = explode('"', $v);
779 if (isset($lst[3])) {
780 $pn[$lst[1]] = $lst[3];
781 } else {
74424a43 782 $pn[$lst[1]] = '';
862ff2d3 783 }
f1e6f580 784 }
785 }
786 $sqimap_delimiter = $pn[0];
787 } else {
788 fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
789 $read = sqimap_read_data($imap_stream, '.', true, $a, $b);
6a15a16b 790 $read = $read['.'][0]; //sqimap_read_data() now returns a tag array of response array
f1e6f580 791 $quote_position = strpos ($read[0], '"');
792 $sqimap_delimiter = substr ($read[0], $quote_position+1, 1);
793 }
794 }
795 return $sqimap_delimiter;
9c737111 796}
052e0c26 797
798
b2306cbe 799function sqimap_encode_mailbox_name($what)
800{
801 if (ereg("[\"\\\r\n]", $what))
802 return '{' . strlen($what) . "}\r\n" . $what; /* 4.3 literal form */
803 return '"' . $what . '"'; /* 4.3 quoted string form */
804}
805
806
3411d4ec 807/* Gets the number of messages in the current mailbox. */
9c737111 808function sqimap_get_num_messages ($imap_stream, $mailbox) {
b2306cbe 809 $read_ary = sqimap_run_command ($imap_stream, 'EXAMINE ' . sqimap_encode_mailbox_name($mailbox), false, $result, $message);
9c737111 810 for ($i = 0; $i < count($read_ary); $i++) {
85fc999e 811 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
812 return $regs[1];
813 }
9c737111 814 }
1f720b34 815 return false; //"BUG! Couldn't get number of messages in $mailbox!";
9c737111 816}
817
cfe1a81e 818
91688e5f 819function parseAddress($address, $max=0) {
820 $aTokens = array();
821 $aAddress = array();
822 $iCnt = strlen($address);
823 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
824 $aReplace = array(' (',' <',' ,',' ;',' :');
825 $address = str_replace($aSpecials,$aReplace,$address);
bc78cc6e 826 $i = $iAddrFound = $bGroup = 0;
91688e5f 827 while ($i < $iCnt) {
828 $cChar = $address{$i};
829 switch($cChar)
329a7ca5 830 {
831 case '<':
832 $iEnd = strpos($address,'>',$i+1);
833 if (!$iEnd) {
834 $sToken = substr($address,$i);
835 $i = $iCnt;
836 } else {
837 $sToken = substr($address,$i,$iEnd - $i +1);
838 $i = $iEnd;
839 }
840 $sToken = str_replace($aReplace, $aSpecials,$sToken);
841 $aTokens[] = $sToken;
842 break;
843 case '"':
844 $iEnd = strpos($address,$cChar,$i+1);
da1e42a1 845 if ($iEnd) {
846 // skip escaped quotes
847 $prev_char = $address{$iEnd-1};
848 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
849 $iEnd = strpos($address,$cChar,$iEnd+1);
850 if ($iEnd) {
851 $prev_char = $address{$iEnd-1};
852 } else {
853 $prev_char = false;
854 }
855 }
856 }
329a7ca5 857 if (!$iEnd) {
858 $sToken = substr($address,$i);
859 $i = $iCnt;
860 } else {
861 // also remove the surrounding quotes
862 $sToken = substr($address,$i+1,$iEnd - $i -1);
863 $i = $iEnd;
864 }
865 $sToken = str_replace($aReplace, $aSpecials,$sToken);
76c3ee28 866 if ($sToken) $aTokens[] = $sToken;
329a7ca5 867 break;
868 case '(':
869 $iEnd = strpos($address,')',$i);
870 if (!$iEnd) {
871 $sToken = substr($address,$i);
872 $i = $iCnt;
873 } else {
874 $sToken = substr($address,$i,$iEnd - $i + 1);
875 $i = $iEnd;
876 }
877 $sToken = str_replace($aReplace, $aSpecials,$sToken);
878 $aTokens[] = $sToken;
879 break;
880 case ',':
bc78cc6e 881 ++$iAddrFound;
329a7ca5 882 case ';':
bc78cc6e 883 if (!$bGroup) {
884 ++$iAddrFound;
885 } else {
886 $bGroup = false;
887 }
888 if ($max && $max == $iAddrFound) {
889 break 2;
890 } else {
891 $aTokens[] = $cChar;
892 break;
893 }
894 case ':':
895 $bGroup = true;
329a7ca5 896 case ' ':
897 $aTokens[] = $cChar;
898 break;
91688e5f 899 default:
329a7ca5 900 $iEnd = strpos($address,' ',$i+1);
901 if ($iEnd) {
902 $sToken = trim(substr($address,$i,$iEnd - $i));
903 $i = $iEnd-1;
91688e5f 904 } else {
329a7ca5 905 $sToken = trim(substr($address,$i));
906 $i = $iCnt;
91688e5f 907 }
329a7ca5 908 if ($sToken) $aTokens[] = $sToken;
91688e5f 909 }
329a7ca5 910 ++$i;
9c737111 911 }
91688e5f 912 $sPersonal = $sEmail = $sComment = $sGroup = '';
913 $aStack = $aComment = array();
914 foreach ($aTokens as $sToken) {
915 if ($max && $max == count($aAddress)) {
329a7ca5 916 return $aAddress;
092d4f2c 917 }
329a7ca5 918 $cChar = $sToken{0};
91688e5f 919 switch ($cChar)
329a7ca5 920 {
921 case '=':
922 case '"':
923 case ' ':
924 $aStack[] = $sToken;
925 break;
926 case '(':
927 $aComment[] = substr($sToken,1,-1);
928 break;
929 case ';':
930 if ($sGroup) {
931 $sEmail = trim(implode(' ',$aStack));
932 $aAddress[] = array($sGroup,$sEmail);
933 $aStack = $aComment = array();
934 $sGroup = '';
935 break;
936 }
937 case ',':
938 if (!$sEmail) {
939 while (count($aStack) && !$sEmail) {
940 $sEmail = trim(array_pop($aStack));
941 }
942 }
943 if (count($aStack)) {
944 $sPersonal = trim(implode('',$aStack));
945 } else {
946 $sPersonal = '';
bea3eb1e 947 }
329a7ca5 948 if (!$sPersonal && count($aComment)) {
949 $sComment = implode(' ',$aComment);
950 $sPersonal .= $sComment;
951 }
952 $aAddress[] = array($sEmail,$sPersonal);
953 $sPersonal = $sComment = $sEmail = '';
954 $aStack = $aComment = array();
955 break;
956 case ':':
957 $sGroup = implode(' ',$aStack); break;
958 $aStack = array();
959 break;
960 case '<':
961 $sEmail = trim(substr($sToken,1,-1));
962 break;
963 case '>':
964 /* skip */
965 break;
966 default: $aStack[] = $sToken; break;
33565ec4 967 }
7e3de682 968 }
91688e5f 969 /* now do the action again for the last address */
970 if (!$sEmail) {
971 while (count($aStack) && !$sEmail) {
329a7ca5 972 $sEmail = trim(array_pop($aStack));
91688e5f 973 }
7e3de682 974 }
91688e5f 975 if (count($aStack)) {
329a7ca5 976 $sPersonal = trim(implode('',$aStack));
098ea084 977 } else {
91688e5f 978 $sPersonal = '';
9c737111 979 }
329a7ca5 980 if (!$sPersonal && count($aComment)) {
91688e5f 981 $sComment = implode(' ',$aComment);
329a7ca5 982 $sPersonal .= $sComment;
91688e5f 983 }
984 $aAddress[] = array($sEmail,$sPersonal);
985 return $aAddress;
986}
987
988
85fc999e 989
9c737111 990/*
3411d4ec 991 * Returns the number of unseen messages in this folder
992 */
9c737111 993function sqimap_unseen_messages ($imap_stream, $mailbox) {
b2306cbe 994 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (UNSEEN)', false, $result, $message);
ea7ff111 995 $i = 0;
1f720b34 996 $regs = array(false, false);
ea7ff111 997 while (isset($read_ary[$i])) {
998 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
999 break;
1000 }
1001 $i++;
1002 }
9c737111 1003 return $regs[1];
1004}
1005
1f720b34 1006/*
4974c2a0 1007 * Returns the number of total/unseen/recent messages in this folder
1f720b34 1008 */
1009function sqimap_status_messages ($imap_stream, $mailbox) {
b2306cbe 1010 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (MESSAGES UNSEEN RECENT)', false, $result, $message);
1f720b34 1011 $i = 0;
b8ec18ef 1012 $messages = $unseen = $recent = false;
1f720b34 1013 $regs = array(false,false);
1014 while (isset($read_ary[$i])) {
1015 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1016 $unseen = $regs[1];
1017 }
1f720b34 1018 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1019 $messages = $regs[1];
1020 }
b8ec18ef 1021 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1022 $recent = $regs[1];
1023 }
1f720b34 1024 $i++;
1025 }
b8ec18ef 1026 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
1f720b34 1027}
1028
9c737111 1029
1030/*
3411d4ec 1031 * Saves a message to a given folder -- used for saving sent messages
1032 */
9c737111 1033function sqimap_append ($imap_stream, $sent_folder, $length) {
b2306cbe 1034 fputs ($imap_stream, sqimap_session_id() . ' APPEND ' . sqimap_encode_mailbox_name($sent_folder) . " (\\Seen) \{$length}\r\n");
85fc999e 1035 $tmp = fgets ($imap_stream, 1024);
9c737111 1036}
1037
8813fb15 1038function sqimap_append_done ($imap_stream, $folder='') {
1f720b34 1039 global $squirrelmail_language, $color;
9c737111 1040 fputs ($imap_stream, "\r\n");
1041 $tmp = fgets ($imap_stream, 1024);
69146537 1042 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
1043 set_up_language($squirrelmail_language);
1f720b34 1044 require_once(SM_PATH . 'functions/display_messages.php');
098ea084 1045 $reason = $regs[3];
1046 if ($regs[2] == 'NO') {
1047 $string = "<b><font color=$color[2]>\n" .
1048 _("ERROR : Could not append message to") ." $folder." .
1049 "</b><br>\n" .
1050 _("Server responded: ") .
1051 $reason . "<br>\n";
1052 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
1053 $string .= _("Solution: ") .
1054 _("Remove unneccessary messages from your folder and start with your Trash folder.")
1055 ."<br>\n";
1056 }
1057 $string .= "</font>\n";
1058 error_box($string,$color);
1059 } else {
8813fb15 1060 $string = "<b><font color=$color[2]>\n" .
098ea084 1061 _("ERROR : Bad or malformed request.") .
1062 "</b><br>\n" .
1063 _("Server responded: ") .
1064 $tmp . "</font><br>\n";
1065 error_box($string,$color);
8813fb15 1066 exit;
098ea084 1067 }
69146537 1068 }
9c737111 1069}
85fc999e 1070
bd9829d7 1071function sqimap_get_user_server ($imap_server, $username) {
bd9829d7 1072 if (substr($imap_server, 0, 4) != "map:") {
1073 return $imap_server;
1074 }
bd9829d7 1075 $function = substr($imap_server, 4);
1076 return $function($username);
1077}
1078
1079/* This is an example that gets imapservers from yellowpages (NIS).
1080 * you can simple put map:map_yp_alias in your $imap_server_address
1081 * in config.php use your own function instead map_yp_alias to map your
1082 * LDAP whatever way to find the users imapserver. */
1083
1084function map_yp_alias($username) {
1085 $yp = `ypmatch $username aliases`;
1086 return chop(substr($yp, strlen($username)+1));
1087}
1088
15e6162e 1089?>