source: github/program/lib/imap.inc @ a9a8ef2

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since a9a8ef2 was a9a8ef2, checked in by thomascube <thomas@…>, 5 years ago

Use INTERNALDATE if Date: header is missing; convert some spaces back to tabs

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