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