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

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