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