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