From 7a229b9e33f7955db3cd6725d357f01735293216 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Thu, 08 Jan 2009 07:40:18 -0500
Subject: [PATCH] - Improve messages display performance

---
 program/include/rcube_imap.php |  122 ++++++++++++++++++++++++++--------------
 1 files changed, 78 insertions(+), 44 deletions(-)

diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 0eb9f7b..36a3463 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -66,6 +66,7 @@
   var $search_sort_field = '';  
   var $debug_level = 1;
   var $error_code = 0;
+  var $options = array('imap' => 'check');
 
 
   /**
@@ -90,7 +91,7 @@
    * @return boolean  TRUE on success, FALSE on failure
    * @access public
    */
-  function connect($host, $user, $pass, $port=143, $use_ssl=null, $auth_type=null)
+  function connect($host, $user, $pass, $port=143, $use_ssl=null)
     {
     global $ICL_SSL, $ICL_PORT, $IMAP_USE_INTERNAL_DATE;
     
@@ -107,7 +108,7 @@
     $ICL_PORT = $port;
     $IMAP_USE_INTERNAL_DATE = false;
 
-    $this->conn = iil_Connect($host, $user, $pass, array('imap' => $auth_type ? $auth_type : 'check'));
+    $this->conn = iil_Connect($host, $user, $pass, $this->options);
     $this->host = $host;
     $this->user = $user;
     $this->pass = $pass;
@@ -172,6 +173,13 @@
       iil_C_Select($this->conn, $this->mailbox);
     }
 
+  /**
+   * Set options to be used in iil_Connect()
+   */
+  function set_options($opt)
+  {
+    $this->options = array_merge($this->options, (array)$opt);
+  }
 
   /**
    * Set a root folder for the IMAP connection.
@@ -188,6 +196,7 @@
       $root = substr($root, 0, -1);
 
     $this->root_dir = $root;
+    $this->options['rootdir'] = $root;
     
     if (empty($this->delimiter))
       $this->get_hierarchy_delimiter();
@@ -294,7 +303,7 @@
       $msgs = split(',', $msgs);
       
     $this->search_string = $str;
-    $this->search_set = (array)$msgs;
+    $this->search_set = $msgs;
     $this->search_charset = $charset;
     $this->search_sort_field = $sort_field;
     }
@@ -673,11 +682,12 @@
       return array_values($a_msg_headers);
       }
     else { // SEARCH searching result, need sorting
-      if ($cnt > $this->pagesize * 2) {
+      $cnt = count($msgs);
+      if ($cnt > 300 && $cnt > $this->page_size) { // experimantal value for best result
         // use memory less expensive (and quick) method for big result set
 	$a_index = $this->message_index($mailbox, $this->sort_field, $this->sort_order);
         // get messages uids for one page...
-        $msgs = array_slice(array_keys($a_index), $start_msg, min(count($msgs)-$start_msg, $this->page_size));
+        $msgs = array_slice(array_keys($a_index), $start_msg, min($cnt-$start_msg, $this->page_size));
 	// ...and fetch headers
         $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
 
@@ -703,7 +713,7 @@
         $a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
       
         // only return the requested part of the set
-        return array_slice(array_values($a_msg_headers), $start_msg, min(count($msgs)-$start_msg, $this->page_size));
+        return array_slice(array_values($a_msg_headers), $start_msg, min($cnt-$start_msg, $this->page_size));
         }
       }
     }
@@ -810,7 +820,7 @@
     // we have a saved search result. get index from there
     if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox)
     {
-      $this->cache[$key] = $a_msg_headers = array();
+      $this->cache[$key] = array();
       
       if ($this->get_capability('sort'))
         {
@@ -831,7 +841,7 @@
         else if ($this->sort_order=="DESC")
           arsort($a_index);
 
-        $this->cache[$key] = $a_index;
+        $this->cache[$key] = array_keys($a_index);
 	}
     }
 
@@ -847,9 +857,8 @@
     if ($cache_status>0)
       {
       $a_index = $this->get_message_cache_index($cache_key, TRUE, $this->sort_field, $this->sort_order);
-      return array_values($a_index);
+      return array_keys($a_index);
       }
-
 
     // fetch complete message index
     $msg_count = $this->_messagecount($mailbox);
@@ -869,7 +878,7 @@
       else if ($this->sort_order=="DESC")
         arsort($a_index);
         
-      $this->cache[$key] = $a_index;
+      $this->cache[$key] = array_keys($a_index);
       }
 
     return $this->cache[$key];
@@ -984,9 +993,8 @@
    */
   function _search_index($mailbox, $criteria='ALL', $charset=NULL, $sort_field=NULL)
     {
-    if ($this->get_capability('sort'))
+    if ($sort_field && $this->get_capability('sort'))
       {
-      $sort_field = $sort_field ? $sort_field : $this->sort_field;
       $charset = $charset ? $charset : $this->default_charset;
       $a_messages = iil_C_Sort($this->conn, $mailbox, $sort_field, $criteria, FALSE, $charset);
       }
@@ -1039,9 +1047,10 @@
    * @param int     Message ID
    * @param string  Mailbox to read from 
    * @param boolean True if $id is the message UID
+   * @param boolean True if we need also BODYSTRUCTURE in headers
    * @return object Message headers representation
    */
-  function get_headers($id, $mbox_name=NULL, $is_uid=TRUE)
+  function get_headers($id, $mbox_name=NULL, $is_uid=TRUE, $bodystr=FALSE)
     {
     $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
     $uid = $is_uid ? $id : $this->_id2uid($id);
@@ -1050,7 +1059,7 @@
     if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
       return $headers;
 
-    $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid);
+    $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid, $bodystr);
 
     // write headers cache
     if ($headers)
