source: github/program/lib/imap.inc @ 76265ef

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