source: github/program/lib/imap.inc @ d8c440c

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since d8c440c was d8c440c, checked in by alecpl <alec@…>, 3 years ago
  • Added 'imap_force_caps' option for after-login CAPABILITY checking (#1485750)
  • Property mode set to 100644
File size: 57.9 KB
Line 
1<?php
2/////////////////////////////////////////////////////////
3//     
4//      Iloha IMAP Library (IIL)
5//
6//      (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org>
7//
8//      This file is part of IlohaMail. IlohaMail is free software released
9//      under the GPL license.  See enclosed file COPYING for details, or
10//      see http://www.fsf.org/copyleft/gpl.html
11//
12/////////////////////////////////////////////////////////
13
14/********************************************************
15
16        FILE: include/imap.inc
17        PURPOSE:
18                Provide alternative IMAP library that doesn't rely on the standard
19                C-Client based version.  This allows IlohaMail to function regardless
20                of whether or not the PHP build it's running on has IMAP functionality
21                built-in.
22        USEAGE:
23                Function containing "_C_" in name require connection handler to be
24                passed as one of the parameters.  To obtain connection handler, use
25                iil_Connect()
26        VERSION:
27                IlohaMail-0.9-20050415
28        CHANGES:
29                File altered by Thomas Bruederli <roundcube@gmail.com>
30                to fit enhanced equirements by the RoundCube Webmail:
31                - Added list of server capabilites and check these before invoking commands
32                - Added junk flag to iilBasicHeader
33                - Enhanced error reporting on fsockopen()
34                - Additional parameter for SORT command
35                - Removed Call-time pass-by-reference because deprecated
36                - Parse charset from content-type in iil_C_FetchHeaders()
37                - Enhanced heaer sorting
38                - Pass message as reference in iil_C_Append (to save memory)
39                - Added BCC and REFERENCE to the list of headers to fetch in iil_C_FetchHeaders()
40                - Leave messageID unchanged in iil_C_FetchHeaders()
41                - Avoid stripslahes in iil_Connect()
42                - Escape quotes and backslashes in iil_C_Login()
43                - Added patch to iil_SortHeaders() by Richard Green
44                - Removed <br> from error messages (better for logging)
45                - Added patch to iil_C_Sort() enabling UID SORT commands
46                - Added function iil_C_ID2UID()
47                - Casting date parts in iil_StrToTime() to avoid mktime() warnings
48                - Also acceppt LIST responses in iil_C_ListSubscribed()
49                - Sanity check of $message_set in iil_C_FetchHeaders(), iil_C_FetchHeaderIndex(), iil_C_FetchThreadHeaders()
50                - Implemented UID FETCH in iil_C_FetchHeaders()
51                - Abort do-loop on socket errors (fgets returns false)
52                - $ICL_SSL is not boolean anymore but contains the connection schema (ssl or tls)
53                - Removed some debuggers (echo ...)
54                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                - handling connection startup response
85                - added UID EXPUNGE support
86                - fixed problem with double quotes and spaces in folder names in LIST and LSUB
87                - rewritten iil_C_FetchHeaderIndex()
88
89********************************************************/
90
91/**
92 * @todo Possibly clean up more CS.
93 * @todo Try to replace most double-quotes with single-quotes.
94 * @todo Split this file into smaller files.
95 * @todo Refactor code.
96 * @todo Replace echo-debugging (make it adhere to config setting and log)
97 */
98
99
100if (!isset($IMAP_USE_HEADER_DATE) || !$IMAP_USE_HEADER_DATE) {
101    $IMAP_USE_INTERNAL_DATE = true;
102}
103
104$GLOBALS['IMAP_FLAGS'] = array(
105    'SEEN'     => '\\Seen',
106    'DELETED'  => '\\Deleted',
107    'RECENT'   => '\\Recent',
108    'ANSWERED' => '\\Answered',
109    'DRAFT'    => '\\Draft',
110    'FLAGGED'  => '\\Flagged',
111    'FORWARDED' => '$Forwarded',
112    'MDNSENT'  => '$MDNSent',
113    '*'        => '\\*',
114);
115
116$iil_error;
117$iil_errornum;
118$iil_selected;
119
120/**
121 * @todo Change class vars to public/private
122 */
123class iilConnection
124{
125        var $fp;
126        var $error;
127        var $errorNum;
128        var $selected;
129        var $message;
130        var $host;
131        var $exists;
132        var $recent;
133        var $rootdir;
134        var $delimiter;
135        var $capability = array();
136        var $permanentflags = array();
137        var $capability_readed = false;
138}
139
140/**
141 * @todo Change class vars to public/private
142 */
143class iilBasicHeader
144{
145        var $id;
146        var $uid;
147        var $subject;
148        var $from;
149        var $to;
150        var $cc;
151        var $replyto;
152        var $in_reply_to;
153        var $date;
154        var $messageID;
155        var $size;
156        var $encoding;
157        var $charset;
158        var $ctype;
159        var $flags;
160        var $timestamp;
161        var $f;
162        var $body_structure;
163        var $internaldate;
164        var $references;
165        var $priority;
166        var $mdn_to;
167        var $mdn_sent = false;
168        var $is_draft = false;
169        var $seen = false;
170        var $deleted = false;
171        var $recent = false;
172        var $answered = false;
173        var $forwarded = false;
174        var $junk = false;
175        var $flagged = false;
176        var $has_children = false;
177        var $depth = 0;
178        var $unread_children = 0;
179        var $others = array();
180}
181
182function iil_xor($string, $string2) {
183        $result = '';
184        $size = strlen($string);
185        for ($i=0; $i<$size; $i++) {
186                $result .= chr(ord($string[$i]) ^ ord($string2[$i]));
187        }
188        return $result;
189}
190
191function iil_PutLine($fp, $string, $endln=true) {
192        global $my_prefs;
193       
194        if (!empty($my_prefs['debug_mode']))
195                write_log('imap', 'C: '. rtrim($string));
196       
197        return fputs($fp, $string . ($endln ? "\r\n" : ''));
198}
199
200// iil_PutLine replacement with Command Continuation Requests (RFC3501 7.5) support
201function iil_PutLineC($fp, $string, $endln=true) {
202        if ($endln)
203                $string .= "\r\n";
204
205        $res = 0;
206        if ($parts = preg_split('/(\{[0-9]+\}\r\n)/m', $string, -1, PREG_SPLIT_DELIM_CAPTURE)) {
207                for($i=0, $cnt=count($parts); $i<$cnt; $i++) {
208                        if(preg_match('/^\{[0-9]+\}\r\n$/', $parts[$i+1])) {
209                                $res += iil_PutLine($fp, $parts[$i].$parts[$i+1], false);
210                                $line = iil_ReadLine($fp, 1000);
211                                // handle error in command
212                                if ($line[0] != '+')
213                                        return false;
214                                $i++;
215                        }
216                        else
217                                $res += iil_PutLine($fp, $parts[$i], false);
218                }
219        }
220        return $res;
221}
222
223function iil_ReadLine($fp, $size=1024) {
224        global $my_prefs;
225       
226        $line = '';
227
228        if (!$fp) {
229                return NULL;
230        }
231   
232        if (!$size) {
233                $size = 1024;
234        }
235   
236        do {
237                if (feof($fp)) {
238                        return $line ? $line : NULL;
239                }
240               
241                $buffer = fgets($fp, $size);
242
243                if ($buffer === false) {
244                        break;
245                }
246                if (!empty($my_prefs['debug_mode']))
247                        write_log('imap', 'S: '. chop($buffer));
248                $line .= $buffer;
249        } while ($buffer[strlen($buffer)-1] != "\n");
250
251        return $line;
252}
253
254function iil_MultLine($fp, $line, $escape=false) {
255        $line = chop($line);
256        if (preg_match('/\{[0-9]+\}$/', $line)) {
257                $out = '';
258       
259                preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
260                $bytes = $a[2][0];
261                while (strlen($out) < $bytes) {
262                        $line = iil_ReadBytes($fp, $bytes);
263                        if ($line === NULL)
264                                break;
265                        $out .= $line;
266                }
267
268                $line = $a[1][0] . '"' . ($escape ? iil_Escape($out) : $out) . '"';
269        }
270        return $line;
271}
272
273function iil_ReadBytes($fp, $bytes) {
274        global $my_prefs;
275        $data = '';
276        $len  = 0;
277        while ($len < $bytes && !feof($fp))
278        {
279                $d = fread($fp, $bytes-$len);
280                if (!empty($my_prefs['debug_mode']))
281                        write_log('imap', 'S: '. $d);
282                $data .= $d;
283                $data_len = strlen($data);
284                if ($len == $data_len) {
285                        break; //nothing was read -> exit to avoid apache lockups
286                }
287                $len = $data_len;
288        };
289       
290        return $data;
291}
292
293// don't use it in loops, until you exactly know what you're doing
294function iil_ReadReply($fp) {
295        do {
296                $line = trim(iil_ReadLine($fp, 1024));
297        } while ($line[0] == '*');
298
299        return $line;
300}
301
302function iil_ParseResult($string) {
303        $a = explode(' ', trim($string));
304        if (count($a) >= 2) {
305                $res = strtoupper($a[1]);
306                if ($res == 'OK') {
307                        return 0;
308                } else if ($res == 'NO') {
309                        return -1;
310                } else if ($res == 'BAD') {
311                        return -2;
312                } else if ($res == 'BYE') {
313                        return -3;
314                }
315        }
316        return -4;
317}
318
319// check if $string starts with $match (or * BYE/BAD)
320function iil_StartsWith($string, $match, $error=false) {
321        $len = strlen($match);
322        if ($len == 0) {
323                return false;
324        }
325        if (strncmp($string, $match, $len) == 0) {
326                return true;
327        }
328        if ($error && preg_match('/^\* (BYE|BAD) /i', $string)) {
329                return true;
330        }
331        return false;
332}
333
334function iil_StartsWithI($string, $match, $error=false) {
335        $len = strlen($match);
336        if ($len == 0) {
337                return false;
338        }
339        if (strncasecmp($string, $match, $len) == 0) {
340                return true;
341        }
342        if ($error && preg_match('/^\* (BYE|BAD) /i', $string)) {
343                return true;
344        }
345        return false;
346}
347
348function iil_Escape($string)
349{
350        return strtr($string, array('"'=>'\\"', '\\' => '\\\\'));
351}
352
353function iil_UnEscape($string)
354{
355        return strtr($string, array('\\"'=>'"', '\\\\' => '\\'));
356}
357
358function iil_C_GetCapability(&$conn, $name)
359{
360        if (in_array($name, $conn->capability)) {
361                return true;
362        }
363        else if ($conn->capability_readed) {
364                return false;
365        }
366
367        // get capabilities (only once) because initial
368        // optional CAPABILITY response may differ
369        $conn->capability = array();
370
371        iil_PutLine($conn->fp, "cp01 CAPABILITY");
372        do {
373                $line = trim(iil_ReadLine($conn->fp, 1024));
374                $a = explode(' ', $line);
375                if ($line[0] == '*') {
376                        while (list($k, $w) = each($a)) {
377                                if ($w != '*' && $w != 'CAPABILITY')
378                                        $conn->capability[] = strtoupper($w);
379                        }
380                }
381        } while ($a[0] != 'cp01');
382       
383        $conn->capability_readed = true;
384
385        if (in_array($name, $conn->capability)) {
386                return true;
387        }
388
389        return false;
390}
391
392function iil_C_ClearCapability($conn)
393{
394        $conn->capability = array();
395        $conn->capability_readed = false;
396}
397
398function iil_C_Authenticate(&$conn, $user, $pass, $encChallenge) {
399   
400    $ipad = '';
401    $opad = '';
402   
403    // initialize ipad, opad
404    for ($i=0;$i<64;$i++) {
405        $ipad .= chr(0x36);
406        $opad .= chr(0x5C);
407    }
408
409    // pad $pass so it's 64 bytes
410    $padLen = 64 - strlen($pass);
411    for ($i=0;$i<$padLen;$i++) {
412        $pass .= chr(0);
413    }
414   
415    // generate hash
416    $hash  = md5(iil_xor($pass,$opad) . pack("H*", md5(iil_xor($pass, $ipad) . base64_decode($encChallenge))));
417   
418    // generate reply
419    $reply = base64_encode($user . ' ' . $hash);
420   
421    // send result, get reply
422    iil_PutLine($conn->fp, $reply);
423    $line = iil_ReadLine($conn->fp, 1024);
424   
425    // process result
426    $result = iil_ParseResult($line);
427    if ($result == 0) {
428        $conn->error    .= '';
429        $conn->errorNum  = 0;
430        return $conn->fp;
431    }
432
433    if ($result == -3) fclose($conn->fp); // BYE response
434
435    $conn->error    .= 'Authentication for ' . $user . ' failed (AUTH): "';
436    $conn->error    .= htmlspecialchars($line) . '"';
437    $conn->errorNum  = $result;
438
439    return $result;
440}
441
442function iil_C_Login(&$conn, $user, $password) {
443
444    iil_PutLine($conn->fp, 'a001 LOGIN "'.iil_Escape($user).'" "'.iil_Escape($password).'"');
445
446    $line = iil_ReadReply($conn->fp);
447
448    // process result
449    $result = iil_ParseResult($line);
450
451    if ($result == 0) {
452        $conn->error    .= '';
453        $conn->errorNum  = 0;
454        return $conn->fp;
455    }
456
457    fclose($conn->fp);
458   
459    $conn->error    .= 'Authentication for ' . $user . ' failed (LOGIN): "';
460    $conn->error    .= htmlspecialchars($line)."\"";
461    $conn->errorNum  = $result;
462
463    return $result;
464}
465
466function iil_ParseNamespace2($str, &$i, $len=0, $l) {
467        if (!$l) {
468            $str = str_replace('NIL', '()', $str);
469        }
470        if (!$len) {
471            $len = strlen($str);
472        }
473        $data      = array();
474        $in_quotes = false;
475        $elem      = 0;
476        for ($i;$i<$len;$i++) {
477                $c = (string)$str[$i];
478                if ($c == '(' && !$in_quotes) {
479                        $i++;
480                        $data[$elem] = iil_ParseNamespace2($str, $i, $len, $l++);
481                        $elem++;
482                } else if ($c == ')' && !$in_quotes) {
483                        return $data;
484                } else if ($c == '\\') {
485                        $i++;
486                        if ($in_quotes) {
487                                $data[$elem] .= $c.$str[$i];
488                        }
489                } else if ($c == '"') {
490                        $in_quotes = !$in_quotes;
491                        if (!$in_quotes) {
492                                $elem++;
493                        }
494                } else if ($in_quotes) {
495                        $data[$elem].=$c;
496                }
497        }
498        return $data;
499}
500
501function iil_C_NameSpace(&$conn) {
502        global $my_prefs;
503
504        if (isset($my_prefs['rootdir']) && is_string($my_prefs['rootdir'])) {
505                $conn->rootdir = $my_prefs['rootdir'];
506                return true;
507        }
508       
509        if (!iil_C_GetCapability($conn, 'NAMESPACE')) {
510            return false;
511        }
512   
513        iil_PutLine($conn->fp, "ns1 NAMESPACE");
514        do {
515                $line = iil_ReadLine($conn->fp, 1024);
516                if (iil_StartsWith($line, '* NAMESPACE')) {
517                        $i    = 0;
518                        $line = iil_UnEscape($line);
519                        $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
520                }
521        } while (!iil_StartsWith($line, 'ns1', true));
522       
523        if (!is_array($data)) {
524            return false;
525        }
526   
527        $user_space_data = $data[0];
528        if (!is_array($user_space_data)) {
529            return false;
530        }
531   
532        $first_userspace = $user_space_data[0];
533        if (count($first_userspace)!=2) {
534            return false;
535        }
536   
537        $conn->rootdir       = $first_userspace[0];
538        $conn->delimiter     = $first_userspace[1];
539        $my_prefs['rootdir'] = substr($conn->rootdir, 0, -1);
540        $my_prefs['delimiter'] = $conn->delimiter;
541       
542        return true;
543}
544
545function iil_Connect($host, $user, $password, $options=null) { 
546        global $iil_error, $iil_errornum;
547        global $ICL_SSL, $ICL_PORT;
548        global $my_prefs, $IMAP_USE_INTERNAL_DATE;
549       
550        $iil_error = '';
551        $iil_errornum = 0;
552
553        // set options
554        if (is_array($options)) {
555        $my_prefs = $options;
556    }
557    // set auth method
558    if (!empty($my_prefs['auth_method'])) {
559            $auth_method = strtoupper($my_prefs['auth_method']);
560        } else {
561                $auth_method = 'CHECK';
562    }
563
564        $message = "INITIAL: $auth_method\n";
565
566        $result = false;
567       
568        // initialize connection
569        $conn              = new iilConnection;
570        $conn->error       = '';
571        $conn->errorNum    = 0;
572        $conn->selected    = '';
573        $conn->user        = $user;
574        $conn->host        = $host;
575
576        if ($my_prefs['sort_field'] == 'INTERNALDATE') {
577                $IMAP_USE_INTERNAL_DATE = true;
578        } else if ($my_prefs['sort_field'] == 'DATE') {
579                $IMAP_USE_INTERNAL_DATE = false;
580        }
581
582        //check input
583        if (empty($host)) {
584                $iil_error = "Empty host";
585                $iil_errornum = -1;
586                return false;
587        }
588        if (empty($user)) {
589                $iil_error = "Empty user";
590                $iil_errornum = -1;
591                return false;
592        }
593        if (empty($password)) {
594                $iil_error = "Empty password";
595                $iil_errornum = -1;
596                return false;
597        }
598
599        if (!$ICL_PORT) {
600                $ICL_PORT = 143;
601        }
602        //check for SSL
603        if ($ICL_SSL && $ICL_SSL != 'tls') {
604                $host = $ICL_SSL . '://' . $host;
605        }
606
607        $conn->fp = @fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
608        if (!$conn->fp) {
609                $iil_error = "Could not connect to $host at port $ICL_PORT: $errstr";
610                $iil_errornum = -2;
611                return false;
612        }
613
614        stream_set_timeout($conn->fp, 10);
615        $line = trim(fgets($conn->fp, 8192));
616
617        if ($my_prefs['debug_mode'] && $line)
618                write_log('imap', 'S: '. $line);
619
620        // Connected to wrong port or connection error?
621        if (!preg_match('/^\* (OK|PREAUTH)/i', $line)) {
622                if ($line)
623                        $iil_error = "Wrong startup greeting ($host:$ICL_PORT): $line";
624                else
625                        $iil_error = "Empty startup greeting ($host:$ICL_PORT)";
626                $iil_errornum = -2;
627                return false;
628        }
629
630        // RFC3501 [7.1] optional CAPABILITY response
631        if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) {
632                $conn->capability = explode(' ', strtoupper($matches[1]));
633        }
634
635        $conn->message .= $line;
636
637        // TLS connection
638        if ($ICL_SSL == 'tls' && iil_C_GetCapability($conn, 'STARTTLS')) {
639                if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
640                        iil_PutLine($conn->fp, 'stls000 STARTTLS');
641
642                        $line = iil_ReadLine($conn->fp, 4096);
643                        if (!iil_StartsWith($line, 'stls000 OK')) {
644                                $iil_error = "Server responded to STARTTLS with: $line";
645                                $iil_errornum = -2;
646                                return false;
647                        }
648
649                        if (!stream_socket_enable_crypto($conn->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
650                                $iil_error = "Unable to negotiate TLS";
651                                $iil_errornum = -2;
652                                return false;
653                        }
654                       
655                        // Now we're authenticated, capabilities need to be reread
656                        iil_C_ClearCapability($conn);
657                }
658        }
659
660        $orig_method = $auth_method;
661
662        if ($auth_method == 'CHECK') {
663                // check for supported auth methods
664                if (iil_C_GetCapability($conn, 'AUTH=CRAM-MD5') || iil_C_GetCapability($conn, 'AUTH=CRAM_MD5')) {
665                        $auth_method = 'AUTH';
666                }
667                else {
668                        // default to plain text auth
669                        $auth_method = 'PLAIN';
670                }
671        }
672
673        if ($auth_method == 'AUTH') {
674                // do CRAM-MD5 authentication
675                iil_PutLine($conn->fp, "a000 AUTHENTICATE CRAM-MD5");
676                $line = trim(iil_ReadLine($conn->fp, 1024));
677
678                if ($line[0] == '+') {
679                        // got a challenge string, try CRAM-MD5
680                        $result = iil_C_Authenticate($conn, $user, $password, substr($line,2));
681                       
682                        // stop if server sent BYE response
683                        if ($result == -3) {
684                                $iil_error = $conn->error;
685                                $iil_errornum = $conn->errorNum;
686                                return false;
687                        }
688                }
689               
690                if (!is_resource($result) && $orig_method == 'CHECK') {
691                        $auth_method = 'PLAIN';
692                }
693        }
694               
695        if ($auth_method == 'PLAIN') {
696                // do plain text auth
697                $result = iil_C_Login($conn, $user, $password);
698        }
699
700        if (is_resource($result)) {
701        if ($my_prefs['force_caps']) {
702                        iil_C_ClearCapability($conn);
703        }
704                iil_C_Namespace($conn);
705                return $conn;
706        } else {
707                $iil_error = $conn->error;
708                $iil_errornum = $conn->errorNum;
709                return false;
710        }
711}
712
713function iil_Close(&$conn) {
714        if (iil_PutLine($conn->fp, "I LOGOUT")) {
715                if (!feof($conn->fp))
716                        fgets($conn->fp, 1024);
717                fclose($conn->fp);
718                $conn->fp = false;
719        }
720}
721
722function iil_ExplodeQuotedString($delimiter, $string) {
723        $result = array();
724        $strlen = strlen($string);
725         
726        for ($q=$p=$i=0; $i < $strlen; $i++) {
727                if ($string[$i] == "\"" && $string[$i-1] != "\\") {
728                        $q = $q ? false : true;
729                }
730                else if (!$q && preg_match("/$delimiter/", $string[$i])) {
731                        $result[] = substr($string, $p, $i - $p);
732                        $p = $i + 1;
733                }
734        }
735
736        $result[] = substr($string, $p);
737        return $result;
738}
739
740function iil_C_Select(&$conn, $mailbox) {
741
742        if (empty($mailbox)) {
743                return false;
744        }
745        if ($conn->selected == $mailbox) {
746                return true;
747        }
748   
749        if (iil_PutLine($conn->fp, "sel1 SELECT \"".iil_Escape($mailbox).'"')) {
750                do {
751                        $line = chop(iil_ReadLine($conn->fp, 300));
752                        $a = explode(' ', $line);
753                        if (count($a) == 3) {
754                                $token = strtoupper($a[2]);
755                                if ($token == 'EXISTS') {
756                                        $conn->exists = (int) $a[1];
757                                }
758                                else if ($token == 'RECENT') {
759                                        $conn->recent = (int) $a[1];
760                                }
761                        }
762                        else if (preg_match('/\[?PERMANENTFLAGS\s+\(([^\)]+)\)\]/U', $line, $match)) {
763                                $conn->permanentflags = explode(' ', $match[1]);
764                        }
765                } while (!iil_StartsWith($line, 'sel1', true));
766
767                if (strcasecmp($a[1], 'OK') == 0) {
768                        $conn->selected = $mailbox;
769                        return true;
770                }
771        }
772        return false;
773}
774
775function iil_C_CheckForRecent(&$conn, $mailbox) {
776        if (empty($mailbox)) {
777                $mailbox = 'INBOX';
778        }
779   
780        iil_C_Select($conn, $mailbox);
781        if ($conn->selected == $mailbox) {
782                return $conn->recent;
783        }
784        return false;
785}
786
787function iil_C_CountMessages(&$conn, $mailbox, $refresh = false) {
788        if ($refresh) {
789                $conn->selected = '';
790        }
791       
792        iil_C_Select($conn, $mailbox);
793        if ($conn->selected == $mailbox) {
794                return $conn->exists;
795        }
796        return false;
797}
798
799function iil_SplitHeaderLine($string) {
800        $pos=strpos($string, ':');
801        if ($pos>0) {
802                $res[0] = substr($string, 0, $pos);
803                $res[1] = trim(substr($string, $pos+1));
804                return $res;
805        }
806        return $string;
807}
808
809function iil_StrToTime($date) {
810
811        // support non-standard "GMTXXXX" literal
812        $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
813        // if date parsing fails, we have a date in non-rfc format.
814        // remove token from the end and try again
815        while ((($ts = @strtotime($date))===false) || ($ts < 0))
816        {
817                $d = explode(' ', $date);
818                array_pop($d);
819                if (!$d) break;
820                $date = implode(' ', $d);
821        }
822
823        $ts = (int) $ts;
824
825        return $ts < 0 ? 0 : $ts;       
826}
827
828function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE,
829    $encoding = 'US-ASCII') {
830
831        $field = strtoupper($field);
832        if ($field == 'INTERNALDATE') {
833            $field = 'ARRIVAL';
834        }
835       
836        $fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1,
837        'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
838
839        if (!$fields[$field]) {
840            return false;
841        }
842
843        /*  Do "SELECT" command */
844        if (!iil_C_Select($conn, $mailbox)) {
845            return false;
846        }
847   
848        $is_uid = $is_uid ? 'UID ' : '';
849       
850        // message IDs
851        if (is_array($add))
852                $add = iil_CompressMessageSet(join(',', $add));
853
854        if (!empty($add))
855            $add = " $add";
856
857        $command  = 's ' . $is_uid . 'SORT (' . $field . ') ';
858        $command .= $encoding . ' ALL' . $add;
859        $line     = $data = '';
860
861        if (!iil_PutLineC($conn->fp, $command)) {
862            return false;
863        }
864        do {
865                $line = chop(iil_ReadLine($conn->fp));
866                if (iil_StartsWith($line, '* SORT')) {
867                        $data .= substr($line, 7);
868                } else if (preg_match('/^[0-9 ]+$/', $line)) {
869                        $data .= $line;
870                }
871        } while (!iil_StartsWith($line, 's ', true));
872       
873        $result_code = iil_ParseResult($line);
874       
875        if ($result_code != 0) {
876                $conn->error = 'iil_C_Sort: ' . $line . "\n";
877                return false;
878        }
879       
880        return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY);
881}
882
883function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) {
884
885        if (is_array($message_set)) {
886                if (!($message_set = iil_CompressMessageSet(join(',', $message_set))))
887                        return false;
888        } else {
889                list($from_idx, $to_idx) = explode(':', $message_set);
890                if (empty($message_set) ||
891                        (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) {
892                        return false;
893                }
894        }
895       
896        $index_field = empty($index_field) ? 'DATE' : strtoupper($index_field);
897       
898        $fields_a['DATE']         = 1;
899        $fields_a['INTERNALDATE'] = 4;
900        $fields_a['ARRIVAL']      = 4;
901        $fields_a['FROM']         = 1;
902        $fields_a['REPLY-TO']     = 1;
903        $fields_a['SENDER']       = 1;
904        $fields_a['TO']           = 1;
905        $fields_a['CC']           = 1;
906        $fields_a['SUBJECT']      = 1;
907        $fields_a['UID']          = 2;
908        $fields_a['SIZE']         = 2;
909        $fields_a['SEEN']         = 3;
910        $fields_a['RECENT']       = 3;
911        $fields_a['DELETED']      = 3;
912
913        if (!($mode = $fields_a[$index_field])) {
914                return false;
915        }
916
917        /*  Do "SELECT" command */
918        if (!iil_C_Select($conn, $mailbox)) {
919                return false;
920        }
921       
922        // build FETCH command string
923        $key     = 'fhi0';
924        $cmd     = $uidfetch ? 'UID FETCH' : 'FETCH';
925        $deleted = $skip_deleted ? ' FLAGS' : '';
926
927        if ($mode == 1 && $index_field == 'DATE')
928                $request = " $cmd $message_set (INTERNALDATE BODY.PEEK[HEADER.FIELDS (DATE)]$deleted)";
929        else if ($mode == 1)
930                $request = " $cmd $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)]$deleted)";
931        else if ($mode == 2) {
932                if ($index_field == 'SIZE')
933                        $request = " $cmd $message_set (RFC822.SIZE$deleted)";
934                else
935                        $request = " $cmd $message_set ($index_field$deleted)";
936        } else if ($mode == 3)
937                $request = " $cmd $message_set (FLAGS)";
938        else // 4
939                $request = " $cmd $message_set (INTERNALDATE$deleted)";
940
941        $request = $key . $request;
942
943        if (!iil_PutLine($conn->fp, $request))
944                return false;
945
946        $result = array();
947
948        do {
949                $line = chop(iil_ReadLine($conn->fp, 200));
950                $line = iil_MultLine($conn->fp, $line);
951
952                if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) {
953
954                        $id = $m[1];
955                        $flags = NULL;
956                                       
957                        if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
958                                $flags = explode(' ', strtoupper($matches[1]));
959                                if (in_array('\\DELETED', $flags)) {
960                                        $deleted[$id] = $id;
961                                        continue;
962                                }
963                        }
964
965                        if ($mode == 1 && $index_field == 'DATE') {
966                                if (preg_match('/BODY\[HEADER\.FIELDS \("*DATE"*\)\] (.*)/', $line, $matches)) {
967                                        $value = preg_replace(array('/^"*[a-z]+:/i'), '', $matches[1]);
968                                        $value = trim($value);
969                                        $result[$id] = iil_StrToTime($value);
970                                }
971                                // non-existent/empty Date: header, use INTERNALDATE
972                                if (empty($result[$id])) {
973                                        if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches))
974                                                $result[$id] = iil_StrToTime($matches[1]);
975                                        else
976                                                $result[$id] = 0;
977                                }
978                        } else if ($mode == 1) {
979                                if (preg_match('/BODY\[HEADER\.FIELDS \("?(FROM|REPLY-TO|SENDER|TO|SUBJECT)"?\)\] (.*)/', $line, $matches)) {
980                                        $value = preg_replace(array('/^"*[a-z]+:/i', '/\s+$/sm'), array('', ''), $matches[2]);
981                                        $result[$id] = trim($value);
982                                } else {
983                                        $result[$id] = '';
984                                }
985                        } else if ($mode == 2) {
986                                if (preg_match('/\((UID|RFC822\.SIZE) ([0-9]+)/', $line, $matches)) {
987                                        $result[$id] = trim($matches[2]);
988                                } else {
989                                        $result[$id] = 0;
990                                }
991                        } else if ($mode == 3) {
992                                if (!$flags && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
993                                        $flags = explode(' ', $matches[1]);
994                                }
995                                $result[$id] = in_array('\\'.$index_field, $flags) ? 1 : 0;
996                        } else if ($mode == 4) {
997                                if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches)) {
998                                        $result[$id] = iil_StrToTime($matches[1]);
999                                } else {
1000                                        $result[$id] = 0;
1001                                }
1002                        }
1003                }
1004        } while (!iil_StartsWith($line, $key, true));
1005
1006        return $result;
1007}
1008
1009function iil_CompressMessageSet($message_set) {
1010        //given a comma delimited list of independent mid's,
1011        //compresses by grouping sequences together
1012       
1013        //if less than 255 bytes long, let's not bother
1014        if (strlen($message_set)<255) {
1015            return $message_set;
1016        }
1017   
1018        //see if it's already been compress
1019        if (strpos($message_set, ':') !== false) {
1020            return $message_set;
1021        }
1022   
1023        //separate, then sort
1024        $ids = explode(',', $message_set);
1025        sort($ids);
1026       
1027        $result = array();
1028        $start  = $prev = $ids[0];
1029
1030        foreach ($ids as $id) {
1031                $incr = $id - $prev;
1032                if ($incr > 1) {                        //found a gap
1033                        if ($start == $prev) {
1034                            $result[] = $prev;  //push single id
1035                        } else {
1036                            $result[] = $start . ':' . $prev;   //push sequence as start_id:end_id
1037                        }
1038                        $start = $id;                   //start of new sequence
1039                }
1040                $prev = $id;
1041        }
1042
1043        //handle the last sequence/id
1044        if ($start==$prev) {
1045            $result[] = $prev;
1046        } else {
1047            $result[] = $start.':'.$prev;
1048        }
1049   
1050        //return as comma separated string
1051        return implode(',', $result);
1052}
1053
1054function iil_C_UIDsToMIDs(&$conn, $mailbox, $uids) {
1055        if (!is_array($uids) || count($uids) == 0) {
1056            return array();
1057        }
1058        return iil_C_Search($conn, $mailbox, 'UID ' . implode(',', $uids));
1059}
1060
1061function iil_C_UIDToMID(&$conn, $mailbox, $uid) {
1062        $result = iil_C_UIDsToMIDs($conn, $mailbox, array($uid));
1063        if (count($result) == 1) {
1064            return $result[0];
1065        }
1066        return false;
1067}
1068
1069function iil_C_FetchUIDs(&$conn,$mailbox) {
1070        global $clock;
1071       
1072        $num = iil_C_CountMessages($conn, $mailbox);
1073        if ($num == 0) {
1074            return array();
1075        }
1076        $message_set = '1' . ($num>1?':' . $num:'');
1077       
1078        return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
1079}
1080
1081function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='')
1082{
1083        global $IMAP_USE_INTERNAL_DATE;
1084       
1085        $result = array();
1086        $fp     = $conn->fp;
1087       
1088        /*  Do "SELECT" command */
1089        if (!iil_C_Select($conn, $mailbox)) {
1090                $conn->error = "Couldn't select $mailbox";
1091                return false;
1092        }
1093
1094        if (is_array($message_set))
1095                $message_set = join(',', $message_set);
1096
1097        $message_set = iil_CompressMessageSet($message_set);
1098
1099        if ($add)
1100                $add = ' '.strtoupper(trim($add));
1101
1102        /* FETCH uid, size, flags and headers */
1103        $key      = 'FH12';
1104        $request  = $key . ($uidfetch ? ' UID' : '') . " FETCH $message_set ";
1105        $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE ";
1106        if ($bodystr)
1107                $request .= "BODYSTRUCTURE ";
1108        $request .= "BODY.PEEK[HEADER.FIELDS ";
1109        $request .= "(DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC ";
1110        $request .= "CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID ";
1111        $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY ";
1112        $request .= "X-DRAFT-INFO".$add.")])";
1113
1114        if (!iil_PutLine($fp, $request)) {
1115                return false;
1116        }
1117        do {
1118                $line = iil_ReadLine($fp, 1024);
1119                $line = iil_MultLine($fp, $line);
1120
1121                $a    = explode(' ', $line);
1122                if (($line[0] == '*') && ($a[2] == 'FETCH')) {
1123                        $id = $a[1];
1124           
1125                        $result[$id]            = new iilBasicHeader;
1126                        $result[$id]->id        = $id;
1127                        $result[$id]->subject   = '';
1128                        $result[$id]->messageID = 'mid:' . $id;
1129
1130                        $lines = array();
1131                        $ln = 0;
1132                        /*
1133                            Sample reply line:
1134                            * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
1135                            INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODYSTRUCTURE (...)
1136                            BODY[HEADER.FIELDS ...
1137                        */
1138
1139                        if (preg_match('/^\* [0-9]+ FETCH \((.*) BODY/s', $line, $matches)) {
1140                                $str = $matches[1];
1141
1142                                // swap parents with quotes, then explode
1143                                $str = preg_replace('/[()]/', '"', $str);
1144                                $a = iil_ExplodeQuotedString(' ', $str);
1145
1146                                // did we get the right number of replies?
1147                                $parts_count = count($a);
1148                                if ($parts_count>=6) {
1149                                        for ($i=0; $i<$parts_count; $i=$i+2) {
1150                                                if ($a[$i] == 'UID')
1151                                                        $result[$id]->uid = $a[$i+1];
1152                                                else if ($a[$i] == 'RFC822.SIZE')
1153                                                        $result[$id]->size = $a[$i+1];
1154                                                else if ($a[$i] == 'INTERNALDATE')
1155                                                        $time_str = $a[$i+1];
1156                                                else if ($a[$i] == 'FLAGS')
1157                                                        $flags_str = $a[$i+1];
1158                                        }
1159
1160                                        $time_str = str_replace('"', '', $time_str);
1161                                       
1162                                        // if time is gmt...
1163                                        $time_str = str_replace('GMT','+0000',$time_str);
1164                                       
1165                                        $result[$id]->internaldate = $time_str;
1166                                        $result[$id]->timestamp = iil_StrToTime($time_str);
1167                                        $result[$id]->date = $time_str;
1168                                }
1169
1170                                // BODYSTRUCTURE
1171                                if($bodystr) {
1172                                        while (!preg_match('/ BODYSTRUCTURE (.*) BODY\[HEADER.FIELDS/s', $line, $m)) {
1173                                                $line2 = iil_ReadLine($fp, 1024);
1174                                                $line .= iil_MultLine($fp, $line2, true);
1175                                        }
1176                                        $result[$id]->body_structure = $m[1];
1177                                }
1178
1179                                // the rest of the result
1180                                preg_match('/ BODY\[HEADER.FIELDS \(.*?\)\]\s*(.*)$/s', $line, $m);
1181                                $reslines = explode("\n", trim($m[1], '"'));
1182                                // re-parse (see below)
1183                                foreach ($reslines as $resln) {
1184                                        if (ord($resln[0])<=32) {
1185                                                $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($resln);
1186                                        } else {
1187                                                $lines[++$ln] = trim($resln);
1188                                        }
1189                                }
1190                        }
1191
1192                        /*
1193                                Start parsing headers.  The problem is, some header "lines" take up multiple lines.
1194                                So, we'll read ahead, and if the one we're reading now is a valid header, we'll
1195                                process the previous line.  Otherwise, we'll keep adding the strings until we come
1196                                to the next valid header line.
1197                        */
1198       
1199                        do {
1200                                $line = chop(iil_ReadLine($fp, 300), "\r\n");
1201
1202                                // The preg_match below works around communigate imap, which outputs " UID <number>)".
1203                                // Without this, the while statement continues on and gets the "FH0 OK completed" message.
1204                                // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249. 
1205                                // This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
1206                                // If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
1207                                // An alternative might be:
1208                                // if (!preg_match("/:/",$line) && preg_match("/\)$/",$line)) break;
1209                                // however, unsure how well this would work with all imap clients.
1210                                if (preg_match("/^\s*UID [0-9]+\)$/", $line)) {
1211                                    break;
1212                                }
1213
1214                                // handle FLAGS reply after headers (AOL, Zimbra?)
1215                                if (preg_match('/\s+FLAGS \((.*)\)\)$/', $line, $matches)) {
1216                                        $flags_str = $matches[1];
1217                                        break;
1218                                }
1219
1220                                if (ord($line[0])<=32) {
1221                                        $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($line);
1222                                } else {
1223                                        $lines[++$ln] = trim($line);
1224                                }
1225                        // patch from "Maksim Rubis" <siburny@hotmail.com>
1226                        } while ($line[0] != ')' && !iil_StartsWith($line, $key, true));
1227
1228                        if (strncmp($line, $key, strlen($key))) {
1229                                // process header, fill iilBasicHeader obj.
1230                                // initialize
1231                                if (is_array($headers)) {
1232                                        reset($headers);
1233                                        while (list($k, $bar) = each($headers)) {
1234                                                $headers[$k] = '';
1235                                        }
1236                                }
1237
1238                                // create array with header field:data
1239                                while ( list($lines_key, $str) = each($lines) ) {
1240                                        list($field, $string) = iil_SplitHeaderLine($str);
1241                                       
1242                                        $field  = strtolower($field);
1243                                        $string = preg_replace('/\n\s*/', ' ', $string);
1244                                       
1245                                        switch ($field) {
1246                                        case 'date';
1247                                                if (!$IMAP_USE_INTERNAL_DATE) {
1248                                                        $result[$id]->date = $string;
1249                                                        $result[$id]->timestamp = iil_StrToTime($string);
1250                                                }
1251                                                break;
1252                                        case 'from':
1253                                                $result[$id]->from = $string;
1254                                                break;
1255                                        case 'to':
1256                                                $result[$id]->to = preg_replace('/undisclosed-recipients:[;,]*/', '', $string);
1257                                                break;
1258                                        case 'subject':
1259                                                $result[$id]->subject = $string;
1260                                                break;
1261                                        case 'reply-to':
1262                                                $result[$id]->replyto = $string;
1263                                                break;
1264                                        case 'cc':
1265                                                $result[$id]->cc = $string;
1266                                                break;
1267                                        case 'bcc':
1268                                                $result[$id]->bcc = $string;
1269                                                break;
1270                                        case 'content-transfer-encoding':
1271                                                $result[$id]->encoding = $string;
1272                                                break;
1273                                        case 'content-type':
1274                                                $ctype_parts = preg_split('/[; ]/', $string);
1275                                                $result[$id]->ctype = array_shift($ctype_parts);
1276                                                if (preg_match('/charset\s*=\s*"?([a-z0-9\-\.\_]+)"?/i', $string, $regs)) {
1277                                                        $result[$id]->charset = $regs[1];
1278                                                }
1279                                                break;
1280                                        case 'in-reply-to':
1281                                                $result[$id]->in_reply_to = preg_replace('/[\n<>]/', '', $string);
1282                                                break;
1283                                        case 'references':
1284                                                $result[$id]->references = $string;
1285                                                break;
1286                                        case 'return-receipt-to':
1287                                        case 'disposition-notification-to':
1288                                        case 'x-confirm-reading-to':
1289                                                $result[$id]->mdn_to = $string;
1290                                                break;
1291                                        case 'message-id':
1292                                                $result[$id]->messageID = $string;
1293                                                break;
1294                                        case 'x-priority':
1295                                                if (preg_match('/^(\d+)/', $string, $matches))
1296                                                        $result[$id]->priority = intval($matches[1]);
1297                                                break;
1298                                        default:
1299                                                if (strlen($field) > 2)
1300                                                        $result[$id]->others[$field] = $string;
1301                                                break;
1302                                        } // end switch ()
1303                                } // end while ()
1304                        } else {
1305                                $a = explode(' ', $line);
1306                        }
1307
1308                        // process flags
1309                        if (!empty($flags_str)) {
1310                                $flags_str = preg_replace('/[\\\"]/', '', $flags_str);
1311                                $flags_a   = explode(' ', $flags_str);
1312                                       
1313                                if (is_array($flags_a)) {
1314                                //      reset($flags_a);
1315                                        foreach($flags_a as $flag) {
1316                                                $flag = strtoupper($flag);
1317                                                if ($flag == 'SEEN') {
1318                                                    $result[$id]->seen = true;
1319                                                } else if ($flag == 'DELETED') {
1320                                                    $result[$id]->deleted = true;
1321                                                } else if ($flag == 'RECENT') {
1322                                                    $result[$id]->recent = true;
1323                                                } else if ($flag == 'ANSWERED') {
1324                                                        $result[$id]->answered = true;
1325                                                } else if ($flag == '$FORWARDED') {
1326                                                        $result[$id]->forwarded = true;
1327                                                } else if ($flag == 'DRAFT') {
1328                                                        $result[$id]->is_draft = true;
1329                                                } else if ($flag == '$MDNSENT') {
1330                                                        $result[$id]->mdn_sent = true;
1331                                                } else if ($flag == 'FLAGGED') {
1332                                                         $result[$id]->flagged = true;
1333                                                }
1334                                        }
1335                                        $result[$id]->flags = $flags_a;
1336                                }
1337                        }
1338                }
1339        } while (!iil_StartsWith($line, $key, true));
1340
1341        return $result;
1342}
1343
1344function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false, $bodystr=false, $add='') {
1345
1346        $a  = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch, $bodystr, $add);
1347        if (is_array($a)) {
1348                return array_shift($a);
1349        }
1350        return false;
1351}
1352
1353function iil_SortHeaders($a, $field, $flag) {
1354        if (empty($field)) {
1355            $field = 'uid';
1356        }
1357        $field = strtolower($field);
1358        if ($field == 'date' || $field == 'internaldate') {
1359            $field = 'timestamp';
1360        }
1361        if (empty($flag)) {
1362            $flag = 'ASC';
1363        }
1364   
1365        $flag     = strtoupper($flag);
1366        $stripArr = ($field=='subject') ? array('Re: ','Fwd: ','Fw: ','"') : array('"');
1367
1368        $c=count($a);
1369        if ($c > 0) {
1370                /*
1371                        Strategy:
1372                        First, we'll create an "index" array.
1373                        Then, we'll use sort() on that array,
1374                        and use that to sort the main array.
1375                */
1376               
1377                // create "index" array
1378                $index = array();
1379                reset($a);
1380                while (list($key, $val)=each($a)) {
1381
1382                        if ($field == 'timestamp') {
1383                                $data = iil_StrToTime($val->date);
1384                                if (!$data) {
1385                                        $data = $val->timestamp;
1386                                }
1387                        } else {
1388                                $data = $val->$field;
1389                                if (is_string($data)) {
1390                                        $data=strtoupper(str_replace($stripArr, '', $data));
1391                                }
1392                        }
1393                        $index[$key]=$data;
1394                }
1395               
1396                // sort index
1397                $i = 0;
1398                if ($flag == 'ASC') {
1399                        asort($index);
1400                } else {
1401                        arsort($index);
1402                }
1403       
1404                // form new array based on index
1405                $result = array();
1406                reset($index);
1407                while (list($key, $val)=each($index)) {
1408                        $result[$key]=$a[$key];
1409                        $i++;
1410                }
1411        }
1412       
1413        return $result;
1414}
1415
1416function iil_C_Expunge(&$conn, $mailbox, $messages=NULL) {
1417
1418        if (iil_C_Select($conn, $mailbox)) {
1419                $c = 0;
1420                $command = $messages ? "UID EXPUNGE $messages" : "EXPUNGE";
1421
1422                iil_PutLine($conn->fp, "exp1 $command");
1423                do {
1424                        $line = iil_ReadLine($conn->fp, 100);
1425                        if ($line[0] == '*') {
1426                $c++;
1427                }
1428                } while (!iil_StartsWith($line, 'exp1', true));
1429               
1430                if (iil_ParseResult($line) == 0) {
1431                        $conn->selected = ''; //state has changed, need to reselect                     
1432                        //$conn->exists-=$c;
1433                        return $c;
1434                }
1435                $conn->error = $line;
1436        }
1437       
1438        return -1;
1439}
1440
1441function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod) {
1442        if ($mod != '+' && $mod != '-') {
1443            return -1;
1444        }
1445   
1446        $flags = $GLOBALS['IMAP_FLAGS'];
1447        $flag = $flags[strtoupper($flag)];
1448   
1449        if (!iil_C_Select($conn, $mailbox)) {
1450            return -1;
1451        }
1452   
1453    $c = 0;
1454        iil_PutLine($conn->fp, "flg UID STORE $messages " . $mod . "FLAGS (" . $flag . ")");
1455        do {
1456                $line = iil_ReadLine($conn->fp, 1000);
1457                if ($line[0] == '*') {
1458                    $c++;
1459        }
1460        } while (!iil_StartsWith($line, 'flg', true));
1461
1462        if (iil_ParseResult($line) == 0) {
1463                return $c;
1464        }
1465
1466        $conn->error = $line;
1467        return -1;
1468}
1469
1470function iil_C_Flag(&$conn, $mailbox, $messages, $flag) {
1471        return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '+');
1472}
1473
1474function iil_C_Unflag(&$conn, $mailbox, $messages, $flag) {
1475        return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '-');
1476}
1477
1478function iil_C_Delete(&$conn, $mailbox, $messages) {
1479        return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '+');
1480}
1481
1482function iil_C_Copy(&$conn, $messages, $from, $to) {
1483
1484        if (empty($from) || empty($to)) {
1485            return -1;
1486        }
1487   
1488        if (!iil_C_Select($conn, $from)) {
1489        return -1;
1490        }
1491       
1492    iil_PutLine($conn->fp, "cpy1 UID COPY $messages \"".iil_Escape($to)."\"");
1493        $line = iil_ReadReply($conn->fp);
1494        return iil_ParseResult($line);
1495}
1496
1497function iil_C_CountUnseen(&$conn, $folder) {
1498    $index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
1499    if (is_array($index))
1500        return count($index);
1501    return false;
1502}
1503
1504function iil_C_UID2ID(&$conn, $folder, $uid) {
1505        if ($uid > 0) {
1506                $id_a = iil_C_Search($conn, $folder, "UID $uid");
1507                if (is_array($id_a) && count($id_a) == 1) {
1508                        return $id_a[0];
1509                }
1510        }
1511        return false;
1512}
1513
1514function iil_C_ID2UID(&$conn, $folder, $id) {
1515
1516        if ($id == 0) {
1517            return      -1;
1518        }
1519        $result = -1;
1520        if (iil_C_Select($conn, $folder)) {
1521                $key = 'fuid';
1522                if (iil_PutLine($conn->fp, "$key FETCH $id (UID)")) {
1523                        do {
1524                                $line = chop(iil_ReadLine($conn->fp, 1024));
1525                                if (preg_match("/^\* $id FETCH \(UID (.*)\)/i", $line, $r)) {
1526                                        $result = $r[1];
1527                                }
1528                        } while (!iil_StartsWith($line, $key, true));
1529                }
1530        }
1531        return $result;
1532}
1533
1534// Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
1535// 7 times instead :-) See comments on http://uk2.php.net/references and this article:
1536// http://derickrethans.nl/files/phparch-php-variables-article.pdf
1537function iil_ParseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) {
1538        $node = array();
1539        if ($str[$begin] != '(') {
1540                $stop = $begin + strspn($str, "1234567890", $begin, $end - $begin);
1541                $msg = substr($str, $begin, $stop - $begin);
1542                if ($msg == 0)
1543                    return $node;
1544                if (is_null($root))
1545                        $root = $msg;
1546                $depthmap[$msg] = $depth;
1547                $haschildren[$msg] = false;
1548                if (!is_null($parent))
1549                        $haschildren[$parent] = true;
1550                if ($stop + 1 < $end)
1551                        $node[$msg] = iil_ParseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren);
1552                else
1553                        $node[$msg] = array();
1554        } else {
1555                $off = $begin;
1556                while ($off < $end) {
1557                        $start = $off;
1558                        $off++;
1559                        $n = 1;
1560                        while ($n > 0) {
1561                                $p = strpos($str, ')', $off);
1562                                if ($p === false) {
1563                                        error_log('Mismatched brackets parsing IMAP THREAD response:');
1564                                        error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20));
1565                                        error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10))));
1566                                        return $node;
1567                                }
1568                                $p1 = strpos($str, '(', $off);
1569                                if ($p1 !== false && $p1 < $p) {
1570                                        $off = $p1 + 1;
1571                                        $n++;
1572                                } else {
1573                                        $off = $p + 1;
1574                                        $n--;
1575                                }
1576                        }
1577                        $node += iil_ParseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren);
1578                }
1579        }
1580       
1581        return $node;
1582}
1583
1584function iil_C_Thread(&$conn, $folder, $algorithm='REFERENCES', $criteria='',
1585    $encoding='US-ASCII') {
1586
1587        if (!iil_C_Select($conn, $folder)) {
1588                $conn->error = "Couldn't select $folder";
1589                return false;
1590        }
1591
1592        $encoding = $encoding ? trim($encoding) : 'US-ASCII';
1593        $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES';
1594        $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL';
1595               
1596        if (!iil_PutLineC($conn->fp, "thrd1 THREAD $algorithm $encoding $criteria")) {
1597                return false;
1598        }
1599        do {
1600                $line = trim(iil_ReadLine($conn->fp, 10000));
1601                if (preg_match('/^\* THREAD/', $line)) {
1602                        $str = trim(substr($line, 8));
1603                        $depthmap = array();
1604                        $haschildren = array();
1605                        $tree = iil_ParseThread($str, 0, strlen($str), null, null, 0, $depthmap, $haschildren);
1606                }
1607        } while (!iil_StartsWith($line, 'thrd1', true));
1608
1609        $result_code = iil_ParseResult($line);
1610        if ($result_code == 0) {
1611            return array($tree, $depthmap, $haschildren);
1612        }
1613
1614        $conn->error = 'iil_C_Thread: ' . $line . "\n";
1615        return false;   
1616}
1617
1618function iil_C_Search(&$conn, $folder, $criteria) {
1619
1620        if (!iil_C_Select($conn, $folder)) {
1621                $conn->error = "Couldn't select $folder";
1622                return false;
1623        }
1624
1625        $data = '';
1626        $query = 'srch1 SEARCH ' . chop($criteria);
1627
1628        if (!iil_PutLineC($conn->fp, $query)) {
1629                return false;
1630        }
1631        do {
1632                $line = trim(iil_ReadLine($conn->fp));
1633                if (iil_StartsWith($line, '* SEARCH')) {
1634                        $data .= substr($line, 8);
1635                } else if (preg_match('/^[0-9 ]+$/', $line)) {
1636                        $data .= $line;
1637                }
1638        } while (!iil_StartsWith($line, 'srch1', true));
1639
1640        $result_code = iil_ParseResult($line);
1641        if ($result_code == 0) {
1642                return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY);
1643        }
1644
1645        $conn->error = 'iil_C_Search: ' . $line . "\n";
1646        return false;   
1647}
1648
1649function iil_C_Move(&$conn, $messages, $from, $to) {
1650
1651    if (!$from || !$to) {
1652        return -1;
1653    }
1654   
1655    $r = iil_C_Copy($conn, $messages, $from, $to);
1656
1657    if ($r==0) {
1658        return iil_C_Delete($conn, $from, $messages);
1659    }
1660    return $r;
1661}
1662
1663/**
1664 * Gets the delimiter, for example:
1665 * INBOX.foo -> .
1666 * INBOX/foo -> /
1667 * INBOX\foo -> \
1668 *
1669 * @return mixed A delimiter (string), or false.
1670 * @param object $conn The current connection.
1671 * @see iil_Connect()
1672 */
1673function iil_C_GetHierarchyDelimiter(&$conn) {
1674
1675        global $my_prefs;
1676       
1677        if ($conn->delimiter) {
1678                return $conn->delimiter;
1679        }
1680        if (!empty($my_prefs['delimiter'])) {
1681            return ($conn->delimiter = $my_prefs['delimiter']);
1682        }
1683   
1684        $fp        = $conn->fp;
1685        $delimiter = false;
1686       
1687        //try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
1688        if (!iil_PutLine($fp, 'ghd LIST "" ""')) {
1689            return false;
1690        }
1691   
1692        do {
1693                $line=iil_ReadLine($fp, 500);
1694                if ($line[0] == '*') {
1695                        $line = rtrim($line);
1696                        $a=iil_ExplodeQuotedString(' ', iil_UnEscape($line));
1697                        if ($a[0] == '*') {
1698                            $delimiter = str_replace('"', '', $a[count($a)-2]);
1699                        }
1700                }
1701        } while (!iil_StartsWith($line, 'ghd', true));
1702
1703        if (strlen($delimiter)>0) {
1704            return $delimiter;
1705        }
1706
1707        //if that fails, try namespace extension
1708        //try to fetch namespace data
1709        iil_PutLine($conn->fp, "ns1 NAMESPACE");
1710        do {
1711                $line = iil_ReadLine($conn->fp, 1024);
1712                if (iil_StartsWith($line, '* NAMESPACE')) {
1713                        $i = 0;
1714                        $line = iil_UnEscape($line);
1715                        $data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
1716                }
1717        } while (!iil_StartsWith($line, 'ns1', true));
1718               
1719        if (!is_array($data)) {
1720            return false;
1721        }
1722   
1723        //extract user space data (opposed to global/shared space)
1724        $user_space_data = $data[0];
1725        if (!is_array($user_space_data)) {
1726            return false;
1727        }
1728   
1729        //get first element
1730        $first_userspace = $user_space_data[0];
1731        if (!is_array($first_userspace)) {
1732            return false;
1733        }
1734   
1735        //extract delimiter
1736        $delimiter = $first_userspace[1];       
1737
1738        return $delimiter;
1739}
1740
1741function iil_C_ListMailboxes(&$conn, $ref, $mailbox) {
1742               
1743        $fp = $conn->fp;
1744       
1745        if (empty($mailbox)) {
1746            $mailbox = '*';
1747        }
1748       
1749        if (empty($ref) && $conn->rootdir) {
1750            $ref = $conn->rootdir;
1751        }
1752   
1753        // send command
1754        if (!iil_PutLine($fp, "lmb LIST \"".$ref."\" \"".iil_Escape($mailbox)."\"")) {
1755            return false;
1756        }
1757   
1758        $i = 0;
1759        // get folder list
1760        do {
1761                $line = iil_ReadLine($fp, 500);
1762                $line = iil_MultLine($fp, $line, true);
1763
1764                $a = explode(' ', $line);
1765                if (($line[0] == '*') && ($a[1] == 'LIST')) {
1766                        $line = rtrim($line);
1767                        // split one line
1768                        $a = iil_ExplodeQuotedString(' ', $line);
1769                        // last string is folder name
1770                        $folders[$i] = preg_replace(array('/^"/', '/"$/'), '', iil_UnEscape($a[count($a)-1]));
1771           
1772                        // second from last is delimiter
1773                        $delim = trim($a[count($a)-2], '"');
1774                        // is it a container?
1775                        $i++;
1776                }
1777        } while (!iil_StartsWith($line, 'lmb', true));
1778
1779        if (is_array($folders)) {
1780            if (!empty($ref)) {
1781                // if rootdir was specified, make sure it's the first element
1782                // some IMAP servers (i.e. Courier) won't return it
1783                if ($ref[strlen($ref)-1]==$delim)
1784                    $ref = substr($ref, 0, strlen($ref)-1);
1785                if ($folders[0]!=$ref)
1786                    array_unshift($folders, $ref);
1787            }
1788            return $folders;
1789        } else if (iil_ParseResult($line) == 0) {
1790                return array('INBOX');
1791        } else {
1792                $conn->error = $line;
1793                return false;
1794        }
1795}
1796
1797function iil_C_ListSubscribed(&$conn, $ref, $mailbox) {
1798       
1799        $fp = $conn->fp;
1800        if (empty($mailbox)) {
1801                $mailbox = '*';
1802        }
1803        if (empty($ref) && $conn->rootdir) {
1804                $ref = $conn->rootdir;
1805        }
1806        $folders = array();
1807
1808        // send command
1809        if (!iil_PutLine($fp, 'lsb LSUB "' . $ref . '" "' . iil_Escape($mailbox).'"')) {
1810                $conn->error = "Couldn't send LSUB command\n";
1811                return false;
1812        }
1813       
1814        $i = 0;
1815       
1816        // get folder list
1817        do {
1818                $line = iil_ReadLine($fp, 500);
1819                $line = iil_MultLine($fp, $line, true);
1820                $a    = explode(' ', $line);
1821       
1822                if (($line[0] == '*') && ($a[1] == 'LSUB' || $a[1] == 'LIST')) {
1823                        $line = rtrim($line);
1824           
1825                        // split one line
1826                        $a = iil_ExplodeQuotedString(' ', $line);
1827                        // last string is folder name
1828                        $folder = preg_replace(array('/^"/', '/"$/'), '', iil_UnEscape($a[count($a)-1]));
1829
1830                        // @TODO: do we need this check???
1831                        if (!in_array($folder, $folders)) {
1832                            $folders[$i] = $folder;
1833                        }
1834           
1835                        // second from last is delimiter
1836                        $delim = trim($a[count($a)-2], '"');
1837           
1838                        // is it a container?
1839                        $i++;
1840                }
1841        } while (!iil_StartsWith($line, 'lsb', true));
1842
1843        if (is_array($folders)) {
1844            if (!empty($ref)) {
1845                // if rootdir was specified, make sure it's the first element
1846                // some IMAP servers (i.e. Courier) won't return it
1847                if ($ref[strlen($ref)-1]==$delim) {
1848                    $ref = substr($ref, 0, strlen($ref)-1);
1849                }
1850                if ($folders[0]!=$ref) {
1851                    array_unshift($folders, $ref);
1852                }
1853            }
1854            return $folders;
1855        }
1856        $conn->error = $line;
1857        return false;
1858}
1859
1860function iil_C_Subscribe(&$conn, $folder) {
1861        $fp = $conn->fp;
1862
1863        $query = 'sub1 SUBSCRIBE "' . iil_Escape($folder). '"';
1864        iil_PutLine($fp, $query);
1865
1866        $line = trim(iil_ReadLine($fp, 512));
1867        return (iil_ParseResult($line) == 0);
1868}
1869
1870function iil_C_UnSubscribe(&$conn, $folder) {
1871        $fp = $conn->fp;
1872
1873        $query = 'usub1 UNSUBSCRIBE "' . iil_Escape($folder) . '"';
1874        iil_PutLine($fp, $query);
1875   
1876        $line = trim(iil_ReadLine($fp, 512));
1877        return (iil_ParseResult($line) == 0);
1878}
1879
1880function iil_C_FetchMIMEHeaders(&$conn, $mailbox, $id, $parts, $mime=true) {
1881       
1882        $fp     = $conn->fp;
1883
1884        if (!iil_C_Select($conn, $mailbox)) {
1885                return false;
1886        }
1887       
1888        $result = false;
1889        $parts = (array) $parts;
1890        $key = 'fmh0';
1891        $peeks = '';
1892        $idx = 0;
1893        $type = $mime ? 'MIME' : 'HEADER';
1894
1895        // format request
1896        foreach($parts as $part)
1897                $peeks[] = "BODY.PEEK[$part.$type]";
1898       
1899        $request = "$key FETCH $id (" . implode(' ', $peeks) . ')';
1900
1901        // send request
1902        if (!iil_PutLine($fp, $request)) {
1903            return false;
1904        }
1905       
1906        do {
1907                $line = iil_ReadLine($fp, 1000);
1908                $line = iil_MultLine($fp, $line);
1909
1910                if (preg_match('/BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) {
1911                        $idx = $matches[1];
1912                        $result[$idx] = preg_replace('/^(\* '.$id.' FETCH \()?\s*BODY\['.$idx.'\.'.$type.'\]\s+/', '', $line);
1913                        $result[$idx] = trim($result[$idx], '"');
1914                        $result[$idx] = rtrim($result[$idx], "\t\r\n\0\x0B");
1915                }
1916        } while (!iil_StartsWith($line, $key, true));
1917
1918        return $result;
1919}
1920
1921function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $is_uid=false, $part=NULL) {
1922
1923        $part = empty($part) ? 'HEADER' : $part.'.MIME';
1924
1925        return iil_C_HandlePartBody($conn, $mailbox, $id, $is_uid, $part);
1926}
1927
1928function iil_C_HandlePartBody(&$conn, $mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL) {
1929       
1930        $fp     = $conn->fp;
1931        $result = false;
1932       
1933        switch ($encoding) {
1934                case 'base64':
1935                        $mode = 1;
1936                break;
1937                case 'quoted-printable':
1938                        $mode = 2;
1939                break;
1940                case 'x-uuencode':
1941                case 'x-uue':
1942                case 'uue':
1943                case 'uuencode':
1944                        $mode = 3;
1945                break;
1946                default:
1947                        $mode = 0;
1948        }
1949       
1950        if (iil_C_Select($conn, $mailbox)) {
1951                $reply_key = '* ' . $id;
1952
1953                // format request
1954                $key     = 'ftch0';
1955                $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id (BODY.PEEK[$part])";
1956                // send request
1957                if (!iil_PutLine($fp, $request)) {
1958                    return false;
1959                }
1960
1961                // receive reply line
1962                do {
1963                        $line = chop(iil_ReadLine($fp, 1000));
1964                        $a    = explode(' ', $line);
1965                } while (!($end = iil_StartsWith($line, $key, true)) && $a[2] != 'FETCH');
1966                $len = strlen($line);
1967
1968                // handle empty "* X FETCH ()" response
1969                if ($line[$len-1] == ')' && $line[$len-2] != '(') {
1970                        // one line response, get everything between first and last quotes
1971                        if (substr($line, -4, 3) == 'NIL') {
1972                                // NIL response
1973                                $result = '';
1974                        } else {
1975                                $from = strpos($line, '"') + 1;
1976                                $to   = strrpos($line, '"');
1977                                $len  = $to - $from;
1978                                $result = substr($line, $from, $len);
1979                        }
1980
1981                        if ($mode == 1)
1982                                $result = base64_decode($result);
1983                        else if ($mode == 2)
1984                                $result = quoted_printable_decode($result);
1985                        else if ($mode == 3)
1986                                $result = convert_uudecode($result);
1987
1988                } else if ($line[$len-1] == '}') {
1989                        //multi-line request, find sizes of content and receive that many bytes
1990                        $from     = strpos($line, '{') + 1;
1991                        $to       = strrpos($line, '}');
1992                        $len      = $to - $from;
1993                        $sizeStr  = substr($line, $from, $len);
1994                        $bytes    = (int)$sizeStr;
1995                        $prev     = '';
1996                       
1997                        while ($bytes > 0) {
1998                                $line      = iil_ReadLine($fp, 1024);
1999                                $len       = strlen($line);
2000               
2001                                if ($len > $bytes) {
2002                                        $line = substr($line, 0, $bytes);
2003                                        $len = strlen($line);
2004                                }
2005                                $bytes -= $len;
2006
2007                                if ($mode == 1) {
2008                                        $line = rtrim($line, "\t\r\n\0\x0B");
2009                                        // create chunks with proper length for base64 decoding
2010                                        $line = $prev.$line;
2011                                        $length = strlen($line);
2012                                        if ($length % 4) {
2013                                                $length = floor($length / 4) * 4;
2014                                                $prev = substr($line, $length);
2015                                                $line = substr($line, 0, $length);
2016                                        }
2017                                        else
2018                                                $prev = '';
2019                                               
2020                                        if ($file)
2021                                                fwrite($file, base64_decode($line));
2022                                        else if ($print)
2023                                                echo base64_decode($line);
2024                                        else
2025                                                $result .= base64_decode($line);
2026                                } else if ($mode == 2) {
2027                                        $line = rtrim($line, "\t\r\0\x0B");
2028                                        if ($file)
2029                                                fwrite($file, quoted_printable_decode($line));
2030                                        else if ($print)
2031                                                echo quoted_printable_decode($line);
2032                                        else
2033                                                $result .= quoted_printable_decode($line);
2034                                } else if ($mode == 3) {
2035                                        $line = rtrim($line, "\t\r\n\0\x0B");
2036                                        if ($line == 'end' || preg_match('/^begin\s+[0-7]+\s+.+$/', $line))
2037                                                continue;
2038                                        if ($file)
2039                                                fwrite($file, convert_uudecode($line));
2040                                        else if ($print)
2041                                                echo convert_uudecode($line);
2042                                        else
2043                                                $result .= convert_uudecode($line);
2044                                } else {
2045                                        $line = rtrim($line, "\t\r\n\0\x0B");
2046                                        if ($file)
2047                                                fwrite($file, $line . "\n");
2048                                        else if ($print)
2049                                                echo $line . "\n";
2050                                        else
2051                                                $result .= $line . "\n";
2052                                }
2053                        }
2054                }
2055                // read in anything up until last line
2056                if (!$end)
2057                        do {
2058                                $line = iil_ReadLine($fp, 1024);
2059                        } while (!iil_StartsWith($line, $key, true));
2060
2061                if ($result) {
2062                        if ($file) {
2063                                fwrite($file, $result);
2064                        } else if ($print) {
2065                                echo $result;
2066                        } else
2067                                return $result;
2068
2069                        return true;
2070                }
2071        }
2072
2073        return false;
2074}
2075
2076function iil_C_CreateFolder(&$conn, $folder) {
2077        $fp = $conn->fp;
2078        if (iil_PutLine($fp, 'c CREATE "' . iil_Escape($folder) . '"')) {
2079                do {
2080                        $line=iil_ReadLine($fp, 300);
2081                } while (!iil_StartsWith($line, 'c ', true));
2082                return (iil_ParseResult($line) == 0);
2083        }
2084        return false;
2085}
2086
2087function iil_C_RenameFolder(&$conn, $from, $to) {
2088        $fp = $conn->fp;
2089        if (iil_PutLine($fp, 'r RENAME "' . iil_Escape($from) . '" "' . iil_Escape($to) . '"')) {
2090                do {
2091                        $line = iil_ReadLine($fp, 300);
2092                } while (!iil_StartsWith($line, 'r ', true));
2093                return (iil_ParseResult($line) == 0);
2094        }
2095        return false;
2096}
2097
2098function iil_C_DeleteFolder(&$conn, $folder) {
2099        $fp = $conn->fp;
2100        if (iil_PutLine($fp, 'd DELETE "' . iil_Escape($folder). '"')) {
2101                do {
2102                        $line=iil_ReadLine($fp, 300);
2103                } while (!iil_StartsWith($line, 'd ', true));
2104                return (iil_ParseResult($line) == 0);
2105        }
2106        return false;
2107}
2108
2109function iil_C_Append(&$conn, $folder, &$message) {
2110        if (!$folder) {
2111                return false;
2112        }
2113        $fp = $conn->fp;
2114
2115        $message = str_replace("\r", '', $message);
2116        $message = str_replace("\n", "\r\n", $message);
2117
2118        $len = strlen($message);
2119        if (!$len) {
2120                return false;
2121        }
2122
2123        $request = 'a APPEND "' . iil_Escape($folder) .'" (\\Seen) {' . $len . '}';
2124
2125        if (iil_PutLine($fp, $request)) {
2126                $line = iil_ReadLine($fp, 512);
2127
2128                if ($line[0] != '+') {
2129                        // $errornum = iil_ParseResult($line);
2130                        $conn->error .= "Cannot write to folder: $line\n";
2131                        return false;
2132                }
2133
2134                iil_PutLine($fp, $message);
2135
2136                do {
2137                        $line = iil_ReadLine($fp);
2138                } while (!iil_StartsWith($line, 'a ', true));
2139       
2140                $result = (iil_ParseResult($line) == 0);
2141                if (!$result) {
2142                    $conn->error .= $line . "\n";
2143                }
2144                return $result;
2145        }
2146
2147        $conn->error .= "Couldn't send command \"$request\"\n";
2148        return false;
2149}
2150
2151function iil_C_AppendFromFile(&$conn, $folder, $path, $headers=null, $separator="\n\n") {
2152        if (!$folder) {
2153            return false;
2154        }
2155   
2156        //open message file
2157        $in_fp = false;
2158        if (file_exists(realpath($path))) {
2159                $in_fp = fopen($path, 'r');
2160        }
2161        if (!$in_fp) {
2162                $conn->error .= "Couldn't open $path for reading\n";
2163                return false;
2164        }
2165       
2166        $fp  = $conn->fp;
2167        $len = filesize($path);
2168        if (!$len) {
2169                return false;
2170        }
2171
2172        if ($headers) {
2173                $headers = preg_replace('/[\r\n]+$/', '', $headers);
2174                $len += strlen($headers) + strlen($separator);
2175        }
2176
2177        //send APPEND command
2178        $request    = 'a APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
2179        if (iil_PutLine($fp, $request)) {
2180                $line = iil_ReadLine($fp, 512);
2181
2182                if ($line[0] != '+') {
2183                        //$errornum = iil_ParseResult($line);
2184                        $conn->error .= "Cannot write to folder: $line\n";
2185                        return false;
2186                }
2187
2188                // send headers with body separator
2189                if ($headers) {
2190                        iil_PutLine($fp, $headers . $separator, false);
2191                }
2192
2193                // send file
2194                while (!feof($in_fp)) {
2195                        $buffer = fgets($in_fp, 4096);
2196                        iil_PutLine($fp, $buffer, false);
2197                }
2198                fclose($in_fp);
2199
2200                iil_PutLine($fp, ''); // \r\n
2201
2202                // read response
2203                do {
2204                        $line = iil_ReadLine($fp);
2205                } while (!iil_StartsWith($line, 'a ', true));
2206
2207                $result = (iil_ParseResult($line) == 0);
2208                if (!$result) {
2209                    $conn->error .= $line . "\n";
2210                }
2211
2212                return $result;
2213        }
2214       
2215        $conn->error .= "Couldn't send command \"$request\"\n";
2216        return false;
2217}
2218
2219function iil_C_FetchStructureString(&$conn, $folder, $id, $is_uid=false) {
2220        $fp     = $conn->fp;
2221        $result = false;
2222       
2223        if (iil_C_Select($conn, $folder)) {
2224                $key = 'F1247';
2225
2226                if (iil_PutLine($fp, $key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)")) {
2227                        do {
2228                                $line = iil_ReadLine($fp, 5000);
2229                                $line = iil_MultLine($fp, $line, true);
2230                                if (!preg_match("/^$key/", $line))
2231                                        $result .= $line;
2232                        } while (!iil_StartsWith($line, $key, true));
2233
2234                        $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -1));
2235                }
2236        }
2237        return $result;
2238}
2239
2240function iil_C_GetQuota(&$conn) {
2241/*
2242 * GETQUOTAROOT "INBOX"
2243 * QUOTAROOT INBOX user/rchijiiwa1
2244 * QUOTA user/rchijiiwa1 (STORAGE 654 9765)
2245 * OK Completed
2246 */
2247        $fp         = $conn->fp;
2248        $result     = false;
2249        $quota_lines = array();
2250       
2251        // get line(s) containing quota info
2252        if (iil_PutLine($fp, 'QUOT1 GETQUOTAROOT "INBOX"')) {
2253                do {
2254                        $line=chop(iil_ReadLine($fp, 5000));
2255                        if (iil_StartsWith($line, '* QUOTA ')) {
2256                                $quota_lines[] = $line;
2257                        }
2258                } while (!iil_StartsWith($line, 'QUOT1', true));
2259        }
2260       
2261        // return false if not found, parse if found
2262        $min_free = PHP_INT_MAX;
2263        foreach ($quota_lines as $key => $quota_line) {
2264                $quota_line   = preg_replace('/[()]/', '', $quota_line);
2265                $parts        = explode(' ', $quota_line);
2266                $storage_part = array_search('STORAGE', $parts);
2267               
2268                if (!$storage_part) continue;
2269       
2270                $used   = intval($parts[$storage_part+1]);
2271                $total  = intval($parts[$storage_part+2]);
2272                $free   = $total - $used;
2273       
2274                // return lowest available space from all quotas
2275                if ($free < $min_free) {
2276                        $min_free = $free;
2277                        $result['used']    = $used;
2278                        $result['total']   = $total;
2279                        $result['percent'] = min(100, round(($used/max(1,$total))*100));
2280                        $result['free']    = 100 - $result['percent'];
2281                }
2282        }
2283        return $result;
2284}
2285
2286function iil_C_ClearFolder(&$conn, $folder) {
2287        $num_in_trash = iil_C_CountMessages($conn, $folder);
2288        if ($num_in_trash > 0) {
2289                iil_C_Delete($conn, $folder, '1:*');
2290        }
2291        return (iil_C_Expunge($conn, $folder) >= 0);
2292}
2293
2294?>
Note: See TracBrowser for help on using the repository browser.