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) --- program/lib/Roundcube/rcube_imap.php | 383 +++++++++++++++++++++++++----------------------------- 1 files changed, 176 insertions(+), 207 deletions(-) diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 65e0950..73d32d0 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1,6 +1,6 @@ <?php -/* +/** +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | | Copyright (C) 2005-2012, The Roundcube Dev Team | @@ -57,7 +57,6 @@ protected $icache = array(); protected $plugins; - protected $list_page = 1; protected $delimiter; protected $namespace; protected $sort_field = ''; @@ -82,7 +81,7 @@ */ public function __construct() { - $this->conn = new rcube_imap_generic(); + $this->conn = new rcube_imap_generic(); $this->plugins = rcube::get_instance()->plugins; // Set namespace and delimiter from session, @@ -95,7 +94,6 @@ } } - /** * Magic getter for backward compat. * @@ -107,7 +105,6 @@ return $this->{$name}; } } - /** * Connect to an IMAP server @@ -203,7 +200,6 @@ return false; } - /** * Close IMAP connection. * Usually done on script shutdown @@ -215,7 +211,6 @@ $this->mcache->close(); } } - /** * Check connection state, connect if not connected. @@ -238,7 +233,6 @@ return $this->is_connected(); } - /** * Checks IMAP connection. * @@ -248,7 +242,6 @@ { return $this->conn->connected(); } - /** * Returns code of last error @@ -260,7 +253,6 @@ return $this->conn->errornum; } - /** * Returns text of last error * @@ -270,7 +262,6 @@ { return $this->conn->error; } - /** * Returns code of last command response @@ -301,7 +292,6 @@ } } - /** * Activate/deactivate debug mode * @@ -313,23 +303,21 @@ $this->conn->setDebug($dbg, array($this, 'debug_handler')); } - /** * Set internal folder reference. * All operations will be perfomed on this folder. * - * @param string $folder Folder name + * @param string $folder Folder name */ public function set_folder($folder) { $this->folder = $folder; } - /** * Save a search result for future message listing methods * - * @param array $set Search set, result from rcube_imap::get_search_set(): + * @param array $set Search set, result from rcube_imap::get_search_set(): * 0 - searching criteria, string * 1 - search result, rcube_result_index|rcube_result_thread * 2 - searching character set, string @@ -352,7 +340,6 @@ } } - /** * Return the saved search set as hash array * @@ -372,7 +359,6 @@ $this->search_sorted, ); } - /** * Returns the IMAP server's capability. @@ -397,14 +383,13 @@ return $_SESSION[$sess_key]; } - /** * Checks the PERMANENTFLAGS capability of the current folder * and returns true if the given flag is supported by the IMAP server * - * @param string $flag Permanentflag name + * @param string $flag Permanentflag name * - * @return boolean True if this flag is supported + * @return boolean True if this flag is supported */ public function check_permflag($flag) { @@ -415,11 +400,10 @@ return $imap_flag && !empty($perm_flags) && in_array_nocase($imap_flag, $perm_flags); } - /** * Returns PERMANENTFLAGS of the specified folder * - * @param string $folder Folder name + * @param string $folder Folder name * * @return array Flags */ @@ -447,25 +431,22 @@ return $permflags; } - /** * Returns the delimiter that is used by the IMAP server for folder separation * - * @return string Delimiter string - * @access public + * @return string Delimiter string */ public function get_hierarchy_delimiter() { return $this->delimiter; } - /** * Get namespace * * @param string $name Namespace array index: personal, other, shared, prefix * - * @return array Namespace data + * @return array Namespace data */ public function get_namespace($name = null) { @@ -479,7 +460,6 @@ return $ns; } - /** * Sets delimiter and namespaces */ @@ -489,7 +469,7 @@ return; } - $config = rcube::get_instance()->config; + $config = rcube::get_instance()->config; $imap_personal = $config->get('imap_ns_personal'); $imap_other = $config->get('imap_ns_other'); $imap_shared = $config->get('imap_ns_shared'); @@ -560,6 +540,51 @@ $_SESSION['imap_delimiter'] = $this->delimiter; } + /** + * Returns IMAP server vendor name + * + * @return string Vendor name + * @since 1.2 + */ + public function get_vendor() + { + if ($_SESSION['imap_vendor'] !== null) { + return $_SESSION['imap_vendor']; + } + + $config = rcube::get_instance()->config; + $imap_vendor = $config->get('imap_vendor'); + + if ($imap_vendor) { + return $imap_vendor; + } + + if (!$this->check_connection()) { + return; + } + + if (($ident = $this->conn->data['ID']) === null) { + $ident = $this->conn->id(array( + 'name' => 'Roundcube', + 'version' => RCUBE_VERSION, + 'php' => PHP_VERSION, + 'os' => PHP_OS, + )); + } + + $vendor = (string) (!empty($ident) ? $ident['name'] : ''); + $ident = strtolower($vendor . ' ' . $this->conn->data['GREETING']); + $vendors = array('cyrus', 'dovecot', 'uw-imap', 'gmail', 'hmail'); + + foreach ($vendors as $v) { + if (strpos($ident, $v) !== false) { + $vendor = $v; + break; + } + } + + return $_SESSION['imap_vendor'] = $vendor; + } /** * Get message count for a specific folder @@ -570,7 +595,7 @@ * @param boolean $status Enables storing folder status info (max UID/count), * required for folder_status() * - * @return int Number of messages + * @return int Number of messages */ public function count($folder='', $mode='ALL', $force=false, $status=true) { @@ -581,29 +606,31 @@ return $this->countmessages($folder, $mode, $force, $status); } - /** - * protected method for getting nr of messages + * Protected method for getting number of messages * - * @param string $folder Folder name - * @param string $mode Mode for count [ALL|THREADS|UNSEEN|RECENT|EXISTS] - * @param boolean $force Force reading from server and update cache - * @param boolean $status Enables storing folder status info (max UID/count), - * required for folder_status() + * @param string $folder Folder name + * @param string $mode Mode for count [ALL|THREADS|UNSEEN|RECENT|EXISTS] + * @param boolean $force Force reading from server and update cache + * @param boolean $status Enables storing folder status info (max UID/count), + * required for folder_status() + * @param boolean $no_search Ignore current search result * * @return int Number of messages * @see rcube_imap::count() */ - protected function countmessages($folder, $mode='ALL', $force=false, $status=true) + protected function countmessages($folder, $mode = 'ALL', $force = false, $status = true, $no_search = false) { $mode = strtoupper($mode); - // count search set, assume search set is always up-to-date (don't check $force flag) - if ($this->search_string && $folder == $this->folder && ($mode == 'ALL' || $mode == 'THREADS')) { + // Count search set, assume search set is always up-to-date (don't check $force flag) + // @TODO: this could be handled in more reliable way, e.g. a separate method + // maybe in rcube_imap_search + if (!$no_search && $this->search_string && $folder == $this->folder) { if ($mode == 'ALL') { return $this->search_set->count_messages(); } - else { + else if ($mode == 'THREADS') { return $this->search_set->count(); } } @@ -696,7 +723,6 @@ return (int)$count; } - /** * Public method for listing message flags * @@ -731,7 +757,6 @@ return $result; } - /** * Public method for listing headers * @@ -741,7 +766,7 @@ * @param string $sort_order Sort order [ASC|DESC] * @param int $slice Number of slice items to extract from result array * - * @return array Indexed array with message header objects + * @return array Indexed array with message header objects */ public function list_messages($folder='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) { @@ -752,7 +777,6 @@ return $this->_list_messages($folder, $page, $sort_field, $sort_order, $slice); } - /** * protected method for listing message headers * @@ -762,8 +786,8 @@ * @param string $sort_order Sort order [ASC|DESC] * @param int $slice Number of slice items to extract from result array * - * @return array Indexed array with message header objects - * @see rcube_imap::list_messages + * @return array Indexed array with message header objects + * @see rcube_imap::list_messages */ protected function _list_messages($folder='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) { @@ -806,16 +830,15 @@ return array_values($a_msg_headers); } - /** * protected method for listing message headers using threads * - * @param string $folder Folder name - * @param int $page Current page to list - * @param int $slice Number of slice items to extract from result array + * @param string $folder Folder name + * @param int $page Current page to list + * @param int $slice Number of slice items to extract from result array * - * @return array Indexed array with message header objects - * @see rcube_imap::list_messages + * @return array Indexed array with message header objects + * @see rcube_imap::list_messages */ protected function list_thread_messages($folder, $page, $slice=0) { @@ -833,7 +856,7 @@ /** * Method for fetching threads data * - * @param string $folder Folder name + * @param string $folder Folder name * * @return rcube_imap_thread Thread data object */ @@ -857,11 +880,10 @@ return $this->icache['threads'] = $result; } - /** * Method for direct fetching of threads data * - * @param string $folder Folder name + * @param string $folder Folder name * * @return rcube_imap_thread Thread data object */ @@ -876,7 +898,6 @@ $this->options['skip_deleted'] ? 'UNDELETED' : '', true); } - /** * protected method for fetching threaded messages headers * @@ -885,7 +906,7 @@ * @param int $page List page number * @param int $slice Number of threads to slice * - * @return array Messages headers + * @return array Messages headers */ protected function fetch_thread_headers($folder, $threads, $page, $slice=0) { @@ -915,13 +936,12 @@ return array_values($a_msg_headers); } - /** * protected method for setting threaded messages flags: * depth, has_children and unread_children * - * @param array $headers Reference to headers array indexed by message UID - * @param rcube_result_thread $threads Threads data object + * @param array $headers Reference to headers array indexed by message UID + * @param rcube_result_thread $threads Threads data object * * @return array Message headers array indexed by message UID */ @@ -947,7 +967,6 @@ } } - /** * protected method for listing a set of message headers (search results) * @@ -955,7 +974,7 @@ * @param int $page Current page to list * @param int $slice Number of slice items to extract from result array * - * @return array Indexed array with message header objects + * @return array Indexed array with message header objects */ protected function list_search_messages($folder, $page, $slice=0) { @@ -1140,7 +1159,6 @@ } } - /** * protected method for listing a set of threaded message headers (search results) * @@ -1148,7 +1166,7 @@ * @param int $page Current page to list * @param int $slice Number of slice items to extract from result array * - * @return array Indexed array with message header objects + * @return array Indexed array with message header objects * @see rcube_imap::list_search_messages() */ protected function list_search_thread_messages($folder, $page, $slice=0) @@ -1163,7 +1181,6 @@ return $this->fetch_thread_headers($folder, clone $this->search_set, $page, $slice); } - /** * Fetches messages headers (by UID) @@ -1212,7 +1229,6 @@ return $a_msg_headers; } - /** * Returns current status of a folder (compared to the last time use) * @@ -1232,7 +1248,7 @@ $old = $this->get_folder_stats($folder); // refresh message count -> will update - $this->countmessages($folder, 'ALL', true); + $this->countmessages($folder, 'ALL', true, true, true); $result = 0; @@ -1260,7 +1276,6 @@ return $result; } - /** * Stores folder statistic data in session * @TODO: move to separate DB table (cache?) @@ -1273,7 +1288,6 @@ { $_SESSION['folders'][$folder][$name] = $data; } - /** * Gets folder statistic data @@ -1290,7 +1304,6 @@ return array(); } - /** * Return sorted list of message UIDs @@ -1369,7 +1382,6 @@ return $this->index_direct($folder, $this->sort_field, $this->sort_order); } - /** * Return sorted list of message UIDs ignoring current search settings. * Doesn't uses cache by default. @@ -1437,7 +1449,6 @@ return $index; } - /** * Return index of threaded message UIDs * @@ -1468,7 +1479,6 @@ return $threads; } - /** * Sort threaded result, using THREAD=REFS method if available. * If not, use any method and re-sort the result in THREAD=REFS way. @@ -1497,7 +1507,6 @@ $threads->revert(); } } - /** * Invoke search request to IMAP server @@ -1574,7 +1583,6 @@ return $results; } - /** * Direct (real and simple) SEARCH request (without result sorting and caching). * @@ -1608,7 +1616,6 @@ return $index; } - /** * protected search method @@ -1686,7 +1693,6 @@ return $messages; } - /** * Converts charset of search criteria string * @@ -1726,7 +1732,6 @@ return $res; } - /** * Refresh saved search set * @@ -1758,7 +1763,6 @@ } } } - /** * Return message headers object of a specific message @@ -1798,13 +1802,12 @@ return $headers; } - /** * Fetch message headers and body structure from the IMAP server and build - * an object structure similar to the one generated by PEAR::Mail_mimeDecode + * an object structure. * - * @param int $uid Message UID to fetch - * @param string $folder Folder to read from + * @param int $uid Message UID to fetch + * @param string $folder Folder to read from * * @return object rcube_message_header Message data */ @@ -1883,8 +1886,8 @@ $structure[1] = $m[2]; } else { - // Try to parse the message using Mail_mimeDecode package - // We need a better solution, Mail_mimeDecode parses message + // Try to parse the message using rcube_mime_decode. + // We need a better solution, it parses message // in memory, which wouldn't work for very big messages, // (it uses up to 10x more memory than the message size) // it's also buggy and not actively developed @@ -1924,7 +1927,6 @@ return $this->icache['message'] = $headers; } - /** * Build message part object * @@ -1932,7 +1934,7 @@ * @param int $count * @param string $parent */ - protected function structure_part($part, $count=0, $parent='', $mime_headers=null) + protected function structure_part($part, $count = 0, $parent = '', $mime_headers = null) { $struct = new rcube_message_part; $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count"; @@ -2150,14 +2152,13 @@ return $struct; } - /** * Set attachment filename from message part structure * - * @param rcube_message_part $part Part object - * @param string $headers Part's raw headers + * @param rcube_message_part $part Part object + * @param string $headers Part's raw headers */ - protected function set_part_filename(&$part, $headers=null) + protected function set_part_filename(&$part, $headers = null) { if (!empty($part->d_parameters['filename'])) { $filename_mime = $part->d_parameters['filename']; @@ -2285,11 +2286,10 @@ } } - /** * Get charset name from message structure (first part) * - * @param array $structure Message structure + * @param array $structure Message structure * * @return string Charset name */ @@ -2318,7 +2318,8 @@ * * @return string Message/part body if not printed */ - public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0, $formatted=true) + public function get_message_part($uid, $part = 1, $o_part = null, $print = null, $fp = null, + $skip_charset_conv = false, $max_bytes = 0, $formatted = true) { if (!$this->check_connection()) { return null; @@ -2370,7 +2371,6 @@ return $body; } - /** * Returns the whole message source as string (or saves to a file) * @@ -2390,7 +2390,6 @@ true, $part, null, false, $fp); } - /** * Returns the message headers as string * @@ -2408,7 +2407,6 @@ return $this->conn->fetchPartHeader($this->folder, $uid, true, $part); } - /** * Sends the whole message source to stdout * @@ -2424,13 +2422,12 @@ $this->conn->handlePartBody($this->folder, $uid, true, null, null, true, null, $formatted); } - /** * Set message flag to one or several messages * * @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 @@ -2483,7 +2480,6 @@ return $result; } - /** * Append a mail message (source) to a specific folder @@ -2541,7 +2537,6 @@ return $saved; } - /** * Move a message from one folder to another @@ -2620,7 +2615,6 @@ return $moved; } - /** * Copy a message from one folder to another * @@ -2656,7 +2650,6 @@ return $copied; } - /** * Mark messages as deleted and expunge them @@ -2714,7 +2707,6 @@ return $deleted; } - /** * Send IMAP expunge command and clear cache @@ -2838,15 +2830,14 @@ return $a_mboxes; } - /** * Method for direct folders listing (LSUB) * - * @param string $root Optional root folder - * @param string $name Optional name pattern + * @param string $root Optional root folder + * @param string $name Optional name pattern * - * @return array List of subscribed folders - * @see rcube_imap::list_folders_subscribed() + * @return array List of subscribed folders + * @see rcube_imap::list_folders_subscribed() */ public function list_folders_subscribed_direct($root='', $name='*') { @@ -2861,42 +2852,47 @@ $list_extended = !$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED'); if ($list_extended) { // This will also set folder options, LSUB doesn't do that - $a_folders = $this->conn->listMailboxes($root, $name, + $result = $this->conn->listMailboxes($root, $name, NULL, array('SUBSCRIBED')); } else { // retrieve list of folders from IMAP server using LSUB - $a_folders = $this->conn->listSubscribed($root, $name); + $result = $this->conn->listSubscribed($root, $name); } - if (!is_array($a_folders)) { + if (!is_array($result)) { return array(); } // #1486796: some server configurations doesn't return folders in all namespaces if ($root == '' && $name == '*' && $config->get('imap_force_ns')) { - $this->list_folders_update($a_folders, ($list_extended ? 'ext-' : '') . 'subscribed'); + $this->list_folders_update($result, ($list_extended ? 'ext-' : '') . 'subscribed'); + } + + // Remove hidden folders + if ($config->get('imap_skip_hidden_folders')) { + $result = array_filter($result, function($v) { return $v[0] != '.'; }); } if ($list_extended) { // unsubscribe non-existent folders, remove from the list - if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) { - foreach ($a_folders as $idx => $folder) { + if ($name == '*' && !empty($this->conn->data['LIST'])) { + foreach ($result as $idx => $folder) { if (($opts = $this->conn->data['LIST'][$folder]) - && in_array('\\NonExistent', $opts) + && in_array_nocase('\\NonExistent', $opts) ) { $this->conn->unsubscribe($folder); - unset($a_folders[$idx]); + unset($result[$idx]); } } } } else { // unsubscribe non-existent folders, remove them from the list - if (is_array($a_folders) && !empty($a_folders) && $name == '*') { + if (!empty($result) && $name == '*') { $existing = $this->list_folders($root, $name); - $nonexisting = array_diff($a_folders, $existing); - $a_folders = array_diff($a_folders, $nonexisting); + $nonexisting = array_diff($result, $existing); + $result = array_diff($result, $nonexisting); foreach ($nonexisting as $folder) { $this->conn->unsubscribe($folder); @@ -2904,9 +2900,8 @@ } } - return $a_folders; + return $result; } - /** * Get a list of all folders available on the server @@ -2976,15 +2971,14 @@ return $a_mboxes; } - /** * Method for direct folders listing (LIST) * - * @param string $root Optional root folder - * @param string $name Optional name pattern + * @param string $root Optional root folder + * @param string $name Optional name pattern * - * @return array List of folders - * @see rcube_imap::list_folders() + * @return array List of folders + * @see rcube_imap::list_folders() */ public function list_folders_direct($root='', $name='*') { @@ -3005,16 +2999,20 @@ $this->list_folders_update($result); } + // Remove hidden folders + if ($config->get('imap_skip_hidden_folders')) { + $result = array_filter($result, function($v) { return $v[0] != '.'; }); + } + return $result; } - /** * Fix folders list by adding folders from other namespaces. * Needed on some servers eg. Courier IMAP * - * @param array $result Reference to folders list - * @param string $type Listing type (ext-subscribed, subscribed or all) + * @param array $result Reference to folders list + * @param string $type Listing type (ext-subscribed, subscribed or all) */ protected function list_folders_update(&$result, $type = null) { @@ -3064,7 +3062,6 @@ } } - /** * Filter the given list of folders according to access rights * @@ -3090,7 +3087,6 @@ return $a_folders; } - /** * Get mailbox quota information * @@ -3107,7 +3103,6 @@ return false; } - /** * Get folder size (size of all messages in a folder) * @@ -3117,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? @@ -3130,7 +3139,6 @@ return $result; } - /** * Subscribe to a specific folder(s) @@ -3145,7 +3153,6 @@ return $this->change_subscription($folders, 'subscribe'); } - /** * Unsubscribe folder(s) * @@ -3158,7 +3165,6 @@ // let this common function do the main work return $this->change_subscription($folders, 'unsubscribe'); } - /** * Create a new folder on the server and register it in local cache @@ -3177,16 +3183,6 @@ $result = $this->conn->createFolder($folder, $type ? array("\\" . ucfirst($type)) : null); - // it's quite often situation that we're trying to create and subscribe - // a folder that already exist, but is unsubscribed - if (!$result) { - if ($this->get_response_code() == rcube_storage::ALREADYEXISTS - || preg_match('/already exists/i', $this->get_error_str()) - ) { - $result = true; - } - } - // try to subscribe it if ($result) { // clear cache @@ -3199,7 +3195,6 @@ return $result; } - /** * Set a new name to an existing folder @@ -3260,7 +3255,6 @@ return $result; } - /** * Remove folder from server * @@ -3308,7 +3302,6 @@ return $result; } - /** * Detect special folder associations stored in storage backend */ @@ -3353,7 +3346,6 @@ return array_merge($result, $special); } - /** * Set special folder associations stored in storage backend */ @@ -3392,7 +3384,6 @@ return true; } - /** * Checks if folder exists and is subscribed * @@ -3420,7 +3411,7 @@ if ($subscription) { // It's possible we already called LIST command, check LIST data if (!empty($this->conn->data['LIST']) && !empty($this->conn->data['LIST'][$folder]) - && in_array('\\Subscribed', $this->conn->data['LIST'][$folder]) + && in_array_nocase('\\Subscribed', $this->conn->data['LIST'][$folder]) ) { $a_folders = array($folder); } @@ -3445,7 +3436,6 @@ return false; } - /** * Returns the namespace where the folder is in @@ -3476,7 +3466,6 @@ return 'personal'; } - /** * Modify folder name according to namespace. @@ -3516,7 +3505,6 @@ return $folder; } - /** * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors * @@ -3548,7 +3536,6 @@ return is_array($opts) ? $opts : array(); } - /** * Gets connection (and current folder) data: UIDVALIDITY, EXISTS, RECENT, @@ -3588,7 +3575,6 @@ return $data; } - /** * Returns extended information about the folder @@ -3685,7 +3671,6 @@ return $options; } - /** * Synchronizes messages cache. * @@ -3697,7 +3682,6 @@ $mcache->synchronize($folder); } } - /** * Get message header names for rcube_imap_generic::fetchHeader(s) @@ -3750,7 +3734,6 @@ return $this->conn->setACL($folder, $user, $acl); } - /** * Removes any <identifier,rights> pair for the * specified user from the ACL for the specified @@ -3775,7 +3758,6 @@ return $this->conn->deleteACL($folder, $user); } - /** * Returns the access control list for folder (GETACL) * @@ -3796,7 +3778,6 @@ return $this->conn->getACL($folder); } - /** * Returns information about what rights can be granted to the @@ -3821,7 +3802,6 @@ return $this->conn->listRights($folder, $user); } - /** * Returns the set of rights that the current user has to * folder (MYRIGHTS) @@ -3843,7 +3823,6 @@ return $this->conn->myRights($folder); } - /** * Sets IMAP metadata/annotations (SETMETADATA/SETANNOTATION) @@ -3878,7 +3857,6 @@ return false; } - /** * Unsets IMAP metadata/annotations (SETMETADATA/SETANNOTATION) * @@ -3912,34 +3890,36 @@ return false; } - /** * 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()) { @@ -3978,13 +3958,15 @@ } if (isset($res)) { - $this->update_cache($cache_key, $res); + if (!$force) { + $this->update_cache($cache_key, $res); + } + return $res; } return null; } - /** * Converts the METADATA extension entry name into the correct @@ -4115,7 +4097,6 @@ } } - /** * Getter for messages cache object */ @@ -4134,7 +4115,6 @@ return $this->mcache; } - /** * Clears the messages cache. * @@ -4147,7 +4127,6 @@ $mcache->clear($folder, $uids); } } - /** * Delete outdated cache entries @@ -4178,7 +4157,6 @@ } } - /** * Sort folders first by default folders and then in alphabethical order * @@ -4192,13 +4170,11 @@ $specials = array_merge(array('INBOX'), array_values($this->get_special_folders())); $folders = array(); - // convert names to UTF-8 and skip folders starting with '.' + // convert names to UTF-8 foreach ($a_folders as $folder) { - if ($folder[0] != '.') { - // for better performance skip encoding conversion - // if the string does not look like UTF7-IMAP - $folders[$folder] = strpos($folder, '&') === false ? $folder : rcube_charset::convert($folder, 'UTF7-IMAP'); - } + // for better performance skip encoding conversion + // if the string does not look like UTF7-IMAP + $folders[$folder] = strpos($folder, '&') === false ? $folder : rcube_charset::convert($folder, 'UTF7-IMAP'); } // sort folders @@ -4265,12 +4241,11 @@ } } - /** * Find UID of the specified message sequence ID * - * @param int $id Message (sequence) ID - * @param string $folder Folder name + * @param int $id Message (sequence) ID + * @param string $folder Folder name * * @return int Message UID */ @@ -4294,7 +4269,6 @@ return $uid; } - /** * Subscribe/unsubscribe a list of folders and update local cache @@ -4328,7 +4302,6 @@ return $updated; } - /** * Increde/decrese messagecount for a specific folder */ @@ -4359,7 +4332,6 @@ return true; } - /** * Remove messagecount of a specific folder from cache */ @@ -4377,7 +4349,6 @@ $this->update_cache('messagecount', $a_folder_cache); } } - /** * Converts date string/object into IMAP date/time format @@ -4400,7 +4371,6 @@ return $date->format('d-M-Y H:i:s O'); } - /** * This is our own debug handler for the IMAP connection @@ -4531,7 +4501,7 @@ return $this->mod_folder($folder, $mode); } - public function mailbox_attributes($folder, $force=false) + public function mailbox_attributes($folder, $force = false) { return $this->folder_attributes($folder, $force); } @@ -4551,9 +4521,8 @@ return $this->folder_sync($folder); } - public function expunge($folder='', $clear_cache=true) + public function expunge($folder = '', $clear_cache = true) { return $this->expunge_folder($folder, $clear_cache); } - } -- Gitblit v1.9.1