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