@@ -1070,9 +1079,10 @@
    * an object structure similar to the one generated by PEAR::Mail_mimeDecode
    *
    * @param int Message UID to fetch
+   * @param string Message BODYSTRUCTURE string (optional)
    * @return object rcube_message_part Message part tree or False on failure
    */
-  function &get_structure($uid)
+  function &get_structure($uid, $structure_str='')
     {
     $cache_key = $this->mailbox.'.msg';
     $headers = &$this->get_cached_message($cache_key, $uid, true);
@@ -1087,7 +1097,8 @@
       return FALSE;
     }
 
-    $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
+    if (!$structure_str)
+      $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
     $structure = iml_GetRawStructureArray($structure_str);
     $struct = false;
 
@@ -1122,7 +1133,7 @@
    *
    * @access private
    */
-  function &_structure_part($part, $count=0, $parent='')
+  function &_structure_part($part, $count=0, $parent='', $raw_headers=null)
     {
     $struct = new rcube_message_part;
     $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";
@@ -1142,11 +1153,25 @@
           
       $struct->mimetype = 'multipart/'.$struct->ctype_secondary;
 
+      // build parts list for headers pre-fetching
+      for ($i=0, $count=0; $i<count($part); $i++)
+        if (is_array($part[$i]) && count($part[$i]) > 3)
+	  // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
+	  if (strtolower($part[$i][0]) == 'message' ||
+	    (in_array('name', (array)$part[$i][2]) && (empty($part[$i][3]) || $part[$i][3]=='NIL'))) {
+	    $part_headers[] = $struct->mime_id ? $struct->mime_id.'.'.$i+1 : $i+1;
+	    }
+    
+      // pre-fetch headers of all parts (in one command for better performance)
+      if ($part_headers)
+        $part_headers = iil_C_FetchMIMEHeaders($this->conn, $this->mailbox, $this->_msg_id, $part_headers);
+
       $struct->parts = array();
       for ($i=0, $count=0; $i<count($part); $i++)
         if (is_array($part[$i]) && count($part[$i]) > 3)
-          $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id);
-          
+          $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
+		$part_headers[$struct->mime_id ? $struck->mime_id.'.'.$i+1 : $i+1]);
+
       return $struct;
       }
     
