alecpl
2009-01-07 46b48e6d349d48e6e211858a82589223b1364518
program/lib/imap.inc
@@ -70,6 +70,12 @@
      - fixed iil_C_Sort() to support very long and/or divided responses
      - added BYE response simple support for endless loop prevention
      - added 3rd argument in iil_StartsWith* functions
      - fix iil_C_FetchPartHeader() in some cases by use of iil_C_HandlePartBody()
      - allow iil_C_HandlePartBody() to fetch whole message
      - optimize iil_C_FetchHeaders() to use only one FETCH command
      - added 4th argument to iil_Connect()
      - allow setting rootdir and delimiter before connect
      - support multiquota result
********************************************************/
@@ -473,13 +479,14 @@
function iil_C_NameSpace(&$conn) {
   global $my_prefs;
   if (isset($my_prefs['rootdir']) && is_string($my_prefs['rootdir'])) {
          $conn->rootdir = $my_prefs['rootdir'];
      return true;
   }
   
   if (!iil_C_GetCapability($conn, 'NAMESPACE')) {
       return false;
   }
   if ($my_prefs["rootdir"]) {
       return true;
   }
    
   iil_PutLine($conn->fp, "ns1 NAMESPACE");
@@ -507,12 +514,13 @@
    
   $conn->rootdir       = $first_userspace[0];
   $conn->delimiter     = $first_userspace[1];
   $my_prefs["rootdir"] = substr($conn->rootdir, 0, -1);
   $my_prefs['rootdir'] = substr($conn->rootdir, 0, -1);
   $my_prefs['delimiter'] = $conn->delimiter;
   
   return true;
}
function iil_Connect($host, $user, $password) {
function iil_Connect($host, $user, $password, $options=null) {
   global $iil_error, $iil_errornum;
   global $ICL_SSL, $ICL_PORT;
   global $IMAP_NO_CACHE;
@@ -520,18 +528,23 @@
   
   $iil_error = '';
   $iil_errornum = 0;
   //set auth method
   $auth_method = 'plain';
   if (func_num_args() >= 4) {
      $auth_array = func_get_arg(3);
      if (is_array($auth_array)) {
         $auth_method = $auth_array['imap'];
          }
      if (empty($auth_method)) {
              $auth_method = "plain";
          }
   // set some imap 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;
         }
      }
   }
   if (empty($auth_method))
          $auth_method = 'check';
   $message = "INITIAL: $auth_method\n";
      
   $result = false;
@@ -583,7 +596,7 @@
   $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 = -1;
          $iil_errornum = -2;
      return false;
   }
@@ -902,10 +915,7 @@
function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE,
    $encoding = 'US-ASCII') {
   /*  Do "SELECT" command */
   if (!iil_C_Select($conn, $mailbox)) {
       return false;
   }
   $field = strtoupper($field);
   if ($field == 'INTERNALDATE') {
       $field = 'ARRIVAL';
@@ -917,6 +927,11 @@
   if (!$fields[$field]) {
       return false;
   }
   /*  Do "SELECT" command */
   if (!iil_C_Select($conn, $mailbox)) {
       return false;
   }
    
   $is_uid = $is_uid ? 'UID ' : '';
   
@@ -924,16 +939,15 @@
       $add = " $add";
   }
   $fp       = $conn->fp;
   $command  = 's ' . $is_uid . 'SORT (' . $field . ') ';
   $command .= $encoding . ' ALL ' . $add;
   $command .= $encoding . ' ALL' . $add;
   $line     = $data = '';
   
   if (!iil_PutLineC($fp, $command)) {
   if (!iil_PutLineC($conn->fp, $command)) {
       return false;
   }
   do {
      $line = chop(iil_ReadLine($fp, 1024));
      $line = chop(iil_ReadLine($conn->fp, 1024));
      if (iil_StartsWith($line, '* SORT')) {
         $data .= ($data ? ' ' : '') . substr($line, 7);
          } else if (preg_match('/^[0-9 ]+$/', $line)) {
@@ -941,9 +955,11 @@
      }
   } while (!iil_StartsWith($line, 's ', true));
   
   if (empty($data)) {
      $conn->error = $line;
      return false;
   $result_code = iil_ParseResult($line);
   if ($result_code != 0) {
                $conn->error = 'iil_C_Sort: ' . $line . "\n";
                return false;
   }
   
   $out = explode(' ',$data);
@@ -1605,7 +1621,6 @@
{
   global $IMAP_USE_INTERNAL_DATE;
   
   $c      = 0;
   $result = array();
   $fp     = $conn->fp;
   
@@ -1643,11 +1658,10 @@
      }
   }
   /* FETCH date,from,subject headers */
   $key     = 'fh' . ($c++);
   $prefix     = $uidfetch?' UID':'';
   $request  = $key . $prefix;
   $request .= " FETCH $message_set (BODY.PEEK[HEADER.FIELDS ";
   /* FETCH uid, size, flags and headers */
   $key       = 'FH12';
   $request  = $key . ($uidfetch ? ' UID' : '') . " FETCH $message_set ";
   $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE 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)])";
