moving functions to separate file, adding configuration files
[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];
1e0a2f0e 50 $response = $response[$tag];
bc78cc6e 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');
6fd95361 56 $string = "<b><font color=\"$color[2]\">\n" .
c5809184 57 _("ERROR : No available imapstream.") .
58 "</b></font>\n";
59 error_box($string,$color);
098ea084 60 return false;
c5809184 61 }
1c72b151 62}
63
1e0a2f0e 64function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
7c7b74b3 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);
1e0a2f0e 69 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
bc78cc6e 70 $tag_uid_a = explode(' ',trim($sid));
71 $tag = $tag_uid_a[0];
1e0a2f0e 72
bc78cc6e 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];
1e0a2f0e 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');
6fd95361 93 $string = "<b><font color=\"$color[2]\">\n" .
c5809184 94 _("ERROR : No available imapstream.") .
95 "</b></font>\n";
96 error_box($string,$color);
098ea084 97 return false;
1e0a2f0e 98 }
bc78cc6e 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
1e0a2f0e 109function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
bc78cc6e 110 &$aServerResponse, &$aServerMessage, $unique_id = false,
1e0a2f0e 111 $filter=false,$outputstream=false,$no_return=false) {
bc78cc6e 112 $aResponse = false;
5c300c60 113
1e0a2f0e 114 /*
52c8b585 115 Do not fire all calls at once to the imap-server but split the calls up
1e0a2f0e 116 in portions of $iChunkSize. If we do not do that I think we misbehave as
52c8b585 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.
1e0a2f0e 120
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;
1e0a2f0e 141 }
52c8b585 142 } else {
143 $aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
bc78cc6e 144 }
1e0a2f0e 145
52c8b585 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) {
1e0a2f0e 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
1e0a2f0e 172/**
48af4b64 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 = '';
1e0a2f0e 213 // NB: fread can also stop at end of a packet on sockets.
977a6085 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) {
1e0a2f0e 232 // in case the filter is base64 decoding we return a remainder
7c0ec1d8 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 = '';
1e0a2f0e 244 } else {
977a6085 245 $results .= $sRead;
7c7b74b3 246 }
7c7b74b3 247 }
1e0a2f0e 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 */
1e0a2f0e 256function sqimap_read_data_list($imap_stream, $tag, $handle_errors,
0dc05a81 257 &$response, &$message, $query = '') {
258 global $color, $squirrelmail_language;
259 set_up_language($squirrelmail_language);
260 require_once(SM_PATH . 'functions/display_messages.php');
6fd95361 261 $string = "<b><font color=\"$color[2]\">\n" .
0dc05a81 262 _("ERROR : Bad function call.") .
6fd95361 263 "</b><br />\n" .
0dc05a81 264 _("Reason:") . ' '.
6fd95361 265 'There is a plugin installed which make use of the <br />' .
266 'SquirrelMail internal function sqimap_read_data_list.<br />'.
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 />'.
270 htmlspecialchars($query) . '<br />' . "</font><br />\n";
0dc05a81 271 error_box($string,$color);
1e0a2f0e 272 echo '</body></html>';
273 exit;
0dc05a81 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');
6fd95361 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')
6fd95361 296 $string .= _("Query:") . ' ' . htmlspecialchars($query) . '<br />';
4974c2a0 297 if ($message_title != '')
298 $string .= $message_title;
299 if ($message != '')
300 $string .= htmlspecialchars($message);
6fd95361 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 */
1e0a2f0e 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;
1e0a2f0e 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 */
1e0a2f0e 411 unset($aLiteral);
bea3eb1e 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;
1e0a2f0e 465 }
bea3eb1e 466 break;
15bc7f66 467 } // end case '*'
468 } // end switch
c0b23345 469 } // end while
1e0a2f0e 470
c0b23345 471 /* error processing in case $read is false */
d7542838 472 if ($read === false) {
d3d9ef2d 473 // try to retrieve an untagged bye respons from the results
474 $sResponse = array_pop($data);
d7542838 475 if ($sResponse !== NULL && strpos($sResponse,'* BYE') !== false) {
476 if (!$handle_errors) {
477 $query = '';
478 }
479 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded:"),$sResponse);
480 echo '</body></html>';
481 exit;
482 } else if ($handle_errors) {
1e0a2f0e 483 unset($data);
ec73f243 484 sqimap_error_box(_("ERROR : Connection dropped by imap-server."), $query);
485 exit;
486 }
b8c285ab 487 }
1e0a2f0e 488
15bc7f66 489 /* Set $resultlist array */
b8c285ab 490 if (!empty($data)) {
bc78cc6e 491 //$resultlist[] = $data;
9c737111 492 }
b8c285ab 493 elseif (empty($resultlist)) {
1e0a2f0e 494 $resultlist[] = array();
b8c285ab 495 }
15bc7f66 496
497 /* Return result or handle errors */
bee165ef 498 if ($handle_errors == false) {
bc78cc6e 499 return $aResponse;
dd381002 500 }
4974c2a0 501 switch ($response[$tag]) {
dd381002 502 case 'OK':
bc78cc6e 503 return $aResponse;
329a7ca5 504 break;
1e0a2f0e 505 case 'NO':
dd381002 506 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
6b6c2e06 507 if (strstr($message[$tag], 'command resulted in') === false) {
4974c2a0 508 sqimap_error_box(_("ERROR : Could not complete request."), $query, _("Reason Given: "), $message[$tag]);
329a7ca5 509 echo '</body></html>';
052e0c26 510 exit;
9c737111 511 }
329a7ca5 512 break;
1e0a2f0e 513 case 'BAD':
4974c2a0 514 sqimap_error_box(_("ERROR : Bad or malformed request."), $query, _("Server responded: "), $message[$tag]);
1e0a2f0e 515 echo '</body></html>';
516 exit;
517 case 'BYE':
4974c2a0 518 sqimap_error_box(_("ERROR : Imap server closed the connection."), $query, _("Server responded: "), $message[$tag]);
1e0a2f0e 519 echo '</body></html>';
9c737111 520 exit;
1e0a2f0e 521 default:
4974c2a0 522 sqimap_error_box(_("ERROR : Unknown imap response."), $query, _("Server responded: "), $message[$tag]);
329a7ca5 523 /* the error is displayed but because we don't know the reponse we
524 return the result anyway */
1e0a2f0e 525 return $aResponse;
329a7ca5 526 break;
9c737111 527 }
9c737111 528}
529
1e0a2f0e 530function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
7c7b74b3 531 &$response, &$message, $query = '',
532 $filter=false,$outputstream=false,$no_return=false) {
bc78cc6e 533
534 $tag_uid_a = explode(' ',trim($tag_uid));
535 $tag = $tag_uid_a[0];
536
1e0a2f0e 537 $res = sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
538 $response, $message, $query,$filter,$outputstream,$no_return);
539 return $res;
9c737111 540}
541
48af4b64 542/**
b98732e8 543 * Connects to the IMAP server and returns a resource identifier for use with
544 * the other SquirrelMail IMAP functions. Does NOT login!
48af4b64 545 * @param string server hostname of IMAP server
546 * @param int port port number to connect to
547 * @param bool tls whether to use TLS when connecting.
548 * @return imap-stream resource identifier
b98732e8 549 */
550function sqimap_create_stream($server,$port,$tls=false) {
3b151851 551 global $username, $use_imap_tls, $squirrelmail_language;
b98732e8 552
57ffe0af 553 if ($tls == true) {
b98732e8 554 if ((check_php_version(4,3)) and (extension_loaded('openssl'))) {
555 /* Use TLS by prefixing "tls://" to the hostname */
57ffe0af 556 $server = 'tls://' . $server;
b98732e8 557 } else {
558 require_once(SM_PATH . 'functions/display_messages.php');
3b151851 559 logout_error( sprintf(_("Error connecting to IMAP server: %s."), $server).
560 '<br />'.
561 _("TLS is enabled, but this version of PHP does not support TLS sockets, or is missing the openssl extension.").
562 '<br /><br />'.
563 _("Please contact your system administrator and report this error.") );
b98732e8 564 }
565 }
566
82f403aa 567 $imap_stream = @fsockopen($server, $port, $error_number, $error_string, 15);
b98732e8 568
569 /* Do some error correction */
570 if (!$imap_stream) {
571 set_up_language($squirrelmail_language, true);
572 require_once(SM_PATH . 'functions/display_messages.php');
3b151851 573 logout_error( sprintf(_("Error connecting to IMAP server: %s."), $server).
574 "<br />\r\n$error_number : $error_string<br />\r\n" );
b98732e8 575 exit;
576 }
577 $server_info = fgets ($imap_stream, 1024);
578 return $imap_stream;
579}
580
48af4b64 581/**
3411d4ec 582 * Logs the user into the imap server. If $hide is set, no error messages
583 * will be displayed. This function returns the imap connection handle.
584 */
9c737111 585function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
fe55c7c7 586 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls,
587 $imap_auth_mech, $sqimap_capabilities;
85fc999e 588
eb2f6102 589 if (!isset($onetimepad) || empty($onetimepad)) {
590 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
591 }
fe55c7c7 592 if (!isset($sqimap_capabilities)) {
593 sqgetglobalvar('sqimap_capabilities' , $capability , SQ_SESSION );
594 }
595
b98732e8 596 $host = $imap_server_address;
bd9829d7 597 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
fe55c7c7 598
b98732e8 599 $imap_stream = sqimap_create_stream($imap_server_address,$imap_port,$use_imap_tls);
fe55c7c7 600
2f1f7a12 601 /* Decrypt the password */
602 $password = OneTimePadDecrypt($password, $onetimepad);
603
b98732e8 604 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
3b151851 605 // We're using some sort of authentication OTHER than plain or login
b98732e8 606 $tag=sqimap_session_id(false);
607 if ($imap_auth_mech == 'digest-md5') {
098ea084 608 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
b98732e8 609 } elseif ($imap_auth_mech == 'cram-md5') {
098ea084 610 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
b98732e8 611 }
612 fputs($imap_stream,$query);
613 $answer=sqimap_fgets($imap_stream);
614 // Trim the "+ " off the front
615 $response=explode(" ",$answer,3);
616 if ($response[0] == '+') {
098ea084 617 // Got a challenge back
b98732e8 618 $challenge=$response[1];
619 if ($imap_auth_mech == 'digest-md5') {
620 $reply = digest_md5_response($username,$password,$challenge,'imap',$host);
621 } elseif ($imap_auth_mech == 'cram-md5') {
622 $reply = cram_md5_response($username,$password,$challenge);
623 }
624 fputs($imap_stream,$reply);
625 $read=sqimap_fgets($imap_stream);
626 if ($imap_auth_mech == 'digest-md5') {
3b151851 627 // DIGEST-MD5 has an extra step..
b98732e8 628 if (substr($read,0,1) == '+') { // OK so far..
098ea084 629 fputs($imap_stream,"\r\n");
630 $read=sqimap_fgets($imap_stream);
098ea084 631 }
b98732e8 632 }
633 $results=explode(" ",$read,3);
634 $response=$results[1];
635 $message=$results[2];
636 } else {
3b151851 637 // Fake the response, so the error trap at the bottom will work
b98732e8 638 $response="BAD";
639 $message='IMAP server does not appear to support the authentication method selected.';
640 $message .= ' Please contact your system administrator.';
641 }
fe0b18b3 642 } elseif ($imap_auth_mech == 'login') {
b98732e8 643 // Original IMAP login code
644 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
645 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
1e7fc1cb 646 } elseif ($imap_auth_mech == 'plain') {
fe55c7c7 647 /***
648 * SASL PLAIN
649 *
650 * RFC 2595 Chapter 6
651 *
652 * The mechanism consists of a single message from the client to the
653 * server. The client sends the authorization identity (identity to
654 * login as), followed by a US-ASCII NUL character, followed by the
655 * authentication identity (identity whose password will be used),
656 * followed by a US-ASCII NUL character, followed by the clear-text
657 * password. The client may leave the authorization identity empty to
658 * indicate that it is the same as the authentication identity.
659 *
660 **/
b98732e8 661 $tag=sqimap_session_id(false);
2bd6b461 662 $sasl = (isset($capability['SASL-IR']) && $capability['SASL-IR']) ? true : false;
b98732e8 663 $auth = base64_encode("$username\0$username\0$password");
fe55c7c7 664 if ($sasl) {
665 // IMAP Extension for SASL Initial Client Response
2bd6b461 666 // <draft-siemborski-imap-sasl-initial-response-01b.txt>
fe55c7c7 667 $query = $tag . " AUTHENTICATE PLAIN $auth\r\n";
668 fputs($imap_stream, $query);
b98732e8 669 $read = sqimap_fgets($imap_stream);
fe55c7c7 670 } else {
671 $query = $tag . " AUTHENTICATE PLAIN\r\n";
672 fputs($imap_stream, $query);
673 $read=sqimap_fgets($imap_stream);
674 if (substr($read,0,1) == '+') { // OK so far..
675 fputs($imap_stream, "$auth\r\n");
676 $read = sqimap_fgets($imap_stream);
677 }
098ea084 678 }
b98732e8 679 $results=explode(" ",$read,3);
680 $response=$results[1];
681 $message=$results[2];
682 } else {
683 $response="BAD";
684 $message="Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
685 }
fe55c7c7 686
b98732e8 687 /* If the connection was not successful, lets see why */
9c737111 688 if ($response != 'OK') {
689 if (!$hide) {
74424a43 690 if ($response != 'NO') {
3411d4ec 691 /* "BAD" and anything else gets reported here. */
098ea084 692 $message = htmlspecialchars($message);
9c737111 693 set_up_language($squirrelmail_language, true);
098ea084 694 require_once(SM_PATH . 'functions/display_messages.php');
9c737111 695 if ($response == 'BAD') {
6fd95361 696 $string = sprintf (_("Bad request: %s")."<br />\r\n", $message);
9c737111 697 } else {
6fd95361 698 $string = sprintf (_("Unknown error: %s") . "<br />\n", $message);
9c737111 699 }
1e7fc1cb 700 if (isset($read) && is_array($read)) {
6fd95361 701 $string .= '<br />' . _("Read data:") . "<br />\n";
9c737111 702 foreach ($read as $line) {
6fd95361 703 $string .= htmlspecialchars($line) . "<br />\n";
9c737111 704 }
705 }
098ea084 706 error_box($string,$color);
9c737111 707 exit;
165e24a7 708 } else {
3411d4ec 709 /*
710 * If the user does not log in with the correct
1c72b151 711 * username and password it is not possible to get the
712 * correct locale from the user's preferences.
713 * Therefore, apply the same hack as on the login
714 * screen.
3411d4ec 715 *
716 * $squirrelmail_language is set by a cookie when
1c72b151 717 * the user selects language and logs out
bee165ef 718 */
fe55c7c7 719
9c737111 720 set_up_language($squirrelmail_language, true);
bd9c880b 721 include_once(SM_PATH . 'functions/display_messages.php' );
098ea084 722 sqsession_destroy();
d5c472f1 723 /* terminate the session nicely */
724 sqimap_logout($imap_stream);
bd9c880b 725 logout_error( _("Unknown user or password incorrect.") );
9c737111 726 exit;
052e0c26 727 }
9c737111 728 } else {
052e0c26 729 exit;
9c737111 730 }
731 }
9c737111 732 return $imap_stream;
733}
f1e6f580 734
48af4b64 735/**
736 * Simply logs out the IMAP session
737 * @param stream imap_stream the IMAP connection to log out.
738 * @return void
739 */
9c737111 740function sqimap_logout ($imap_stream) {
8d936b0c 741 /* Logout is not valid until the server returns 'BYE'
742 * If we don't have an imap_ stream we're already logged out */
26a2cc8b 743 if(isset($imap_stream) && $imap_stream)
8d936b0c 744 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
9c737111 745}
746
48af4b64 747/**
748 * Retreive the CAPABILITY string from the IMAP server.
749 * If capability is set, returns only that specific capability,
750 * else returns array of all capabilities.
751 */
487daa81 752function sqimap_capability($imap_stream, $capability='') {
9c737111 753 global $sqimap_capabilities;
9c737111 754 if (!is_array($sqimap_capabilities)) {
1c72b151 755 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
756
9c737111 757 $c = explode(' ', $read[0]);
758 for ($i=2; $i < count($c); $i++) {
759 $cap_list = explode('=', $c[$i]);
3411d4ec 760 if (isset($cap_list[1])) {
ecc92309 761 // FIX ME. capabilities can occure multiple times.
762 // THREAD=REFERENCES THREAD=ORDEREDSUBJECT
9c737111 763 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
3411d4ec 764 } else {
9c737111 765 $sqimap_capabilities[$cap_list[0]] = TRUE;
3411d4ec 766 }
f1e6f580 767 }
9c737111 768 }
487daa81 769 if ($capability) {
098ea084 770 if (isset($sqimap_capabilities[$capability])) {
771 return $sqimap_capabilities[$capability];
772 } else {
773 return false;
774 }
f1e6f580 775 }
487daa81 776 return $sqimap_capabilities;
9c737111 777}
778
48af4b64 779/**
780 * Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test
781 */
9c737111 782function sqimap_get_delimiter ($imap_stream = false) {
3411d4ec 783 global $sqimap_delimiter, $optional_delimiter;
85fc999e 784
9c737111 785 /* Use configured delimiter if set */
786 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
787 return $optional_delimiter;
788 }
85fc999e 789
9c737111 790 /* Do some caching here */
791 if (!$sqimap_delimiter) {
792 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
3411d4ec 793 /*
794 * According to something that I can't find, this is supposed to work on all systems
795 * OS: This won't work in Courier IMAP.
796 * OS: According to rfc2342 response from NAMESPACE command is:
797 * OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
798 * OS: We want to lookup all personal NAMESPACES...
799 */
1c72b151 800 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
f1e6f580 801 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
9c737111 802 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
f1e6f580 803 $pn = $data2[1];
9c737111 804 }
f1e6f580 805 $pna = explode(')(', $pn);
9c737111 806 while (list($k, $v) = each($pna)) {
862ff2d3 807 $lst = explode('"', $v);
808 if (isset($lst[3])) {
809 $pn[$lst[1]] = $lst[3];
810 } else {
74424a43 811 $pn[$lst[1]] = '';
862ff2d3 812 }
f1e6f580 813 }
814 }
815 $sqimap_delimiter = $pn[0];
816 } else {
817 fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
818 $read = sqimap_read_data($imap_stream, '.', true, $a, $b);
6a15a16b 819 $read = $read['.'][0]; //sqimap_read_data() now returns a tag array of response array
f1e6f580 820 $quote_position = strpos ($read[0], '"');
821 $sqimap_delimiter = substr ($read[0], $quote_position+1, 1);
822 }
823 }
824 return $sqimap_delimiter;
9c737111 825}
052e0c26 826
48af4b64 827/**
828 * This encodes a mailbox name for use in IMAP commands.
829 * @param string what the mailbox to encode
830 * @return string the encoded mailbox string
831 */
b2306cbe 832function sqimap_encode_mailbox_name($what)
833{
834 if (ereg("[\"\\\r\n]", $what))
835 return '{' . strlen($what) . "}\r\n" . $what; /* 4.3 literal form */
836 return '"' . $what . '"'; /* 4.3 quoted string form */
837}
838
48af4b64 839/**
840 * Gets the number of messages in the current mailbox.
1e0a2f0e 841 *
842 * OBSOLETE use sqimap_status_messages instead.
48af4b64 843 */
9c737111 844function sqimap_get_num_messages ($imap_stream, $mailbox) {
b2306cbe 845 $read_ary = sqimap_run_command ($imap_stream, 'EXAMINE ' . sqimap_encode_mailbox_name($mailbox), false, $result, $message);
9c737111 846 for ($i = 0; $i < count($read_ary); $i++) {
85fc999e 847 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
848 return $regs[1];
849 }
9c737111 850 }
1f720b34 851 return false; //"BUG! Couldn't get number of messages in $mailbox!";
9c737111 852}
1e0a2f0e 853include_once(SM_PATH . 'functions/rfc822address.php');
9c737111 854
1e0a2f0e 855/**
856 * OBSOLETE FUNCTION should be removed after mailbox_display,
857 * printMessage function is adapted
858 */
91688e5f 859function parseAddress($address, $max=0) {
1e0a2f0e 860 $aAddress = parseRFC822Address($address,array('limit'=> $max));
861 /*
862 * Because the expected format of the array element is changed we adapt it now.
863 * This also implies that this function is obsolete and should be removed after the
864 * rest of the source is adapted. See Rfc822Address.php for the new function.
865 */
866 array_walk($aAddress, '_adaptAddress');
867 return $aAddress;
868}
91688e5f 869
1e0a2f0e 870/**
871 * OBSOLETE FUNCTION should be removed after mailbox_display,
872 * printMessage function is adapted
873 */
874function _adaptAddress(&$aAddr,$k) {
875 $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL]) && $aAddr[SQM_ADDR_PERSONAL]) ?
876 $aAddr[SQM_ADDR_PERSONAL] : '';
877 $sEmail = ($aAddr[SQM_ADDR_HOST]) ?
3cd52f38 878 $aAddr[SQM_ADDR_MAILBOX] . '@'.$aAddr[SQM_ADDR_HOST] :
879 $aAddr[SQM_ADDR_MAILBOX];
1e0a2f0e 880 $aAddr = array($sEmail,$sPersonal);
881}
91688e5f 882
48af4b64 883/**
884 * Returns the number of unseen messages in this folder.
26e90c74 885 * obsoleted by sqimap_status_messages !
3411d4ec 886 */
9c737111 887function sqimap_unseen_messages ($imap_stream, $mailbox) {
26e90c74 888 $aStatus = sqimap_status_messages($imap_stream,$mailbox,array('UNSEEN'));
889 return $aStatus['UNSEEN'];
9c737111 890}
891
48af4b64 892/**
26e90c74 893 * Returns the status items of a mailbox.
894 * Default it returns MESSAGES,UNSEEN and RECENT
895 * Supported status items are MESSAGES, UNSEEN, RECENT, UIDNEXT and UIDVALIDITY
1f720b34 896 */
1e0a2f0e 897function sqimap_status_messages ($imap_stream, $mailbox,
26e90c74 898 $aStatusItems = array('MESSAGES','UNSEEN','RECENT')) {
899
3fedd15b 900 $aStatusItems = implode(' ',$aStatusItems);
26e90c74 901 $read_ary = sqimap_run_command ($imap_stream, 'STATUS ' . sqimap_encode_mailbox_name($mailbox) .
902 " ($aStatusItems)", false, $result, $message);
1f720b34 903 $i = 0;
26e90c74 904 $messages = $unseen = $recent = $uidnext = $uidvalidity = false;
1f720b34 905 $regs = array(false,false);
906 while (isset($read_ary[$i])) {
907 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 908 $unseen = $regs[1];
909 }
1f720b34 910 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 911 $messages = $regs[1];
912 }
b8ec18ef 913 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
098ea084 914 $recent = $regs[1];
1e0a2f0e 915 }
26e90c74 916 if (preg_match('/UIDNEXT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
917 $uidnext = $regs[1];
1e0a2f0e 918 }
26e90c74 919 if (preg_match('/UIDVALIDITY\s+([0-9]+)/i', $read_ary[$i], $regs)) {
920 $uidvalidity = $regs[1];
1e0a2f0e 921 }
1f720b34 922 $i++;
923 }
1e0a2f0e 924 return array('MESSAGES' => $messages,
925 'UNSEEN'=>$unseen,
26e90c74 926 'RECENT' => $recent,
927 'UIDNEXT' => $uidnext,
928 'UIDVALIDITY' => $uidvalidity);
1f720b34 929}
930
9c737111 931
48af4b64 932/**
933 * Saves a message to a given folder -- used for saving sent messages
3411d4ec 934 */
9c737111 935function sqimap_append ($imap_stream, $sent_folder, $length) {
b2306cbe 936 fputs ($imap_stream, sqimap_session_id() . ' APPEND ' . sqimap_encode_mailbox_name($sent_folder) . " (\\Seen) \{$length}\r\n");
85fc999e 937 $tmp = fgets ($imap_stream, 1024);
9c737111 938}
939
8813fb15 940function sqimap_append_done ($imap_stream, $folder='') {
1f720b34 941 global $squirrelmail_language, $color;
9c737111 942 fputs ($imap_stream, "\r\n");
943 $tmp = fgets ($imap_stream, 1024);
69146537 944 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
945 set_up_language($squirrelmail_language);
1f720b34 946 require_once(SM_PATH . 'functions/display_messages.php');
098ea084 947 $reason = $regs[3];
948 if ($regs[2] == 'NO') {
6fd95361 949 $string = "<b><font color=\"$color[2]\">\n" .
098ea084 950 _("ERROR : Could not append message to") ." $folder." .
6fd95361 951 "</b><br />\n" .
098ea084 952 _("Server responded: ") .
6fd95361 953 $reason . "<br />\n";
098ea084 954 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
1e0a2f0e 955 $string .= _("Solution: ") .
956 _("Remove unneccessary messages from your folder and start with your Trash folder.")
6fd95361 957 ."<br />\n";
098ea084 958 }
959 $string .= "</font>\n";
960 error_box($string,$color);
961 } else {
6fd95361 962 $string = "<b><font color=\"$color[2]\">\n" .
098ea084 963 _("ERROR : Bad or malformed request.") .
6fd95361 964 "</b><br />\n" .
098ea084 965 _("Server responded: ") .
6fd95361 966 $tmp . "</font><br />\n";
098ea084 967 error_box($string,$color);
8813fb15 968 exit;
098ea084 969 }
69146537 970 }
9c737111 971}
85fc999e 972
bd9829d7 973function sqimap_get_user_server ($imap_server, $username) {
bd9829d7 974 if (substr($imap_server, 0, 4) != "map:") {
975 return $imap_server;
976 }
bd9829d7 977 $function = substr($imap_server, 4);
978 return $function($username);
979}
980
48af4b64 981/**
982 * This is an example that gets imapservers from yellowpages (NIS).
1e0a2f0e 983 * you can simple put map:map_yp_alias in your $imap_server_address
bd9829d7 984 * in config.php use your own function instead map_yp_alias to map your
48af4b64 985 * LDAP whatever way to find the users imapserver.
986 */
bd9829d7 987function map_yp_alias($username) {
988 $yp = `ypmatch $username aliases`;
989 return chop(substr($yp, strlen($username)+1));
1e0a2f0e 990}
bd9829d7 991
3b151851 992?>