@@ -1211,8 +1236,9 @@
     
     // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
     if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
-      $part_headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id);
-      $struct->headers = $this->_parse_headers($part_headers) + $struct->headers;
+      if (empty($raw_headers))
+        $raw_headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id);
+      $struct->headers = $this->_parse_headers($raw_headers) + $struct->headers;
     }
 
     if ($struct->ctype_primary=='message') {
@@ -1365,6 +1391,8 @@
       
     // TODO: Add caching for message parts
 
+    if (!$part) $part = 'TEXT';
+
     if ($print)
       {
       $mode = $o_part->encoding == 'base64' ? 3 : ($o_part->encoding == 'quoted-printable' ? 1 : 2);
@@ -1436,10 +1464,7 @@
     if (!($msg_id = $this->_uid2id($uid)))
       return FALSE;
 
-    $body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
-    $body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1);
-
-    return $body;    
+    return iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id);
     }
 
 
@@ -1470,8 +1495,6 @@
     if (!($msg_id = $this->_uid2id($uid)))
       return FALSE;
 
-    print iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
-    flush();
     iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 2);
     }
 
@@ -2149,8 +2172,7 @@
       {
       $this->db->query(
         "UPDATE ".get_table_name('cache')."
-         SET    created=".$this->db->now().",
-                data=?
+         SET    created=". $this->db->now().", data=?
          WHERE  user_id=?
          AND    cache_key=?",
         $data,
@@ -2337,7 +2359,7 @@
     {
     if (empty($key) || !is_object($headers) || empty($headers->uid))
         return;
-    
+
     // add to internal (fast) cache
     $this->cache['__single_msg'][$headers->uid] = $headers;
     $this->cache['__single_msg'][$headers->uid]->structure = $struct;
@@ -2500,12 +2522,13 @@
   /**
    * Decode a mime-encoded string to internal charset
    *
-   * @param string  Header value
-   * @param string  Fallback charset if none specified
+   * @param string $input    Header value
+   * @param string $fallback Fallback charset if none specified
+   *
    * @return string Decoded string
    * @static
    */
-  function decode_mime_string($input, $fallback=null)
+  public static function decode_mime_string($input, $fallback=null)
     {
     // Initialize variable
     $out = '';
@@ -2714,32 +2737,43 @@
         $folders[$folder] = rc_strtolower(rcube_charset_convert($folder, 'UTF-7'));
       }
 
+    // sort folders and place defaults on the top
     asort($folders, SORT_LOCALE_STRING);
     ksort($a_defaults);
-
     $folders = array_merge($a_defaults, array_keys($folders));
 
     // finally we must rebuild the list to move 
     // subfolders of default folders to their place...
     // ...also do this for the rest of folders because
     // asort() is not properly sorting case sensitive names
-
-    // set the type of folder name variable (#1485527) 
     while (list($key, $folder) = each($folders)) {
+      // set the type of folder name variable (#1485527) 
       $a_out[] = (string) $folder;
       unset($folders[$key]);
-      foreach ($folders as $idx => $f) {
-	if (strpos($f, $folder.$delimiter) === 0) {
-    	  $a_out[] = (string) $f;
-	  unset($folders[$idx]);
-	  }
-        }
-      reset($folders);  
+      $this->_rsort($folder, $delimiter, $folders, $a_out);	
       }
 
     return $a_out;
     }
 
+
+  /**
+   * @access private
+   */
+  function _rsort($folder, $delimiter, &$list, &$out)
+    {
+      while (list($key, $name) = each($list)) {
+	if (strpos($name, $folder.$delimiter) === 0) {
+	  // set the type of folder name variable (#1485527) 
+    	  $out[] = (string) $name;
+	  unset($list[$key]);
+	  $this->_rsort($name, $delimiter, $list, $out);
+	  }
+        }
+      reset($list);	
+    }
+
+
   /**
    * @access private
    */

--
Gitblit v1.9.1