source: github/program/lib/imap.inc @ 06583c3

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