Fix two typos "squirrelmai" -> "squirrelmail". Sheesh.
[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$
d6c32258 12 * @package squirrelmail
35586184 13 */
14
d6c32258 15/** Includes.. */
b68edc75 16require_once(SM_PATH . 'functions/page_header.php');
47a29326 17require_once(SM_PATH . 'functions/auth.php');
18
35586184 19
41600f7d 20global $sqimap_session_id;
21$sqimap_session_id = 1;
71257f8b 22
3411d4ec 23/* Sets an unique session id in order to avoid simultanous sessions crash. */
487daa81 24function sqimap_session_id($unique_id = false) {
41600f7d 25 global $data_dir, $username, $sqimap_session_id;
487daa81 26 if (!$unique_id) {
098ea084 27 return( sprintf("A%03d", $sqimap_session_id++) );
487daa81 28 } else {
098ea084 29 return( sprintf("A%03d", $sqimap_session_id++) . ' UID' );
487daa81 30 }
9c737111 31}
32
3411d4ec 33/*
34 * Both send a command and accept the result from the command.
35 * This is to allow proper session number handling.
36 */
487daa81 37function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id = false) {
c5809184 38 if ($imap_stream) {
098ea084 39 $sid = sqimap_session_id($unique_id);
40 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
bc78cc6e 41 $tag_uid_a = explode(' ',trim($sid));
42 $tag = $tag_uid_a[0];
0dc05a81 43 $read = sqimap_retrieve_imap_response ($imap_stream, $tag, $handle_errors, $response, $message, $query );
bc78cc6e 44 /* get the response and the message */
45 $message = $message[$tag];
46 $response = $response[$tag];
47 return $read[$tag];
c5809184 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);
098ea084 56 return false;
c5809184 57 }
1c72b151 58}
59
7c7b74b3 60function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
61 &$message, $unique_id = false,$filter=false,
62 $outputstream=false,$no_return=false) {
c5809184 63 if ($imap_stream) {
64 $sid = sqimap_session_id($unique_id);
bc78cc6e 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,
7c7b74b3 70 $message, $query,$filter,$outputstream,$no_return);
bc78cc6e 71 /* retrieve the response and the message */
72 $response = $response[$tag];
73 $message = $message[$tag];
5c300c60 74
bc78cc6e 75 if (!empty($read[$tag])) {
76 return $read[$tag][0];
77 } else {
78 return $read[$tag];
79 }
c5809184 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);
098ea084 88 return false;
bc78cc6e 89 }
90}
91function 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];
0b38d197 95 $query = $sid . ' '.$new_query."\r\n";
bc78cc6e 96 $aQuery[$tag] = $query;
42a07ac1 97}
98
52c8b585 99function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
bc78cc6e 100 &$aServerResponse, &$aServerMessage, $unique_id = false,
5c300c60 101 $filter=false,$outputstream=false,$no_return=false) {
bc78cc6e 102 $aResponse = false;
5c300c60 103
52c8b585 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.
0b38d197 110
c03cba15 111 After testing it on Exchange 2000 we discovered that a chunksize of 32
0b38d197 112 was quicker then when we raised it to 128.
52c8b585 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
5c300c60 120 if ($iLoops * $iChunkSize != $iQueryCount) ++$iLoops;
52c8b585 121
122 if (!function_exists('array_chunk')) { // arraychunk replacement
5c300c60 123 reset($aQueryList);
52c8b585 124 for($i=0;$i<$iLoops;++$i) {
5c300c60 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 }
52c8b585 132 } else {
133 $aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
bc78cc6e 134 }
52c8b585 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 }
52c8b585 142 foreach($aQuery as $tag => $query) {
5c300c60 143 if ($aResults[$tag] == false) {
0dc05a81 144 $aReturnedResponse = sqimap_retrieve_imap_response ($imap_stream, $tag,
bc78cc6e 145 $handle_errors, $response, $message, $query,
146 $filter,$outputstream,$no_return);
52c8b585 147 foreach ($aReturnedResponse as $returned_tag => $aResponse) {
5c300c60 148 if (!empty($aResponse)) {
52c8b585 149 $aResults[$returned_tag] = $aResponse[0];
5c300c60 150 } else {
151 $aResults[$returned_tag] = $aResponse;
152 }
52c8b585 153 $aServerResponse[$returned_tag] = $response[$returned_tag];
154 $aServerMessage[$returned_tag] = $message[$returned_tag];
155 }
bc78cc6e 156 }
157 }
158 }
b7277e4f 159 return $aResults;
bc78cc6e 160}
9c737111 161
b8c285ab 162/*
163 * custom fgets function. gets a line from IMAP
164 * no matter how big it may be
165 */
166
167function sqimap_fgets($imap_stream) {
168 $read = '';
169 $buffer = 4096;
170 $results = '';
c41daf03 171 $offset = 0;
172 while (strpos($results, "\r\n", $offset) === false) {
b8c285ab 173 if (!($read = fgets($imap_stream, $buffer))) {
329a7ca5 174 /* this happens in case of an error */
175 /* reset $results because it's useless */
176 $results = false;
b8c285ab 177 break;
178 }
c41daf03 179 if ( $results != '' ) {
180 $offset = strlen($results) - 1;
181 }
b8c285ab 182 $results .= $read;
183 }
184 return $results;
185}
186
7c7b74b3 187function sqimap_fread($imap_stream,$iSize,$filter=false,
188 $outputstream=false, $no_return=false) {
189 if (!$filter || !$outputstream) {
190 $iBufferSize = $iSize;
191 } else {
811318c5 192 // see php bug 24033. They changed fread behaviour %$^&$%
977a6085 193 $iBufferSize = 7800; // multiple of 78 in case of base64 decoding.
194 }
195 if ($iSize < $iBufferSize) {
196 $iBufferSize = $iSize;
7c7b74b3 197 }
5eba0be2 198 $iRetrieved = 0;
977a6085 199 $results = '';
200 $sRead = $sReadRem = '';
201 // NB: fread can also stop at end of a packet on sockets.
202 while ($iRetrieved < $iSize) {
7c7b74b3 203 $sRead = fread($imap_stream,$iBufferSize);
977a6085 204 $iLength = strlen($sRead);
205 $iRetrieved += $iLength ;
206 $iRemaining = $iSize - $iRetrieved;
207 if ($iRemaining < $iBufferSize) {
208 $iBufferSize = $iRemaining;
209 }
bc78cc6e 210 if (!$sRead) {
7c7b74b3 211 $results = false;
212 break;
213 }
977a6085 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) {
7c7b74b3 230 $filter($sRead);
231 }
977a6085 232 if ($outputstream && $sRead) {
7c7b74b3 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 = '';
977a6085 241 } else {
242 $results .= $sRead;
7c7b74b3 243 }
7c7b74b3 244 }
245 return $results;
246}
977a6085 247
0dc05a81 248/* obsolete function, inform plugins that use it */
249function 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>'.
5c300c60 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>'.
0dc05a81 263 htmlspecialchars($query) . '<br>' . "</font><br>\n";
264 error_box($string,$color);
265 echo '</body></html>';
266 exit;
267}
7c7b74b3 268
4974c2a0 269function 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";
bf15f116 276 $cmd = explode(' ',$query);
277 $cmd= strtolower($cmd[0]);
278
279 if ($query != '' && $cmd != 'login')
4974c2a0 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
bee165ef 289/*
3411d4ec 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
bee165ef 293 */
b8c285ab 294
0dc05a81 295function sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
7c7b74b3 296 &$response, &$message, $query = '',
297 $filter = false, $outputstream = false, $no_return = false) {
9c737111 298 global $color, $squirrelmail_language;
9c737111 299 $read = '';
bc78cc6e 300 if (!is_array($message)) $message = array();
301 if (!is_array($response)) $response = array();
b8c285ab 302 $resultlist = array();
303 $data = array();
304 $read = sqimap_fgets($imap_stream);
5c300c60 305 $i = $k = 0;
bea3eb1e 306 while ($read) {
307 $char = $read{0};
308 switch ($char)
309 {
15bc7f66 310 case '+':
311 default:
312 $read = sqimap_fgets($imap_stream);
313 break;
314
315 case $tag{0}:
316 {
bea3eb1e 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);
5c300c60 325 if ($found_tag) {
bea3eb1e 326 switch ($arg)
327 {
15bc7f66 328 case 'OK':
329 case 'BAD':
330 case 'NO':
331 case 'BYE':
332 case 'PREAUTH':
bc78cc6e 333 $response[$found_tag] = $arg;
334 $message[$found_tag] = trim(substr($read,$i+strlen($arg)));
335 if (!empty($data)) {
336 $resultlist[] = $data;
337 }
5c300c60 338 $aResponse[$found_tag] = $resultlist;
339 $data = $resultlist = array();
340 if ($found_tag == $tag) {
341 break 3; /* switch switch while */
342 }
343 break;
329a7ca5 344 default:
bea3eb1e 345 /* this shouldn't happen */
bc78cc6e 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;
5c300c60 352 $data = $resultlist = array();
353 if ($found_tag == $tag) {
354 break 3; /* switch switch while */
355 }
bc78cc6e 356 }
bea3eb1e 357 }
5c300c60 358 $read = sqimap_fgets($imap_stream);
359 if ($read === false) { /* error */
360 break 3; /* switch switch while */
361 }
362 break;
15bc7f66 363 } // end case $tag{0}
364
365 case '*':
366 {
bea3eb1e 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;
7c7b74b3 381 $sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
329a7ca5 382 if ($sLiteral === false) { /* error */
383 break 4; /* while while switch while */
384 }
bea3eb1e 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 */
329a7ca5 397 $read = sqimap_fgets($imap_stream);
398 if ($read === false) { /* error */
399 break 4; /* while while switch while */
400 }
c0b23345 401 $fetch_data[] = $read;
bea3eb1e 402 } else {
329a7ca5 403 $fetch_data[] = $read;
bea3eb1e 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);
329a7ca5 408 if ($read === false) { /* error */
409 break 4; /* while while switch while */
410 }
bea3eb1e 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} !== '*' &&
106d4087 417 substr($read,0,strlen($tag)) !== $tag);
329a7ca5 418 $resultlist[] = $fetch_data;
bea3eb1e 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;
329a7ca5 428 $sLiteral = fread($imap_stream,$iLit);
c0b23345 429 if ($sLiteral === false) { /* error */
329a7ca5 430 $read = false;
431 break 3; /* while switch while */
432 }
c0b23345 433 $data[] = $sLiteral;
b7277e4f 434 $data[] = sqimap_fgets($imap_stream);
bea3eb1e 435 } else {
329a7ca5 436 $data[] = $read;
bea3eb1e 437 }
438 $read = sqimap_fgets($imap_stream);
329a7ca5 439 if ($read === false) {
440 break 3; /* while switch while */
441 } else if ($read{0} == '*') {
106d4087 442 break;
443 }
bea3eb1e 444 $s = substr($read,-3);
445 } while ($s === "}\r\n");
446 break 1;
5c300c60 447 }
bea3eb1e 448 break;
15bc7f66 449 } // end case '*'
450 } // end switch
c0b23345 451 } // end while
452
453 /* error processing in case $read is false */
454 if ($read === false) {
455 unset($data);
4974c2a0 456 sqimap_error_box(_("ERROR : Connection dropped by imap-server."), $query);
c0b23345 457 exit;
b8c285ab 458 }
c0b23345 459
15bc7f66 460 /* Set $resultlist array */
b8c285ab 461 if (!empty($data)) {
bc78cc6e 462 //$resultlist[] = $data;
9c737111 463 }
b8c285ab 464 elseif (empty($resultlist)) {
465 $resultlist[] = array();
466 }
15bc7f66 467
468 /* Return result or handle errors */
bee165ef 469 if ($handle_errors == false) {
bc78cc6e 470 return $aResponse;
4974c2a0 471 return( $resultlist ); //?? Why this?
dd381002 472 }
4974c2a0 473 switch ($response[$tag]) {
dd381002 474 case 'OK':
bc78cc6e 475 return $aResponse;
329a7ca5 476 break;
dd381002 477 case 'NO':
478 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
6b6c2e06 479 if (strstr($message[$tag], 'command resulted in') === false) {
4974c2a0 480 sqimap_error_box(_("ERROR : Could not complete request."), $query, _("Reason Given: "), $message[$tag]);
329a7ca5 481 echo '</body></html>';
052e0c26 482 exit;
9c737111 483 }
329a7ca5 484 break;
dd381002 485 case 'BAD':
4974c2a0 486 sqimap_error_box(_("ERROR : Bad or malformed request."), $query, _("Server responded: "), $message[$tag]);
329a7ca5 487 echo '</body></html>';
dd381002 488 exit;
489 case 'BYE':
4974c2a0 490 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded: "), $message[$tag]);
329a7ca5 491 echo '</body></html>';
9c737111 492 exit;
dd381002 493 default:
4974c2a0 494 sqimap_error_box(_("ERROR : Unknown imap response."), $query, _("Server responded: "), $message[$tag]);
329a7ca5 495 /* the error is displayed but because we don't know the reponse we
496 return the result anyway */
bc78cc6e 497 return $aResponse;
329a7ca5 498 break;
9c737111 499 }
9c737111 500}
501
7c7b74b3 502function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
503 &$response, &$message, $query = '',
504 $filter=false,$outputstream=false,$no_return=false) {
bc78cc6e 505
506 $tag_uid_a = explode(' ',trim($tag_uid));
507 $tag = $tag_uid_a[0];
508
0dc05a81 509 $res = sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
7c7b74b3 510 $response, $message, $query,$filter,$outputstream,$no_return);
863936bb 511 /* sqimap_read_data should be called for one response
0dc05a81 512 but since it just calls sqimap_retrieve_imap_response which
863936bb 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
ba8f367a 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// }
863936bb 524 if (isset($result)) {
bc78cc6e 525 return $result[$tag];
56afb33f 526 }
56afb33f 527 else {
bc78cc6e 528 return $res;
56afb33f 529 }
9c737111 530}
531
b98732e8 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 */
537function 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
3411d4ec 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 */
9c737111 573function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
fe55c7c7 574 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls,
575 $imap_auth_mech, $sqimap_capabilities;
85fc999e 576
eb2f6102 577 if (!isset($onetimepad) || empty($onetimepad)) {
578 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
579 }
fe55c7c7 580 if (!isset($sqimap_capabilities)) {
581 sqgetglobalvar('sqimap_capabilities' , $capability , SQ_SESSION );
582 }
583
b98732e8 584 $host = $imap_server_address;
bd9829d7 585 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
fe55c7c7 586
b98732e8 587 $imap_stream = sqimap_create_stream($imap_server_address,$imap_port,$use_imap_tls);
fe55c7c7 588
2f1f7a12 589 /* Decrypt the password */
590 $password = OneTimePadDecrypt($password, $onetimepad);
591
b98732e8 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') {
098ea084 596 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
b98732e8 597 } elseif ($imap_auth_mech == 'cram-md5') {
098ea084 598 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
b98732e8 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] == '+') {
098ea084 605 // Got a challenge back
b98732e8 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..
098ea084 617 fputs($imap_stream,"\r\n");
618 $read=sqimap_fgets($imap_stream);
098ea084 619 }
b98732e8 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 }
fe0b18b3 630 } elseif ($imap_auth_mech == 'login') {
b98732e8 631 // Original IMAP login code
632 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
633 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
1e7fc1cb 634 } elseif ($imap_auth_mech == 'plain') {
fe55c7c7 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 **/
b98732e8 649 $tag=sqimap_session_id(false);
fe55c7c7 650 $sasl = (isset($capability['SASL']) && $capability['SASL']) ? true : false;
b98732e8 651 $auth = base64_encode("$username\0$username\0$password");
fe55c7c7 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);
b98732e8 657 $read = sqimap_fgets($imap_stream);
fe55c7c7 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 }
098ea084 666 }
b98732e8 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 }
fe55c7c7 674
b98732e8 675 /* If the connection was not successful, lets see why */
9c737111 676 if ($response != 'OK') {
677 if (!$hide) {
74424a43 678 if ($response != 'NO') {
3411d4ec 679 /* "BAD" and anything else gets reported here. */
098ea084 680 $message = htmlspecialchars($message);
9c737111 681 set_up_language($squirrelmail_language, true);
098ea084 682 require_once(SM_PATH . 'functions/display_messages.php');
9c737111 683 if ($response == 'BAD') {
bea3eb1e 684 $string = sprintf (_("Bad request: %s")."<br>\r\n", $message);
9c737111 685 } else {
bea3eb1e 686 $string = sprintf (_("Unknown error: %s") . "<br>\n", $message);
9c737111 687 }
1e7fc1cb 688 if (isset($read) && is_array($read)) {
bea3eb1e 689 $string .= '<br>' . _("Read data:") . "<br>\n";
9c737111 690 foreach ($read as $line) {
1f720b34 691 $string .= htmlspecialchars($line) . "<br>\n";
9c737111 692 }
693 }
098ea084 694 error_box($string,$color);
9c737111 695 exit;
165e24a7 696 } else {
3411d4ec 697 /*
698 * If the user does not log in with the correct
1c72b151 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.
3411d4ec 703 *
704 * $squirrelmail_language is set by a cookie when
1c72b151 705 * the user selects language and logs out
bee165ef 706 */
fe55c7c7 707
9c737111 708 set_up_language($squirrelmail_language, true);
bd9c880b 709 include_once(SM_PATH . 'functions/display_messages.php' );
098ea084 710 sqsession_destroy();
bd9c880b 711 logout_error( _("Unknown user or password incorrect.") );
9c737111 712 exit;
052e0c26 713 }
9c737111 714 } else {
052e0c26 715 exit;
9c737111 716 }
717 }
9c737111 718 return $imap_stream;
719}
f1e6f580 720
3411d4ec 721/* Simply logs out the IMAP session */
9c737111 722function sqimap_logout ($imap_stream) {
8d936b0c 723 /* Logout is not valid until the server returns 'BYE'
724 * If we don't have an imap_ stream we're already logged out */
26a2cc8b 725 if(isset($imap_stream) && $imap_stream)
8d936b0c 726 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
9c737111 727}
728
487daa81 729function sqimap_capability($imap_stream, $capability='') {
9c737111 730 global $sqimap_capabilities;
9c737111 731 if (!is_array($sqimap_capabilities)) {
1c72b151 732 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
733
9c737111 734 $c = explode(' ', $read[0]);
735 for ($i=2; $i < count($c); $i++) {
736 $cap_list = explode('=', $c[$i]);
3411d4ec 737 if (isset($cap_list[1])) {
9c737111 738 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
3411d4ec 739 } else {
9c737111 740 $sqimap_capabilities[$cap_list[0]] = TRUE;
3411d4ec 741 }
f1e6f580 742 }
9c737111 743 }
487daa81 744 if ($capability) {
098ea084 745 if (isset($sqimap_capabilities[$capability])) {
746 return $sqimap_capabilities[$capability];
747 } else {
748 return false;
749 }
f1e6f580 750 }
487daa81 751 return $sqimap_capabilities;
9c737111 752}
753
3411d4ec 754/* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test */
9c737111 755function sqimap_get_delimiter ($imap_stream = false) {
3411d4ec 756 global $sqimap_delimiter, $optional_delimiter;
85fc999e 757
9c737111 758 /* Use configured delimiter if set */
759 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
760 return $optional_delimiter;
761 }
85fc999e 762
9c737111 763 /* Do some caching here */
764 if (!$sqimap_delimiter) {
765 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
3411d4ec 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 */
1c72b151 773 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
f1e6f580 774 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
9c737111 775 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
f1e6f580 776 $pn = $data2[1];
9c737111 777 }
f1e6f580 778 $pna = explode(')(', $pn);
9c737111 779 while (list($k, $v) = each($pna)) {
862ff2d3 780 $lst = explode('"', $v);
781 if (isset($lst[3])) {
782 $pn[$lst[1]] = $lst[3];
783 } else {
74424a43 784 $pn[$lst[1]] = '';
862ff2d3 785 }
f1e6f580 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);
6a15a16b 792 $read = $read['.'][0]; //sqimap_read_data() now returns a tag array of response array
f1e6f580 793 $quote_position = strpos ($read[0], '"');
794 $sqimap_delimiter = substr ($read[0], $quote_position+1, 1);
795 }
796 }
797 return $sqimap_delimiter;
9c737111 798}
052e0c26 799
800
b2306cbe 801function 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
3411d4ec 809/* Gets the number of messages in the current mailbox. */
9c737111 810function sqimap_get_num_messages ($imap_stream, $mailbox) {
b2306cbe 811 $read_ary = sqimap_run_command ($imap_stream, 'EXAMINE ' . sqimap_encode_mailbox_name($mailbox), false, $result, $message);
9c737111 812 for ($i = 0; $i < count($read_ary); $i++) {
85fc999e 813 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
814 return $regs[1];
815 }
9c737111 816 }
1f720b34 817 return false; //"BUG! Couldn't get number of messages in $mailbox!";
9c737111 818}
819
cfe1a81e 820
91688e5f 821function 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);
bc78cc6e 828 $i = $iAddrFound = $bGroup = 0;
91688e5f 829 while ($i < $iCnt) {
830 $cChar = $address{$i};
831 switch($cChar)
329a7ca5 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);
da1e42a1 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 }
329a7ca5 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);
76c3ee28 868 if ($sToken) $aTokens[] = $sToken;
329a7ca5 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 ',':
bc78cc6e 883 ++$iAddrFound;
329a7ca5 884 case ';':
bc78cc6e 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;
329a7ca5 898 case ' ':
899 $aTokens[] = $cChar;
900 break;
91688e5f 901 default:
329a7ca5 902 $iEnd = strpos($address,' ',$i+1);
903 if ($iEnd) {
904 $sToken = trim(substr($address,$i,$iEnd - $i));
905 $i = $iEnd-1;
91688e5f 906 } else {
329a7ca5 907 $sToken = trim(substr($address,$i));
908 $i = $iCnt;
91688e5f 909 }
329a7ca5 910 if ($sToken) $aTokens[] = $sToken;
91688e5f 911 }
329a7ca5 912 ++$i;
9c737111 913 }
91688e5f 914 $sPersonal = $sEmail = $sComment = $sGroup = '';
915 $aStack = $aComment = array();
916 foreach ($aTokens as $sToken) {
917 if ($max && $max == count($aAddress)) {
329a7ca5 918 return $aAddress;
092d4f2c 919 }
329a7ca5 920 $cChar = $sToken{0};
91688e5f 921 switch ($cChar)
329a7ca5 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 = '';
bea3eb1e 949 }
329a7ca5 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;
33565ec4 969 }
7e3de682 970 }
91688e5f 971 /* now do the action again for the last address */
972 if (!$sEmail) {
973 while (count($aStack) && !$sEmail) {
329a7ca5 974 $sEmail = trim(array_pop($aStack));
91688e5f 975 }
7e3de682 976 }
91688e5f 977 if (count($aStack)) {
329a7ca5 978 $sPersonal = trim(implode('',$aStack));
098ea084 979 } else {
91688e5f 980 $sPersonal = '';
9c737111 981 }
329a7ca5 982 if (!$sPersonal && count($aComment)) {
91688e5f 983 $sComment = implode(' ',$aComment);
329a7ca5 984 $sPersonal .= $sComment;
91688e5f 985 }
986 $aAddress[] = array($sEmail,$sPersonal);
987 return $aAddress;
988}
989
990
85fc999e 991
9c737111 992/*
3411d4ec 993 * Returns the number of unseen messages in this folder
994 */
9c737111 995function sqimap_unseen_messages ($imap_stream, $mailbox) {
b2306cbe 996 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (UNSEEN)', false, $result, $message);
ea7ff111 997 $i = 0;
1f720b34 998 $regs = array(false, false);
ea7ff111 999 while (isset($read_ary[$i])) {
1000 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
1001 break;
1002 }
1003 $i++;
1004 }
9c737111 1005 return $regs[1];
1006}
1007
1f720b34 1008/*
4974c2a0 1009 * Returns the number of total/unseen/recent messages in this folder
1f720b34 1010 */
1011function sqimap_status_messages ($imap_stream, $mailbox) {
b2306cbe 1012 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) . ' (MESSAGES UNSEEN RECENT)', false, $result, $message);
1f720b34 1013 $i = 0;
b8ec18ef 1014 $messages = $unseen = $recent = false;
1f720b34 1015 $regs = array(false,false);
1016 while (isset($read_ary[$i])) {
1017 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1018 $unseen = $regs[1];
1019 }
1f720b34 1020 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1021 $messages = $regs[1];
1022 }
b8ec18ef 1023 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 1024 $recent = $regs[1];
1025 }
1f720b34 1026 $i++;
1027 }
b8ec18ef 1028 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
1f720b34 1029}
1030
9c737111 1031
1032/*
3411d4ec 1033 * Saves a message to a given folder -- used for saving sent messages
1034 */
9c737111 1035function sqimap_append ($imap_stream, $sent_folder, $length) {
b2306cbe 1036 fputs ($imap_stream, sqimap_session_id() . ' APPEND ' . sqimap_encode_mailbox_name($sent_folder) . " (\\Seen) \{$length}\r\n");
85fc999e 1037 $tmp = fgets ($imap_stream, 1024);
9c737111 1038}
1039
8813fb15 1040function sqimap_append_done ($imap_stream, $folder='') {
1f720b34 1041 global $squirrelmail_language, $color;
9c737111 1042 fputs ($imap_stream, "\r\n");
1043 $tmp = fgets ($imap_stream, 1024);
69146537 1044 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
1045 set_up_language($squirrelmail_language);
1f720b34 1046 require_once(SM_PATH . 'functions/display_messages.php');
098ea084 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 {
8813fb15 1062 $string = "<b><font color=$color[2]>\n" .
098ea084 1063 _("ERROR : Bad or malformed request.") .
1064 "</b><br>\n" .
1065 _("Server responded: ") .
1066 $tmp . "</font><br>\n";
1067 error_box($string,$color);
8813fb15 1068 exit;
098ea084 1069 }
69146537 1070 }
9c737111 1071}
85fc999e 1072
bd9829d7 1073function sqimap_get_user_server ($imap_server, $username) {
bd9829d7 1074 if (substr($imap_server, 0, 4) != "map:") {
1075 return $imap_server;
1076 }
bd9829d7 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
1086function map_yp_alias($username) {
1087 $yp = `ypmatch $username aliases`;
1088 return chop(substr($yp, strlen($username)+1));
1089}
1090
15e6162e 1091?>