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