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

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