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

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