alecpl
2010-03-25 fb7ec576ab3a353b6eb99614e9636a9096462807
program/lib/imap.inc
@@ -84,6 +84,7 @@
      - handling connection startup response
      - added UID EXPUNGE support
      - fixed problem with double quotes and spaces in folder names in LIST and LSUB 
      - rewritten iil_C_FetchHeaderIndex()
********************************************************/
@@ -99,15 +100,6 @@
if (!isset($IMAP_USE_HEADER_DATE) || !$IMAP_USE_HEADER_DATE) {
    $IMAP_USE_INTERNAL_DATE = true;
}
/**
 * @todo Maybe use date() to generate this.
 */
$GLOBALS['IMAP_MONTHS'] = array("Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4,
    "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10,
    "Nov" => 11, "Dec" => 12);
$GLOBALS['IMAP_SERVER_TZ'] = date('Z');
$GLOBALS['IMAP_FLAGS'] = array(
    'SEEN'     => '\\Seen',
@@ -181,18 +173,10 @@
   var $forwarded = false;
   var $junk = false;
   var $flagged = false;
   var $has_children = false;
   var $depth = 0;
   var $unread_children = 0;
   var $others = array();
}
/**
 * @todo Change class vars to public/private
 */
class iilThreadHeader
{
   var $id;
   var $sbj;
   var $irt;
   var $mid;
}
function iil_xor($string, $string2) {
@@ -205,7 +189,11 @@
}
function iil_PutLine($fp, $string, $endln=true) {
//      console('C: '. rtrim($string));
   global $my_prefs;
   if (!empty($my_prefs['debug_mode']))
          write_log('imap', 'C: '. rtrim($string));
        return fputs($fp, $string . ($endln ? "\r\n" : ''));
}
@@ -233,10 +221,12 @@
}
function iil_ReadLine($fp, $size=1024) {
   global $my_prefs;
   $line = '';
   if (!$fp) {
          return $line;
          return NULL;
   }
    
   if (!$size) {
@@ -244,14 +234,20 @@
   }
    
   do {
      if (feof($fp)) {
         return $line ? $line : NULL;
      }
          $buffer = fgets($fp, $size);
          if ($buffer === false) {
              break;
          }
//      console('S: '. chop($buffer));
      if (!empty($my_prefs['debug_mode']))
         write_log('imap', 'S: '. chop($buffer));
          $line .= $buffer;
   } while ($buffer[strlen($buffer)-1] != "\n");
   return $line;
}
@@ -264,49 +260,58 @@
      $bytes = $a[2][0];
      while (strlen($out) < $bytes) {
         $line = iil_ReadBytes($fp, $bytes); 
         if ($line === NULL)
            break;
         $out .= $line;
      }
      $line = $a[1][0] . '"' . ($escape ? iil_Escape($out) : $out) . '"';
