From 309f49f09bc8b663a3ddf834ca0e79f909a0928c Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Fri, 04 Jun 2010 05:58:37 -0400 Subject: [PATCH] - performance improvement: skip SEARCH command when mailbox is empty and SEARCH is called just after SELECT --- program/include/rcube_imap.php | 215 ++++++++++++++++++++++++++--------------------------- 1 files changed, 107 insertions(+), 108 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 768e833..55b0820 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -297,7 +297,9 @@ { if (is_array($str) && $msgs == null) list($str, $msgs, $charset, $sort_field, $threads) = $str; - if ($msgs != null && !is_array($msgs)) + if ($msgs === false) + $msgs = array(); + else if ($msgs != null && !is_array($msgs)) $msgs = explode(',', $msgs); $this->search_string = $str; @@ -472,14 +474,9 @@ // get message count and store in cache if ($mode == 'UNSEEN') $search_str .= " UNSEEN"; - // get message count using SEARCH // not very performant but more precise (using UNDELETED) - // disable THREADS for this request - $threads = $this->threading; - $this->threading = false; - $index = $this->_search_index($mailbox, $search_str); - $this->threading = $threads; + $index = $this->conn->search($mailbox, $search_str); $count = is_array($index) ? count($index) : 0; @@ -608,7 +605,7 @@ else $msg_index = array(); - if ($slice) + if ($slice && $msg_index) $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); // fetch reqested headers from server @@ -823,8 +820,11 @@ return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice); // search set is threaded, we need a new one - if ($this->search_threads) + if ($this->search_threads) { + if (empty($this->search_set['tree'])) + return array(); $this->search('', $this->search_string, $this->search_charset, $sort_field); + } $msgs = $this->search_set; $a_msg_headers = array(); @@ -946,8 +946,15 @@ private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) { // update search_set if previous data was fetched with disabled threading - if (!$this->search_threads) + if (!$this->search_threads) { + if (empty($this->search_set)) + return array(); $this->search('', $this->search_string, $this->search_charset, $sort_field); + } + + // empty result + if (empty($this->search_set['tree'])) + return array(); $thread_tree = $this->search_set['tree']; $msg_depth = $this->search_set['depth']; @@ -1188,20 +1195,20 @@ $a_index = range(1, $max); } - if ($this->sort_order == 'DESC') + if ($a_index !== false && $this->sort_order == 'DESC') $a_index = array_reverse($a_index); $this->cache[$key] = $a_index; } // fetch complete message index else if ($this->get_capability('SORT')) { - if ($a_index = $this->conn->sort($mailbox, - $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')) { - if ($this->sort_order == 'DESC') - $a_index = array_reverse($a_index); + $a_index = $this->conn->sort($mailbox, + $this->sort_field, $this->skip_deleted ? 'UNDELETED' : ''); - $this->cache[$key] = $a_index; - } + if ($a_index !== false && $this->sort_order == 'DESC') + $a_index = array_reverse($a_index); + + $this->cache[$key] = $a_index; } else if ($a_index = $this->conn->fetchHeaderIndex( $mailbox, "1:*", $this->sort_field, $this->skip_deleted)) { @@ -1213,7 +1220,7 @@ $this->cache[$key] = array_keys($a_index); } - return $this->cache[$key]; + return $this->cache[$key] !== false ? $this->cache[$key] : array(); } @@ -1370,32 +1377,6 @@ $results = $this->_search_index($mailbox, $str, $charset, $sort_field); - // try search with US-ASCII charset (should be supported by server) - // only if UTF-8 search is not supported - if (empty($results) && !is_array($results) && !empty($charset) && $charset != 'US-ASCII') - { - // convert strings to US_ASCII - if(preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) { - $last = 0; $res = ''; - foreach($matches[1] as $m) - { - $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n - $string = substr($str, $string_offset - 1, $m[0]); - $string = rcube_charset_convert($string, $charset, 'US-ASCII'); - if (!$string) - continue; - $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string); - $last = $m[0] + $string_offset - 1; - } - if ($last < strlen($str)) - $res .= substr($str, $last, strlen($str)-$last); - } - else // strings for conversion not found - $res = $str; - - $results = $this->search($mbox_name, $res, NULL, $sort_field); - } - $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading); return $results; @@ -1417,21 +1398,32 @@ $criteria = 'UNDELETED '.$criteria; if ($this->threading) { - list ($thread_tree, $msg_depth, $has_children) = $this->conn->thread( - $mailbox, $this->threading, $criteria, $charset); + $a_messages = $this->conn->thread($mailbox, $this->threading, $criteria, $charset); - $a_messages = array( - 'tree' => $thread_tree, - 'depth' => $msg_depth, - 'children' => $has_children - ); + // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, + // but I've seen that Courier doesn't support UTF-8) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->thread($mailbox, $this->threading, + $this->convert_criteria($criteria, $charset), 'US-ASCII'); + + if ($a_messages !== false) { + list ($thread_tree, $msg_depth, $has_children) = $a_messages; + $a_messages = array( + 'tree' => $thread_tree, + 'depth' => $msg_depth, + 'children' => $has_children + ); + } } else if ($sort_field && $this->get_capability('SORT')) { $charset = $charset ? $charset : $this->default_charset; $a_messages = $this->conn->sort($mailbox, $sort_field, $criteria, false, $charset); - if (!$a_messages) - return array(); + // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, + // but I've seen that Courier doesn't support UTF-8) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->sort($mailbox, $sort_field, + $this->convert_criteria($criteria, $charset), false, 'US-ASCII'); } else { if ($orig_criteria == 'ALL') { @@ -1440,14 +1432,16 @@ } else { $a_messages = $this->conn->search($mailbox, - ($charset ? "CHARSET $charset " : '') . $criteria); + ($charset ? "CHARSET $charset " : '') . $criteria); - if (!$a_messages) - return array(); + // Error, try with US-ASCII (some servers may support only US-ASCII) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->search($mailbox, + 'CHARSET US-ASCII ' . $this->convert_criteria($criteria, $charset)); - // I didn't found that SEARCH always returns sorted IDs - if (!$this->sort_field) - sort($a_messages); + // I didn't found that SEARCH should return sorted IDs + if (is_array($a_messages) && !$this->sort_field) + sort($a_messages); } } @@ -1478,6 +1472,39 @@ $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; return $this->conn->search($mailbox, $str, $ret_uid); + } + + + /** + * Converts charset of search criteria string + * + * @param string Search string + * @param string Original charset + * @param string Destination charset (default US-ASCII) + * @return string Search string + * @access private + */ + private function convert_criteria($str, $charset, $dest_charset='US-ASCII') + { + // convert strings to US_ASCII + if (preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) { + $last = 0; $res = ''; + foreach ($matches[1] as $m) { + $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n + $string = substr($str, $string_offset - 1, $m[0]); + $string = rcube_charset_convert($string, $charset, $dest_charset); + if (!$string) + continue; + $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string); + $last = $m[0] + $string_offset - 1; + } + if ($last < strlen($str)) + $res .= substr($str, $last, strlen($str)-$last); + } + else // strings for conversion not found + $res = $str; + + return $res; } @@ -2034,7 +2061,7 @@ return true; // convert charset (if text or message part) - if ($o_part->ctype_primary=='text' || $o_part->ctype_primary=='message') { + if ($o_part->ctype_primary == 'text' || $o_part->ctype_primary == 'message') { // assume default if no charset specified if (empty($o_part->charset) || strtolower($o_part->charset) == 'us-ascii') $o_part->charset = $this->default_charset; @@ -2512,7 +2539,7 @@ * --------------------------------*/ /** - * Public method for mailbox listing. + * Public method for listing subscribed folders * * Converts mailbox name with root dir first * @@ -2552,16 +2579,16 @@ */ private function _list_mailboxes($root='', $filter='*') { - $a_defaults = $a_out = array(); - // get cached folder list $a_mboxes = $this->get_cache('mailboxes'); if (is_array($a_mboxes)) return $a_mboxes; + $a_defaults = $a_out = array(); + // Give plugins a chance to provide a list of mailboxes $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes', - array('root'=>$root,'filter'=>$filter)); + array('root' => $root, 'filter' => $filter, 'mode' => 'LSUB')); if (isset($data['folders'])) { $a_folders = $data['folders']; @@ -2585,17 +2612,26 @@ * Get a list of all folders available on the IMAP server * * @param string IMAP root dir + * @param string Optional filter for mailbox listing * @return array Indexed array with folder names */ - function list_unsubscribed($root='') + function list_unsubscribed($root='', $filter='*') { - static $a_folders; + // Give plugins a chance to provide a list of mailboxes + $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes', + array('root' => $root, 'filter' => $filter, 'mode' => 'LIST')); - if (is_array($a_folders)) - return $a_folders; - - // retrieve list of folders from IMAP server - $a_mboxes = $this->conn->listMailboxes($this->mod_mailbox($root), '*'); + if (isset($data['folders'])) { + $a_mboxes = $data['folders']; + } + else { + // retrieve list of folders from IMAP server + $a_mboxes = $this->conn->listMailboxes($this->mod_mailbox($root), $filter); + } + + $a_folders = array(); + if (!is_array($a_mboxes)) + $a_mboxes = array(); // modify names with root dir foreach ($a_mboxes as $idx => $mbox_name) { @@ -3370,43 +3406,6 @@ } return $out; - } - - - /** - * Decode a Microsoft Outlook TNEF part (winmail.dat) - * - * @param object rcube_message_part Message part to decode - * @param string UID of the message - * @return array List of rcube_message_parts extracted from windmail.dat - */ - function tnef_decode(&$part, $uid) - { - if (!isset($part->body)) - $part->body = $this->get_message_part($uid, $part->mime_id, $part); - - require_once('lib/tnef_decoder.inc'); - - $pid = 0; - $tnef_parts = array(); - $tnef_arr = tnef_decode($part->body); - - foreach ($tnef_arr as $winatt) { - $tpart = new rcube_message_part; - $tpart->filename = $winatt["name"]; - $tpart->encoding = 'stream'; - $tpart->ctype_primary = $winatt["type0"]; - $tpart->ctype_secondary = $winatt["type1"]; - $tpart->mimetype = strtolower($winatt["type0"] . "/" . $winatt["type1"]); - $tpart->mime_id = "winmail." . $part->mime_id . ".$pid"; - $tpart->size = $winatt["size"]; - $tpart->body = $winatt['stream']; - - $tnef_parts[] = $tpart; - $pid++; - } - - return $tnef_parts; } -- Gitblit v1.9.1