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