//      console('[...] '. $out);
   }
   return $line;
}
function iil_ReadBytes($fp, $bytes) {
   global $my_prefs;
   $data = '';
   $len  = 0;
   do {
          $data .= fread($fp, $bytes-$len);
      if ($len == strlen($data)) {
   while ($len < $bytes && !feof($fp))
   {
      $d = fread($fp, $bytes-$len);
      if (!empty($my_prefs['debug_mode']))
         write_log('imap', 'S: '. $d);
                $data .= $d;
      $data_len = strlen($data);
      if ($len == $data_len) {
                  break; //nothing was read -> exit to avoid apache lockups
          }
          $len = strlen($data);
   } while ($len < $bytes);
          $len = $data_len;
   };
   
   return $data;
}
// don't use it in loops, until you exactly know what you're doing
function iil_ReadReply($fp) {
   do {
      $line = trim(iil_ReadLine($fp, 1024));
   } while ($line[0] == '*');
   return $line;
}
function iil_ParseResult($string) {
   $a = explode(' ', $string);
   if (count($a) > 2) {
      if (strcasecmp($a[1], 'OK') == 0) {
   $a = explode(' ', trim($string));
   if (count($a) >= 2) {
      $res = strtoupper($a[1]);
      if ($res == 'OK') {
         return 0;
      } else if (strcasecmp($a[1], 'NO') == 0) {
      } else if ($res == 'NO') {
         return -1;
      } else if (strcasecmp($a[1], 'BAD') == 0) {
      } else if ($res == 'BAD') {
         return -2;
      } else if (strcasecmp($a[1], 'BYE') == 0) {
      } else if ($res == 'BYE') {
         return -3;
          }
      }
   }
   return -4;
}
@@ -326,7 +331,7 @@
   return false;
}
function iil_StartsWithI($string, $match, $bye=false) {
function iil_StartsWithI($string, $match, $error=false) {
   $len = strlen($match);
   if ($len == 0) {
      return false;
@@ -334,9 +339,8 @@
   if (strncasecmp($string, $match, $len) == 0) {
      return true;
   }
   if ($bye && strncmp($string, '* BYE ', 6) == 0) {
   if ($error && preg_match('/^\* (BYE|BAD) /i', $string)) {
      return true;
   }
   return false;
}
@@ -439,13 +443,8 @@
    iil_PutLine($conn->fp, 'a001 LOGIN "'.iil_Escape($user).'" "'.iil_Escape($password).'"');
    do {
        $line = iil_ReadReply($conn->fp);
        if ($line === false) {
            break;
        }
    } while (!iil_StartsWith($line, 'a001 ', true));
    $line = iil_ReadReply($conn->fp);
    // process result
    $result = iil_ParseResult($line);
@@ -551,24 +550,19 @@
   $iil_error = '';
   $iil_errornum = 0;
   // set some imap options
   // set options
   if (is_array($options)) {
      foreach($options as $optkey => $optval) {
         if ($optkey == 'imap') {
            $auth_method = $optval;
         } else if ($optkey == 'rootdir') {
                $my_prefs['rootdir'] = $optval;
         } else if ($optkey == 'delimiter') {
                $my_prefs['delimiter'] = $optval;
         }
      }
   }
        $my_prefs = $options;
    }
    // set auth method
    if (!empty($my_prefs['auth_method'])) {
            $auth_method = strtoupper($my_prefs['auth_method']);
   } else {
          $auth_method = 'CHECK';
    }
   if (empty($auth_method))
          $auth_method = 'check';
   $message = "INITIAL: $auth_method\n";
   $result = false;
   
   // initialize connection
@@ -578,14 +572,13 @@
   $conn->selected    = '';
   $conn->user        = $user;
   $conn->host        = $host;
   if ($my_prefs['sort_field'] == 'INTERNALDATE') {
      $IMAP_USE_INTERNAL_DATE = true;
   } else if ($my_prefs['sort_field'] == 'DATE') {
          $IMAP_USE_INTERNAL_DATE = false;
   }
   //echo '<!-- conn sort_field: '.$my_prefs['sort_field'].' //-->';
   //check input
   if (empty($host)) {
      $iil_error = "Empty host";
@@ -611,7 +604,7 @@
      $host = $ICL_SSL . '://' . $host;
   }
   $conn->fp = fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
   $conn->fp = @fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
   if (!$conn->fp) {
          $iil_error = "Could not connect to $host at port $ICL_PORT: $errstr";
          $iil_errornum = -2;
@@ -619,7 +612,10 @@
   }
   stream_set_timeout($conn->fp, 10);
   $line = stream_get_line($conn->fp, 8192, "\r\n");
   $line = trim(fgets($conn->fp, 8192));
   if ($my_prefs['debug_mode'] && $line)
      write_log('imap', 'S: '. $line);
   // Connected to wrong port or connection error?
   if (!preg_match('/^\* (OK|PREAUTH)/i', $line)) {
@@ -630,7 +626,7 @@
           $iil_errornum = -2;
           return false;
   }
   // RFC3501 [7.1] optional CAPABILITY response
   if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) {
      $conn->capability = explode(' ', strtoupper($matches[1]));
@@ -661,54 +657,50 @@
           }
   }
   if (strcasecmp($auth_method, "check") == 0) {
      //check for supported auth methods
   $orig_method = $auth_method;
   if ($auth_method == 'CHECK') {
      // check for supported auth methods
      if (iil_C_GetCapability($conn, 'AUTH=CRAM-MD5') || iil_C_GetCapability($conn, 'AUTH=CRAM_MD5')) {
         $auth_method = 'auth';
         $auth_method = 'AUTH';
      }
      else {
         //default to plain text auth
         $auth_method = 'plain';
         // default to plain text auth
         $auth_method = 'PLAIN';
      }
   }
   if (strcasecmp($auth_method, 'auth') == 0) {
      $conn->message .= "Trying CRAM-MD5\n";
      //do CRAM-MD5 authentication
   if ($auth_method == 'AUTH') {
      // do CRAM-MD5 authentication
      iil_PutLine($conn->fp, "a000 AUTHENTICATE CRAM-MD5");
      $line = trim(iil_ReadLine($conn->fp, 1024));
      $conn->message .= "$line\n";
      if ($line[0] == '+') {
         $conn->message .= 'Got challenge: ' . htmlspecialchars($line) . "\n";
         //got a challenge string, try CRAM-5
         // got a challenge string, try CRAM-MD5
         $result = iil_C_Authenticate($conn, $user, $password, substr($line,2));
              
         // stop if server sent BYE response
         if($result == -3) {
         if ($result == -3) {
                     $iil_error = $conn->error;
                     $iil_errornum = $conn->errorNum;
            return false;
         }
         $conn->message .= "Tried CRAM-MD5: $result \n";
      } else {
         $conn->message .='No challenge ('.htmlspecialchars($line)."), try plain\n";
         $auth = 'plain';
      }
      if (!is_resource($result) && $orig_method == 'CHECK') {
         $auth_method = 'PLAIN';
      }
   }
      
   if ((!$result)||(strcasecmp($auth, "plain") == 0)) {
      //do plain text auth
   if ($auth_method == 'PLAIN') {
      // do plain text auth
      $result = iil_C_Login($conn, $user, $password);
      $conn->message .= "Tried PLAIN: $result \n";
   }
   $conn->message .= $auth;
   if (!is_int($result)) {
   if (is_resource($result)) {
        if ($my_prefs['force_caps']) {
         iil_C_ClearCapability($conn);
        }
      iil_C_Namespace($conn);
      return $conn;
   } else {
@@ -720,7 +712,8 @@
function iil_Close(&$conn) {
   if (iil_PutLine($conn->fp, "I LOGOUT")) {
      fgets($conn->fp, 1024);
      if (!feof($conn->fp))
         fgets($conn->fp, 1024);
      fclose($conn->fp);
      $conn->fp = false;
   }
@@ -744,38 +737,12 @@
   return $result;
}
function iil_CheckForRecent($host, $user, $password, $mailbox) {
   if (empty($mailbox)) {
      $mailbox = 'INBOX';
   }
   $conn = iil_Connect($host, $user, $password, 'plain');
   $fp   = $conn->fp;
   if ($fp) {
      iil_PutLine($fp, "a002 EXAMINE \"".iil_Escape($mailbox)."\"");
      do {
         $line=chop(iil_ReadLine($fp, 300));
         $a=explode(' ', $line);
         if (($a[0] == '*') && (strcasecmp($a[2], 'RECENT') == 0)) {
             $result = (int) $a[1];
              }
      } while (!iil_StartsWith($a[0], 'a002', true));
      iil_PutLine($fp, "a003 LOGOUT");
      fclose($fp);
   } else {
       $result = -2;
   }
   return $result;
}
function iil_C_Select(&$conn, $mailbox) {
   if (empty($mailbox)) {
      return false;
   }
   if (strcmp($conn->selected, $mailbox) == 0) {
   if ($conn->selected == $mailbox) {
      return true;
   }
    
@@ -784,10 +751,11 @@
         $line = chop(iil_ReadLine($conn->fp, 300));
         $a = explode(' ', $line);
         if (count($a) == 3) {
            if (strcasecmp($a[2], 'EXISTS') == 0) {
            $token = strtoupper($a[2]);
            if ($token == 'EXISTS') {
               $conn->exists = (int) $a[1];
            }
            if (strcasecmp($a[2], 'RECENT') == 0) {
            else if ($token == 'RECENT') {
               $conn->recent = (int) $a[1];
            }
         }
@@ -795,8 +763,6 @@
            $conn->permanentflags = explode(' ', $match[1]);
         }
      } while (!iil_StartsWith($line, 'sel1', true));
      $a = explode(' ', $line);
      if (strcasecmp($a[1], 'OK') == 0) {
         $conn->selected = $mailbox;
@@ -840,46 +806,23 @@
   return $string;
}
function iil_StrToTime($str) {
   $IMAP_MONTHS    = $GLOBALS['IMAP_MONTHS'];
   $IMAP_SERVER_TZ = $GLOBALS['IMAP_SERVER_TZ'];
   if ($str) {
           $time1 = strtotime($str);
   }
   if ($time1 && $time1 != -1) {
       return $time1-$IMAP_SERVER_TZ;
   }
   //echo '<!--'.$str.'//-->';
   //replace double spaces with single space
   $str = trim($str);
   $str = str_replace('  ', ' ', $str);
   //strip off day of week
   $pos = strpos($str, ' ');
   if (!is_numeric(substr($str, 0, $pos))) {
       $str = substr($str, $pos+1);
   }
   //explode, take good parts
   $a = explode(' ', $str);
function iil_StrToTime($date) {
   $month_str = $a[1];
   $month     = $IMAP_MONTHS[$month_str];
   $day       = (int)$a[0];
   $year      = (int)$a[2];
   $time      = $a[3];
   $tz_str    = $a[4];
   $tz        = substr($tz_str, 0, 3);
   $ta        = explode(':', $time);
   $hour      = (int)$ta[0]-(int)$tz;
   $minute    = (int)$ta[1];
   $second    = (int)$ta[2];
   //make UNIX timestamp
   $time2 = mktime($hour, $minute, $second, $month, $day, $year);
   //echo '<!--'.$time1.' '.$time2.' //-->'."\n";
   return $time2;
   // support non-standard "GMTXXXX" literal
   $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
        // if date parsing fails, we have a date in non-rfc format.
   // remove token from the end and try again
   while ((($ts = @strtotime($date))===false) || ($ts < 0))
   {
           $d = explode(' ', $date);
      array_pop($d);
      if (!$d) break;
      $date = implode(' ', $d);
   }
   $ts = (int) $ts;
   return $ts < 0 ? 0 : $ts;
}
function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE,
@@ -892,7 +835,7 @@
   
   $fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1,
        'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
   if (!$fields[$field]) {
       return false;
   }
@@ -904,21 +847,24 @@
    
   $is_uid = $is_uid ? 'UID ' : '';
   
   if (!empty($add)) {
   // message IDs
   if (is_array($add))
      $add = iil_CompressMessageSet(join(',', $add));
   if (!empty($add))
       $add = " $add";
   }
   $command  = 's ' . $is_uid . 'SORT (' . $field . ') ';
   $command .= $encoding . ' ALL' . $add;
   $line     = $data = '';
   if (!iil_PutLineC($conn->fp, $command)) {
       return false;
   }
   do {
      $line = chop(iil_ReadLine($conn->fp, 1024));
      $line = chop(iil_ReadLine($conn->fp));
      if (iil_StartsWith($line, '* SORT')) {
         $data .= ($data ? ' ' : '') . substr($line, 7);
         $data .= substr($line, 7);
          } else if (preg_match('/^[0-9 ]+$/', $line)) {
         $data .= $line;
      }
@@ -931,212 +877,132 @@
                return false;
   }
   
   $out = explode(' ',$data);
   return $out;
   return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY);
}
function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field,
    $normalize=true) {
   global $IMAP_USE_INTERNAL_DATE;
   $c=0;
   $result=array();
   $fp = $conn->fp;
   if (empty($index_field)) {
       $index_field = 'DATE';
   }
   $index_field = strtoupper($index_field);
   list($from_idx, $to_idx) = explode(':', $message_set);
   if (empty($message_set) || (isset($to_idx)
           && (int)$from_idx > (int)$to_idx)) {
      return false;
function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) {
   if (is_array($message_set)) {
      if (!($message_set = iil_CompressMessageSet(join(',', $message_set))))
         return false;
   } else {
      list($from_idx, $to_idx) = explode(':', $message_set);
      if (empty($message_set) ||
         (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) {
         return false;
      }
   }
   
   //$fields_a['DATE'] = ($IMAP_USE_INTERNAL_DATE?6:1);
   $index_field = empty($index_field) ? 'DATE' : strtoupper($index_field);
   $fields_a['DATE']         = 1;
   $fields_a['INTERNALDATE'] = 6;
   $fields_a['INTERNALDATE'] = 4;
   $fields_a['ARRIVAL']      = 4;
   $fields_a['FROM']         = 1;
   $fields_a['REPLY-TO']     = 1;
   $fields_a['SENDER']       = 1;
   $fields_a['TO']           = 1;
   $fields_a['CC']           = 1;
   $fields_a['SUBJECT']      = 1;
   $fields_a['UID']          = 2;
   $fields_a['SIZE']         = 2;
   $fields_a['SEEN']         = 3;
   $fields_a['RECENT']       = 4;
   $fields_a['DELETED']      = 5;
   $mode=$fields_a[$index_field];
   if (!($mode > 0)) {
       return false;
   $fields_a['RECENT']       = 3;
   $fields_a['DELETED']      = 3;
   if (!($mode = $fields_a[$index_field])) {
      return false;
   }
   /*  Do "SELECT" command */
   if (!iil_C_Select($conn, $mailbox)) {
       return false;
      return false;
   }
   /* FETCH date,from,subject headers */
   if ($mode == 1) {
      $key     = 'fhi' . ($c++);
      $request = $key . " FETCH $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)])";
      if (!iil_PutLine($fp, $request)) {
          return false;
          }
      do {
         $line=chop(iil_ReadLine($fp, 200));
         $a=explode(' ', $line);
         if (($line[0] == '*') && ($a[2] == 'FETCH')
                      && ($line[strlen($line)-1] != ')')) {
            $id=$a[1];
   // build FETCH command string
   $key     = 'fhi0';
   $cmd     = $uidfetch ? 'UID FETCH' : 'FETCH';
   $deleted = $skip_deleted ? ' FLAGS' : '';
            $str=$line=chop(iil_ReadLine($fp, 300));
   if ($mode == 1 && $index_field == 'DATE')
      $request = " $cmd $message_set (INTERNALDATE BODY.PEEK[HEADER.FIELDS (DATE)]$deleted)";
   else if ($mode == 1)
      $request = " $cmd $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)]$deleted)";
   else if ($mode == 2) {
      if ($index_field == 'SIZE')
         $request = " $cmd $message_set (RFC822.SIZE$deleted)";
      else
         $request = " $cmd $message_set ($index_field$deleted)";
   } else if ($mode == 3)
      $request = " $cmd $message_set (FLAGS)";
   else // 4
      $request = " $cmd $message_set (INTERNALDATE$deleted)";
            while ($line[0] != ')') {               //caution, this line works only in this particular case
               $line=chop(iil_ReadLine($fp, 300));
               if ($line[0] != ')') {
                  if (ord($line[0]) <= 32) {         //continuation from previous header line
                     $str.= ' ' . trim($line);
                  }
                  if ((ord($line[0]) > 32) || (strlen($line[0]) == 0)) {
                     list($field, $string) = iil_SplitHeaderLine($str);
                     if (strcasecmp($field, 'date') == 0) {
                        $result[$id] = iil_StrToTime($string);
                     } else {
                        $result[$id] = str_replace('"', '', $string);
                        if ($normalize) {
                            $result[$id] = strtoupper($result[$id]);
                                           }
                     }
                     $str=$line;
                  }
               }
            }
         }
         /*
         $end_pos = strlen($line)-1;
         if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[$end_pos]=="}")) {
            $id = $a[1];
            $pos = strrpos($line, "{")+1;
            $bytes = (int)substr($line, $pos, $end_pos-$pos);
            $received = 0;
            do {
               $line      = iil_ReadLine($fp, 0);
               $received += strlen($line);
               $line      = chop($line);
   $request = $key . $request;
   if (!iil_PutLine($conn->fp, $request))
      return false;
   $result = array();
   do {
      $line = chop(iil_ReadLine($conn->fp, 200));
      $line = iil_MultLine($conn->fp, $line);
      if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) {
                  $id = $m[1];
         $flags = NULL;
               
               if ($received>$bytes) {
                                break;
               } else if (!$line) {
                                continue;
               }
               list($field, $string) = explode(': ', $line);
               if (strcasecmp($field, 'date') == 0) {
                  $result[$id] = iil_StrToTime($string);
               } else if ($index_field != 'DATE') {
                  $result[$id]=strtoupper(str_replace('"', '', $string));
                         }
            } while ($line[0] != ')');
         } else {
            //one line response, not expected so ignore
         if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
            $flags = explode(' ', strtoupper($matches[1]));
            if (in_array('\\DELETED', $flags)) {
               $deleted[$id] = $id;
               continue;
            }
         }
         */
      } while (!iil_StartsWith($line, $key, true));
   }else if ($mode == 6) {
      $key     = 'fhi' . ($c++);
      $request = $key . " FETCH $message_set (INTERNALDATE)";
      if (!iil_PutLine($fp, $request)) {
          return false;
          }
      do {
         $line=chop(iil_ReadLine($fp, 200));
         if ($line[0] == '*') {
            /*
             * original:
             * "* 10 FETCH (INTERNALDATE "31-Jul-2002 09:18:02 -0500")"
             */
            $paren_pos = strpos($line, '(');
            $foo       = substr($line, 0, $paren_pos);
            $a         = explode(' ', $foo);
            $id        = $a[1];
            $open_pos  = strpos($line, '"') + 1;
            $close_pos = strrpos($line, '"');
            if ($open_pos && $close_pos) {
               $len         = $close_pos - $open_pos;
               $time_str    = substr($line, $open_pos, $len);
               $result[$id] = strtotime($time_str);
         if ($mode == 1 && $index_field == 'DATE') {
            if (preg_match('/BODY\[HEADER\.FIELDS \("*DATE"*\)\] (.*)/', $line, $matches)) {
               $value = preg_replace(array('/^"*[a-z]+:/i'), '', $matches[1]);
               $value = trim($value);
               $result[$id] = iil_StrToTime($value);
            }
         } else {
            $a = explode(' ', $line);
         }
      } while (!iil_StartsWith($a[0], $key, true));
   } else {
      if ($mode >= 3) {
          $field_name = 'FLAGS';
      } else if ($index_field == 'SIZE') {
          $field_name = 'RFC822.SIZE';
      } else {
          $field_name = $index_field;
          }
      /*          FETCH uid, size, flags      */
      $key     = 'fhi' .($c++);
      $request = $key . " FETCH $message_set ($field_name)";
      if (!iil_PutLine($fp, $request)) {
          return false;
          }
      do {
         $line=chop(iil_ReadLine($fp, 200));
         $a = explode(' ', $line);
         if (($line[0] == '*') && ($a[2] == 'FETCH')) {
            $line = str_replace('(', '', $line);
            $line = str_replace(')', '', $line);
            $a    = explode(' ', $line);
            $id = $a[1];
            if (isset($result[$id])) {
                continue; //if we already got the data, skip forward
            // non-existent/empty Date: header, use INTERNALDATE
            if (empty($result[$id])) {
               if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches))
                  $result[$id] = iil_StrToTime($matches[1]);
               else
                  $result[$id] = 0;
            }
                     if ($a[3]!=$field_name) {
                         continue;  //make sure it's returning what we requested
            }
            /*  Caution, bad assumptions, next several lines */
            if ($mode == 2) {
                $result[$id] = $a[4];
         } else if ($mode == 1) {
            if (preg_match('/BODY\[HEADER\.FIELDS \("?(FROM|REPLY-TO|SENDER|TO|SUBJECT)"?\)\] (.*)/', $line, $matches)) {
               $value = preg_replace(array('/^"*[a-z]+:/i', '/\s+$/sm'), array('', ''), $matches[2]);
               $result[$id] = trim($value);
            } else {
               $haystack    = strtoupper($line);
               $result[$id] = (strpos($haystack, $index_field) > 0 ? "F" : "N");
               $result[$id] = '';
            }
         } else if ($mode == 2) {
            if (preg_match('/\((UID|RFC822\.SIZE) ([0-9]+)/', $line, $matches)) {
               $result[$id] = trim($matches[2]);
            } else {
               $result[$id] = 0;
            }
         } else if ($mode == 3) {
            if (!$flags && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
               $flags = explode(' ', $matches[1]);
            }
            $result[$id] = in_array('\\'.$index_field, $flags) ? 1 : 0;
         } else if ($mode == 4) {
            if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches)) {
               $result[$id] = iil_StrToTime($matches[1]);
            } else {
               $result[$id] = 0;
            }
         }
      } while (!iil_StartsWith($line, $key, true));
   }
   //check number of elements...
   list($start_mid, $end_mid) = explode(':', $message_set);
   if (is_numeric($start_mid) && is_numeric($end_mid)) {
      //count how many we should have
      $should_have = $end_mid - $start_mid +1;
      //if we have less, try and fill in the "gaps"
      if (count($result) < $should_have) {
         for ($i=$start_mid; $i<=$end_mid; $i++) {
            if (!isset($result[$i])) {
                   $result[$i] = '';
                     }
              }
      }
   }
   } while (!iil_StartsWith($line, $key, true));
   return $result;   
}
@@ -1200,317 +1066,15 @@
        return false;
}
function iil_C_FetchUIDs(&$conn,$mailbox) {
function iil_C_FetchUIDs(&$conn, $mailbox, $message_set=null) {
   global $clock;
   $num = iil_C_CountMessages($conn, $mailbox);
   if ($num == 0) {
       return array();
   }
   $message_set = '1' . ($num>1?':' . $num:'');
   return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
}
function iil_SortThreadHeaders($headers, $index_a, $uids) {
   asort($index_a);
   $result = array();
   foreach ($index_a as $mid=>$foobar) {
      $uid = $uids[$mid];
      $result[$uid] = $headers[$uid];
   }
   return $result;
}
function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set) {
   global $clock;
   global $index_a;
   if (is_array($message_set))
      $message_set = join(',', $message_set);
    else if (empty($message_set))
      $message_set = '1:*';
   
   list($from_idx, $to_idx) = explode(':', $message_set);
   if (empty($message_set) || (isset($to_idx)
        && (int)$from_idx > (int)$to_idx)) {
      return false;
   }
   $result = array();
   $uids   = iil_C_FetchUIDs($conn, $mailbox);
   $debug  = false;
   $message_set = iil_CompressMessageSet($message_set);
   /* if we're missing any, get them */
   if ($message_set) {
      /* FETCH date,from,subject headers */
      $key        = 'fh';
      $fp         = $conn->fp;
      $request    = $key . " FETCH $message_set ";
          $request   .= "(BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])";
      $mid_to_id  = array();
      if (!iil_PutLine($fp, $request)) {
          return false;
          }
      do {
         $line = chop(iil_ReadLine($fp, 1024));
         if ($debug) {
             echo $line . "\n";
              }
         if (preg_match('/\{[0-9]+\}$/', $line)) {
            $a     = explode(' ', $line);
            $new = array();
            $new_thhd = new iilThreadHeader;
            $new_thhd->id = $a[1];
            do {
               $line = chop(iil_ReadLine($fp, 1024), "\r\n");
               if (iil_StartsWithI($line, 'Message-ID:')
                                || (iil_StartsWithI($line,'In-Reply-To:'))
                                || (iil_StartsWithI($line,'SUBJECT:'))) {
                  $pos        = strpos($line, ':');
                  $field_name = substr($line, 0, $pos);
                  $field_val  = substr($line, $pos+1);
                  $new[strtoupper($field_name)] = trim($field_val);
               } else if (preg_match('/^\s+/', $line)) {
                  $new[strtoupper($field_name)] .= trim($line);
               }
            } while ($line[0] != ')');
            $new_thhd->sbj = $new['SUBJECT'];
            $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1);
            $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1);
            $result[$uids[$new_thhd->id]] = $new_thhd;
         }
      } while (!iil_StartsWith($line, 'fh'));
   }
   /* sort headers */
   if (is_array($index_a)) {
      $result = iil_SortThreadHeaders($result, $index_a, $uids);
   }
   //echo 'iil_FetchThreadHeaders:'."\n";
   //print_r($result);
   return $result;
}
function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock) {
   global $index_a;
   list($from_idx, $to_idx) = explode(':', $message_set);
   if (empty($message_set) || (isset($to_idx)
          && (int)$from_idx > (int)$to_idx)) {
      return false;
   }
   $result    = array();
   $roots     = array();
   $root_mids = array();
   $sub_mids  = array();
   $strays    = array();
   $messages  = array();
   $fp        = $conn->fp;
   $debug     = false;
   $sbj_filter_pat = '/[a-z]{2,3}(\[[0-9]*\])?:(\s*)/i';
   /*  Do "SELECT" command */
   if (!iil_C_Select($conn, $mailbox)) {
       return false;
   }
   /* FETCH date,from,subject headers */
   $mid_to_id = array();
   $messages  = array();
   $headers   = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set);
   if ($clock) {
       $clock->register('fetched headers');
   }
   if ($debug) {
       print_r($headers);
   }
   /* go through header records */
   foreach ($headers as $header) {
      //$id = $header['i'];
      //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'],
      //         'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']);
      $id  = $header->id;
      $new = array('id' => $id, 'MESSAGE-ID' => $header->mid,
              'IN-REPLY-TO' => $header->irt, 'SUBJECT' => $header->sbj);
      /* add to message-id -> mid lookup table */
      $mid_to_id[$new['MESSAGE-ID']] = $id;
      /* if no subject, use message-id */
      if (empty($new['SUBJECT'])) {
          $new['SUBJECT'] = $new['MESSAGE-ID'];
      }
      /* if subject contains 'RE:' or has in-reply-to header, it's a reply */
      $sbj_pre = '';
      $has_re = false;
      if (preg_match($sbj_filter_pat, $new['SUBJECT'])) {
          $has_re = true;
      }
          if ($has_re || $new['IN-REPLY-TO']) {
               $sbj_pre = 'RE:';
      }
      /* strip out 're:', 'fw:' etc */
      if ($has_re) {
          $sbj = preg_replace($sbj_filter_pat, '', $new['SUBJECT']);
      } else {
          $sbj = $new['SUBJECT'];
      }
          $new['SUBJECT'] = $sbj_pre.$sbj;
      /* if subject not a known thread-root, add to list */
      if ($debug) {
          echo $id . ' ' . $new['SUBJECT'] . "\t" . $new['MESSAGE-ID'] . "\n";
      }
          $root_id = $roots[$sbj];
      if ($root_id && ($has_re || !$root_in_root[$root_id])) {
         if ($debug) {
             echo "\tfound root: $root_id\n";
         }
              $sub_mids[$new['MESSAGE-ID']] = $root_id;
         $result[$root_id][]           = $id;
      } else if (!isset($roots[$sbj]) || (!$has_re && $root_in_root[$root_id])) {
         /* try to use In-Reply-To header to find root
            unless subject contains 'Re:' */
         if ($has_re&&$new['IN-REPLY-TO']) {
            if ($debug) {
                echo "\tlooking: ".$new['IN-REPLY-TO']."\n";
            }
            //reply to known message?
            $temp = $sub_mids[$new['IN-REPLY-TO']];
            if ($temp) {
               //found it, root:=parent's root
               if ($debug) {
                   echo "\tfound parent: ".$new['SUBJECT']."\n";
               }
                         $result[$temp][]              = $id;
               $sub_mids[$new['MESSAGE-ID']] = $temp;
               $sbj                          = '';
            } else {
               //if we can't find referenced parent, it's a "stray"
               $strays[$id] = $new['IN-REPLY-TO'];
            }
         }
         //add subject as root
         if ($sbj) {
            if ($debug) {
                echo "\t added to root\n";
            }
                     $roots[$sbj]                  = $id;
            $root_in_root[$id]            = !$has_re;
            $sub_mids[$new['MESSAGE-ID']] = $id;
            $result[$id]                  = array($id);
         }
         if ($debug) {
             echo $new['MESSAGE-ID'] . "\t" . $sbj . "\n";
              }
      }
   }
   //now that we've gone through all the messages,
   //go back and try and link up the stray threads
   if (count($strays) > 0) {
      foreach ($strays as $id=>$irt) {
         $root_id = $sub_mids[$irt];
         if (!$root_id || $root_id==$id) {
             continue;
         }
              $result[$root_id] = array_merge($result[$root_id],$result[$id]);
         unset($result[$id]);
      }
   }
   if ($clock) {
       $clock->register('data prepped');
   }
   if ($debug) {
       print_r($roots);
   }
   return $result;
}
function iil_SortThreads(&$tree, $index, $sort_order = 'ASC') {
   if (!is_array($tree) || !is_array($index)) {
       return false;
   }
   //create an id to position lookup table
   $i = 0;
   foreach ($index as $id=>$val) {
      $i++;
      $index[$id] = $i;
   }
   $max = $i+1;
   //for each tree, set array key to position
   $itree = array();
   foreach ($tree as $id=>$node) {
      if (count($tree[$id])<=1) {
         //for "threads" with only one message, key is position of that message
         $n         = $index[$id];
         $itree[$n] = array($n=>$id);
      } else {
         //for "threads" with multiple messages,
         $min   = $max;
         $new_a = array();
         foreach ($tree[$id] as $mid) {
            $new_a[$index[$mid]] = $mid;      //create new sub-array mapping position to id
            $pos                 = $index[$mid];
            if ($pos&&$pos<$min) {
                $min = $index[$mid];   //find smallest position
            }
         }
         $n = $min;   //smallest position of child is thread position
         //assign smallest position to root level key
         //set children array to one created above
         ksort($new_a);
         $itree[$n] = $new_a;
      }
   }
   //sort by key, this basically sorts all threads
   ksort($itree);
   $i   = 0;
   $out = array();
   foreach ($itree as $k=>$node) {
      $out[$i] = $itree[$k];
      $i++;
   }
   return $out;
}
function iil_IndexThreads(&$tree) {
   /* creates array mapping mid to thread id */
   if (!is_array($tree)) {
       return false;
   }
   $t_index = array();
   foreach ($tree as $pos=>$kids) {
      foreach ($kids as $kid) $t_index[$kid] = $pos;
   }
   return $t_index;
   return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID', false);
}
function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='')
@@ -1520,17 +1084,16 @@
   $result = array();
   $fp     = $conn->fp;
   
   list($from_idx, $to_idx) = explode(':', $message_set);
   if (empty($message_set) || (isset($to_idx)
      && (int)$from_idx > (int)$to_idx)) {
      return false;
   }
   /*  Do "SELECT" command */
   if (!iil_C_Select($conn, $mailbox)) {
      $conn->error = "Couldn't select $mailbox";
      return false;
   }
   if (is_array($message_set))
      $message_set = join(',', $message_set);
   $message_set = iil_CompressMessageSet($message_set);
   if ($add)
      $add = ' '.strtoupper(trim($add));
@@ -1544,7 +1107,8 @@
   $request .= "BODY.PEEK[HEADER.FIELDS ";
   $request .= "(DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC ";
   $request .= "CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID ";
   $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY".$add.")])";
   $request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY ";
   $request .= "X-DRAFT-INFO".$add.")])";
   if (!iil_PutLine($fp, $request)) {
      return false;
@@ -1582,13 +1146,13 @@
            $parts_count = count($a);
            if ($parts_count>=6) {
               for ($i=0; $i<$parts_count; $i=$i+2) {
                  if (strcasecmp($a[$i],'UID') == 0)
                  if ($a[$i] == 'UID')
                     $result[$id]->uid = $a[$i+1];
                  else if (strcasecmp($a[$i],'RFC822.SIZE') == 0)
                  else if ($a[$i] == 'RFC822.SIZE')
                     $result[$id]->size = $a[$i+1];
                  else if (strcasecmp($a[$i],'INTERNALDATE') == 0)
                  else if ($a[$i] == 'INTERNALDATE')
                     $time_str = $a[$i+1];
                  else if (strcasecmp($a[$i],'FLAGS') == 0)
                  else if ($a[$i] == 'FLAGS')
                     $flags_str = $a[$i+1];
               }
@@ -1597,25 +1161,8 @@
               // if time is gmt...
                              $time_str = str_replace('GMT','+0000',$time_str);
               
               //get timezone
               $time_str      = substr($time_str, 0, -1);
               $time_zone_str = substr($time_str, -5); // extract timezone
               $time_str      = substr($time_str, 0, -5); // remove timezone
               $time_zone     = (float)substr($time_zone_str, 1, 2); // get first two digits
               if ($time_zone_str[3] != '0') {
                        $time_zone += 0.5;  //handle half hour offset
               }
               if ($time_zone_str[0] == '-') {
                       $time_zone = $time_zone * -1.0; //minus?
               }
               //calculate timestamp
                                        $timestamp     = strtotime($time_str); //return's server's time
               $timestamp    -= $time_zone * 3600; //compensate for tz, get GMT
               $result[$id]->internaldate = $time_str;
               $result[$id]->timestamp = $timestamp;
               $result[$id]->timestamp = iil_StrToTime($time_str);
               $result[$id]->date = $time_str;
            }
@@ -1623,20 +1170,20 @@
            if($bodystr) {
               while (!preg_match('/ BODYSTRUCTURE (.*) BODY\[HEADER.FIELDS/s', $line, $m)) {
                  $line2 = iil_ReadLine($fp, 1024);
                  $line .= iil_MultLine($fp, $line2);
                  $line .= iil_MultLine($fp, $line2, true);
               }
               $result[$id]->body_structure = $m[1];
            }
            // the rest of the result
            preg_match('/ BODY\[HEADER.FIELDS \(.*\)\]\s*(.*)/s', $line, $m);
            preg_match('/ BODY\[HEADER.FIELDS \(.*?\)\]\s*(.*)$/s', $line, $m);
            $reslines = explode("\n", trim($m[1], '"'));
            // re-parse (see below)
            foreach ($reslines as $line) {
               if (ord($line[0])<=32) {
                  $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($line);
            foreach ($reslines as $resln) {
               if (ord($resln[0])<=32) {
                  $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($resln);
               } else {
                  $lines[++$ln] = trim($line);
                  $lines[++$ln] = trim($resln);
               }
            }
         }
@@ -1675,7 +1222,7 @@
               $lines[++$ln] = trim($line);
            }
         // patch from "Maksim Rubis" <siburny@hotmail.com>
         } while (trim($line[0]) != ')' && strncmp($line, $key, strlen($key)));
         } while ($line[0] != ')' && !iil_StartsWith($line, $key, true));
         if (strncmp($line, $key, strlen($key))) { 
            // process header, fill iilBasicHeader obj.
@@ -1686,7 +1233,7 @@
                  $headers[$k] = '';
               }
            }
            // create array with header field:data
            while ( list($lines_key, $str) = each($lines) ) {
               list($field, $string) = iil_SplitHeaderLine($str);
@@ -1723,13 +1270,10 @@
                  $result[$id]->encoding = $string;
                  break;
               case 'content-type':
                  $ctype_parts = explode(";", $string);
                  $ctype_parts = preg_split('/[; ]/', $string);
                  $result[$id]->ctype = array_shift($ctype_parts);
                  foreach ($ctype_parts as $ctype_add) {
                     if (preg_match('/charset="?([a-z0-9\-\.\_]+)"?/i',
                        $ctype_add, $regs)) {
                        $result[$id]->charset = $regs[1];
                     }
                  if (preg_match('/charset\s*=\s*"?([a-z0-9\-\.\_]+)"?/i', $string, $regs)) {
                     $result[$id]->charset = $regs[1];
                  }
                  break;
               case 'in-reply-to':
@@ -1766,23 +1310,24 @@
            $flags_a   = explode(' ', $flags_str);
               
            if (is_array($flags_a)) {
               reset($flags_a);
               while (list(,$val)=each($flags_a)) {
                  if (strcasecmp($val,'Seen') == 0) {
            //   reset($flags_a);
               foreach($flags_a as $flag) {
                  $flag = strtoupper($flag);
                  if ($flag == 'SEEN') {
                      $result[$id]->seen = true;
                  } else if (strcasecmp($val, 'Deleted') == 0) {
                      $result[$id]->deleted=true;
                  } else if (strcasecmp($val, 'Recent') == 0) {
                  } else if ($flag == 'DELETED') {
                      $result[$id]->deleted = true;
                  } else if ($flag == 'RECENT') {
                      $result[$id]->recent = true;
                  } else if (strcasecmp($val, 'Answered') == 0) {
                  } else if ($flag == 'ANSWERED') {
                     $result[$id]->answered = true;
                  } else if (strcasecmp($val, '$Forwarded') == 0) {
                  } else if ($flag == '$FORWARDED') {
                     $result[$id]->forwarded = true;
                  } else if (strcasecmp($val, 'Draft') == 0) {
                  } else if ($flag == 'DRAFT') {
                     $result[$id]->is_draft = true;
                  } else if (strcasecmp($val, '$MDNSent') == 0) {
                  } else if ($flag == '$MDNSENT') {
                     $result[$id]->mdn_sent = true;
                  } else if (strcasecmp($val, 'Flagged') == 0) {
                  } else if ($flag == 'FLAGGED') {
                           $result[$id]->flagged = true;
                  }
               }
@@ -1790,7 +1335,7 @@
            }
         }
      }
   } while (strcmp($a[0], $key) != 0);
   } while (!iil_StartsWith($line, $key, true));
   return $result;
}
@@ -1834,8 +1379,8 @@
      while (list($key, $val)=each($a)) {
         if ($field == 'timestamp') {
            $data = @strtotime($val->date);
            if ($data == false) {
            $data = iil_StrToTime($val->date);
            if (!$data) {
               $data = $val->timestamp;
                     }
         } else {
@@ -1875,10 +1420,10 @@
      iil_PutLine($conn->fp, "exp1 $command");
      do {
         $line=chop(iil_ReadLine($conn->fp, 100));
         $line = iil_ReadLine($conn->fp, 100);
         if ($line[0] == '*') {
                     $c++;
              }
               $c++;
           }
      } while (!iil_StartsWith($line, 'exp1', true));
      
      if (iil_ParseResult($line) == 0) {
@@ -1897,29 +1442,27 @@
       return -1;
   }
    
   $fp    = $conn->fp;
   $flags = $GLOBALS['IMAP_FLAGS'];
   $flag = strtoupper($flag);
   $flag = $flags[$flag];
   $flag = $flags[strtoupper($flag)];
    
   if (iil_C_Select($conn, $mailbox)) {
      $c = 0;
      iil_PutLine($fp, "flg UID STORE $messages " . $mod . "FLAGS (" . $flag . ")");
      do {
         $line=chop(iil_ReadLine($fp, 100));
         if ($line[0] == '*') {
             $c++;
              }
      } while (!iil_StartsWith($line, 'flg', true));
      if (iil_ParseResult($line) == 0) {
         return $c;
      }
      $conn->error = $line;
      return -1;
   if (!iil_C_Select($conn, $mailbox)) {
       return -1;
   }
   $conn->error = 'Select failed';
    $c = 0;
   iil_PutLine($conn->fp, "flg UID STORE $messages " . $mod . "FLAGS (" . $flag . ")");
   do {
      $line = iil_ReadLine($conn->fp, 1000);
      if ($line[0] == '*') {
          $c++;
        }
   } while (!iil_StartsWith($line, 'flg', true));
   if (iil_ParseResult($line) == 0) {
      return $c;
   }
   $conn->error = $line;
   return -1;
}
@@ -1935,46 +1478,26 @@
   return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '+');
}
function iil_C_Undelete(&$conn, $mailbox, $messages) {
   return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '-');
}
function iil_C_Unseen(&$conn, $mailbox, $messages) {
   return iil_C_ModFlag($conn, $mailbox, $messages, 'SEEN', '-');
}
function iil_C_Copy(&$conn, $messages, $from, $to) {
   $fp = $conn->fp;
   if (empty($from) || empty($to)) {
       return -1;
   }
    
   if (iil_C_Select($conn, $from)) {
      $c=0;
      iil_PutLine($fp, "cpy1 UID COPY $messages \"".iil_Escape($to)."\"");
      $line=iil_ReadReply($fp);
      return iil_ParseResult($line);
   } else {
      return -1;
   if (!iil_C_Select($conn, $from)) {
        return -1;
   }
}
function iil_FormatSearchDate($month, $day, $year) {
   $month  = (int) $month;
   $months = $GLOBALS['IMAP_MONTHS'];
   return $day . '-' . $months[$month] . '-' . $year;
    iil_PutLine($conn->fp, "cpy1 UID COPY $messages \"".iil_Escape($to)."\"");
   $line = iil_ReadReply($conn->fp);
   return iil_ParseResult($line);
}
function iil_C_CountUnseen(&$conn, $folder) {
   $index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
   if (is_array($index)) {
      if (($cnt = count($index)) && $index[0] != '') {
         return $cnt;
      }
   }
   return false;
    $index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
    if (is_array($index))
        return count($index);
    return false;
}
function iil_C_UID2ID(&$conn, $folder, $uid) {
@@ -1988,51 +1511,138 @@
}
function iil_C_ID2UID(&$conn, $folder, $id) {
   $fp = $conn->fp;
   if ($id == 0) {
       return    -1;
   }
   $result = -1;
   if (iil_C_Select($conn, $folder)) {
      $key = 'FUID';
      if (iil_PutLine($fp, "$key FETCH $id (UID)")) {
      $key = 'fuid';
      if (iil_PutLine($conn->fp, "$key FETCH $id (UID)")) {
         do {
            $line=chop(iil_ReadLine($fp, 1024));
            $line = chop(iil_ReadLine($conn->fp, 1024));
            if (preg_match("/^\* $id FETCH \(UID (.*)\)/i", $line, $r)) {
               $result = $r[1];
            }
         } while (!preg_match("/^$key/", $line));
         } while (!iil_StartsWith($line, $key, true));
      }
   }
   return $result;
}
function iil_C_Search(&$conn, $folder, $criteria) {
   $fp = $conn->fp;
   if (iil_C_Select($conn, $folder)) {
      $c = 0;
      $query = 'srch1 SEARCH ' . chop($criteria);
      if (!iil_PutLineC($fp, $query)) {
         return false;
      }
      do {
         $line=trim(iil_ReadLine($fp, 10000));
         if (preg_match('/^\* SEARCH/i', $line)) {
            $str = trim(substr($line, 8));
            $messages = explode(' ', $str);
// Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
// 7 times instead :-) See comments on http://uk2.php.net/references and this article:
// http://derickrethans.nl/files/phparch-php-variables-article.pdf
function iil_ParseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) {
   $node = array();
   if ($str[$begin] != '(') {
      $stop = $begin + strspn($str, "1234567890", $begin, $end - $begin);
      $msg = substr($str, $begin, $stop - $begin);
      if ($msg == 0)
          return $node;
      if (is_null($root))
         $root = $msg;
      $depthmap[$msg] = $depth;
      $haschildren[$msg] = false;
      if (!is_null($parent))
         $haschildren[$parent] = true;
      if ($stop + 1 < $end)
         $node[$msg] = iil_ParseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren);
      else
         $node[$msg] = array();
   } else {
      $off = $begin;
      while ($off < $end) {
         $start = $off;
         $off++;
         $n = 1;
         while ($n > 0) {
            $p = strpos($str, ')', $off);
            if ($p === false) {
               error_log('Mismatched brackets parsing IMAP THREAD response:');
               error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20));
               error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10))));
               return $node;
            }
            $p1 = strpos($str, '(', $off);
            if ($p1 !== false && $p1 < $p) {
               $off = $p1 + 1;
               $n++;
            } else {
               $off = $p + 1;
               $n--;
            }
         }
      } while (!iil_StartsWith($line, 'srch1', true));
      $result_code = iil_ParseResult($line);
      if ($result_code == 0) {
          return $messages;
         $node += iil_ParseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren);
      }
      $conn->error = 'iil_C_Search: ' . $line . "\n";
      return false;
   }
   $conn->error = "iil_C_Search: Couldn't select \"$folder\"\n";
   return false;
   return $node;
}
function iil_C_Thread(&$conn, $folder, $algorithm='REFERENCES', $criteria='',
    $encoding='US-ASCII') {
   if (!iil_C_Select($conn, $folder)) {
      $conn->error = "Couldn't select $folder";
      return false;
   }
   $encoding = $encoding ? trim($encoding) : 'US-ASCII';
   $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES';
   $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL';
   if (!iil_PutLineC($conn->fp, "thrd1 THREAD $algorithm $encoding $criteria")) {
      return false;
   }
   do {
      $line = trim(iil_ReadLine($conn->fp, 10000));
      if (preg_match('/^\* THREAD/', $line)) {
         $str = trim(substr($line, 8));
         $depthmap = array();
         $haschildren = array();
         $tree = iil_ParseThread($str, 0, strlen($str), null, null, 0, $depthmap, $haschildren);
      }
   } while (!iil_StartsWith($line, 'thrd1', true));
   $result_code = iil_ParseResult($line);
   if ($result_code == 0) {
       return array($tree, $depthmap, $haschildren);
   }
   $conn->error = 'iil_C_Thread: ' . $line . "\n";
   return false;
}
function iil_C_Search(&$conn, $folder, $criteria) {
   if (!iil_C_Select($conn, $folder)) {
      $conn->error = "Couldn't select $folder";
      return false;
   }
   $data = '';
   $query = 'srch1 SEARCH ' . chop($criteria);
   if (!iil_PutLineC($conn->fp, $query)) {
      return false;
   }
   do {
      $line = trim(iil_ReadLine($conn->fp));
      if (iil_StartsWith($line, '* SEARCH')) {
         $data .= substr($line, 8);
      } else if (preg_match('/^[0-9 ]+$/', $line)) {
         $data .= $line;
      }
   } while (!iil_StartsWith($line, 'srch1', true));
   $result_code = iil_ParseResult($line);
   if ($result_code == 0) {
      return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY);
   }
   $conn->error = 'iil_C_Search: ' . $line . "\n";
   return false;
}
function iil_C_Move(&$conn, $messages, $from, $to) {
@@ -2128,9 +1738,6 @@
}
function iil_C_ListMailboxes(&$conn, $ref, $mailbox) {
   global $IGNORE_FOLDERS;
   $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
      
   $fp = $conn->fp;
   
@@ -2159,12 +1766,7 @@
              // split one line
         $a = iil_ExplodeQuotedString(' ', $line);
              // last string is folder name
         $folder = preg_replace(array('/^"/', '/"$/'), '', iil_UnEscape($a[count($a)-1]));
              if (empty($ignore) || (!empty($ignore)
                     && !preg_match('/'.preg_quote(ignore, '/').'/i', $folder))) {
                     $folders[$i] = $folder;
              }
         $folders[$i] = preg_replace(array('/^"/', '/"$/'), '', iil_UnEscape($a[count($a)-1]));
            
              // second from last is delimiter
              $delim = trim($a[count($a)-2], '"');
@@ -2192,9 +1794,6 @@
}
function iil_C_ListSubscribed(&$conn, $ref, $mailbox) {
   global $IGNORE_FOLDERS;
   $ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
   
   $fp = $conn->fp;
   if (empty($mailbox)) {
@@ -2226,9 +1825,9 @@
         $a = iil_ExplodeQuotedString(' ', $line);
              // last string is folder name
             $folder = preg_replace(array('/^"/', '/"$/'), '', iil_UnEscape($a[count($a)-1]));
         if ((!in_array($folder, $folders)) && (empty($ignore)
                     || (!empty($ignore) && !preg_match('/'.preg_quote(ignore, '/').'/i', $folder)))) {
         // @TODO: do we need this check???
         if (!in_array($folder, $folders)) {
             $folders[$i] = $folder;
              }
            
@@ -2263,8 +1862,8 @@
   $query = 'sub1 SUBSCRIBE "' . iil_Escape($folder). '"';
   iil_PutLine($fp, $query);
   $line = trim(iil_ReadLine($fp, 10000));
   return iil_ParseResult($line);
   $line = trim(iil_ReadLine($fp, 512));
   return (iil_ParseResult($line) == 0);
}
function iil_C_UnSubscribe(&$conn, $folder) {
@@ -2273,11 +1872,11 @@
   $query = 'usub1 UNSUBSCRIBE "' . iil_Escape($folder) . '"';
   iil_PutLine($fp, $query);
    
   $line = trim(iil_ReadLine($fp, 10000));
   return iil_ParseResult($line);
   $line = trim(iil_ReadLine($fp, 512));
   return (iil_ParseResult($line) == 0);
}
function iil_C_FetchMIMEHeaders(&$conn, $mailbox, $id, $parts) {
function iil_C_FetchMIMEHeaders(&$conn, $mailbox, $id, $parts, $mime=true) {
   
   $fp     = $conn->fp;
@@ -2290,10 +1889,11 @@
   $key = 'fmh0';
   $peeks = '';
   $idx = 0;
        $type = $mime ? 'MIME' : 'HEADER';
   // format request
   foreach($parts as $part)
      $peeks[] = "BODY.PEEK[$part.MIME]";
      $peeks[] = "BODY.PEEK[$part.$type]";
   
   $request = "$key FETCH $id (" . implode(' ', $peeks) . ')';
@@ -2306,9 +1906,9 @@
           $line = iil_ReadLine($fp, 1000);
           $line = iil_MultLine($fp, $line);
      if (preg_match('/BODY\[([0-9\.]+)\.MIME\]/', $line, $matches)) {
      if (preg_match('/BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) {
         $idx = $matches[1];
         $result[$idx] = preg_replace('/^(\* '.$id.' FETCH \()?\s*BODY\['.$idx.'\.MIME\]\s+/', '', $line);
         $result[$idx] = preg_replace('/^(\* '.$id.' FETCH \()?\s*BODY\['.$idx.'\.'.$type.'\]\s+/', '', $line);
         $result[$idx] = trim($result[$idx], '"');
             $result[$idx] = rtrim($result[$idx], "\t\r\n\0\x0B");
          }
@@ -2317,14 +1917,14 @@
   return $result;
}
function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part) {
function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $is_uid=false, $part=NULL) {
   $part = empty($part) ? 'HEADER' : $part.'.MIME';
        return iil_C_HandlePartBody($conn, $mailbox, $id, $part);
        return iil_C_HandlePartBody($conn, $mailbox, $id, $is_uid, $part);
}
function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part='', $encoding=NULL, $print=NULL, $file=NULL) {
function iil_C_HandlePartBody(&$conn, $mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL) {
   
   $fp     = $conn->fp;
   $result = false;
@@ -2351,7 +1951,7 @@
          // format request
      $key     = 'ftch0';
      $request = $key . " FETCH $id (BODY.PEEK[$part])";
      $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id (BODY.PEEK[$part])";
          // send request
      if (!iil_PutLine($fp, $request)) {
          return false;
@@ -2376,7 +1976,7 @@
                 $len  = $to - $from;
            $result = substr($line, $from, $len);
         }
              if ($mode == 1)
            $result = base64_decode($result);
         else if ($mode == 2)
@@ -2399,8 +1999,9 @@
                
                      if ($len > $bytes) {
                             $line = substr($line, 0, $bytes);
               $len = strlen($line);
                      }
                     $bytes -= strlen($line);
                     $bytes -= $len;
                      if ($mode == 1) {
               $line = rtrim($line, "\t\r\n\0\x0B");
@@ -2414,7 +2015,7 @@
               }
               else
                  $prev = '';
               if ($file)
                  fwrite($file, base64_decode($line));
                        else if ($print)
@@ -2450,35 +2051,25 @@
            }
              }
          }
           // read in anything up until last line
      if (!$end)
         do {
                 $line = iil_ReadLine($fp, 1024);
         } while (!iil_StartsWith($line, $key, true));
          if ($result) {
             $result = rtrim($result, "\t\r\n\0\x0B");
         if ($file) {
            fwrite($file, $result);
         } else if ($print) {
            echo $result;
         } else
            return $result; // substr($result, 0, strlen($result)-1);
            return $result;
         return true;
          }
      return true;
   }
   return false;
}
function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part, $file=NULL) {
   return iil_C_HandlePartBody($conn, $mailbox, $id, $part, NULL, NULL, $file);
}
function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part) {
   iil_C_HandlePartBody($conn, $mailbox, $id, $part, NULL, true, NULL);
}
function iil_C_CreateFolder(&$conn, $folder) {
@@ -2486,8 +2077,7 @@
   if (iil_PutLine($fp, 'c CREATE "' . iil_Escape($folder) . '"')) {
      do {
         $line=iil_ReadLine($fp, 300);
      } while ($line[0] != 'c');
        $conn->error = $line;
      } while (!iil_StartsWith($line, 'c ', true));
      return (iil_ParseResult($line) == 0);
   }
   return false;
