source: subversion/trunk/roundcubemail/program/lib/imap.inc @ 2377

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