From 427ab2f3938122d7ce1d0862a583a5adaed6c6c9 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Wed, 02 Sep 2015 09:17:44 -0400
Subject: [PATCH] Optimize folder_size() on Cyrus IMAP by using special folder annotation (#1490514)

---
 CHANGELOG                               |    1 +
 program/lib/Roundcube/rcube_imap.php    |   56 ++++++++++++++++++++++++++++++++++++++------------------
 program/lib/Roundcube/rcube_storage.php |    3 ++-
 3 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 596bd6c..a8a52ca 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Optimize folder_size() on Cyrus IMAP by using special folder annotation (#1490514)
 - Make optional hidding of folders with name starting with a dot - imap_skip_hidden_folders (#1490468)
 - Add option to enable HTML editor always, except when replying to plain text messages (#1489365)
 - Emoticons: Added option to switch on/off emoticons in compose editor (#1485732)
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index 51c5442..73d32d0 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -2427,7 +2427,7 @@
      *
      * @param mixed   $uids       Message UIDs as array or comma-separated string, or '*'
      * @param string  $flag       Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
-     * @param string  $folder    Folder name
+     * @param string  $folder     Folder name
      * @param boolean $skip_cache True to skip message cache clean up
      *
      * @return boolean  Operation status
@@ -3112,8 +3112,22 @@
      */
     public function folder_size($folder)
     {
+        if (!strlen($folder)) {
+            return false;
+        }
+
         if (!$this->check_connection()) {
             return 0;
+        }
+
+        // On Cyrus we can use special folder annotation, which should be much faster
+        if ($this->get_vendor() == 'cyrus') {
+            $idx    = '/shared/vendor/cmu/cyrus-imapd/size';
+            $result = $this->get_metadata($folder, $idx, array(), true);
+
+            if (!empty($result) && is_numeric($result[$folder][$idx])) {
+                return $result[$folder][$idx];
+            }
         }
 
         // @TODO: could we try to use QUOTA here?
@@ -3879,30 +3893,33 @@
     /**
      * Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION)
      *
-     * @param string $folder  Folder name (empty for server metadata)
-     * @param array  $entries Entries
-     * @param array  $options Command options (with MAXSIZE and DEPTH keys)
+     * @param string  $folder   Folder name (empty for server metadata)
+     * @param array   $entries  Entries
+     * @param array   $options  Command options (with MAXSIZE and DEPTH keys)
+     * @param bool    $force    Disables cache use
      *
      * @return array Metadata entry-value hash array on success, NULL on error
      * @since 0.5-beta
      */
-    public function get_metadata($folder, $entries, $options=array())
+    public function get_metadata($folder, $entries, $options = array(), $force = false)
     {
-        $entries = (array)$entries;
+        $entries = (array) $entries;
 
-        // create cache key
-        // @TODO: this is the simplest solution, but we do the same with folders list
-        //        maybe we should store data per-entry and merge on request
-        sort($options);
-        sort($entries);
-        $cache_key = 'mailboxes.metadata.' . $folder;
-        $cache_key .= '.' . md5(serialize($options).serialize($entries));
+        if (!$force) {
+            // create cache key
+            // @TODO: this is the simplest solution, but we do the same with folders list
+            //        maybe we should store data per-entry and merge on request
+            sort($options);
+            sort($entries);
+            $cache_key = 'mailboxes.metadata.' . $folder;
+            $cache_key .= '.' . md5(serialize($options).serialize($entries));
 
-        // get cached data
-        $cached_data = $this->get_cache($cache_key);
+            // get cached data
+            $cached_data = $this->get_cache($cache_key);
 
-        if (is_array($cached_data)) {
-            return $cached_data;
+            if (is_array($cached_data)) {
+                return $cached_data;
+            }
         }
 
         if (!$this->check_connection()) {
@@ -3941,7 +3958,10 @@
         }
 
         if (isset($res)) {
-            $this->update_cache($cache_key, $res);
+            if (!$force) {
+                $this->update_cache($cache_key, $res);
+            }
+
             return $res;
         }
 
diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php
index c427f48..48ab91f 100644
--- a/program/lib/Roundcube/rcube_storage.php
+++ b/program/lib/Roundcube/rcube_storage.php
@@ -945,10 +945,11 @@
      * @param string $folder   Folder name (empty for server metadata)
      * @param array  $entries  Entries
      * @param array  $options  Command options (with MAXSIZE and DEPTH keys)
+     * @param bool   $force    Disables cache use
      *
      * @return array Metadata entry-value hash array on success, NULL on error
      */
-    abstract function get_metadata($folder, $entries, $options = array());
+    abstract function get_metadata($folder, $entries, $options = array(), $force = false);
 
     /* -----------------------------------------
      *   Cache related functions

--
Gitblit v1.9.1