@@ -2498,7 +2088,7 @@
   if (iil_PutLine($fp, 'r RENAME "' . iil_Escape($from) . '" "' . iil_Escape($to) . '"')) {
      do {
         $line = iil_ReadLine($fp, 300);
      } while ($line[0] != 'r');
      } while (!iil_StartsWith($line, 'r ', true));
      return (iil_ParseResult($line) == 0);
   }
   return false;
@@ -2509,40 +2099,47 @@
   if (iil_PutLine($fp, 'd DELETE "' . iil_Escape($folder). '"')) {
      do {
         $line=iil_ReadLine($fp, 300);
      } while ($line[0] != 'd');
      } while (!iil_StartsWith($line, 'd ', true));
      return (iil_ParseResult($line) == 0);
   }
   $conn->error = "Couldn't send command\n";
   return false;
}
function iil_C_Append(&$conn, $folder, &$message) {
   if (!$folder) {
          return false;
      return false;
   }
   $fp = $conn->fp;
   $message = str_replace("\r", '', $message);
   $message = str_replace("\n", "\r\n", $message);
   $message = str_replace("\n", "\r\n", $message);
   $len = strlen($message);
   if (!$len) {
          return false;
      return false;
   }
   $request = 'A APPEND "' . iil_Escape($folder) .'" (\\Seen) {' . $len . '}';
   $request = 'a APPEND "' . iil_Escape($folder) .'" (\\Seen) {' . $len . '}';
   if (iil_PutLine($fp, $request)) {
      $line=iil_ReadLine($fp, 100);
      $sent = fwrite($fp, $message."\r\n");
      $line = iil_ReadLine($fp, 512);
      if ($line[0] != '+') {
         // $errornum = iil_ParseResult($line);
         $conn->error .= "Cannot write to folder: $line\n";
         return false;
      }
      iil_PutLine($fp, $message);
      do {
         $line=iil_ReadLine($fp, 1000);
      } while ($line[0] != 'A');
         $line = iil_ReadLine($fp);
      } while (!iil_StartsWith($line, 'a ', true));
   
      $result = (iil_ParseResult($line) == 0);
      if (!$result) {
          $conn->error .= $line . "\n";
          }
      }
      return $result;
   }