@@ -1656,7 +1670,7 @@
      return false;
   }
   do {
      $line = chop(iil_ReadLine($fp, 200));
      $line = chop(iil_ReadLine($fp, 1024));
      $a    = explode(' ', $line);
      if (($line[0] == '*') && ($a[2] == 'FETCH')) {
         $id = $a[1];
@@ -1667,15 +1681,100 @@
         $result[$id]->messageID = 'mid:' . $id;
         /*
             Sample reply line:
             * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
             INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODY[HEADER.FIELDS ...
         */
         if (preg_match('/^\* [0-9]+ FETCH \((.*) BODY\[HEADER/', $line, $matches)) {
            $str = $matches[1];
            //swap parents with quotes, then explode
            $str = eregi_replace("[()]", "\"", $str);
            $a = iil_ExplodeQuotedString(' ', $str);
            //did we get the right number of replies?
            $parts_count = count($a);
            if ($parts_count>=8) {
               for ($i=0; $i<$parts_count; $i=$i+2) {
                  if (strcasecmp($a[$i],'UID') == 0)
                     $result[$id]->uid = $a[$i+1];
                  else if (strcasecmp($a[$i],'RFC822.SIZE') == 0)
                     $result[$id]->size = $a[$i+1];
                  else if (strcasecmp($a[$i],'INTERNALDATE') == 0)
                     $time_str = $a[$i+1];
                  else if (strcasecmp($a[$i],'FLAGS') == 0)
                     $flags_str = $a[$i+1];
               }
               // process flags
               $flags_str = eregi_replace('[\\\"]', '', $flags_str);
               $flags_a   = explode(' ', $flags_str);
               if (is_array($flags_a)) {
                  reset($flags_a);
                  while (list(,$val)=each($flags_a)) {
                     if (strcasecmp($val,'Seen') == 0) {
                         $result[$id]->seen = true;
                     } else if (strcasecmp($val, 'Deleted') == 0) {
                         $result[$id]->deleted=true;
                     } else if (strcasecmp($val, 'Recent') == 0) {
                         $result[$id]->recent = true;
                     } else if (strcasecmp($val, 'Answered') == 0) {
                         $result[$id]->answered = true;
                     } else if (strcasecmp($val, '$Forwarded') == 0) {
                         $result[$id]->forwarded = true;
                     } else if (strcasecmp($val, 'Draft') == 0) {
                         $result[$id]->is_draft = true;
                     } else if (strcasecmp($val, '$MDNSent') == 0) {
                         $result[$id]->mdn_sent = true;
                     } else if (strcasecmp($val, 'Flagged') == 0) {
                          $result[$id]->flagged = true;
                     }
                  }
                  $result[$id]->flags = $flags_a;
               }
               $time_str = str_replace('"', '', $time_str);
               // 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]->date = $time_str;
            }
         }
         /*
            Start parsing headers.  The problem is, some header "lines" take up multiple lines.
            So, we'll read ahead, and if the one we're reading now is a valid header, we'll
            process the previous line.  Otherwise, we'll keep adding the strings until we come
            to the next valid header line.
         */
         $i     = 0;
         $lines = array();
         do {
            $line = chop(iil_ReadLine($fp, 300), "\r\n");
            if (ord($line[0])<=32) {
                $lines[$i] .= (empty($lines[$i])?'':"\n").trim($line);
            } else {
@@ -1684,7 +1783,7 @@
            }
            /* 
               The preg_match below works around communigate imap, which outputs " UID <number>)".
               Without this, the while statement continues on and gets the "fh0 OK completed" message.
               Without this, the while statement continues on and gets the "FH0 OK completed" message.
               If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.  
               This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
               If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
@@ -1697,7 +1796,7 @@
            }
         // patch from "Maksim Rubis" <siburny@hotmail.com>
         } while (trim($line[0]) != ')' && strncmp($line, $key, strlen($key)));
         if (strncmp($line, $key, strlen($key))) { 
            //process header, fill iilBasicHeader obj.
            //   initialize
@@ -1717,8 +1816,10 @@
               
               switch ($field) {
               case 'date';
                  $result[$id]->date = $string;
                  $result[$id]->timestamp = iil_StrToTime($string);
                  if (!$IMAP_USE_INTERNAL_DATE) {
                     $result[$id]->date = $string;
                     $result[$id]->timestamp = iil_StrToTime($string);
                  }
                  break;
               case 'from':
                  $result[$id]->from = $string;
@@ -1771,128 +1872,23 @@
                  break;
               } // end switch ()
            } // end while ()
            if ($conn->do_cache) {
               $uid = $result[$id]->uid;
               $conn->cache[$mailbox][$uid] = $result[$id];
               $conn->cache_dirty[$mailbox] = true;
            }
         } else {
            $a = explode(' ', $line);
         }
      }
   } while (strcmp($a[0], $key) != 0);
   /*
      FETCH uid, size, flags
      Sample reply line: "* 3 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen \Deleted))"
   */
   $command_key = 'fh' . ($c++);
   $request     = $command_key . $prefix;
   $request    .= " FETCH $message_set (UID RFC822.SIZE FLAGS INTERNALDATE)";
   if (!iil_PutLine($fp, $request)) {
       return false;
   }
   do {
      $line = chop(iil_ReadLine($fp, 200));
      //$a = explode(' ', $line);
      //if (($line[0]=="*") && ($a[2]=="FETCH")) {
      if ($line[0] == '*') {
         //echo "<!-- $line //-->\n";
         //get outter most parens
         $open_pos = strpos($line, "(") + 1;
         $close_pos = strrpos($line, ")");
         if ($open_pos && $close_pos) {
            //extract ID from pre-paren
            $pre_str = substr($line, 0, $open_pos);
            $pre_a = explode(' ', $line);
            $id = $pre_a[1];
            //get data
            $len = $close_pos - $open_pos;
            $str = substr($line, $open_pos, $len);
            //swap parents with quotes, then explode
            $str = eregi_replace("[()]", "\"", $str);
            $a = iil_ExplodeQuotedString(' ', $str);
            //did we get the right number of replies?
            $parts_count = count($a);
            if ($parts_count>=8) {
               for ($i=0;$i<$parts_count;$i=$i+2) {
                  if (strcasecmp($a[$i],"UID") == 0) $result[$id]->uid=$a[$i+1];
                  else if (strcasecmp($a[$i],"RFC822.SIZE") == 0) $result[$id]->size=$a[$i+1];
                  else if (strcasecmp($a[$i],"INTERNALDATE") == 0) $time_str = $a[$i+1];
                  else if (strcasecmp($a[$i],"FLAGS") == 0) $flags_str = $a[$i+1];
               }
               // process flags
               $flags_str = eregi_replace('[\\\"]', '', $flags_str);
               $flags_a   = explode(' ', $flags_str);
               if (is_array($flags_a)) {
                  reset($flags_a);
                  while (list($key,$val)=each($flags_a)) {
                     if (strcasecmp($val,'Seen') == 0) {
                         $result[$id]->seen = true;
                     } else if (strcasecmp($val, 'Deleted') == 0) {
                         $result[$id]->deleted=true;
                     } else if (strcasecmp($val, 'Recent') == 0) {
                         $result[$id]->recent = true;
                     } else if (strcasecmp($val, 'Answered') == 0) {
                         $result[$id]->answered = true;
                     } else if (strcasecmp($val, '$Forwarded') == 0) {
                         $result[$id]->forwarded = true;
                     } else if (strcasecmp($val, 'Draft') == 0) {
                         $result[$id]->is_draft = true;
                     } else if (strcasecmp($val, '$MDNSent') == 0) {
                         $result[$id]->mdn_sent = true;
                     } else if (strcasecmp($val, 'Flagged') == 0) {
                          $result[$id]->flagged = true;
                     }
                  }
                  $result[$id]->flags = $flags_a;
               }
               // 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, 1, -6); //remove quotes
               $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?
               }
               $result[$id]->internaldate = $time_str;
               if ($IMAP_USE_INTERNAL_DATE || empty($result[$id]->date)) {
                  //calculate timestamp
                  $timestamp     = strtotime($time_str); //return's server's time
                  $na_timestamp  = $timestamp;
                  $timestamp    -= $time_zone * 3600; //compensate for tz, get GMT
                  $result[$id]->timestamp = $timestamp;
                  $result[$id]->date = $time_str;
               }
               if ($conn->do_cache) {
                  $uid = $result[$id]->uid;
                  $conn->cache[$mailbox][$uid] = $result[$id];
                  $conn->cache_dirty[$mailbox] = true;
               }
               //echo "<!-- ID: $id : $time_str -- local: $na_timestamp (".date("F j, Y, g:i a", $na_timestamp).") tz: $time_zone -- GMT: ".$timestamp." (".date("F j, Y, g:i a", $timestamp).")  //-->\n";
            } else {
               //echo "<!-- ERROR: $id : $str //-->\n";
            }
         }
      }
   } while (strpos($line, $command_key) === false);
   return $result;
}
function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false) {
   $fp = $conn->fp;
   $a  = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch);
   if (is_array($a)) {
      return array_shift($a);
@@ -2116,7 +2112,7 @@
            $messages = explode(' ', $str);
         }
      } while (!iil_StartsWith($line, 'srch1', true));
      $result_code = iil_ParseResult($line);
      if ($result_code == 0) {
          return $messages;
@@ -2152,8 +2148,14 @@
 * @see iil_Connect()
 */
function iil_C_GetHierarchyDelimiter(&$conn) {
   global $my_prefs;
   if ($conn->delimiter) {
        return $conn->delimiter;
          return $conn->delimiter;
   }
   if (!empty($my_prefs['delimiter'])) {
           return ($conn->delimiter = $my_prefs['delimiter']);
   }
    
   $fp        = $conn->fp;
@@ -2365,36 +2367,13 @@
}
function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part) {
   $fp     = $conn->fp;
   $result = false;
   if (($part == 0) || (empty($part))) {
       $part = 'HEADER';
   } else {
           $part .= '.MIME';
   }
   if (iil_C_Select($conn, $mailbox)) {
      $key     = 'fh' . ($c++);
      $request = $key . " FETCH $id (BODY.PEEK[$part])";
      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] != ')')) {
            $line=iil_ReadLine($fp, 300);
            while (trim($line) != ')') {
               $result .= $line;
               $line=iil_ReadLine($fp, 300);
            }
         }
      } while (strcmp($a[0], $key) != 0 && ($a[0] != '*' || $a[1] != 'BYE'));
   }
   return $result;
   $part = empty($part) ? 'HEADER' : $part.'.MIME';
   return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1);
}
function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part, $mode, $file=NULL) {
function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part='', $mode=1, $file=NULL) {
   /* modes:
        1: return string (or write to $file pointer)
        2: print
@@ -2403,9 +2382,6 @@
   
   $fp     = $conn->fp;
   $result = false;
   if (($part == 0) || empty($part)) {
      $part = 'TEXT';
   }
   
   if (iil_C_Select($conn, $mailbox)) {
          $reply_key = '* ' . $id;
@@ -2681,31 +2657,41 @@
 * GETQUOTAROOT "INBOX"
 * QUOTAROOT INBOX user/rchijiiwa1
 * QUOTA user/rchijiiwa1 (STORAGE 654 9765)
 b OK Completed
 * OK Completed
 */
   $fp         = $conn->fp;
   $result     = false;
   $quota_line = '';
   $quota_lines = array();
   
   //get line containing quota info
   // get line(s) containing quota info
   if (iil_PutLine($fp, 'QUOT1 GETQUOTAROOT "INBOX"')) {
      do {
         $line=chop(iil_ReadLine($fp, 5000));
         if (iil_StartsWith($line, '* QUOTA ')) {
            $quota_line = $line;
            $quota_lines[] = $line;
              }
      } while (!iil_StartsWith($line, 'QUOT1', true));
   }
   
   //return false if not found, parse if found
   if (!empty($quota_line)) {
   // return false if not found, parse if found
   $min_free = PHP_INT_MAX;
   foreach ($quota_lines as $key => $quota_line) {
      $quota_line   = eregi_replace('[()]', '', $quota_line);
      $parts        = explode(' ', $quota_line);
      $storage_part = array_search('STORAGE', $parts);
      if ($storage_part > 0) {
         $result['used']    = intval($parts[$storage_part+1]);
         $result['total']   = intval($parts[$storage_part+2]);
         $result['percent'] = min(100, round(($result['used']/max(1,$result['total']))*100));
      if (!$storage_part) continue;
      $used   = intval($parts[$storage_part+1]);
      $total   = intval($parts[$storage_part+2]);
      $free   = $total - $used;
      // return lowest available space from all quotas
      if ($free < $min_free) {
              $min_free = $free;
         $result['used']    = $used;
         $result['total']   = $total;
         $result['percent'] = min(100, round(($used/max(1,$total))*100));
         $result['free']    = 100 - $result['percent'];
      }
   }