source: github/program/lib/imap.inc @ 5bc0ab1

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

Distinguish ssl and tls for imap connections (#1484667)

  • Property mode set to 100644
File size: 65.4 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 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                        } while (trim($line[0]) != ')' && strncmp($line, $key, strlen($key)));  // patch from "Maksim Rubis" <siburny@hotmail.com>
1568                       
1569            if (strncmp($line, $key, strlen($key))) {
1570                        //process header, fill iilBasicHeader obj.
1571                        //      initialize
1572                        if (is_array($headers)) {
1573                                reset($headers);
1574                                while ( list($k, $bar) = each($headers) ) $headers[$k] = '';
1575                        }
1576   
1577                        //      create array with header field:data
1578                        while ( list($lines_key, $str) = each($lines) ) {
1579                                list($field, $string) = iil_SplitHeaderLine($str);
1580                   
1581                                $field = strtolower($field);
1582                               
1583                                switch ($field) {
1584                                        case 'date';
1585                                                $result[$id]->date      = $string;
1586                                                $result[$id]->timestamp = iil_StrToTime($string);
1587                                                break;
1588                                        case 'from':
1589                                                $result[$id]->from = $string;
1590                                                break;
1591                                        case 'to':
1592                                                $result[$id]->to = str_replace("\n", " ", $string);
1593                                                break;
1594                                        case 'subject':
1595                                                $result[$id]->subject = str_replace("\n", '', $string);
1596                                                break;
1597                                        case 'reply-to':
1598                                                $result[$id]->replyto = str_replace("\n", " ", $string);
1599                                                break;
1600                                        case 'cc':
1601                                                $result[$id]->cc = str_replace("\n", " ", $string);
1602                                                break;
1603                                        case 'bcc':
1604                                                $result[$id]->bcc = str_replace("\n", " ", $string);
1605                                                break;
1606                                        case 'content-transfer-encoding':
1607                                                $result[$id]->encoding = str_replace("\n", " ", $string);
1608                                                break;
1609                                        case 'content-type':
1610                                                $ctype_parts = explode(";", $string);
1611                                                $result[$id]->ctype = array_shift($ctype_parts);
1612                                                foreach ($ctype_parts as $ctype_add) {
1613                            if (preg_match('/charset="?([a-z0-9\-\.\_]+)"?/i',
1614                                $ctype_add, $regs)) {
1615                                $result[$id]->charset = $regs[1];
1616                                                        }
1617                        }
1618                        break;
1619                                        case 'in-reply-to':
1620                                                $result[$id]->in_reply_to = ereg_replace("[\n<>]", '', $string);
1621                                                break;
1622                                        case 'references':
1623                                                $result[$id]->references = $string;
1624                                                break;
1625                                        case 'return-receipt-to':
1626                                        case 'disposition-notification-to':
1627                                        case 'x-confirm-reading-to':
1628                                                $result[$id]->mdn_to = str_replace("\n", " ", $string);
1629                                                break;
1630                                        case 'message-id':
1631                                                $result[$id]->messageID = $string;
1632                                                break;
1633                                        case 'x-priority':
1634                                                if (preg_match('/^(\d+)/', $string, $matches))
1635                                                        $result[$id]->priority = intval($matches[1]);
1636                                                break;
1637                                } // end switch ()
1638                        } // end while ()
1639                    } else {
1640                            $a=explode(' ', $line);
1641                    }
1642                }
1643        } while (strcmp($a[0], $key)!=0);
1644               
1645        /*
1646                FETCH uid, size, flags
1647                Sample reply line: "* 3 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen \Deleted))"
1648        */
1649        $command_key = 'fh' . ($c++);
1650   
1651        $request  = $command_key . $prefix;
1652    $request .= " FETCH $message_set (UID RFC822.SIZE FLAGS INTERNALDATE)\r\n";
1653        if (!fputs($fp, $request)) {
1654            return false;
1655        }
1656    do {
1657                $line=chop(iil_ReadLine($fp, 200));
1658                //$a = explode(' ', $line);
1659                //if (($line[0]=="*") && ($a[2]=="FETCH")) {
1660                if ($line[0]=="*") {
1661                        //echo "<!-- $line //-->\n";
1662                        //get outter most parens
1663                        $open_pos = strpos($line, "(") + 1;
1664                        $close_pos = strrpos($line, ")");
1665                        if ($open_pos && $close_pos) {
1666                                //extract ID from pre-paren
1667                                $pre_str = substr($line, 0, $open_pos);
1668                                $pre_a = explode(' ', $line);
1669                                $id = $pre_a[1];
1670                               
1671                                //get data
1672                                $len = $close_pos - $open_pos;
1673                                $str = substr($line, $open_pos, $len);
1674                               
1675                                //swap parents with quotes, then explode
1676                                $str = eregi_replace("[()]", "\"", $str);
1677                                $a = iil_ExplodeQuotedString(' ', $str);
1678                               
1679                                //did we get the right number of replies?
1680                                $parts_count = count($a);
1681                                if ($parts_count>=8) {
1682                                        for ($i=0;$i<$parts_count;$i=$i+2) {
1683                                                if (strcasecmp($a[$i],"UID") == 0) $result[$id]->uid=$a[$i+1];
1684                                                else if (strcasecmp($a[$i],"RFC822.SIZE") == 0) $result[$id]->size=$a[$i+1];
1685                                                else if (strcasecmp($a[$i],"INTERNALDATE") == 0) $time_str = $a[$i+1];
1686                                                else if (strcasecmp($a[$i],"FLAGS") == 0) $flags_str = $a[$i+1];
1687                                        }
1688
1689                                        // process flags
1690                                        $flags_str = eregi_replace('[\\\"]', '', $flags_str);
1691                                        $flags_a = explode(' ', $flags_str);
1692                                        //echo "<!-- ID: $id FLAGS: ".implode(",", $flags_a)." //-->\n";
1693                                       
1694                                        if (is_array($flags_a)) {
1695                                                reset($flags_a);
1696                                                while (list($key,$val)=each($flags_a)) {
1697                                                        if (strcasecmp($val,'Seen') == 0) {
1698                                                            $result[$id]->seen = true;
1699                                                        } else if (strcasecmp($val, 'Deleted') == 0) {
1700                                                            $result[$id]->deleted=true;
1701                                                        } else if (strcasecmp($val, 'Recent') == 0) {
1702                                                            $result[$id]->recent = true;
1703                                                        } else if (strcasecmp($val, 'Answered') == 0) {
1704                                                            $result[$id]->answered = true;
1705                                                        } else if (strcasecmp($val, "\$MDNSent") == 0) {
1706                                                            $result[$id]->mdn_sent = true;
1707                            }
1708                                                }
1709                                                $result[$id]->flags = $flags_a;
1710                                        }
1711                       
1712                                        // if time is gmt...   
1713                                        $time_str = str_replace('GMT','+0000',$time_str);
1714                                       
1715                                        //get timezone
1716                                        $time_str      = substr($time_str, 0, -1);
1717                                        $time_zone_str = substr($time_str, -5); //extract timezone
1718                                        $time_str      = substr($time_str, 1, -6); //remove quotes
1719                                        $time_zone     = (float)substr($time_zone_str, 1, 2); //get first two digits
1720                                        if ($time_zone_str[3] != '0') {
1721                                            $time_zone += 0.5;  //handle half hour offset
1722                                        }
1723                    if ($time_zone_str[0] == '-') {
1724                        $time_zone = $time_zone * -1.0; //minus?
1725                                        }
1726                    $result[$id]->internaldate = $time_str;
1727                                       
1728                                        if ($IMAP_USE_INTERNAL_DATE) {
1729                                                //calculate timestamp
1730                                                $timestamp     = strtotime($time_str); //return's server's time
1731                                                $na_timestamp  = $timestamp;
1732                                                $timestamp    -= $time_zone * 3600; //compensate for tz, get GMT
1733                                               
1734                                                $result[$id]->timestamp = $timestamp;
1735                                        }
1736                                               
1737                                        if ($conn->do_cache) {
1738                                                $uid = $result[$id]->uid;
1739                                                $conn->cache[$mailbox][$uid] = $result[$id];
1740                                                $conn->cache_dirty[$mailbox] = true;
1741                                        }
1742                                        //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";
1743                                } else {
1744                                        //echo "<!-- ERROR: $id : $str //-->\n";
1745                                }
1746                        }
1747                }
1748        } while (strpos($line, $command_key) === false);
1749               
1750        return $result;
1751}
1752
1753
1754function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false) {
1755        $fp = $conn->fp;
1756        $a  = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch);
1757        if (is_array($a)) {
1758            return array_shift($a);
1759    }
1760        return false;
1761}
1762
1763
1764function iil_SortHeaders($a, $field, $flag) {
1765        if (empty($field)) {
1766            $field = 'uid';
1767    }
1768        $field = strtolower($field);
1769        if ($field == 'date' || $field == 'internaldate') {
1770            $field = 'timestamp';
1771    }
1772        if (empty($flag)) {
1773            $flag = 'ASC';
1774    }
1775   
1776        $flag     = strtoupper($flag);
1777        $stripArr = ($field=='subject') ? array('Re: ','Fwd: ','Fw: ','"') : array('"');
1778
1779        $c=count($a);
1780        if ($c>0) {
1781                /*
1782                        Strategy:
1783                        First, we'll create an "index" array.
1784                        Then, we'll use sort() on that array,
1785                        and use that to sort the main array.
1786                */
1787               
1788                // create "index" array
1789                $index = array();
1790                reset($a);
1791                while (list($key, $val)=each($a)) {
1792
1793                        if ($field == 'timestamp') {
1794                                $data = @strtotime($val->date);
1795                                if ($data == false) {
1796                                        $data = $val->timestamp;
1797                }
1798                        } else {
1799                                $data = $val->$field;
1800                                if (is_string($data)) {
1801                                        $data=strtoupper(str_replace($stripArr, '', $data));
1802                }
1803                        }
1804                        $index[$key]=$data;
1805                }
1806               
1807                // sort index
1808                $i=0;
1809                if ($flag == 'ASC') {
1810                    asort($index);
1811        } else {
1812            arsort($index);
1813                }
1814       
1815                // form new array based on index
1816                $result = array();
1817                reset($index);
1818                while (list($key, $val)=each($index)) {
1819                        $result[$key]=$a[$key];
1820                        $i++;
1821                }
1822        }
1823       
1824        return $result;
1825}
1826
1827function iil_C_Expunge(&$conn, $mailbox) {
1828        $fp = $conn->fp;
1829        if (iil_C_Select($conn, $mailbox)) {
1830                $c=0;
1831                fputs($fp, "exp1 EXPUNGE\r\n");
1832                do {
1833                        $line=chop(iil_ReadLine($fp, 100));
1834                        if ($line[0]=="*") $c++;
1835                } while (!iil_StartsWith($line, 'exp1'));
1836               
1837                if (iil_ParseResult($line) == 0) {
1838                        $conn->selected = ''; //state has changed, need to reselect                     
1839                        //$conn->exists-=$c;
1840                        return $c;
1841                }
1842                $conn->error = $line;
1843        }
1844       
1845        return -1;
1846}
1847
1848function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod) {
1849        if ($mod != '+' && $mod != '-') {
1850            return -1;
1851        }
1852   
1853        $fp    = $conn->fp;
1854        $flags = array(
1855        'SEEN'     => '\\Seen',
1856        'DELETED'  => '\\Deleted',
1857        'RECENT'   => '\\Recent',
1858        'ANSWERED' => '\\Answered',
1859        'DRAFT'    => '\\Draft',
1860        'FLAGGED'  => '\\Flagged',
1861        'MDNSENT'  => "\$MDNSent");
1862       
1863        $flag = strtoupper($flag);
1864        $flag = $flags[$flag];
1865   
1866        if (iil_C_Select($conn, $mailbox)) {
1867                $c=0;
1868                fputs($fp, "flg STORE $messages " . $mod . "FLAGS (" . $flag . ")\r\n");
1869                do {
1870                        $line=chop(iil_ReadLine($fp, 100));
1871                        if ($line[0] == '*') {
1872                            $c++;
1873            }
1874                } while (!iil_StartsWith($line, 'flg'));
1875
1876                if (iil_ParseResult($line) == 0) {
1877                        iil_C_ExpireCachedItems($conn, $mailbox, $messages);
1878                        return $c;
1879                }
1880                $conn->error = $line;
1881                return -1;
1882        }
1883        $conn->error = 'Select failed';
1884        return -1;
1885}
1886
1887function iil_C_Flag(&$conn, $mailbox, $messages, $flag) {
1888        return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '+');
1889}
1890
1891function iil_C_Unflag(&$conn, $mailbox, $messages, $flag) {
1892        return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '-');
1893}
1894
1895function iil_C_Delete(&$conn, $mailbox, $messages) {
1896        return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '+');
1897}
1898
1899function iil_C_Undelete(&$conn, $mailbox, $messages) {
1900        return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '-');
1901}
1902
1903
1904function iil_C_Unseen(&$conn, $mailbox, $messages) {
1905        return iil_C_ModFlag($conn, $mailbox, $messages, 'SEEN', '-');
1906}
1907
1908
1909function iil_C_Copy(&$conn, $messages, $from, $to) {
1910        $fp = $conn->fp;
1911
1912        if (empty($from) || empty($to)) {
1913            return -1;
1914    }
1915   
1916        if (iil_C_Select($conn, $from)) {
1917                $c=0;
1918               
1919                fputs($fp, "cpy1 COPY $messages \"$to\"\r\n");
1920                $line=iil_ReadReply($fp);
1921                return iil_ParseResult($line);
1922        } else {
1923                return -1;
1924        }
1925}
1926
1927function iil_FormatSearchDate($month, $day, $year) {
1928        $month  = (int) $month;
1929    $months = $GLOBALS['IMAP_MONTHS'];
1930        /* $months=array(
1931                        1=>"Jan", 2=>"Feb", 3=>"Mar", 4=>"Apr",
1932                        5=>"May", 6=>"Jun", 7=>"Jul", 8=>"Aug",
1933                        9=>"Sep", 10=>"Oct", 11=>"Nov", 12=>"Dec"
1934                        );
1935    */
1936        return $day . '-' . $months[$month] . '-' . $year;
1937}
1938
1939function iil_C_CountUnseen(&$conn, $folder) {
1940        $index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
1941        if (is_array($index)) {
1942                $str = implode(',', $index);
1943                if (empty($str)) {
1944                    return false;
1945        }
1946                return count($index);
1947        }
1948    return false;
1949}
1950
1951function iil_C_UID2ID(&$conn, $folder, $uid) {
1952        if ($uid > 0) {
1953                $id_a = iil_C_Search($conn, $folder, "UID $uid");
1954                if (is_array($id_a)) {
1955                        $count = count($id_a);
1956                        if ($count > 1) {
1957                            return false;
1958            }
1959                        return $id_a[0];
1960                }
1961        }
1962        return false;
1963}
1964
1965function iil_C_ID2UID(&$conn, $folder, $id) {
1966        $fp = $conn->fp;
1967        if ($id == 0) {
1968            return      -1;
1969    }
1970    $result = -1;
1971        if (iil_C_Select($conn, $folder)) {
1972                $key = 'FUID';
1973                if (fputs($fp, "$key FETCH $id (UID)\r\n")) {
1974                        do {
1975                                $line=chop(iil_ReadLine($fp, 1024));
1976                                if (eregi("^\* $id FETCH \(UID (.*)\)", $line, $r)) {
1977                                        $result = $r[1];
1978                                }
1979                        } while (!preg_match("/^$key/", $line));
1980                }
1981        }
1982        return $result;
1983}
1984
1985function iil_C_Search(&$conn, $folder, $criteria) {
1986        $fp = $conn->fp;
1987        if (iil_C_Select($conn, $folder)) {
1988                $c=0;
1989               
1990                $query = 'srch1 SEARCH ' . chop($criteria) . "\r\n";
1991                fputs($fp, $query);
1992                do {
1993                        $line=trim(chop(iil_ReadLine($fp, 10000)));
1994                        if (eregi("^\* SEARCH", $line)) {
1995                                $str = trim(substr($line, 8));
1996                                $messages = explode(' ', $str);
1997                        }
1998                } while (!iil_StartsWith($line, "srch1"));
1999               
2000                $result_code=iil_ParseResult($line);
2001                if ($result_code==0) {
2002                    return $messages;
2003                }
2004                $conn->error = 'iil_C_Search: ' . $line . "\n";
2005                return false;
2006               
2007        }
2008        $conn->error = "iil_C_Search: Couldn't select \"$folder\"\n";
2009        return false;
2010}
2011
2012function iil_C_Move(&$conn, $messages, $from, $to) {
2013        $fp = $conn->fp;
2014       
2015        if (!$from || !$to) {
2016            return -1;
2017        }
2018   
2019        $r=iil_C_Copy($conn, $messages, $from,$to);
2020        if ($r==0) {
2021                return iil_C_Delete($conn, $from, $messages);
2022        }
2023    return $r;
2024}
2025
2026/**
2027 * Gets the delimiter, for example:
2028 * INBOX.foo -> .
2029 * INBOX/foo -> /
2030 * INBOX\foo -> \
2031 *
2032 * @return mixed A delimiter (string), or false.
2033 * @param object $conn The current connection.
2034 * @see iil_Connect()
2035 */
2036function iil_C_GetHierarchyDelimiter(&$conn) {
2037        if ($conn->delimiter) {
2038        return $conn->delimiter;
2039        }
2040   
2041        $fp        = $conn->fp;
2042        $delimiter = false;
2043       
2044        //try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
2045        if (!fputs($fp, 'ghd LIST "" ""' . "\r\n")) {
2046            return false;
2047    }
2048   
2049        do {
2050                $line=iil_ReadLine($fp, 500);
2051                if ($line[0] == '*') {
2052                        $line = rtrim($line);
2053                        $a=iil_ExplodeQuotedString(' ', $line);
2054                        if ($a[0] == '*') {
2055                            $delimiter = str_replace('"', '', $a[count($a)-2]);
2056            }
2057                }
2058        } while (!iil_StartsWith($line, 'ghd'));
2059
2060        if (strlen($delimiter)>0) {
2061            return $delimiter;
2062        }
2063   
2064        //if that fails, try namespace extension
2065        //try to fetch namespace data
2066        fputs($conn->fp, "ns1 NAMESPACE\r\n");
2067        do {
2068                $line = iil_ReadLine($conn->fp, 1024);
2069                if (iil_StartsWith($line, '* NAMESPACE')) {
2070                        $i = 0;
2071                        $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
2072                }
2073        } while (!iil_StartsWith($line, 'ns1'));
2074               
2075        if (!is_array($data)) {
2076            return false;
2077        }
2078   
2079        //extract user space data (opposed to global/shared space)
2080        $user_space_data = $data[0];
2081        if (!is_array($user_space_data)) {
2082            return false;
2083        }
2084   
2085        //get first element
2086        $first_userspace = $user_space_data[0];
2087        if (!is_array($first_userspace)) {
2088            return false;
2089    }
2090   
2091        //extract delimiter
2092        $delimiter = $first_userspace[1];       
2093
2094        return $delimiter;
2095}
2096
2097function iil_C_ListMailboxes(&$conn, $ref, $mailbox) {
2098        global $IGNORE_FOLDERS;
2099       
2100        $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
2101               
2102        $fp = $conn->fp;
2103        if (empty($mailbox)) {
2104            $mailbox = '*';
2105    }
2106        if (empty($ref) && $conn->rootdir) {
2107            $ref = $conn->rootdir;
2108        }
2109   
2110    // send command
2111        if (!fputs($fp, "lmb LIST \"".$ref."\" \"$mailbox\"\r\n")) {
2112            return false;
2113    }
2114   
2115        $i = 0;
2116    // get folder list
2117        do {
2118                $line = iil_ReadLine($fp, 500);
2119                $line = iil_MultLine($fp, $line);
2120
2121                $a = explode(' ', $line);
2122                if (($line[0] == '*') && ($a[1] == 'LIST')) {
2123                        $line = rtrim($line);
2124            // split one line
2125                        $a = iil_ExplodeQuotedString(' ', $line);
2126           
2127            // last string is folder name
2128                        $folder = str_replace('"', '', $a[count($a)-1]);
2129           
2130            if (empty($ignore) || (!empty($ignore)
2131                && !eregi($ignore, $folder))) {
2132                $folders[$i] = $folder;
2133            }
2134           
2135            // second from last is delimiter
2136            $delim = str_replace('"', '', $a[count($a)-2]);
2137            // is it a container?
2138            $i++;
2139                }
2140        } while (!iil_StartsWith($line, 'lmb'));
2141
2142        if (is_array($folders)) {
2143        if (!empty($ref)) {
2144            // if rootdir was specified, make sure it's the first element
2145            // some IMAP servers (i.e. Courier) won't return it
2146            if ($ref[strlen($ref)-1]==$delim) $ref = substr($ref, 0, strlen($ref)-1);
2147            if ($folders[0]!=$ref) array_unshift($folders, $ref);
2148        }
2149        return $folders;
2150        }else if (iil_ParseResult($line) == 0) {
2151                return array('INBOX');
2152        } else {
2153                $conn->error = $line;
2154                return false;
2155        }
2156}
2157
2158
2159function iil_C_ListSubscribed(&$conn, $ref, $mailbox) {
2160        global $IGNORE_FOLDERS;
2161       
2162        $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
2163       
2164        $fp = $conn->fp;
2165        if (empty($mailbox)) {
2166                $mailbox = '*';
2167        }
2168        if (empty($ref) && $conn->rootdir) {
2169                $ref = $conn->rootdir;
2170        }
2171        $folders = array();
2172
2173    // send command
2174        if (!fputs($fp, 'lsb LSUB "' . $ref . '" "' . $mailbox.'"' . "\r\n")) {
2175                $conn->error = "Couldn't send LSUB command\n";
2176                return false;
2177        }
2178       
2179        $i = 0;
2180       
2181    // get folder list
2182        do {
2183                $line = iil_ReadLine($fp, 500);
2184                $line = iil_MultLine($fp, $line);
2185                $a    = explode(' ', $line);
2186       
2187                if (($line[0] == '*') && ($a[1] == 'LSUB' || $a[1] == 'LIST')) {
2188                        $line = rtrim($line);
2189           
2190            // split one line
2191                        $a = iil_ExplodeQuotedString(' ', $line);
2192           
2193            // last string is folder name
2194            //$folder = UTF7DecodeString(str_replace('"', '', $a[count($a)-1]));
2195            $folder = str_replace('"', '', $a[count($a)-1]);
2196           
2197                        if ((!in_array($folder, $folders)) && (empty($ignore)
2198                || (!empty($ignore) && !eregi($ignore, $folder)))) {
2199                            $folders[$i] = $folder;
2200            }
2201           
2202            // second from last is delimiter
2203            $delim = str_replace('"', '', $a[count($a)-2]);
2204           
2205            // is it a container?
2206            $i++;
2207                }
2208        } while (!iil_StartsWith($line, 'lsb'));
2209
2210        if (is_array($folders)) {
2211        if (!empty($ref)) {
2212            // if rootdir was specified, make sure it's the first element
2213            // some IMAP servers (i.e. Courier) won't return it
2214            if ($ref[strlen($ref)-1]==$delim) {
2215                $ref = substr($ref, 0, strlen($ref)-1);
2216            }
2217            if ($folders[0]!=$ref) {
2218                array_unshift($folders, $ref);
2219            }
2220        }
2221        return $folders;
2222        }
2223        $conn->error = $line;
2224        return false;
2225}
2226
2227
2228function iil_C_Subscribe(&$conn, $folder) {
2229        $fp = $conn->fp;
2230
2231        $query = 'sub1 SUBSCRIBE "' . $folder. '"' . "\r\n";
2232        fputs($fp, $query);
2233        $line = trim(chop(iil_ReadLine($fp, 10000)));
2234        return iil_ParseResult($line);
2235}
2236
2237
2238function iil_C_UnSubscribe(&$conn, $folder) {
2239        $fp = $conn->fp;
2240
2241        $query = 'usub1 UNSUBSCRIBE "' . $folder . '"' . "\r\n";
2242        fputs($fp, $query);
2243   
2244        $line = trim(chop(iil_ReadLine($fp, 10000)));
2245        return iil_ParseResult($line);
2246}
2247
2248
2249function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part) {
2250        $fp     = $conn->fp;
2251        $result = false;
2252        if (($part == 0) || (empty($part))) {
2253            $part = 'HEADER';
2254    } else {
2255        $part .= '.MIME';
2256        }
2257   
2258        if (iil_C_Select($conn, $mailbox)) {
2259                $key     = 'fh' . ($c++);
2260                $request = $key . " FETCH $id (BODY.PEEK[$part])\r\n";
2261                if (!fputs($fp, $request)) return false;
2262                do {
2263                        $line=chop(iil_ReadLine($fp, 200));
2264                        $a=explode(' ', $line);
2265                        if (($line[0] == '*') && ($a[2] == 'FETCH')
2266                && ($line[strlen($line)-1] != ')')) {
2267                                $line=iil_ReadLine($fp, 300);
2268                                while (chop($line)!=")") {
2269                                        $result.=$line;
2270                                        $line=iil_ReadLine($fp, 300);
2271                                }
2272                        }
2273                } while (strcmp($a[0], $key)!=0);
2274        }
2275       
2276        return $result;
2277}
2278
2279
2280function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part, $mode) {
2281    /* modes:
2282        1: return string
2283        2: print
2284        3: base64 and print
2285    */
2286        $fp     = $conn->fp;
2287        $result = false;
2288        if (($part==0) || (empty($part))) {
2289            $part = 'TEXT';
2290        }
2291   
2292        if (iil_C_Select($conn, $mailbox)) {
2293        $reply_key='* ' . $id;
2294       
2295        // format request
2296                $key     = 'ftch' . ($c++) . ' ';
2297                $request = $key . "FETCH $id (BODY.PEEK[$part])\r\n";
2298        // send request
2299                if (!fputs($fp, $request)) {
2300                    return false;
2301        }
2302       
2303        // receive reply line
2304        do {
2305            $line = chop(iil_ReadLine($fp, 1000));
2306            $a    = explode(' ', $line);
2307        } while ($a[2] != 'FETCH');
2308        $len = strlen($line);
2309        if ($line[$len-1] == ')') {
2310            //one line response, get everything between first and last quotes
2311            $from = strpos($line, '"') + 1;
2312            $to   = strrpos($line, '"');
2313            $len  = $to - $from;
2314            if ($mode == 1) {
2315                $result = substr($line, $from, $len);
2316            } else if ($mode == 2) {
2317                echo substr($line, $from, $len);
2318            } else if ($mode == 3) {
2319                echo base64_decode(substr($line, $from, $len));
2320            }
2321        }else if ($line[$len-1] == '}') {
2322            //multi-line request, find sizes of content and receive that many bytes
2323            $from     = strpos($line, '{') + 1;
2324            $to       = strrpos($line, '}');
2325            $len      = $to - $from;
2326            $sizeStr  = substr($line, $from, $len);
2327            $bytes    = (int)$sizeStr;
2328            $received = 0;
2329            while ($received < $bytes) {
2330                $remaining = $bytes - $received;
2331                $line      = iil_ReadLine($fp, 1024);
2332                $len       = strlen($line);
2333               
2334                if ($len > $remaining) {
2335                    $line = substr($line, 0, $remaining);
2336                }
2337                $received += strlen($line);
2338                if ($mode==1) {
2339                    $result .= chop($line)."\n";
2340                } else if ($mode==2) {
2341                    echo chop($line)."\n"; flush();
2342                } else if ($mode==3) {
2343                    echo base64_decode($line); flush();
2344                }
2345            }
2346        }
2347        // read in anything up until 'til last line
2348                do {
2349            $line = iil_ReadLine($fp, 1024);
2350                } while (!iil_StartsWith($line, $key));
2351       
2352        if ($result) {
2353                        $result = chop($result);
2354            return $result; // substr($result, 0, strlen($result)-1);
2355        }
2356        return false;
2357        } else {
2358                echo 'Select failed.';
2359        }
2360   
2361    if ($mode==1) {
2362        return $result;
2363    }
2364    return $received;
2365}
2366
2367function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part) {
2368    return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1);
2369}
2370
2371function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part) {
2372    iil_C_HandlePartBody($conn, $mailbox, $id, $part, 2);
2373}
2374
2375function iil_C_PrintBase64Body(&$conn, $mailbox, $id, $part) {
2376    iil_C_HandlePartBody($conn, $mailbox, $id, $part, 3);
2377}
2378
2379function iil_C_CreateFolder(&$conn, $folder) {
2380        $fp = $conn->fp;
2381        if (fputs($fp, 'c CREATE "' . $folder . '"' . "\r\n")) {
2382                do {
2383                        $line=iil_ReadLine($fp, 300);
2384                } while ($line[0] != 'c');
2385        $conn->error = $line;
2386                return (iil_ParseResult($line) == 0);
2387        }
2388        return false;
2389}
2390
2391function iil_C_RenameFolder(&$conn, $from, $to) {
2392        $fp = $conn->fp;
2393        if (fputs($fp, 'r RENAME "' . $from . '" "' . $to . '"' . "\r\n")) {
2394                do {
2395                        $line=iil_ReadLine($fp, 300);
2396                } while ($line[0]!="r");
2397                return (iil_ParseResult($line) == 0);
2398        }
2399    return false;       
2400}
2401
2402function iil_C_DeleteFolder(&$conn, $folder) {
2403        $fp = $conn->fp;
2404        if (fputs($fp, 'd DELETE "' . $folder. '"' . "\r\n")) {
2405                do {
2406                        $line=iil_ReadLine($fp, 300);
2407                } while ($line[0]!="d");
2408                return (iil_ParseResult($line) == 0);
2409        }
2410    $conn->error = "Couldn't send command\n";
2411        return false;
2412}
2413
2414function iil_C_Append(&$conn, $folder, &$message) {
2415        if (!$folder) return false;
2416        $fp = $conn->fp;
2417
2418        $message = str_replace("\r", '', $message);
2419        $message = str_replace("\n", "\r\n", $message);         
2420
2421        $len = strlen($message);
2422        if (!$len) return false;
2423       
2424        $request = 'A APPEND "' . $folder .'" (\\Seen) {' . $len . "}\r\n";
2425   
2426        if (fputs($fp, $request)) {
2427                $line=iil_ReadLine($fp, 100);           
2428                $sent = fwrite($fp, $message."\r\n");
2429                flush();
2430                do {
2431                        $line=iil_ReadLine($fp, 1000);
2432                } while ($line[0] != 'A');
2433       
2434                $result = (iil_ParseResult($line) == 0);
2435                if (!$result) {
2436                    $conn->error .= $line . "\n";
2437        }
2438                return $result;
2439       
2440        }
2441        $conn->error .= "Couldn't send command \"$request\"\n";
2442    return false;
2443}
2444
2445
2446function iil_C_AppendFromFile(&$conn, $folder, $path) {
2447        if (!$folder) {
2448            return false;
2449        }
2450   
2451        //open message file
2452        $in_fp = false;                         
2453        if (file_exists(realpath($path))) {
2454            $in_fp = fopen($path, 'r');
2455    }
2456        if (!$in_fp) {
2457                $conn->error .= "Couldn't open $path for reading\n";
2458                return false;
2459        }
2460       
2461        $fp  = $conn->fp;
2462        $len = filesize($path);
2463        if (!$len) {
2464            return false;
2465        }
2466   
2467        //send APPEND command
2468        $request    = 'A APPEND "' . $folder . '" (\\Seen) {' . $len . "}\r\n";
2469        $bytes_sent = 0;
2470        if (fputs($fp, $request)) {
2471                $line=iil_ReadLine($fp, 100);
2472                               
2473                //send file
2474                while (!feof($in_fp)) {
2475                        $buffer = fgets($in_fp, 4096);
2476                        $bytes_sent += strlen($buffer);
2477                        fputs($fp, $buffer);
2478                }
2479                fclose($in_fp);
2480
2481                fputs($fp, "\r\n");
2482
2483                //read response
2484                do {
2485                        $line=iil_ReadLine($fp, 1000);
2486                } while ($line[0] != 'A');
2487                       
2488                $result = (iil_ParseResult($line) == 0);
2489                if (!$result) {
2490                    $conn->error .= $line . "\n";
2491                }
2492        return $result;
2493       
2494        }
2495        $conn->error .= "Couldn't send command \"$request\"\n";
2496        return false;
2497}
2498
2499
2500function iil_C_FetchStructureString(&$conn, $folder, $id) {
2501        $fp     = $conn->fp;
2502        $result = false;
2503        if (iil_C_Select($conn, $folder)) {
2504                $key = 'F1247';
2505       
2506                if (fputs($fp, "$key FETCH $id (BODYSTRUCTURE)\r\n")) {
2507                        do {
2508                                $line=chop(iil_ReadLine($fp, 5000));
2509                                if ($line[0] == '*') {
2510                                        if (ereg("\}$", $line)) {
2511                                                preg_match('/(.+)\{([0-9]+)\}/', $line, $match); 
2512                                                $result = $match[1];
2513                                                do {
2514                                                        $line = chop(iil_ReadLine($fp, 100));
2515                                                        if (!preg_match("/^$key/", $line)) {
2516                                                            $result .= $line;
2517                            } else {
2518                                $done = true;
2519                            }
2520                                                } while (!$done);
2521                                        } else {
2522                                                $result = $line;
2523                                        }
2524                                        list($pre, $post) = explode('BODYSTRUCTURE ', $result);
2525                                       
2526                                        //truncate last ')' and return
2527                                        $result = substr($post, 0, strlen($post)-1);
2528                                }
2529                        } while (!preg_match("/^$key/",$line));
2530                }
2531        }
2532        return $result;
2533}
2534
2535function iil_C_PrintSource(&$conn, $folder, $id, $part) {
2536        $header = iil_C_FetchPartHeader($conn, $folder, $id, $part);
2537        //echo str_replace("\r", '', $header);
2538        echo $header;
2539        echo iil_C_PrintPartBody($conn, $folder, $id, $part);
2540}
2541
2542function iil_C_GetQuota(&$conn) {
2543/*
2544 * GETQUOTAROOT "INBOX"
2545 * QUOTAROOT INBOX user/rchijiiwa1
2546 * QUOTA user/rchijiiwa1 (STORAGE 654 9765)
2547 b OK Completed
2548 */
2549        $fp         = $conn->fp;
2550        $result     = false;
2551        $quota_line = '';
2552       
2553        //get line containing quota info
2554        if (fputs($fp, 'QUOT1 GETQUOTAROOT "INBOX"' . "\r\n")) {
2555                do {
2556                        $line=chop(iil_ReadLine($fp, 5000));
2557                        if (iil_StartsWith($line, '* QUOTA ')) {
2558                            $quota_line = $line;
2559            }
2560                } while (!iil_StartsWith($line, 'QUOT1'));
2561        }
2562       
2563        //return false if not found, parse if found
2564        if (!empty($quota_line)) {
2565                $quota_line = eregi_replace('[()]', '', $quota_line);
2566                $parts = explode(' ', $quota_line);
2567                $storage_part = array_search('STORAGE', $parts);
2568                if ($storage_part>0) {
2569                        $result = array();
2570                        $used   = $parts[$storage_part+1];
2571                        $total  = $parts[$storage_part+2];
2572           
2573                        $result['used']    = $used;
2574                        $result['total']   = (empty($total)?"??":$total);
2575                        $result['percent'] = (empty($total)?"??":round(($used/$total)*100));
2576                        $result['free']    = 100 - $result['percent'];
2577                }
2578        }
2579       
2580        return $result;
2581}
2582
2583
2584function iil_C_ClearFolder(&$conn, $folder) {
2585        $num_in_trash = iil_C_CountMessages($conn, $folder);
2586        if ($num_in_trash > 0) {
2587            iil_C_Delete($conn, $folder, '1:' . $num_in_trash);
2588    }
2589        return (iil_C_Expunge($conn, $folder) >= 0);
2590}
2591
2592?>
Note: See TracBrowser for help on using the repository browser.