source: subversion/trunk/roundcubemail/program/lib/imap.inc @ 1424

Last change on this file since 1424 was 1424, checked in by till, 5 years ago

fix to #1485083

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