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