]> git.donarmstrong.com Git - roundcube.git/blob - program/lib/imap.inc
Merge commit 'debian/0.2.2-1' into debian
[roundcube.git] / program / lib / imap.inc
1 <?php
2 /////////////////////////////////////////////////////////
3 //      
4 //      Iloha IMAP Library (IIL)
5 //
6 //      (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org>
7 //
8 //      This file is part of IlohaMail. IlohaMail is free software released 
9 //      under the GPL license.  See enclosed file COPYING for details, or 
10 //      see http://www.fsf.org/copyleft/gpl.html
11 //
12 /////////////////////////////////////////////////////////
13
14 /********************************************************
15
16         FILE: include/imap.inc
17         PURPOSE:
18                 Provide alternative IMAP library that doesn't rely on the standard 
19                 C-Client based version.  This allows IlohaMail to function regardless
20                 of whether or not the PHP build it's running on has IMAP functionality
21                 built-in.
22         USEAGE:
23                 Function containing "_C_" in name require connection handler to be
24                 passed as one of the parameters.  To obtain connection handler, use
25                 iil_Connect()
26         VERSION:
27                 IlohaMail-0.9-20050415
28         CHANGES:
29                 File altered by Thomas Bruederli <roundcube@gmail.com>
30                 to fit enhanced equirements by the RoundCube Webmail:
31                 - Added list of server capabilites and check these before invoking commands
32                 - Added junk flag to iilBasicHeader
33                 - Enhanced error reporting on fsockopen()
34                 - Additional parameter for SORT command
35                 - Removed Call-time pass-by-reference because deprecated
36                 - Parse charset from content-type in iil_C_FetchHeaders()
37                 - Enhanced heaer sorting
38                 - Pass message as reference in iil_C_Append (to save memory)
39                 - Added BCC and REFERENCE to the list of headers to fetch in iil_C_FetchHeaders()
40                 - Leave messageID unchanged in iil_C_FetchHeaders()
41                 - Avoid stripslahes in iil_Connect()
42                 - Escape quotes and backslashes in iil_C_Login()
43                 - Added patch to iil_SortHeaders() by Richard Green
44                 - Removed <br> from error messages (better for logging)
45                 - Added patch to iil_C_Sort() enabling UID SORT commands
46                 - Added function iil_C_ID2UID()
47                 - Casting date parts in iil_StrToTime() to avoid mktime() warnings
48                 - Also acceppt LIST responses in iil_C_ListSubscribed()
49                 - Sanity check of $message_set in iil_C_FetchHeaders(), iil_C_FetchHeaderIndex(), iil_C_FetchThreadHeaders()
50                 - Implemented UID FETCH in iil_C_FetchHeaders()
51                 - Abort do-loop on socket errors (fgets returns false)
52                 - $ICL_SSL is not boolean anymore but contains the connection schema (ssl or tls)
53                 - Removed some debuggers (echo ...)
54                 File altered by Aleksander Machniak <alec@alec.pl>
55                 - trim(chop()) replaced by trim()
56                 - added iil_Escape()/iil_UnEscape() with support for " and \ in folder names
57                 - support \ character in username in iil_C_Login()
58                 - fixed iil_MultLine(): use iil_ReadBytes() instead of iil_ReadLine()
59                 - fixed iil_C_FetchStructureString() to handle many literal strings in response
60                 - removed hardcoded data size in iil_ReadLine() 
61                 - added iil_PutLine() wrapper for fputs()
62                 - code cleanup and identation fixes
63                 - removed flush() calls in iil_C_HandlePartBody() to prevent from memory leak (#1485187)
64                 - don't return "??" from iil_C_GetQuota()
65                 - RFC3501 [7.1] don't call CAPABILITY if was returned in server 
66                   optional resposne in iil_Connect(), added iil_C_GetCapability()
67                 - remove 'undisclosed-recipients' string from 'To' header
68                 - iil_C_HandlePartBody(): added 6th argument and fixed endless loop
69                 - added iil_PutLineC() 
70                 - fixed iil_C_Sort() to support very long and/or divided responses
71                 - added BYE/BAD response simple support for endless loop prevention
72                 - added 3rd argument in iil_StartsWith* functions
73                 - fix iil_C_FetchPartHeader() in some cases by use of iil_C_HandlePartBody()
74                 - allow iil_C_HandlePartBody() to fetch whole message
75                 - optimize iil_C_FetchHeaders() to use only one FETCH command
76                 - added 4th argument to iil_Connect()
77                 - allow setting rootdir and delimiter before connect
78                 - support multiquota result
79                 - include BODYSTRUCTURE in iil_C_FetchHeaders()
80                 - added iil_C_FetchMIMEHeaders() function
81                 - added \* flag support 
82
83 ********************************************************/
84
85 /**
86  * @todo Possibly clean up more CS.
87  * @todo Try to replace most double-quotes with single-quotes.
88  * @todo Split this file into smaller files.
89  * @todo Refactor code.
90  * @todo Replace echo-debugging (make it adhere to config setting and log)
91  */
92
93 // changed path to work within roundcube webmail
94 include_once 'lib/icl_commons.inc';
95
96
97 if (!isset($IMAP_USE_HEADER_DATE) || !$IMAP_USE_HEADER_DATE) {
98     $IMAP_USE_INTERNAL_DATE = true;
99 }
100
101 /**
102  * @todo Maybe use date() to generate this.
103  */
104 $GLOBALS['IMAP_MONTHS'] = array("Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4,
105     "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10,
106     "Nov" => 11, "Dec" => 12);
107
108 $GLOBALS['IMAP_SERVER_TZ'] = date('Z');
109
110 $GLOBALS['IMAP_FLAGS'] = array(
111     'SEEN'     => '\\Seen',
112     'DELETED'  => '\\Deleted',
113     'RECENT'   => '\\Recent',
114     'ANSWERED' => '\\Answered',
115     'DRAFT'    => '\\Draft',
116     'FLAGGED'  => '\\Flagged',
117     'FORWARDED' => '$Forwarded',
118     'MDNSENT'  => '$MDNSent',
119     '*'        => '\\*',
120 );
121
122 $iil_error;
123 $iil_errornum;
124 $iil_selected;
125
126 /**
127  * @todo Change class vars to public/private
128  */
129 class iilConnection
130 {
131         var $fp;
132         var $error;
133         var $errorNum;
134         var $selected;
135         var $message;
136         var $host;
137         var $cache;
138         var $uid_cache;
139         var $do_cache;
140         var $exists;
141         var $recent;
142         var $rootdir;
143         var $delimiter;
144         var $capability = array();
145         var $permanentflags = array();
146         var $capability_readed = false;
147 }
148
149 /**
150  * @todo Change class vars to public/private
151  */
152 class iilBasicHeader
153 {
154         var $id;
155         var $uid;
156         var $subject;
157         var $from;
158         var $to;
159         var $cc;
160         var $replyto;
161         var $in_reply_to;
162         var $date;
163         var $messageID;
164         var $size;
165         var $encoding;
166         var $charset;
167         var $ctype;
168         var $flags;
169         var $timestamp;
170         var $f;
171         var $body_structure;
172         var $internaldate;
173         var $references;
174         var $priority;
175         var $mdn_to;
176         var $mdn_sent = false;
177         var $is_draft = false;
178         var $seen = false;
179         var $deleted = false;
180         var $recent = false;
181         var $answered = false;
182         var $forwarded = false;
183         var $junk = false;
184         var $flagged = false;
185 }
186
187 /**
188  * @todo Change class vars to public/private
189  */
190 class iilThreadHeader
191 {
192         var $id;
193         var $sbj;
194         var $irt;
195         var $mid;
196 }
197
198 function iil_xor($string, $string2) {
199         $result = '';
200         $size = strlen($string);
201         for ($i=0; $i<$size; $i++) {
202                 $result .= chr(ord($string[$i]) ^ ord($string2[$i]));
203         }
204         return $result;
205 }
206
207 function iil_PutLine($fp, $string, $endln=true) {
208 //      console('C: '. rtrim($string));
209         return fputs($fp, $string . ($endln ? "\r\n" : ''));
210 }
211
212 // iil_PutLine replacement with Command Continuation Requests (RFC3501 7.5) support
213 function iil_PutLineC($fp, $string, $endln=true) {
214         if ($endln)
215                 $string .= "\r\n";
216
217         $res = 0;
218         if ($parts = preg_split('/(\{[0-9]+\}\r\n)/m', $string, -1, PREG_SPLIT_DELIM_CAPTURE)) {
219                 for($i=0, $cnt=count($parts); $i<$cnt; $i++) {
220                         if(preg_match('/^\{[0-9]+\}\r\n$/', $parts[$i+1])) {
221                                 $res += iil_PutLine($fp, $parts[$i].$parts[$i+1], false);
222                                 $line = iil_ReadLine($fp, 1000);
223                                 // handle error in command
224                                 if ($line[0] != '+')
225                                         return false;
226                                 $i++;
227                         }
228                         else
229                                 $res += iil_PutLine($fp, $parts[$i], false);
230                 }
231         }
232         return $res;
233 }
234
235 function iil_ReadLine($fp, $size) {
236         $line = '';
237
238         if (!$fp) {
239                 return $line;
240         }
241     
242         if (!$size) {
243                 $size = 1024;
244         }
245     
246         do {
247                 $buffer = fgets($fp, $size);
248                 if ($buffer === false) {
249                         break;
250                 }
251 //              console('S: '. chop($buffer));
252                 $line .= $buffer;
253         } while ($buffer[strlen($buffer)-1] != "\n");
254         
255         return $line;
256 }
257
258 function iil_MultLine($fp, $line) {
259         $line = chop($line);
260         if (ereg('\{[0-9]+\}$', $line)) {
261                 $out = '';
262         
263                 preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
264                 $bytes = $a[2][0];
265                 while (strlen($out) < $bytes) {
266                         $line = iil_ReadBytes($fp, $bytes); 
267                         $out .= $line;
268                 }
269                 $line = $a[1][0] . "\"$out\"";
270 //              console('[...] '. $out);
271         }
272         return $line;
273 }
274
275 function iil_ReadBytes($fp, $bytes) {
276         $data = '';
277         $len  = 0;
278         do {
279                 $data .= fread($fp, $bytes-$len);
280                 if ($len == strlen($data)) {
281                         break; //nothing was read -> exit to avoid apache lockups
282                 }
283                 $len = strlen($data);
284         } while ($len < $bytes);
285         
286         return $data;
287 }
288
289 function iil_ReadReply($fp) {
290         do {
291                 $line = trim(iil_ReadLine($fp, 1024));
292         } while ($line[0] == '*');
293         
294         return $line;
295 }
296
297 function iil_ParseResult($string) {
298         $a = explode(' ', $string);
299         if (count($a) > 2) {
300                 if (strcasecmp($a[1], 'OK') == 0) {
301                         return 0;
302                 } else if (strcasecmp($a[1], 'NO') == 0) {
303                         return -1;
304                 } else if (strcasecmp($a[1], 'BAD') == 0) {
305                         return -2;
306                 } else if (strcasecmp($a[1], 'BYE') == 0) {
307                         return -3;
308                 }
309         }
310         return -4;
311 }
312
313 // check if $string starts with $match (or * BYE/BAD)
314 function iil_StartsWith($string, $match, $error=false) {
315         $len = strlen($match);
316         if ($len == 0) {
317                 return false;
318         }
319         if (strncmp($string, $match, $len) == 0) {
320                 return true;
321         }
322         if ($error && preg_match('/^\* (BYE|BAD) /', $string)) {
323                 return true;
324         }
325         return false;
326 }
327
328 function iil_StartsWithI($string, $match, $bye=false) {
329         $len = strlen($match);
330         if ($len == 0) {
331                 return false;
332         }
333         if (strncasecmp($string, $match, $len) == 0) {
334                 return true;
335         }
336         if ($bye && strncmp($string, '* BYE ', 6) == 0) {
337                 return true;
338
339         }
340         return false;
341 }
342
343 function iil_Escape($string)
344 {
345         return strtr($string, array('"'=>'\\"', '\\' => '\\\\')); 
346 }
347
348 function iil_UnEscape($string)
349 {
350         return strtr($string, array('\\"'=>'"', '\\\\' => '\\')); 
351 }
352
353 function iil_C_GetCapability(&$conn, $name)
354 {
355         if (in_array($name, $conn->capability)) {
356                 return true;
357         }
358         else if ($conn->capability_readed) {
359                 return false;
360         }
361
362         // get capabilities (only once) because initial 
363         // optional CAPABILITY response may differ
364         $conn->capability = array();
365
366         iil_PutLine($conn->fp, "cp01 CAPABILITY");
367         do {
368                 $line = trim(iil_ReadLine($conn->fp, 1024));
369                 $a = explode(' ', $line);
370                 if ($line[0] == '*') {
371                         while (list($k, $w) = each($a)) {
372                                 if ($w != '*' && $w != 'CAPABILITY')
373                                         $conn->capability[] = strtoupper($w);
374                         }
375                 }
376         } while ($a[0] != 'cp01');
377         
378         $conn->capability_readed = true;
379
380         if (in_array($name, $conn->capability)) {
381                 return true;
382         }
383
384         return false;
385 }
386
387 function iil_C_ClearCapability(&$conn)
388 {
389         $conn->capability = array();
390         $conn->capability_readed = false;
391 }
392
393 function iil_C_Authenticate(&$conn, $user, $pass, $encChallenge) {
394     
395     $ipad = '';
396     $opad = '';
397     
398     // initialize ipad, opad
399     for ($i=0;$i<64;$i++) {
400         $ipad .= chr(0x36);
401         $opad .= chr(0x5C);
402     }
403
404     // pad $pass so it's 64 bytes
405     $padLen = 64 - strlen($pass);
406     for ($i=0;$i<$padLen;$i++) {
407         $pass .= chr(0);
408     }
409     
410     // generate hash
411     $hash  = md5(iil_xor($pass,$opad) . pack("H*", md5(iil_xor($pass, $ipad) . base64_decode($encChallenge))));
412     
413     // generate reply
414     $reply = base64_encode($user . ' ' . $hash);
415     
416     // send result, get reply
417     iil_PutLine($conn->fp, $reply);
418     $line = iil_ReadLine($conn->fp, 1024);
419     
420     // process result
421     $result = iil_ParseResult($line);
422     if ($result == 0) {
423         $conn->error    .= '';
424         $conn->errorNum  = 0;
425         return $conn->fp;
426     }
427
428     if ($result == -3) fclose($conn->fp); // BYE response
429
430     $conn->error    .= 'Authentication for ' . $user . ' failed (AUTH): "';
431     $conn->error    .= htmlspecialchars($line) . '"';
432     $conn->errorNum  = $result;
433
434     return $result;
435 }
436
437 function iil_C_Login(&$conn, $user, $password) {
438
439     iil_PutLine($conn->fp, 'a001 LOGIN "'.iil_Escape($user).'" "'.iil_Escape($password).'"');
440
441     do {
442         $line = iil_ReadReply($conn->fp);
443         if ($line === false) {
444             break;
445         }
446     } while (!iil_StartsWith($line, 'a001 ', true));
447     
448     // process result
449     $result = iil_ParseResult($line);
450
451     if ($result == 0) {
452         $conn->error    .= '';
453         $conn->errorNum  = 0;
454         return $conn->fp;
455     }
456
457     fclose($conn->fp);
458     
459     $conn->error    .= 'Authentication for ' . $user . ' failed (LOGIN): "';
460     $conn->error    .= htmlspecialchars($line)."\"";
461     $conn->errorNum  = $result;
462
463     return $result;
464 }
465
466 function iil_ParseNamespace2($str, &$i, $len=0, $l) {
467         if (!$l) {
468             $str = str_replace('NIL', '()', $str);
469         }
470         if (!$len) {
471             $len = strlen($str);
472         }
473         $data      = array();
474         $in_quotes = false;
475         $elem      = 0;
476         for ($i;$i<$len;$i++) {
477                 $c = (string)$str[$i];
478                 if ($c == '(' && !$in_quotes) {
479                         $i++;
480                         $data[$elem] = iil_ParseNamespace2($str, $i, $len, $l++);
481                         $elem++;
482                 } else if ($c == ')' && !$in_quotes) {
483                         return $data;
484                 } else if ($c == '\\') {
485                         $i++;
486                         if ($in_quotes) {
487                                 $data[$elem] .= $c.$str[$i];
488                         }
489                 } else if ($c == '"') {
490                         $in_quotes = !$in_quotes;
491                         if (!$in_quotes) {
492                                 $elem++;
493                         }
494                 } else if ($in_quotes) {
495                         $data[$elem].=$c;
496                 }
497         }
498         return $data;
499 }
500
501 function iil_C_NameSpace(&$conn) {
502         global $my_prefs;
503
504         if (isset($my_prefs['rootdir']) && is_string($my_prefs['rootdir'])) {
505                 $conn->rootdir = $my_prefs['rootdir'];
506                 return true;
507         }
508         
509         if (!iil_C_GetCapability($conn, 'NAMESPACE')) {
510             return false;
511         }
512     
513         iil_PutLine($conn->fp, "ns1 NAMESPACE");
514         do {
515                 $line = iil_ReadLine($conn->fp, 1024);
516                 if (iil_StartsWith($line, '* NAMESPACE')) {
517                         $i    = 0;
518                         $line = iil_UnEscape($line);
519                         $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
520                 }
521         } while (!iil_StartsWith($line, 'ns1', true));
522         
523         if (!is_array($data)) {
524             return false;
525         }
526     
527         $user_space_data = $data[0];
528         if (!is_array($user_space_data)) {
529             return false;
530         }
531     
532         $first_userspace = $user_space_data[0];
533         if (count($first_userspace)!=2) {
534             return false;
535         }
536     
537         $conn->rootdir       = $first_userspace[0];
538         $conn->delimiter     = $first_userspace[1];
539         $my_prefs['rootdir'] = substr($conn->rootdir, 0, -1);
540         $my_prefs['delimiter'] = $conn->delimiter;
541         
542         return true;
543 }
544
545 function iil_Connect($host, $user, $password, $options=null) {  
546         global $iil_error, $iil_errornum;
547         global $ICL_SSL, $ICL_PORT;
548         global $IMAP_NO_CACHE;
549         global $my_prefs, $IMAP_USE_INTERNAL_DATE;
550         
551         $iil_error = '';
552         $iil_errornum = 0;
553
554         // set some imap options
555         if (is_array($options)) {
556                 foreach($options as $optkey => $optval) {
557                         if ($optkey == 'imap') {
558                                 $auth_method = $optval;
559                         } else if ($optkey == 'rootdir') {
560                                 $my_prefs['rootdir'] = $optval;
561                         } else if ($optkey == 'delimiter') {
562                                 $my_prefs['delimiter'] = $optval;
563                         }
564                 }
565         }
566
567         if (empty($auth_method))
568                 $auth_method = 'check';
569                 
570         $message = "INITIAL: $auth_method\n";
571                 
572         $result = false;
573         
574         // initialize connection
575         $conn              = new iilConnection;
576         $conn->error       = '';
577         $conn->errorNum    = 0;
578         $conn->selected    = '';
579         $conn->user        = $user;
580         $conn->host        = $host;
581         $conn->cache       = array();
582         $conn->do_cache    = (function_exists("cache_write")&&!$IMAP_NO_CACHE);
583         $conn->cache_dirty = array();
584         
585         if ($my_prefs['sort_field'] == 'INTERNALDATE') {
586                 $IMAP_USE_INTERNAL_DATE = true;
587         } else if ($my_prefs['sort_field'] == 'DATE') {
588                 $IMAP_USE_INTERNAL_DATE = false;
589         }
590         //echo '<!-- conn sort_field: '.$my_prefs['sort_field'].' //-->';
591         
592         //check input
593         if (empty($host)) {
594                 $iil_error = "Empty host";
595                 $iil_errornum = -1;
596                 return false;
597         }
598         if (empty($user)) {
599                 $iil_error = "Empty user";
600                 $iil_errornum = -1;
601                 return false;
602         }
603         if (empty($password)) {
604                 $iil_error = "Empty password";
605                 $iil_errornum = -1;
606                 return false;
607         }
608
609         if (!$ICL_PORT) {
610                 $ICL_PORT = 143;
611         }
612         //check for SSL
613         if ($ICL_SSL && $ICL_SSL != 'tls') {
614                 $host = $ICL_SSL . '://' . $host;
615         }
616
617         $conn->fp = fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
618         if (!$conn->fp) {
619                 $iil_error = "Could not connect to $host at port $ICL_PORT: $errstr";
620                 $iil_errornum = -2;
621                 return false;
622         }
623
624         $iil_error .= "Socket connection established\r\n";
625         $line       = iil_ReadLine($conn->fp, 4096);
626
627         // RFC3501 [7.1] optional CAPABILITY response
628         if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) {
629                 $conn->capability = explode(' ', strtoupper($matches[1]));
630         }
631
632         $conn->message .= $line;
633
634         // TLS connection
635         if ($ICL_SSL == 'tls' && iil_C_GetCapability($conn, 'STARTTLS')) {
636                 if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
637                         iil_PutLine($conn->fp, 'stls000 STARTTLS');
638
639                         $line = iil_ReadLine($conn->fp, 4096);
640                         if (!iil_StartsWith($line, 'stls000 OK')) {
641                                 $iil_error = "Server responded to STARTTLS with: $line";
642                                 $iil_errornum = -2;
643                                 return false;
644                         }
645
646                         if (!stream_socket_enable_crypto($conn->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
647                                 $iil_error = "Unable to negotiate TLS";
648                                 $iil_errornum = -2;
649                                 return false;
650                         }
651                         
652                         // Now we're authenticated, capabilities need to be reread
653                         iil_C_ClearCapability($conn);
654                 }
655         }
656
657         if (strcasecmp($auth_method, "check") == 0) {
658                 //check for supported auth methods
659                 if (iil_C_GetCapability($conn, 'AUTH=CRAM-MD5') || iil_C_GetCapability($conn, 'AUTH=CRAM_MD5')) {
660                         $auth_method = 'auth';
661                 }
662                 else {
663                         //default to plain text auth
664                         $auth_method = 'plain';
665                 }
666         }
667
668         if (strcasecmp($auth_method, 'auth') == 0) {
669                 $conn->message .= "Trying CRAM-MD5\n";
670
671                 //do CRAM-MD5 authentication
672                 iil_PutLine($conn->fp, "a000 AUTHENTICATE CRAM-MD5");
673                 $line = trim(iil_ReadLine($conn->fp, 1024));
674
675                 $conn->message .= "$line\n";
676
677                 if ($line[0] == '+') {
678                         $conn->message .= 'Got challenge: ' . htmlspecialchars($line) . "\n";
679
680                         //got a challenge string, try CRAM-5
681                         $result = iil_C_Authenticate($conn, $user, $password, substr($line,2));
682                         
683                         // stop if server sent BYE response
684                         if($result == -3) {
685                                 $iil_error = $conn->error;
686                                 $iil_errornum = $conn->errorNum;
687                                 return false;
688                         }
689                         $conn->message .= "Tried CRAM-MD5: $result \n";
690                 } else {
691                         $conn->message .='No challenge ('.htmlspecialchars($line)."), try plain\n";
692                         $auth = 'plain';
693                 }
694         }
695                 
696         if ((!$result)||(strcasecmp($auth, "plain") == 0)) {
697                 //do plain text auth
698                 $result = iil_C_Login($conn, $user, $password);
699                 $conn->message .= "Tried PLAIN: $result \n";
700         }
701                 
702         $conn->message .= $auth;
703                         
704         if (!is_int($result)) {
705                 iil_C_Namespace($conn);
706                 return $conn;
707         } else {
708                 $iil_error = $conn->error;
709                 $iil_errornum = $conn->errorNum;
710                 return false;
711         }
712 }
713
714 function iil_Close(&$conn) {
715         iil_C_WriteCache($conn);
716         if (iil_PutLine($conn->fp, "I LOGOUT")) {
717                 fgets($conn->fp, 1024);
718                 fclose($conn->fp);
719                 $conn->fp = false;
720         }
721 }
722
723 function iil_ClearCache($user, $host) {
724 }
725
726 function iil_C_WriteCache(&$conn) {
727         //echo "<!-- doing iil_C_WriteCache //-->\n";
728         if (!$conn->do_cache) return false;
729         
730         if (is_array($conn->cache)) {
731                 while (list($folder,$data)=each($conn->cache)) {
732                         if ($folder && is_array($data) && $conn->cache_dirty[$folder]) {
733                                 $key = $folder.".imap";
734                                 $result = cache_write($conn->user, $conn->host, $key, $data, true);
735                                 //echo "<!-- writing $key $data: $result //-->\n";
736                         }
737                 }
738         }
739 }
740
741 function iil_C_EnableCache(&$conn) {
742         $conn->do_cache = true;
743 }
744
745 function iil_C_DisableCache(&$conn) {
746         $conn->do_cache = false;
747 }
748
749 function iil_C_LoadCache(&$conn, $folder) {
750         if (!$conn->do_cache) {
751             return false;
752         }
753     
754         $key = $folder.'.imap';
755         if (!is_array($conn->cache[$folder])) {
756                 $conn->cache[$folder]       = cache_read($conn->user, $conn->host, $key);
757                 $conn->cache_dirty[$folder] = false;
758         }
759 }
760
761 function iil_C_ExpireCachedItems(&$conn, $folder, $message_set) {
762         
763         if (!$conn->do_cache) {
764                 return; //caching disabled
765         }
766         if (!is_array($conn->cache[$folder])) {
767                 return; //cache not initialized|empty
768         }
769         if (count($conn->cache[$folder]) == 0) {
770                 return; //cache not initialized|empty
771         }
772     
773         $uids = iil_C_FetchHeaderIndex($conn, $folder, $message_set, 'UID');
774         $num_removed = 0;
775         if (is_array($uids)) {
776                 //echo "<!-- unsetting: ".implode(",",$uids)." //-->\n";
777                 while (list($n,$uid)=each($uids)) {
778                         unset($conn->cache[$folder][$uid]);
779                         //$conn->cache[$folder][$uid] = false;
780                         //$num_removed++;
781                 }
782                 $conn->cache_dirty[$folder] = true;
783
784                 //echo '<!--'."\n";
785                 //print_r($conn->cache);
786                 //echo "\n".'//-->'."\n";
787         } else {
788                 echo "<!-- failed to get uids: $message_set //-->\n";
789         }
790         
791         /*
792         if ($num_removed>0) {
793                 $new_cache;
794                 reset($conn->cache[$folder]);
795                 while (list($uid,$item)=each($conn->cache[$folder])) {
796                         if ($item) $new_cache[$uid] = $conn->cache[$folder][$uid];
797                 }
798                 $conn->cache[$folder] = $new_cache;
799         }
800         */
801 }
802
803 function iil_ExplodeQuotedString($delimiter, $string) {
804         $quotes = explode('"', $string);
805         while ( list($key, $val) = each($quotes)) {
806                 if (($key % 2) == 1) {
807                         $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
808                 }
809         }
810         $string = implode('"', $quotes);
811         
812         $result = explode($delimiter, $string);
813         while ( list($key, $val) = each($result) ) {
814                 $result[$key] = str_replace('_!@!_', $delimiter, $result[$key]);
815         }
816     
817         return $result;
818 }
819
820 function iil_CheckForRecent($host, $user, $password, $mailbox) {
821         if (empty($mailbox)) {
822                 $mailbox = 'INBOX';
823         }
824     
825         $conn = iil_Connect($host, $user, $password, 'plain');
826         $fp   = $conn->fp;
827         if ($fp) {
828                 iil_PutLine($fp, "a002 EXAMINE \"".iil_Escape($mailbox)."\"");
829                 do {
830                         $line=chop(iil_ReadLine($fp, 300));
831                         $a=explode(' ', $line);
832                         if (($a[0] == '*') && (strcasecmp($a[2], 'RECENT') == 0)) {
833                             $result = (int) $a[1];
834                         }
835                 } while (!iil_StartsWith($a[0], 'a002', true));
836
837                 iil_PutLine($fp, "a003 LOGOUT");
838                 fclose($fp);
839         } else {
840             $result = -2;
841         }
842     
843         return $result;
844 }
845
846 function iil_C_Select(&$conn, $mailbox) {
847
848         if (empty($mailbox)) {
849                 return false;
850         }
851         if (strcmp($conn->selected, $mailbox) == 0) {
852                 return true;
853         }
854     
855         iil_C_LoadCache($conn, $mailbox);
856         
857         if (iil_PutLine($conn->fp, "sel1 SELECT \"".iil_Escape($mailbox).'"')) {
858                 do {
859                         $line = chop(iil_ReadLine($conn->fp, 300));
860                         $a = explode(' ', $line);
861                         if (count($a) == 3) {
862                                 if (strcasecmp($a[2], 'EXISTS') == 0) {
863                                         $conn->exists = (int) $a[1];
864                                 }
865                                 if (strcasecmp($a[2], 'RECENT') == 0) {
866                                         $conn->recent = (int) $a[1];
867                                 }
868                         }
869                         else if (preg_match('/\[?PERMANENTFLAGS\s+\(([^\)]+)\)\]/U', $line, $match)) {
870                                 $conn->permanentflags = explode(' ', $match[1]);
871                         }
872                 } while (!iil_StartsWith($line, 'sel1', true));
873
874                 $a = explode(' ', $line);
875
876                 if (strcasecmp($a[1], 'OK') == 0) {
877                         $conn->selected = $mailbox;
878                         return true;
879                 }
880         }
881         return false;
882 }
883
884 function iil_C_CheckForRecent(&$conn, $mailbox) {
885         if (empty($mailbox)) {
886                 $mailbox = 'INBOX';
887         }
888     
889         iil_C_Select($conn, $mailbox);
890         if ($conn->selected == $mailbox) {
891                 return $conn->recent;
892         }
893         return false;
894 }
895
896 function iil_C_CountMessages(&$conn, $mailbox, $refresh = false) {
897         if ($refresh) {
898                 $conn->selected = '';
899         }
900         
901         iil_C_Select($conn, $mailbox);
902         if ($conn->selected == $mailbox) {
903                 return $conn->exists;
904         }
905         return false;
906 }
907
908 function iil_SplitHeaderLine($string) {
909         $pos=strpos($string, ':');
910         if ($pos>0) {
911                 $res[0] = substr($string, 0, $pos);
912                 $res[1] = trim(substr($string, $pos+1));
913                 return $res;
914         }
915         return $string;
916 }
917
918 function iil_StrToTime($str) {
919         $IMAP_MONTHS    = $GLOBALS['IMAP_MONTHS'];
920         $IMAP_SERVER_TZ = $GLOBALS['IMAP_SERVER_TZ'];
921                 
922         if ($str) {
923             $time1 = strtotime($str);
924         }
925         if ($time1 && $time1 != -1) {
926             return $time1-$IMAP_SERVER_TZ;
927         }
928         //echo '<!--'.$str.'//-->';
929         
930         //replace double spaces with single space
931         $str = trim($str);
932         $str = str_replace('  ', ' ', $str);
933         
934         //strip off day of week
935         $pos = strpos($str, ' ');
936         if (!is_numeric(substr($str, 0, $pos))) {
937             $str = substr($str, $pos+1);
938         }
939         //explode, take good parts
940         $a = explode(' ', $str);
941
942         $month_str = $a[1];
943         $month     = $IMAP_MONTHS[$month_str];
944         $day       = (int)$a[0];
945         $year      = (int)$a[2];
946         $time      = $a[3];
947         $tz_str    = $a[4];
948         $tz        = substr($tz_str, 0, 3);
949         $ta        = explode(':', $time);
950         $hour      = (int)$ta[0]-(int)$tz;
951         $minute    = (int)$ta[1];
952         $second    = (int)$ta[2];
953         
954         //make UNIX timestamp
955         $time2 = mktime($hour, $minute, $second, $month, $day, $year);
956         //echo '<!--'.$time1.' '.$time2.' //-->'."\n";
957         return $time2;
958 }
959
960 function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE,
961     $encoding = 'US-ASCII') {
962
963         $field = strtoupper($field);
964         if ($field == 'INTERNALDATE') {
965             $field = 'ARRIVAL';
966         }
967         
968         $fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1,
969         'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
970         
971         if (!$fields[$field]) {
972             return false;
973         }
974
975         /*  Do "SELECT" command */
976         if (!iil_C_Select($conn, $mailbox)) {
977             return false;
978         }
979     
980         $is_uid = $is_uid ? 'UID ' : '';
981         
982         if (!empty($add)) {
983             $add = " $add";
984         }
985
986         $command  = 's ' . $is_uid . 'SORT (' . $field . ') ';
987         $command .= $encoding . ' ALL' . $add;
988         $line     = $data = '';
989         
990         if (!iil_PutLineC($conn->fp, $command)) {
991             return false;
992         }
993         do {
994                 $line = chop(iil_ReadLine($conn->fp, 1024));
995                 if (iil_StartsWith($line, '* SORT')) {
996                         $data .= ($data ? ' ' : '') . substr($line, 7);
997                 } else if (preg_match('/^[0-9 ]+$/', $line)) {
998                         $data .= $line;
999                 }
1000         } while (!iil_StartsWith($line, 's ', true));
1001         
1002         $result_code = iil_ParseResult($line);
1003         
1004         if ($result_code != 0) {
1005                 $conn->error = 'iil_C_Sort: ' . $line . "\n";
1006                 return false;
1007         }
1008         
1009         $out = explode(' ',$data);
1010         return $out;
1011 }
1012
1013 function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field,
1014     $normalize=true) {
1015         global $IMAP_USE_INTERNAL_DATE;
1016         
1017         $c=0;
1018         $result=array();
1019         $fp = $conn->fp;
1020                 
1021         if (empty($index_field)) {
1022             $index_field = 'DATE';
1023         }
1024         $index_field = strtoupper($index_field);
1025         
1026         list($from_idx, $to_idx) = explode(':', $message_set);
1027         if (empty($message_set) || (isset($to_idx)
1028             && (int)$from_idx > (int)$to_idx)) {
1029                 return false;
1030         }
1031         
1032         //$fields_a['DATE'] = ($IMAP_USE_INTERNAL_DATE?6:1);
1033         $fields_a['DATE']         = 1;
1034         $fields_a['INTERNALDATE'] = 6;
1035         $fields_a['FROM']         = 1;
1036         $fields_a['REPLY-TO']     = 1;
1037         $fields_a['SENDER']       = 1;
1038         $fields_a['TO']           = 1;
1039         $fields_a['SUBJECT']      = 1;
1040         $fields_a['UID']          = 2;
1041         $fields_a['SIZE']         = 2;
1042         $fields_a['SEEN']         = 3;
1043         $fields_a['RECENT']       = 4;
1044         $fields_a['DELETED']      = 5;
1045         
1046         $mode=$fields_a[$index_field];
1047         if (!($mode > 0)) {
1048             return false;
1049         }
1050     
1051         /*  Do "SELECT" command */
1052         if (!iil_C_Select($conn, $mailbox)) {
1053             return false;
1054         }
1055     
1056         /* FETCH date,from,subject headers */
1057         if ($mode == 1) {
1058                 $key     = 'fhi' . ($c++);
1059                 $request = $key . " FETCH $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)])";
1060                 if (!iil_PutLine($fp, $request)) {
1061                     return false;
1062                 }
1063                 do {
1064                         
1065                         $line=chop(iil_ReadLine($fp, 200));
1066                         $a=explode(' ', $line);
1067                         if (($line[0] == '*') && ($a[2] == 'FETCH')
1068                             && ($line[strlen($line)-1] != ')')) {
1069                                 $id=$a[1];
1070
1071                                 $str=$line=chop(iil_ReadLine($fp, 300));
1072
1073                                 while ($line[0] != ')') {                                       //caution, this line works only in this particular case
1074                                         $line=chop(iil_ReadLine($fp, 300));
1075                                         if ($line[0] != ')') {
1076                                                 if (ord($line[0]) <= 32) {                      //continuation from previous header line
1077                                                         $str.= ' ' . trim($line);
1078                                                 }
1079                                                 if ((ord($line[0]) > 32) || (strlen($line[0]) == 0)) {
1080                                                         list($field, $string) = iil_SplitHeaderLine($str);
1081                                                         if (strcasecmp($field, 'date') == 0) {
1082                                                                 $result[$id] = iil_StrToTime($string);
1083                                                         } else {
1084                                                                 $result[$id] = str_replace('"', '', $string);
1085                                                                 if ($normalize) {
1086                                                                     $result[$id] = strtoupper($result[$id]);
1087                                                                 }
1088                                                         }
1089                                                         $str=$line;
1090                                                 }
1091                                         }
1092                                 }
1093                         }
1094                         /*
1095                         $end_pos = strlen($line)-1;
1096                         if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[$end_pos]=="}")) {
1097                                 $id = $a[1];
1098                                 $pos = strrpos($line, "{")+1;
1099                                 $bytes = (int)substr($line, $pos, $end_pos-$pos);
1100                                 $received = 0;
1101                                 do {
1102                                         $line      = iil_ReadLine($fp, 0);
1103                                         $received += strlen($line);
1104                                         $line      = chop($line);
1105                                         
1106                                         if ($received>$bytes) {
1107                                                 break;
1108                                         } else if (!$line) {
1109                                                 continue;
1110                                         }
1111
1112                                         list($field, $string) = explode(': ', $line);
1113                                         
1114                                         if (strcasecmp($field, 'date') == 0) {
1115                                                 $result[$id] = iil_StrToTime($string);
1116                                         } else if ($index_field != 'DATE') {
1117                                                 $result[$id]=strtoupper(str_replace('"', '', $string));
1118                                         }
1119                                 } while ($line[0] != ')');
1120                         } else {
1121                                 //one line response, not expected so ignore                             
1122                         }
1123                         */
1124                 } while (!iil_StartsWith($line, $key, true));
1125
1126         }else if ($mode == 6) {
1127
1128                 $key     = 'fhi' . ($c++);
1129                 $request = $key . " FETCH $message_set (INTERNALDATE)";
1130                 if (!iil_PutLine($fp, $request)) {
1131                     return false;
1132                 }
1133                 do {
1134                         $line=chop(iil_ReadLine($fp, 200));
1135                         if ($line[0] == '*') {
1136                                 /*
1137                                  * original:
1138                                  * "* 10 FETCH (INTERNALDATE "31-Jul-2002 09:18:02 -0500")"
1139                                  */
1140                                 $paren_pos = strpos($line, '(');
1141                                 $foo       = substr($line, 0, $paren_pos);
1142                                 $a         = explode(' ', $foo);
1143                                 $id        = $a[1];
1144                                 
1145                                 $open_pos  = strpos($line, '"') + 1;
1146                                 $close_pos = strrpos($line, '"');
1147                                 if ($open_pos && $close_pos) {
1148                                         $len         = $close_pos - $open_pos;
1149                                         $time_str    = substr($line, $open_pos, $len);
1150                                         $result[$id] = strtotime($time_str);
1151                                 }
1152                         } else {
1153                                 $a = explode(' ', $line);
1154                         }
1155                 } while (!iil_StartsWith($a[0], $key, true));
1156         } else {
1157                 if ($mode >= 3) {
1158                     $field_name = 'FLAGS';
1159                 } else if ($index_field == 'SIZE') {
1160                     $field_name = 'RFC822.SIZE';
1161                 } else {
1162                     $field_name = $index_field;
1163                 }
1164         
1165                 /*                      FETCH uid, size, flags          */
1166                 $key     = 'fhi' .($c++);
1167                 $request = $key . " FETCH $message_set ($field_name)";
1168
1169                 if (!iil_PutLine($fp, $request)) {
1170                     return false;
1171                 }
1172                 do {
1173                         $line=chop(iil_ReadLine($fp, 200));
1174                         $a = explode(' ', $line);
1175                         if (($line[0] == '*') && ($a[2] == 'FETCH')) {
1176                                 $line = str_replace('(', '', $line);
1177                                 $line = str_replace(')', '', $line);
1178                                 $a    = explode(' ', $line);
1179                                 
1180                                 $id = $a[1];
1181
1182                                 if (isset($result[$id])) {
1183                                     continue; //if we already got the data, skip forward
1184                                 }
1185                                 if ($a[3]!=$field_name) {
1186                                         continue;  //make sure it's returning what we requested
1187                                 }
1188                 
1189                                 /*  Caution, bad assumptions, next several lines */
1190                                 if ($mode == 2) {
1191                                     $result[$id] = $a[4];
1192                                 } else {
1193                                         $haystack    = strtoupper($line);
1194                                         $result[$id] = (strpos($haystack, $index_field) > 0 ? "F" : "N");
1195                                 }
1196                         }
1197                 } while (!iil_StartsWith($line, $key, true));
1198         }
1199
1200         //check number of elements...
1201         list($start_mid, $end_mid) = explode(':', $message_set);
1202         if (is_numeric($start_mid) && is_numeric($end_mid)) {
1203                 //count how many we should have
1204                 $should_have = $end_mid - $start_mid +1;
1205                 
1206                 //if we have less, try and fill in the "gaps"
1207                 if (count($result) < $should_have) {
1208                         for ($i=$start_mid; $i<=$end_mid; $i++) {
1209                                 if (!isset($result[$i])) {
1210                                         $result[$i] = '';
1211                                 }
1212                         }
1213                 }
1214         }
1215         return $result; 
1216 }
1217
1218 function iil_CompressMessageSet($message_set) {
1219         //given a comma delimited list of independent mid's, 
1220         //compresses by grouping sequences together
1221         
1222         //if less than 255 bytes long, let's not bother
1223         if (strlen($message_set)<255) {
1224             return $message_set;
1225         }
1226     
1227         //see if it's already been compress
1228         if (strpos($message_set, ':') !== false) {
1229             return $message_set;
1230         }
1231     
1232         //separate, then sort
1233         $ids = explode(',', $message_set);
1234         sort($ids);
1235         
1236         $result = array();
1237         $start  = $prev = $ids[0];
1238
1239         foreach ($ids as $id) {
1240                 $incr = $id - $prev;
1241                 if ($incr > 1) {                        //found a gap
1242                         if ($start == $prev) {
1243                             $result[] = $prev;  //push single id
1244                         } else {
1245                             $result[] = $start . ':' . $prev;   //push sequence as start_id:end_id
1246                         }
1247                         $start = $id;                   //start of new sequence
1248                 }
1249                 $prev = $id;
1250         }
1251
1252         //handle the last sequence/id
1253         if ($start==$prev) {
1254             $result[] = $prev;
1255         } else {
1256             $result[] = $start.':'.$prev;
1257         }
1258     
1259         //return as comma separated string
1260         return implode(',', $result);
1261 }
1262
1263 function iil_C_UIDsToMIDs(&$conn, $mailbox, $uids) {
1264         if (!is_array($uids) || count($uids) == 0) {
1265             return array();
1266         }
1267         return iil_C_Search($conn, $mailbox, 'UID ' . implode(',', $uids));
1268 }
1269
1270 function iil_C_UIDToMID(&$conn, $mailbox, $uid) {
1271         $result = iil_C_UIDsToMIDs($conn, $mailbox, array($uid));
1272         if (count($result) == 1) {
1273             return $result[0];
1274         }
1275         return false;
1276 }
1277
1278 function iil_C_FetchUIDs(&$conn,$mailbox) {
1279         global $clock;
1280         
1281         $num = iil_C_CountMessages($conn, $mailbox);
1282         if ($num == 0) {
1283             return array();
1284         }
1285         $message_set = '1' . ($num>1?':' . $num:'');
1286         
1287         //if cache not enabled, just call iil_C_FetchHeaderIndex on 'UID' field
1288         if (!$conn->do_cache)
1289                 return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
1290
1291         //otherwise, let's check cache first
1292         $key        = $mailbox.'.uids';
1293         $cache_good = true;
1294         if ($conn->uid_cache) {
1295             $data = $conn->uid_cache;
1296         } else {
1297             $data = cache_read($conn->user, $conn->host, $key);
1298         }
1299     
1300         //was anything cached at all?
1301         if ($data === false) {
1302             $cache_good = -1;
1303         }
1304     
1305         //make sure number of messages were the same
1306         if ($cache_good > 0 && $data['n'] != $num) {
1307             $cache_good = -2;
1308         }
1309     
1310         //if everything's okay so far...
1311         if ($cache_good > 0) {
1312                 //check UIDs of highest mid with current and cached
1313                 $temp = iil_C_Search($conn, $mailbox, 'UID ' . $data['d'][$num]);
1314                 if (!$temp || !is_array($temp) || $temp[0] != $num) {
1315                     $cache_good = -3;
1316                 }
1317         }
1318
1319         //if cached data's good, return it
1320         if ($cache_good > 0) {
1321                 return $data['d'];
1322         }
1323
1324         //otherwise, we need to fetch it
1325         $data      = array('n' => $num, 'd' => array());
1326         $data['d'] = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
1327     
1328         cache_write($conn->user, $conn->host, $key, $data);
1329         $conn->uid_cache = $data;
1330         return $data['d'];
1331 }
1332
1333 function iil_SortThreadHeaders($headers, $index_a, $uids) {
1334         asort($index_a);
1335         $result = array();
1336         foreach ($index_a as $mid=>$foobar) {
1337                 $uid = $uids[$mid];
1338                 $result[$uid] = $headers[$uid];
1339         }
1340         return $result;
1341 }
1342
1343 function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set) {
1344         global $clock;
1345         global $index_a;
1346         
1347         list($from_idx, $to_idx) = explode(':', $message_set);
1348         if (empty($message_set) || (isset($to_idx)
1349         && (int)$from_idx > (int)$to_idx)) {
1350                 return false;
1351         }
1352
1353         $result = array();
1354         $uids   = iil_C_FetchUIDs($conn, $mailbox);
1355         $debug  = false;
1356         
1357         /* Get cached records where possible */
1358         if ($conn->do_cache) {
1359                 $cached = cache_read($conn->user, $conn->host, $mailbox.'.thhd');
1360                 if ($cached && is_array($uids) && count($uids)>0) {
1361                         $needed_set = '';
1362                         foreach ($uids as $id=>$uid) {
1363                                 if ($cached[$uid]) {
1364                                         $result[$uid]     = $cached[$uid];
1365                                         $result[$uid]->id = $id;
1366                                 } else {
1367                                     $needed_set .= ($needed_set ? ',' : '') . $id;
1368                                 }
1369                         }
1370                         if ($needed_set) {
1371                             $message_set = $needed_set;
1372                         } else {
1373                             $message_set = '';
1374                         }
1375                 }
1376         }
1377         $message_set = iil_CompressMessageSet($message_set);
1378         if ($debug) {
1379             echo "Still need: ".$message_set;
1380         }
1381     
1382         /* if we're missing any, get them */
1383         if ($message_set) {
1384                 /* FETCH date,from,subject headers */
1385                 $key        = 'fh';
1386                 $fp         = $conn->fp;
1387                 $request    = $key . " FETCH $message_set ";
1388                 $request   .= "(BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])";
1389                 $mid_to_id  = array();
1390                 if (!iil_PutLine($fp, $request)) {
1391                     return false;
1392                 }
1393                 do {
1394                         $line = chop(iil_ReadLine($fp, 1024));
1395                         if ($debug) {
1396                             echo $line . "\n";
1397                         }
1398                         if (ereg('\{[0-9]+\}$', $line)) {
1399                                 $a       = explode(' ', $line);
1400                                 $new = array();
1401
1402                                 $new_thhd = new iilThreadHeader;
1403                                 $new_thhd->id = $a[1];
1404                                 do {
1405                                         $line = chop(iil_ReadLine($fp, 1024), "\r\n");
1406                                         if (iil_StartsWithI($line, 'Message-ID:')
1407                                                 || (iil_StartsWithI($line,'In-Reply-To:'))
1408                                                 || (iil_StartsWithI($line,'SUBJECT:'))) {
1409
1410                                                 $pos        = strpos($line, ':');
1411                                                 $field_name = substr($line, 0, $pos);
1412                                                 $field_val  = substr($line, $pos+1);
1413
1414                                                 $new[strtoupper($field_name)] = trim($field_val);
1415
1416                                         } else if (ereg('^[[:space:]]', $line)) {
1417                                                 $new[strtoupper($field_name)] .= trim($line);
1418                                         }
1419                                 } while ($line[0] != ')');
1420                 
1421                                 $new_thhd->sbj = $new['SUBJECT'];
1422                                 $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1);
1423                                 $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1);
1424                                 
1425                                 $result[$uids[$new_thhd->id]] = $new_thhd;
1426                         }
1427                 } while (!iil_StartsWith($line, 'fh'));
1428         }
1429         
1430         /* sort headers */
1431         if (is_array($index_a)) {
1432                 $result = iil_SortThreadHeaders($result, $index_a, $uids);      
1433         }
1434         
1435         /* write new set to cache */
1436         if ($conn->do_cache) {
1437                 if (count($result)!=count($cached)) {
1438                         cache_write($conn->user, $conn->host, $mailbox . '.thhd', $result);
1439                 }
1440         }
1441         
1442         //echo 'iil_FetchThreadHeaders:'."\n";
1443         //print_r($result);
1444         
1445         return $result;
1446 }
1447
1448 function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock) {
1449         global $index_a;
1450
1451         list($from_idx, $to_idx) = explode(':', $message_set);
1452         if (empty($message_set) || (isset($to_idx)
1453                 && (int)$from_idx > (int)$to_idx)) {
1454                 return false;
1455         }
1456     
1457         $result    = array();
1458         $roots     = array();
1459         $root_mids = array();
1460         $sub_mids  = array();
1461         $strays    = array();
1462         $messages  = array();
1463         $fp        = $conn->fp;
1464         $debug     = false;
1465         
1466         $sbj_filter_pat = '[a-zA-Z]{2,3}(\[[0-9]*\])?:([[:space:]]*)';
1467         
1468         /*  Do "SELECT" command */
1469         if (!iil_C_Select($conn, $mailbox)) {
1470             return false;
1471         }
1472     
1473         /* FETCH date,from,subject headers */
1474         $mid_to_id = array();
1475         $messages  = array();
1476         $headers   = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set);
1477         if ($clock) {
1478             $clock->register('fetched headers');
1479         }
1480     
1481         if ($debug) {
1482             print_r($headers);
1483         }
1484     
1485         /* go through header records */
1486         foreach ($headers as $header) {
1487                 //$id = $header['i'];
1488                 //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'], 
1489                 //                      'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']);
1490                 $id  = $header->id;
1491                 $new = array('id' => $id, 'MESSAGE-ID' => $header->mid, 
1492                         'IN-REPLY-TO' => $header->irt, 'SUBJECT' => $header->sbj);
1493
1494                 /* add to message-id -> mid lookup table */
1495                 $mid_to_id[$new['MESSAGE-ID']] = $id;
1496                 
1497                 /* if no subject, use message-id */
1498                 if (empty($new['SUBJECT'])) {
1499                     $new['SUBJECT'] = $new['MESSAGE-ID'];
1500                 }
1501         
1502                 /* if subject contains 'RE:' or has in-reply-to header, it's a reply */
1503                 $sbj_pre ='';
1504                 $has_re = false;
1505                 if (eregi($sbj_filter_pat, $new['SUBJECT'])) {
1506                     $has_re = true;
1507                 }
1508                 if ($has_re||$new['IN-REPLY-TO']) {
1509                     $sbj_pre = 'RE:';
1510                 }
1511         
1512                 /* strip out 're:', 'fw:' etc */
1513                 if ($has_re) {
1514                     $sbj = ereg_replace($sbj_filter_pat, '', $new['SUBJECT']);
1515                 } else {
1516                     $sbj = $new['SUBJECT'];
1517                 }
1518                 $new['SUBJECT'] = $sbj_pre.$sbj;
1519                 
1520                 
1521                 /* if subject not a known thread-root, add to list */
1522                 if ($debug) {
1523                     echo $id . ' ' . $new['SUBJECT'] . "\t" . $new['MESSAGE-ID'] . "\n";
1524                 }
1525                 $root_id = $roots[$sbj];
1526                 
1527                 if ($root_id && ($has_re || !$root_in_root[$root_id])) {
1528                         if ($debug) {
1529                             echo "\tfound root: $root_id\n";
1530                         }
1531                         $sub_mids[$new['MESSAGE-ID']] = $root_id;
1532                         $result[$root_id][]           = $id;
1533                 } else if (!isset($roots[$sbj]) || (!$has_re && $root_in_root[$root_id])) {
1534                         /* try to use In-Reply-To header to find root 
1535                                 unless subject contains 'Re:' */
1536                         if ($has_re&&$new['IN-REPLY-TO']) {
1537                                 if ($debug) {
1538                                     echo "\tlooking: ".$new['IN-REPLY-TO']."\n";
1539                                 }
1540                                 //reply to known message?
1541                                 $temp = $sub_mids[$new['IN-REPLY-TO']];
1542                                 
1543                                 if ($temp) {
1544                                         //found it, root:=parent's root
1545                                         if ($debug) {
1546                                             echo "\tfound parent: ".$new['SUBJECT']."\n";
1547                                         }
1548                                         $result[$temp][]              = $id;
1549                                         $sub_mids[$new['MESSAGE-ID']] = $temp;
1550                                         $sbj                          = '';
1551                                 } else {
1552                                         //if we can't find referenced parent, it's a "stray"
1553                                         $strays[$id] = $new['IN-REPLY-TO'];
1554                                 }
1555                         }
1556                         
1557                         //add subject as root
1558                         if ($sbj) {
1559                                 if ($debug) {
1560                                     echo "\t added to root\n";
1561                                 }
1562                                 $roots[$sbj]                  = $id;
1563                                 $root_in_root[$id]            = !$has_re;
1564                                 $sub_mids[$new['MESSAGE-ID']] = $id;
1565                                 $result[$id]                  = array($id);
1566                         }
1567                         if ($debug) {
1568                             echo $new['MESSAGE-ID'] . "\t" . $sbj . "\n";
1569                         }
1570                 }
1571         }
1572         
1573         //now that we've gone through all the messages,
1574         //go back and try and link up the stray threads
1575         if (count($strays) > 0) {
1576                 foreach ($strays as $id=>$irt) {
1577                         $root_id = $sub_mids[$irt];
1578                         if (!$root_id || $root_id==$id) {
1579                             continue;
1580                         }
1581                         $result[$root_id] = array_merge($result[$root_id],$result[$id]);
1582                         unset($result[$id]);
1583                 }
1584         }
1585         
1586         if ($clock) {
1587             $clock->register('data prepped');
1588         }
1589     
1590         if ($debug) {
1591             print_r($roots);
1592         }
1593
1594         return $result;
1595 }
1596
1597 function iil_SortThreads(&$tree, $index, $sort_order = 'ASC') {
1598         if (!is_array($tree) || !is_array($index)) {
1599             return false;
1600         }
1601     
1602         //create an id to position lookup table
1603         $i = 0;
1604         foreach ($index as $id=>$val) {
1605                 $i++;
1606                 $index[$id] = $i;
1607         }
1608         $max = $i+1;
1609         
1610         //for each tree, set array key to position
1611         $itree = array();
1612         foreach ($tree as $id=>$node) {
1613                 if (count($tree[$id])<=1) {
1614                         //for "threads" with only one message, key is position of that message
1615                         $n         = $index[$id];
1616                         $itree[$n] = array($n=>$id);
1617                 } else {
1618                         //for "threads" with multiple messages, 
1619                         $min   = $max;
1620                         $new_a = array();
1621                         foreach ($tree[$id] as $mid) {
1622                                 $new_a[$index[$mid]] = $mid;            //create new sub-array mapping position to id
1623                                 $pos                 = $index[$mid];
1624                                 if ($pos&&$pos<$min) {
1625                                     $min = $index[$mid];        //find smallest position
1626                                 }
1627                         }
1628                         $n = $min;      //smallest position of child is thread position
1629                         
1630                         //assign smallest position to root level key
1631                         //set children array to one created above
1632                         ksort($new_a);
1633                         $itree[$n] = $new_a;
1634                 }
1635         }
1636         
1637         //sort by key, this basically sorts all threads
1638         ksort($itree);
1639         $i   = 0;
1640         $out = array();
1641         foreach ($itree as $k=>$node) {
1642                 $out[$i] = $itree[$k];
1643                 $i++;
1644         }
1645         
1646         return $out;
1647 }
1648
1649 function iil_IndexThreads(&$tree) {
1650         /* creates array mapping mid to thread id */
1651         
1652         if (!is_array($tree)) {
1653             return false;
1654         }
1655     
1656         $t_index = array();
1657         foreach ($tree as $pos=>$kids) {
1658                 foreach ($kids as $kid) $t_index[$kid] = $pos;
1659         }
1660         
1661         return $t_index;
1662 }
1663
1664 function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false)
1665 {
1666         global $IMAP_USE_INTERNAL_DATE;
1667         
1668         $result = array();
1669         $fp     = $conn->fp;
1670         
1671         list($from_idx, $to_idx) = explode(':', $message_set);
1672         if (empty($message_set) || (isset($to_idx)
1673                 && (int)$from_idx > (int)$to_idx)) {
1674                 return false;
1675         }
1676                 
1677         /*  Do "SELECT" command */
1678         if (!iil_C_Select($conn, $mailbox)) {
1679                 $conn->error = "Couldn't select $mailbox";
1680                 return false;
1681         }
1682                 
1683         /* Get cached records where possible */
1684         if ($conn->do_cache) {
1685                 $uids = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, "UID");
1686                 if (is_array($uids) && count($conn->cache[$mailbox]>0)) {
1687                         $needed_set = '';
1688                         while (list($id,$uid)=each($uids)) {
1689                                 if ($conn->cache[$mailbox][$uid]) {
1690                                         $result[$id]     = $conn->cache[$mailbox][$uid];
1691                                         $result[$id]->id = $id;
1692                                 } else {
1693                                     $needed_set.=($needed_set ? ',': '') . $id;
1694                                 }
1695                         }
1696                         //echo "<!-- iil_C_FetchHeader\nMessage Set: $message_set\nNeeded Set:$needed_set\n//-->\n";
1697                         if ($needed_set) {
1698                                 $message_set = iil_CompressMessageSet($needed_set);
1699                         } else {
1700                                 return $result;
1701                         }
1702                 }
1703         }
1704
1705         /* FETCH uid, size, flags and headers */
1706         $key      = 'FH12';
1707         $request  = $key . ($uidfetch ? ' UID' : '') . " FETCH $message_set ";
1708         $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE ";
1709         if ($bodystr)
1710                 $request .= "BODYSTRUCTURE ";
1711         $request .= "BODY.PEEK[HEADER.FIELDS ";
1712         $request .= "(DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC ";
1713         $request .= "CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID ";
1714         $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY)])";
1715
1716         if (!iil_PutLine($fp, $request)) {
1717                 return false;
1718         }
1719         do {
1720                 $line = iil_ReadLine($fp, 1024);
1721                 $line = iil_MultLine($fp, $line);
1722
1723                 $a    = explode(' ', $line);
1724                 if (($line[0] == '*') && ($a[2] == 'FETCH')) {
1725                         $id = $a[1];
1726             
1727                         $result[$id]            = new iilBasicHeader;
1728                         $result[$id]->id        = $id;
1729                         $result[$id]->subject   = '';
1730                         $result[$id]->messageID = 'mid:' . $id;
1731
1732                         $lines = array();
1733                         $ln = 0;
1734                         /*
1735                             Sample reply line:
1736                             * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
1737                             INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODYSTRUCTURE (...)
1738                             BODY[HEADER.FIELDS ...
1739                         */
1740
1741                         if (preg_match('/^\* [0-9]+ FETCH \((.*) BODY/s', $line, $matches)) {
1742                                 $str = $matches[1];
1743
1744                                 // swap parents with quotes, then explode
1745                                 $str = eregi_replace("[()]", "\"", $str);
1746                                 $a = iil_ExplodeQuotedString(' ', $str);
1747
1748                                 // did we get the right number of replies?
1749                                 $parts_count = count($a);
1750                                 if ($parts_count>=6) {
1751                                         for ($i=0; $i<$parts_count; $i=$i+2) {
1752                                                 if (strcasecmp($a[$i],'UID') == 0)
1753                                                         $result[$id]->uid = $a[$i+1];
1754                                                 else if (strcasecmp($a[$i],'RFC822.SIZE') == 0)
1755                                                         $result[$id]->size = $a[$i+1];
1756                                                 else if (strcasecmp($a[$i],'INTERNALDATE') == 0)
1757                                                         $time_str = $a[$i+1];
1758                                                 else if (strcasecmp($a[$i],'FLAGS') == 0)
1759                                                         $flags_str = $a[$i+1];
1760                                         }
1761
1762                                         $time_str = str_replace('"', '', $time_str);
1763                                         
1764                                         // if time is gmt...
1765                                         $time_str = str_replace('GMT','+0000',$time_str);
1766                                         
1767                                         //get timezone
1768                                         $time_str      = substr($time_str, 0, -1);
1769                                         $time_zone_str = substr($time_str, -5); // extract timezone
1770                                         $time_str      = substr($time_str, 0, -5); // remove timezone
1771                                         $time_zone     = (float)substr($time_zone_str, 1, 2); // get first two digits
1772                         
1773                                         if ($time_zone_str[3] != '0') {
1774                                                  $time_zone += 0.5;  //handle half hour offset
1775                                         }
1776                                         if ($time_zone_str[0] == '-') {
1777                                                 $time_zone = $time_zone * -1.0; //minus?
1778                                         }
1779                                         
1780                                         //calculate timestamp
1781                                         $timestamp     = strtotime($time_str); //return's server's time
1782                                         $timestamp    -= $time_zone * 3600; //compensate for tz, get GMT
1783
1784                                         $result[$id]->internaldate = $time_str;
1785                                         $result[$id]->timestamp = $timestamp;
1786                                         $result[$id]->date = $time_str;
1787                                 }
1788
1789                                 // BODYSTRUCTURE 
1790                                 if($bodystr) {
1791                                         while (!preg_match('/ BODYSTRUCTURE (.*) BODY\[HEADER.FIELDS/s', $line, $m)) {
1792                                                 $line2 = iil_ReadLine($fp, 1024);
1793                                                 $line .= iil_MultLine($fp, $line2);
1794                                         }
1795                                         $result[$id]->body_structure = $m[1];
1796                                 }
1797
1798                                 // the rest of the result
1799                                 preg_match('/ BODY\[HEADER.FIELDS \(.*\)\]\s*(.*)/s', $line, $m);
1800                                 $reslines = explode("\n", trim($m[1], '"'));
1801                                 // re-parse (see below)
1802                                 foreach ($reslines as $line) {
1803                                         if (ord($line[0])<=32) {
1804                                                 $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($line);
1805                                         } else {
1806                                                 $lines[++$ln] = trim($line);
1807                                         }
1808                                 }
1809                         }
1810
1811                         /*
1812                                 Start parsing headers.  The problem is, some header "lines" take up multiple lines.
1813                                 So, we'll read ahead, and if the one we're reading now is a valid header, we'll
1814                                 process the previous line.  Otherwise, we'll keep adding the strings until we come
1815                                 to the next valid header line.
1816                         */
1817         
1818                         do {
1819                                 $line = chop(iil_ReadLine($fp, 300), "\r\n");
1820
1821                                 // The preg_match below works around communigate imap, which outputs " UID <number>)".
1822                                 // Without this, the while statement continues on and gets the "FH0 OK completed" message.
1823                                 // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.  
1824                                 // This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
1825                                 // If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
1826                                 // An alternative might be:
1827                                 // if (!preg_match("/:/",$line) && preg_match("/\)$/",$line)) break;
1828                                 // however, unsure how well this would work with all imap clients.
1829                                 if (preg_match("/^\s*UID [0-9]+\)$/", $line)) {
1830                                     break;
1831                                 }
1832
1833                                 // handle FLAGS reply after headers (AOL, Zimbra?)
1834                                 if (preg_match('/\s+FLAGS \((.*)\)\)$/', $line, $matches)) {
1835                                         $flags_str = $matches[1];
1836                                         break;
1837                                 }
1838
1839                                 if (ord($line[0])<=32) {
1840                                         $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($line);
1841                                 } else {
1842                                         $lines[++$ln] = trim($line);
1843                                 }
1844                         // patch from "Maksim Rubis" <siburny@hotmail.com>
1845                         } while (trim($line[0]) != ')' && strncmp($line, $key, strlen($key)));
1846
1847                         if (strncmp($line, $key, strlen($key))) { 
1848                                 // process header, fill iilBasicHeader obj.
1849                                 // initialize
1850                                 if (is_array($headers)) {
1851                                         reset($headers);
1852                                         while (list($k, $bar) = each($headers)) {
1853                                                 $headers[$k] = '';
1854                                         }
1855                                 }
1856         
1857                                 // create array with header field:data
1858                                 while ( list($lines_key, $str) = each($lines) ) {
1859                                         list($field, $string) = iil_SplitHeaderLine($str);
1860                                         
1861                                         $field  = strtolower($field);
1862                                         $string = ereg_replace("\n[[:space:]]*"," ",$string); 
1863                                         
1864                                         switch ($field) {
1865                                         case 'date';
1866                                                 if (!$IMAP_USE_INTERNAL_DATE) {
1867                                                         $result[$id]->date = $string;
1868                                                         $result[$id]->timestamp = iil_StrToTime($string);
1869                                                 }
1870                                                 break;
1871                                         case 'from':
1872                                                 $result[$id]->from = $string;
1873                                                 break;
1874                                         case 'to':
1875                                                 $result[$id]->to = preg_replace('/undisclosed-recipients:[;,]*/', '', $string);
1876                                                 break;
1877                                         case 'subject':
1878                                                 $result[$id]->subject = $string;
1879                                                 break;
1880                                         case 'reply-to':
1881                                                 $result[$id]->replyto = $string;
1882                                                 break;
1883                                         case 'cc':
1884                                                 $result[$id]->cc = $string;
1885                                                 break;
1886                                         case 'bcc':
1887                                                 $result[$id]->bcc = $string;
1888                                                 break;
1889                                         case 'content-transfer-encoding':
1890                                                 $result[$id]->encoding = $string;
1891                                                 break;
1892                                         case 'content-type':
1893                                                 $ctype_parts = explode(";", $string);
1894                                                 $result[$id]->ctype = array_shift($ctype_parts);
1895                                                 foreach ($ctype_parts as $ctype_add) {
1896                                                         if (preg_match('/charset="?([a-z0-9\-\.\_]+)"?/i',
1897                                                                 $ctype_add, $regs)) {
1898                                                                 $result[$id]->charset = $regs[1];
1899                                                         }
1900                                                 }
1901                                                 break;
1902                                         case 'in-reply-to':
1903                                                 $result[$id]->in_reply_to = ereg_replace("[\n<>]", '', $string);
1904                                                 break;
1905                                         case 'references':
1906                                                 $result[$id]->references = $string;
1907                                                 break;
1908                                         case 'return-receipt-to':
1909                                         case 'disposition-notification-to':
1910                                         case 'x-confirm-reading-to':
1911                                                 $result[$id]->mdn_to = $string;
1912                                                 break;
1913                                         case 'message-id':
1914                                                 $result[$id]->messageID = $string;
1915                                                 break;
1916                                         case 'x-priority':
1917                                                 if (preg_match('/^(\d+)/', $string, $matches))
1918                                                         $result[$id]->priority = intval($matches[1]);
1919                                                 break;
1920                                         } // end switch ()
1921                                 } // end while ()
1922                 
1923                                 if ($conn->do_cache) {
1924                                         $uid = $result[$id]->uid;
1925                                         $conn->cache[$mailbox][$uid] = $result[$id];
1926                                         $conn->cache_dirty[$mailbox] = true;
1927                                 }
1928                         } else {
1929                                 $a = explode(' ', $line);
1930                         }
1931
1932                         // process flags
1933                         if (!empty($flags_str)) {
1934                                 $flags_str = eregi_replace('[\\\"]', '', $flags_str);
1935                                 $flags_a   = explode(' ', $flags_str);
1936                                         
1937                                 if (is_array($flags_a)) {
1938                                         reset($flags_a);
1939                                         while (list(,$val)=each($flags_a)) {
1940                                                 if (strcasecmp($val,'Seen') == 0) {
1941                                                     $result[$id]->seen = true;
1942                                                 } else if (strcasecmp($val, 'Deleted') == 0) {
1943                                                     $result[$id]->deleted=true;
1944                                                 } else if (strcasecmp($val, 'Recent') == 0) {
1945                                                     $result[$id]->recent = true;
1946                                                 } else if (strcasecmp($val, 'Answered') == 0) {
1947                                                         $result[$id]->answered = true;
1948                                                 } else if (strcasecmp($val, '$Forwarded') == 0) {
1949                                                         $result[$id]->forwarded = true;
1950                                                 } else if (strcasecmp($val, 'Draft') == 0) {
1951                                                         $result[$id]->is_draft = true;
1952                                                 } else if (strcasecmp($val, '$MDNSent') == 0) {
1953                                                         $result[$id]->mdn_sent = true;
1954                                                 } else if (strcasecmp($val, 'Flagged') == 0) {
1955                                                          $result[$id]->flagged = true;
1956                                                 }
1957                                         }
1958                                         $result[$id]->flags = $flags_a;
1959                                 }
1960                         }
1961                 }
1962         } while (strcmp($a[0], $key) != 0);
1963
1964         return $result;
1965 }
1966
1967 function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false, $bodystr=false) {
1968
1969         $a  = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch, $bodystr);
1970         if (is_array($a)) {
1971                 return array_shift($a);
1972         }
1973         return false;
1974 }
1975
1976 function iil_SortHeaders($a, $field, $flag) {
1977         if (empty($field)) {
1978             $field = 'uid';
1979         }
1980         $field = strtolower($field);
1981         if ($field == 'date' || $field == 'internaldate') {
1982             $field = 'timestamp';
1983         }
1984         if (empty($flag)) {
1985             $flag = 'ASC';
1986         }
1987     
1988         $flag     = strtoupper($flag);
1989         $stripArr = ($field=='subject') ? array('Re: ','Fwd: ','Fw: ','"') : array('"');
1990
1991         $c=count($a);
1992         if ($c > 0) {
1993                 /*
1994                         Strategy:
1995                         First, we'll create an "index" array.
1996                         Then, we'll use sort() on that array, 
1997                         and use that to sort the main array.
1998                 */
1999                 
2000                 // create "index" array
2001                 $index = array();
2002                 reset($a);
2003                 while (list($key, $val)=each($a)) {
2004
2005                         if ($field == 'timestamp') {
2006                                 $data = @strtotime($val->date);
2007                                 if ($data == false) {
2008                                         $data = $val->timestamp;
2009                                 }
2010                         } else {
2011                                 $data = $val->$field;
2012                                 if (is_string($data)) {
2013                                         $data=strtoupper(str_replace($stripArr, '', $data));
2014                                 }
2015                         }
2016                         $index[$key]=$data;
2017                 }
2018                 
2019                 // sort index
2020                 $i = 0;
2021                 if ($flag == 'ASC') {
2022                         asort($index);
2023                 } else {
2024                         arsort($index);
2025                 }
2026         
2027                 // form new array based on index 
2028                 $result = array();
2029                 reset($index);
2030                 while (list($key, $val)=each($index)) {
2031                         $result[$key]=$a[$key];
2032                         $i++;
2033                 }
2034         }
2035         
2036         return $result;
2037 }
2038
2039 function iil_C_Expunge(&$conn, $mailbox) {
2040
2041         if (iil_C_Select($conn, $mailbox)) {
2042                 $c = 0;
2043                 iil_PutLine($conn->fp, "exp1 EXPUNGE");
2044                 do {
2045                         $line=chop(iil_ReadLine($conn->fp, 100));
2046                         if ($line[0] == '*') {
2047                                 $c++;
2048                         }
2049                 } while (!iil_StartsWith($line, 'exp1', true));
2050                 
2051                 if (iil_ParseResult($line) == 0) {
2052                         $conn->selected = ''; //state has changed, need to reselect                     
2053                         //$conn->exists-=$c;
2054                         return $c;
2055                 }
2056                 $conn->error = $line;
2057         }
2058         
2059         return -1;
2060 }
2061
2062 function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod) {
2063         if ($mod != '+' && $mod != '-') {
2064             return -1;
2065         }
2066     
2067         $fp    = $conn->fp;
2068         $flags = $GLOBALS['IMAP_FLAGS'];
2069         
2070         $flag = strtoupper($flag);
2071         $flag = $flags[$flag];
2072     
2073         if (iil_C_Select($conn, $mailbox)) {
2074                 $c = 0;
2075                 iil_PutLine($fp, "flg STORE $messages " . $mod . "FLAGS (" . $flag . ")");
2076                 do {
2077                         $line=chop(iil_ReadLine($fp, 100));
2078                         if ($line[0] == '*') {
2079                             $c++;
2080                         }
2081                 } while (!iil_StartsWith($line, 'flg', true));
2082
2083                 if (iil_ParseResult($line) == 0) {
2084                         iil_C_ExpireCachedItems($conn, $mailbox, $messages);
2085                         return $c;
2086                 }
2087                 $conn->error = $line;
2088                 return -1;
2089         }
2090         $conn->error = 'Select failed';
2091         return -1;
2092 }
2093
2094 function iil_C_Flag(&$conn, $mailbox, $messages, $flag) {
2095         return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '+');
2096 }
2097
2098 function iil_C_Unflag(&$conn, $mailbox, $messages, $flag) {
2099         return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '-');
2100 }
2101
2102 function iil_C_Delete(&$conn, $mailbox, $messages) {
2103         return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '+');
2104 }
2105
2106 function iil_C_Undelete(&$conn, $mailbox, $messages) {
2107         return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '-');
2108 }
2109
2110 function iil_C_Unseen(&$conn, $mailbox, $messages) {
2111         return iil_C_ModFlag($conn, $mailbox, $messages, 'SEEN', '-');
2112 }
2113
2114 function iil_C_Copy(&$conn, $messages, $from, $to) {
2115         $fp = $conn->fp;
2116
2117         if (empty($from) || empty($to)) {
2118             return -1;
2119         }
2120     
2121         if (iil_C_Select($conn, $from)) {
2122                 $c=0;
2123                 
2124                 iil_PutLine($fp, "cpy1 COPY $messages \"".iil_Escape($to)."\"");
2125                 $line=iil_ReadReply($fp);
2126                 return iil_ParseResult($line);
2127         } else {
2128                 return -1;
2129         }
2130 }
2131
2132 function iil_FormatSearchDate($month, $day, $year) {
2133         $month  = (int) $month;
2134         $months = $GLOBALS['IMAP_MONTHS'];
2135         return $day . '-' . $months[$month] . '-' . $year;
2136 }
2137
2138 function iil_C_CountUnseen(&$conn, $folder) {
2139         $index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
2140         if (is_array($index)) {
2141                 if (($cnt = count($index)) && $index[0] != '') {
2142                         return $cnt;
2143                 }
2144         }
2145         return false;
2146 }
2147
2148 function iil_C_UID2ID(&$conn, $folder, $uid) {
2149         if ($uid > 0) {
2150                 $id_a = iil_C_Search($conn, $folder, "UID $uid");
2151                 if (is_array($id_a) && count($id_a) == 1) {
2152                         return $id_a[0];
2153                 }
2154         }
2155         return false;
2156 }
2157
2158 function iil_C_ID2UID(&$conn, $folder, $id) {
2159         $fp = $conn->fp;
2160         if ($id == 0) {
2161             return      -1;
2162         }
2163         $result = -1;
2164         if (iil_C_Select($conn, $folder)) {
2165                 $key = 'FUID';
2166                 if (iil_PutLine($fp, "$key FETCH $id (UID)")) {
2167                         do {
2168                                 $line=chop(iil_ReadLine($fp, 1024));
2169                                 if (eregi("^\* $id FETCH \(UID (.*)\)", $line, $r)) {
2170                                         $result = $r[1];
2171                                 }
2172                         } while (!preg_match("/^$key/", $line));
2173                 }
2174         }
2175         return $result;
2176 }
2177
2178 function iil_C_Search(&$conn, $folder, $criteria) {
2179         $fp = $conn->fp;
2180         if (iil_C_Select($conn, $folder)) {
2181                 $c = 0;
2182                 
2183                 $query = 'srch1 SEARCH ' . chop($criteria);
2184                 if (!iil_PutLineC($fp, $query)) {
2185                         return false;
2186                 }
2187                 do {
2188                         $line=trim(iil_ReadLine($fp, 10000));
2189                         if (eregi("^\* SEARCH", $line)) {
2190                                 $str = trim(substr($line, 8));
2191                                 $messages = explode(' ', $str);
2192                         }
2193                 } while (!iil_StartsWith($line, 'srch1', true));
2194
2195                 $result_code = iil_ParseResult($line);
2196                 if ($result_code == 0) {
2197                     return $messages;
2198                 }
2199                 $conn->error = 'iil_C_Search: ' . $line . "\n";
2200                 return false;   
2201         }
2202         $conn->error = "iil_C_Search: Couldn't select \"$folder\"\n";
2203         return false;
2204 }
2205
2206 function iil_C_Move(&$conn, $messages, $from, $to) {
2207     $fp = $conn->fp;
2208
2209     if (!$from || !$to) {
2210         return -1;
2211     }
2212     $r = iil_C_Copy($conn, $messages, $from,$to);
2213     if ($r==0) {
2214         return iil_C_Delete($conn, $from, $messages);
2215     }
2216     return $r;
2217 }
2218
2219 /**
2220  * Gets the delimiter, for example:
2221  * INBOX.foo -> .
2222  * INBOX/foo -> /
2223  * INBOX\foo -> \
2224  * 
2225  * @return mixed A delimiter (string), or false. 
2226  * @param object $conn The current connection.
2227  * @see iil_Connect()
2228  */
2229 function iil_C_GetHierarchyDelimiter(&$conn) {
2230
2231         global $my_prefs;
2232         
2233         if ($conn->delimiter) {
2234                 return $conn->delimiter;
2235         }
2236         if (!empty($my_prefs['delimiter'])) {
2237             return ($conn->delimiter = $my_prefs['delimiter']);
2238         }
2239     
2240         $fp        = $conn->fp;
2241         $delimiter = false;
2242         
2243         //try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
2244         if (!iil_PutLine($fp, 'ghd LIST "" ""')) {
2245             return false;
2246         }
2247     
2248         do {
2249                 $line=iil_ReadLine($fp, 500);
2250                 if ($line[0] == '*') {
2251                         $line = rtrim($line);
2252                         $a=iil_ExplodeQuotedString(' ', iil_UnEscape($line));
2253                         if ($a[0] == '*') {
2254                             $delimiter = str_replace('"', '', $a[count($a)-2]);
2255                         }
2256                 }
2257         } while (!iil_StartsWith($line, 'ghd', true));
2258
2259         if (strlen($delimiter)>0) {
2260             return $delimiter;
2261         }
2262
2263         //if that fails, try namespace extension
2264         //try to fetch namespace data
2265         iil_PutLine($conn->fp, "ns1 NAMESPACE");
2266         do {
2267                 $line = iil_ReadLine($conn->fp, 1024);
2268                 if (iil_StartsWith($line, '* NAMESPACE')) {
2269                         $i = 0;
2270                         $line = iil_UnEscape($line);
2271                         $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
2272                 }
2273         } while (!iil_StartsWith($line, 'ns1', true));
2274                 
2275         if (!is_array($data)) {
2276             return false;
2277         }
2278     
2279         //extract user space data (opposed to global/shared space)
2280         $user_space_data = $data[0];
2281         if (!is_array($user_space_data)) {
2282             return false;
2283         }
2284     
2285         //get first element
2286         $first_userspace = $user_space_data[0];
2287         if (!is_array($first_userspace)) {
2288             return false;
2289         }
2290     
2291         //extract delimiter
2292         $delimiter = $first_userspace[1];       
2293
2294         return $delimiter;
2295 }
2296
2297 function iil_C_ListMailboxes(&$conn, $ref, $mailbox) {
2298         global $IGNORE_FOLDERS;
2299         
2300         $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
2301                 
2302         $fp = $conn->fp;
2303         
2304         if (empty($mailbox)) {
2305             $mailbox = '*';
2306         }
2307         
2308         if (empty($ref) && $conn->rootdir) {
2309             $ref = $conn->rootdir;
2310         }
2311     
2312         // send command
2313         if (!iil_PutLine($fp, "lmb LIST \"".$ref."\" \"".iil_Escape($mailbox)."\"")) {
2314             return false;
2315         }
2316     
2317         $i = 0;
2318         // get folder list
2319         do {
2320                 $line = iil_ReadLine($fp, 500);
2321                 $line = iil_MultLine($fp, $line);
2322
2323                 $a = explode(' ', $line);
2324                 if (($line[0] == '*') && ($a[1] == 'LIST')) {
2325                         $line = rtrim($line);
2326                         // split one line
2327                         $a = iil_ExplodeQuotedString(' ', $line);
2328                         // last string is folder name
2329                         $folder = trim($a[count($a)-1], '"');
2330             
2331                         if (empty($ignore) || (!empty($ignore)
2332                                 && !eregi($ignore, $folder))) {
2333                                 $folders[$i] = $folder;
2334                         }
2335             
2336                         // second from last is delimiter
2337                         $delim = trim($a[count($a)-2], '"');
2338                         // is it a container?
2339                         $i++;
2340                 }
2341         } while (!iil_StartsWith($line, 'lmb', true));
2342
2343         if (is_array($folders)) {
2344             if (!empty($ref)) {
2345                 // if rootdir was specified, make sure it's the first element
2346                 // some IMAP servers (i.e. Courier) won't return it
2347                 if ($ref[strlen($ref)-1]==$delim)
2348                     $ref = substr($ref, 0, strlen($ref)-1);
2349                 if ($folders[0]!=$ref)
2350                     array_unshift($folders, $ref);
2351             }
2352             return $folders;
2353         } else if (iil_ParseResult($line) == 0) {
2354                 return array('INBOX');
2355         } else {
2356                 $conn->error = $line;
2357                 return false;
2358         }
2359 }
2360
2361 function iil_C_ListSubscribed(&$conn, $ref, $mailbox) {
2362         global $IGNORE_FOLDERS;
2363         
2364         $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
2365         
2366         $fp = $conn->fp;
2367         if (empty($mailbox)) {
2368                 $mailbox = '*';
2369         }
2370         if (empty($ref) && $conn->rootdir) {
2371                 $ref = $conn->rootdir;
2372         }
2373         $folders = array();
2374
2375         // send command
2376         if (!iil_PutLine($fp, 'lsb LSUB "' . $ref . '" "' . iil_Escape($mailbox).'"')) {
2377                 $conn->error = "Couldn't send LSUB command\n";
2378                 return false;
2379         }
2380         
2381         $i = 0;
2382         
2383         // get folder list
2384         do {
2385                 $line = iil_ReadLine($fp, 500);
2386                 $line = iil_MultLine($fp, $line);
2387                 $a    = explode(' ', $line);
2388         
2389                 if (($line[0] == '*') && ($a[1] == 'LSUB' || $a[1] == 'LIST')) {
2390                         $line = rtrim($line);
2391             
2392                         // split one line
2393                         $a = iil_ExplodeQuotedString(' ', $line);
2394             
2395                         // last string is folder name
2396                         //$folder = UTF7DecodeString(str_replace('"', '', $a[count($a)-1]));
2397                         $folder = trim($a[count($a)-1], '"');
2398             
2399                         if ((!in_array($folder, $folders)) && (empty($ignore)
2400                                 || (!empty($ignore) && !eregi($ignore, $folder)))) {
2401                             $folders[$i] = $folder;
2402                         }
2403             
2404                         // second from last is delimiter
2405                         $delim = trim($a[count($a)-2], '"');
2406             
2407                         // is it a container?
2408                         $i++;
2409                 }
2410         } while (!iil_StartsWith($line, 'lsb', true));
2411
2412         if (is_array($folders)) {
2413             if (!empty($ref)) {
2414                 // if rootdir was specified, make sure it's the first element
2415                 // some IMAP servers (i.e. Courier) won't return it
2416                 if ($ref[strlen($ref)-1]==$delim) {
2417                     $ref = substr($ref, 0, strlen($ref)-1);
2418                 }
2419                 if ($folders[0]!=$ref) {
2420                     array_unshift($folders, $ref);
2421                 }
2422             }
2423             return $folders;
2424         }
2425         $conn->error = $line;
2426         return false;
2427 }
2428
2429 function iil_C_Subscribe(&$conn, $folder) {
2430         $fp = $conn->fp;
2431
2432         $query = 'sub1 SUBSCRIBE "' . iil_Escape($folder). '"';
2433         iil_PutLine($fp, $query);
2434
2435         $line = trim(iil_ReadLine($fp, 10000));
2436         return iil_ParseResult($line);
2437 }
2438
2439 function iil_C_UnSubscribe(&$conn, $folder) {
2440         $fp = $conn->fp;
2441
2442         $query = 'usub1 UNSUBSCRIBE "' . iil_Escape($folder) . '"';
2443         iil_PutLine($fp, $query);
2444     
2445         $line = trim(iil_ReadLine($fp, 10000));
2446         return iil_ParseResult($line);
2447 }
2448
2449 function iil_C_FetchMIMEHeaders(&$conn, $mailbox, $id, $parts) {
2450         
2451         $fp     = $conn->fp;
2452
2453         if (!iil_C_Select($conn, $mailbox)) {
2454                 return false;
2455         }
2456         
2457         $result = false;
2458         $parts = (array) $parts;
2459         $key = 'fmh0';
2460         $peeks = '';
2461         $idx = 0;
2462
2463         // format request
2464         foreach($parts as $part)
2465                 $peeks[] = "BODY.PEEK[$part.MIME]";
2466         
2467         $request = "$key FETCH $id (" . implode(' ', $peeks) . ')';
2468
2469         // send request
2470         if (!iil_PutLine($fp, $request)) {
2471             return false;
2472         }
2473         
2474         do {
2475                 $line = iil_ReadLine($fp, 1000);
2476                 $line = iil_MultLine($fp, $line);
2477
2478                 if (preg_match('/BODY\[([0-9\.]+)\.MIME\]/', $line, $matches)) {
2479                         $idx = $matches[1];
2480                         $result[$idx] = preg_replace('/^(\* '.$id.' FETCH \()?\s*BODY\['.$idx.'\.MIME\]\s+/', '', $line);
2481                         $result[$idx] = trim($result[$idx], '"');
2482                         $result[$idx] = rtrim($result[$idx], "\t\r\n\0\x0B");
2483                 }
2484         } while (!iil_StartsWith($line, $key, true));
2485
2486         return $result;
2487 }
2488
2489 function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part) {
2490
2491         $part = empty($part) ? 'HEADER' : $part.'.MIME';
2492
2493         return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1);
2494 }
2495
2496 function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part='', $mode=1, $file=NULL) {
2497         /* modes:
2498         1: return string (or write to $file pointer)
2499         2: print
2500         3: base64 and print (or write to $file pointer)
2501         */
2502         
2503         $fp     = $conn->fp;
2504         $result = false;
2505         
2506         if (iil_C_Select($conn, $mailbox)) {
2507                 $reply_key = '* ' . $id;
2508         
2509                 // format request
2510                 $key     = 'ftch' . ($c++) . ' ';
2511                 $request = $key . "FETCH $id (BODY.PEEK[$part])";
2512                 // send request
2513                 if (!iil_PutLine($fp, $request)) {
2514                     return false;
2515                 }
2516         
2517                 // receive reply line
2518                 do {
2519                         $line = chop(iil_ReadLine($fp, 1000));
2520                         $a    = explode(' ', $line);
2521                 } while ($a[2] != 'FETCH');
2522                 $len = strlen($line);
2523
2524                 // handle empty "* X FETCH ()" response
2525                 if ($line[$len-1] == ')' && $line[$len-2] != '(') {
2526                         // one line response, get everything between first and last quotes
2527                         if (substr($line, -4, 3) == 'NIL') {
2528                                 // NIL response
2529                                 $result = '';
2530                         } else {
2531                                 $from = strpos($line, '"') + 1;
2532                                 $to   = strrpos($line, '"');
2533                                 $len  = $to - $from;
2534                                 $result = substr($line, $from, $len);
2535                         }
2536             
2537                         if ($mode == 2) {
2538                                 echo $result;
2539                         } else if ($mode == 3) {
2540                                 if ($file)
2541                                         fwrite($file, base64_decode($result));
2542                                 else
2543                                         echo base64_decode($result);
2544                         }                            
2545                 } else if ($line[$len-1] == '}') {
2546                         //multi-line request, find sizes of content and receive that many bytes
2547                         $from     = strpos($line, '{') + 1;
2548                         $to       = strrpos($line, '}');
2549                         $len      = $to - $from;
2550                         $sizeStr  = substr($line, $from, $len);
2551                         $bytes    = (int)$sizeStr;
2552                         $prev     = '';
2553                         
2554                         while ($bytes > 0) {
2555                                 $line      = iil_ReadLine($fp, 1024);
2556                                 $len       = strlen($line);
2557                 
2558                                 if ($len > $bytes) {
2559                                         $line = substr($line, 0, $bytes);
2560                                 }
2561                                 $bytes -= strlen($line);
2562
2563                                 $line = rtrim($line, "\t\r\n\0\x0B");
2564
2565                                 if ($mode == 1) {
2566                                         if ($file)
2567                                                 fwrite($file, $line . "\n");
2568                                         else
2569                                                 $result .= $line . "\n";
2570                                 } else if ($mode == 2) {
2571                                         echo $line . "\n";
2572                                 } else if ($mode == 3) {
2573                                         // create chunks with proper length for base64 decoding
2574                                         $line = $prev.$line;
2575                                         $length = strlen($line);
2576                                         if ($length % 4) {
2577                                                 $length = floor($length / 4) * 4;
2578                                                 $prev = substr($line, $length);
2579                                                 $line = substr($line, 0, $length);
2580                                         }
2581                                         else
2582                                                 $prev = '';
2583
2584                                         if ($file)
2585                                                 fwrite($file, base64_decode($line));
2586                                         else
2587                                                 echo base64_decode($line);
2588                                 }
2589                         }
2590                 }
2591                 // read in anything up until last line
2592                 do {
2593                         $line = iil_ReadLine($fp, 1024);
2594                 } while (!iil_StartsWith($line, $key, true));
2595         
2596                 if ($mode == 3 && $file) {
2597                         return true;
2598                 }
2599         
2600                 if ($result) {
2601                         $result = rtrim($result, "\t\r\n\0\x0B");
2602                         if ($file) {
2603                                 fwrite($file, $result);
2604                                 return true;
2605                         }       
2606                         return $result; // substr($result, 0, strlen($result)-1);
2607                 }
2608                 
2609                 return false;
2610         } else {
2611                 echo 'Select failed.';
2612         }
2613     
2614         if ($mode==1) {
2615                 if ($file) {
2616                         fwrite($file, $result);
2617                         return true;
2618                 }
2619                 return $result;
2620         }
2621         
2622         return false;
2623 }
2624
2625 function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part, $file=NULL) {
2626         return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1, $file);
2627 }
2628
2629 function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part) {
2630         iil_C_HandlePartBody($conn, $mailbox, $id, $part, 2);
2631 }
2632
2633 function iil_C_PrintBase64Body(&$conn, $mailbox, $id, $part) {
2634         iil_C_HandlePartBody($conn, $mailbox, $id, $part, 3);
2635 }
2636
2637 function iil_C_CreateFolder(&$conn, $folder) {
2638         $fp = $conn->fp;
2639         if (iil_PutLine($fp, 'c CREATE "' . iil_Escape($folder) . '"')) {
2640                 do {
2641                         $line=iil_ReadLine($fp, 300);
2642                 } while ($line[0] != 'c');
2643         $conn->error = $line;
2644                 return (iil_ParseResult($line) == 0);
2645         }
2646         return false;
2647 }
2648
2649 function iil_C_RenameFolder(&$conn, $from, $to) {
2650         $fp = $conn->fp;
2651         if (iil_PutLine($fp, 'r RENAME "' . iil_Escape($from) . '" "' . iil_Escape($to) . '"')) {
2652                 do {
2653                         $line = iil_ReadLine($fp, 300);
2654                 } while ($line[0] != 'r');
2655                 return (iil_ParseResult($line) == 0);
2656         }
2657         return false;
2658 }
2659
2660 function iil_C_DeleteFolder(&$conn, $folder) {
2661         $fp = $conn->fp;
2662         if (iil_PutLine($fp, 'd DELETE "' . iil_Escape($folder). '"')) {
2663                 do {
2664                         $line=iil_ReadLine($fp, 300);
2665                 } while ($line[0] != 'd');
2666                 return (iil_ParseResult($line) == 0);
2667         }
2668         $conn->error = "Couldn't send command\n";
2669         return false;
2670 }
2671
2672 function iil_C_Append(&$conn, $folder, &$message) {
2673         if (!$folder) {
2674                 return false;
2675         }
2676         $fp = $conn->fp;
2677
2678         $message = str_replace("\r", '', $message);
2679         $message = str_replace("\n", "\r\n", $message);         
2680
2681         $len = strlen($message);
2682         if (!$len) {
2683                 return false;
2684         }
2685
2686         $request = 'A APPEND "' . iil_Escape($folder) .'" (\\Seen) {' . $len . '}';
2687     
2688         if (iil_PutLine($fp, $request)) {
2689                 $line=iil_ReadLine($fp, 100);           
2690                 $sent = fwrite($fp, $message."\r\n");
2691                 do {
2692                         $line=iil_ReadLine($fp, 1000);
2693                 } while ($line[0] != 'A');
2694         
2695                 $result = (iil_ParseResult($line) == 0);
2696                 if (!$result) {
2697                     $conn->error .= $line . "\n";
2698                 }
2699                 return $result;
2700         }
2701
2702         $conn->error .= "Couldn't send command \"$request\"\n";
2703         return false;
2704 }
2705
2706 function iil_C_AppendFromFile(&$conn, $folder, $path) {
2707         if (!$folder) {
2708             return false;
2709         }
2710     
2711         //open message file
2712         $in_fp = false;                         
2713         if (file_exists(realpath($path))) {
2714                 $in_fp = fopen($path, 'r');
2715         }
2716         if (!$in_fp) { 
2717                 $conn->error .= "Couldn't open $path for reading\n";
2718                 return false;
2719         }
2720         
2721         $fp  = $conn->fp;
2722         $len = filesize($path);
2723         if (!$len) {
2724                 return false;
2725         }
2726     
2727         //send APPEND command
2728         $request    = 'A APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
2729         $bytes_sent = 0;
2730         if (iil_PutLine($fp, $request)) {
2731                 $line = iil_ReadLine($fp, 100);
2732                                 
2733                 //send file
2734                 while (!feof($in_fp)) {
2735                         $buffer      = fgets($in_fp, 4096);
2736                         $bytes_sent += strlen($buffer);
2737                         iil_PutLine($fp, $buffer, false);
2738                 }
2739                 fclose($in_fp);
2740
2741                 iil_PutLine($fp, '');
2742
2743                 //read response
2744                 do {
2745                         $line = iil_ReadLine($fp, 1000);
2746                 } while ($line[0] != 'A');
2747                         
2748                 $result = (iil_ParseResult($line) == 0);
2749                 if (!$result) {
2750                     $conn->error .= $line . "\n";
2751                 }
2752         
2753                 return $result;
2754         }
2755         
2756         $conn->error .= "Couldn't send command \"$request\"\n";
2757         return false;
2758 }
2759
2760 function iil_C_FetchStructureString(&$conn, $folder, $id) {
2761         $fp     = $conn->fp;
2762         $result = false;
2763         
2764         if (iil_C_Select($conn, $folder)) {
2765                 $key = 'F1247';
2766
2767                 if (iil_PutLine($fp, "$key FETCH $id (BODYSTRUCTURE)")) {
2768                         do {
2769                                 $line = iil_ReadLine($fp, 5000);
2770                                 $line = iil_MultLine($fp, $line);
2771                                 list(, $index, $cmd, $rest) = explode(' ', $line);
2772                                 if ($cmd != 'FETCH' || $index == $id || preg_match("/^$key/", $line))
2773                                         $result .= $line;
2774                         } while (!preg_match("/^$key/", $line));
2775
2776                         $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -(strlen($result)-strrpos($result, $key)+1)));
2777                 }
2778         }
2779         return $result;
2780 }
2781
2782 function iil_C_PrintSource(&$conn, $folder, $id, $part) {
2783         $header = iil_C_FetchPartHeader($conn, $folder, $id, $part);
2784         //echo str_replace("\r", '', $header);
2785         echo $header;
2786         echo iil_C_PrintPartBody($conn, $folder, $id, $part);
2787 }
2788
2789 function iil_C_GetQuota(&$conn) {
2790 /*
2791  * GETQUOTAROOT "INBOX"
2792  * QUOTAROOT INBOX user/rchijiiwa1
2793  * QUOTA user/rchijiiwa1 (STORAGE 654 9765)
2794  * OK Completed
2795  */
2796         $fp         = $conn->fp;
2797         $result     = false;
2798         $quota_lines = array();
2799         
2800         // get line(s) containing quota info
2801         if (iil_PutLine($fp, 'QUOT1 GETQUOTAROOT "INBOX"')) {
2802                 do {
2803                         $line=chop(iil_ReadLine($fp, 5000));
2804                         if (iil_StartsWith($line, '* QUOTA ')) {
2805                                 $quota_lines[] = $line;
2806                         }
2807                 } while (!iil_StartsWith($line, 'QUOT1', true));
2808         }
2809         
2810         // return false if not found, parse if found
2811         $min_free = PHP_INT_MAX;
2812         foreach ($quota_lines as $key => $quota_line) {
2813                 $quota_line   = eregi_replace('[()]', '', $quota_line);
2814                 $parts        = explode(' ', $quota_line);
2815                 $storage_part = array_search('STORAGE', $parts);
2816                 
2817                 if (!$storage_part) continue;
2818         
2819                 $used   = intval($parts[$storage_part+1]);
2820                 $total  = intval($parts[$storage_part+2]);
2821                 $free   = $total - $used; 
2822         
2823                 // return lowest available space from all quotas
2824                 if ($free < $min_free) { 
2825                         $min_free = $free; 
2826                         $result['used']    = $used;
2827                         $result['total']   = $total;
2828                         $result['percent'] = min(100, round(($used/max(1,$total))*100));
2829                         $result['free']    = 100 - $result['percent'];
2830                 }
2831         }
2832         return $result;
2833 }
2834
2835 function iil_C_ClearFolder(&$conn, $folder) {
2836         $num_in_trash = iil_C_CountMessages($conn, $folder);
2837         if ($num_in_trash > 0) {
2838                 iil_C_Delete($conn, $folder, '1:' . $num_in_trash);
2839         }
2840         return (iil_C_Expunge($conn, $folder) >= 0);
2841 }
2842
2843 ?>