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