@@ -2550,13 +2147,13 @@
   return false;
}
function iil_C_AppendFromFile(&$conn, $folder, $path) {
function iil_C_AppendFromFile(&$conn, $folder, $path, $headers=null, $separator="\n\n") {
   if (!$folder) {
       return false;
   }
    
   //open message file
   $in_fp = false;
   $in_fp = false;
   if (file_exists(realpath($path))) {
      $in_fp = fopen($path, 'r');
   }
@@ -2570,33 +2167,47 @@
   if (!$len) {
      return false;
   }
        if ($headers) {
                $headers = preg_replace('/[\r\n]+$/', '', $headers);
                $len += strlen($headers) + strlen($separator);
        }
   //send APPEND command
   $request    = 'A APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
   $bytes_sent = 0;
   $request    = 'a APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
   if (iil_PutLine($fp, $request)) {
      $line = iil_ReadLine($fp, 100);
      //send file
      $line = iil_ReadLine($fp, 512);
      if ($line[0] != '+') {
         //$errornum = iil_ParseResult($line);
         $conn->error .= "Cannot write to folder: $line\n";
         return false;
      }
                // send headers with body separator
                if ($headers) {
         iil_PutLine($fp, $headers . $separator, false);
                }
      // send file
      while (!feof($in_fp)) {
         $buffer      = fgets($in_fp, 4096);
         $bytes_sent += strlen($buffer);
         $buffer = fgets($in_fp, 4096);
         iil_PutLine($fp, $buffer, false);
      }
      fclose($in_fp);
      iil_PutLine($fp, '');
      iil_PutLine($fp, ''); // \r\n
      //read response
      // read response
      do {
         $line = iil_ReadLine($fp, 1000);
      } while ($line[0] != 'A');
         $line = iil_ReadLine($fp);
      } while (!iil_StartsWith($line, 'a ', true));
      $result = (iil_ParseResult($line) == 0);
      if (!$result) {
          $conn->error .= $line . "\n";
      }
      return $result;
   }
   
@@ -2604,23 +2215,22 @@
   return false;
}
function iil_C_FetchStructureString(&$conn, $folder, $id) {
function iil_C_FetchStructureString(&$conn, $folder, $id, $is_uid=false) {
   $fp     = $conn->fp;
   $result = false;
   
   if (iil_C_Select($conn, $folder)) {
      $key = 'F1247';
      if (iil_PutLine($fp, "$key FETCH $id (BODYSTRUCTURE)")) {
      if (iil_PutLine($fp, $key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)")) {
         do {
            $line = iil_ReadLine($fp, 5000);
            $line = iil_MultLine($fp, $line);
            list(, $index, $cmd, $rest) = explode(' ', $line);
            if ($cmd != 'FETCH' || $index == $id || preg_match("/^$key/", $line))
            $line = iil_MultLine($fp, $line, true);
            if (!preg_match("/^$key/", $line))
               $result .= $line;
         } while (!preg_match("/^$key/", $line));
         } while (!iil_StartsWith($line, $key, true));
         $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -(strlen($result)-strrpos($result, $key)+1)));
         $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -1));
      }
   }
   return $result;