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