From 05003d05969938165161a78f20cb70362bc759b5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 7 Jan 2014 16:28:06 +0100 Subject: [PATCH] Fixes/improvements for use with kolab_cache=false --- plugins/libkolab/lib/kolab_storage_cache.php | 221 +++++++++---------- 1 file changed, 107 insertions(+), 114 deletions(-) diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php index 8c386145..186f261d 100644 --- a/plugins/libkolab/lib/kolab_storage_cache.php +++ b/plugins/libkolab/lib/kolab_storage_cache.php @@ -29,7 +29,6 @@ class kolab_storage_cache protected $folder; protected $uid2msg; protected $objects; - protected $index = null; protected $metadata = array(); protected $folder_id; protected $resource_uri; @@ -139,65 +138,71 @@ class kolab_storage_cache // increase time limit @set_time_limit($this->max_sync_lock_time); - // read cached folder metadata - $this->_read_folder_data(); - - // check cache status hash first ($this->metadata is set in _read_folder_data()) - if ($this->metadata['ctag'] != $this->folder->get_ctag()) { - - // lock synchronization for this folder or wait if locked - $this->_sync_lock(); - - // disable messages cache if configured to do so - $this->bypass(true); - - // synchronize IMAP mailbox cache + if (!$this->ready) { + // kolab cache is disabled, synchronize IMAP mailbox cache only $this->imap->folder_sync($this->folder->name); + } + else { + // read cached folder metadata + $this->_read_folder_data(); - // compare IMAP index with object cache index - $imap_index = $this->imap->index($this->folder->name); - $this->index = $imap_index->get(); + // check cache status hash first ($this->metadata is set in _read_folder_data()) + if ($this->metadata['ctag'] != $this->folder->get_ctag()) { + // lock synchronization for this folder or wait if locked + $this->_sync_lock(); - // determine objects to fetch or to invalidate - if ($this->ready) { - // read cache index - $sql_result = $this->db->query( - "SELECT msguid, uid FROM $this->cache_table WHERE folder_id=?", - $this->folder_id - ); + // disable messages cache if configured to do so + $this->bypass(true); - $old_index = array(); - while ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $old_index[] = $sql_arr['msguid']; - $this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid']; - } + // synchronize IMAP mailbox cache + $this->imap->folder_sync($this->folder->name); - // fetch new objects from imap - foreach (array_diff($this->index, $old_index) as $msguid) { - if ($object = $this->folder->read_object($msguid, '*')) { - $this->_extended_insert($msguid, $object); - } - } - $this->_extended_insert(0, null); + // compare IMAP index with object cache index + $imap_index = $this->imap->index($this->folder->name, null, null, true, true); - // delete invalid entries from local DB - $del_index = array_diff($old_index, $this->index); - if (!empty($del_index)) { - $quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index)); - $this->db->query( - "DELETE FROM $this->cache_table WHERE folder_id=? AND msguid IN ($quoted_ids)", + // determine objects to fetch or to invalidate + if (!$imap_index->is_error()) { + $imap_index = $imap_index->get(); + + // read cache index + $sql_result = $this->db->query( + "SELECT msguid, uid FROM $this->cache_table WHERE folder_id=?", $this->folder_id ); + + $old_index = array(); + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $old_index[] = $sql_arr['msguid']; + $this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid']; + } + + // fetch new objects from imap + foreach (array_diff($imap_index, $old_index) as $msguid) { + if ($object = $this->folder->read_object($msguid, '*')) { + $this->_extended_insert($msguid, $object); + } + } + $this->_extended_insert(0, null); + + // delete invalid entries from local DB + $del_index = array_diff($old_index, $imap_index); + if (!empty($del_index)) { + $quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index)); + $this->db->query( + "DELETE FROM $this->cache_table WHERE folder_id=? AND msguid IN ($quoted_ids)", + $this->folder_id + ); + } + + // update ctag value (will be written to database in _sync_unlock()) + $this->metadata['ctag'] = $this->folder->get_ctag(); } - // update ctag value (will be written to database in _sync_unlock()) - $this->metadata['ctag'] = $this->folder->get_ctag(); + $this->bypass(false); + + // remove lock + $this->_sync_unlock(); } - - $this->bypass(false); - - // remove lock - $this->_sync_unlock(); } $this->synched = time(); @@ -351,22 +356,27 @@ class kolab_storage_cache */ public function move($msguid, $uid, $target_folder) { - $target = kolab_storage::get_folder($target_folder); + if ($this->ready) { + $target = kolab_storage::get_folder($target_folder); - // resolve new message UID in target folder - if ($new_msguid = $target->cache->uid2msguid($uid)) { - $this->_read_folder_data(); + // resolve new message UID in target folder + if ($new_msguid = $target->cache->uid2msguid($uid)) { + $this->_read_folder_data(); - $this->db->query( - "UPDATE $this->cache_table SET folder_id=?, msguid=? ". - "WHERE folder_id=? AND msguid=?", - $target->cache->get_folder_id(), - $new_msguid, - $this->folder_id, - $msguid - ); + $this->db->query( + "UPDATE $this->cache_table SET folder_id=?, msguid=? ". + "WHERE folder_id=? AND msguid=?", + $target->cache->get_folder_id(), + $new_msguid, + $this->folder_id, + $msguid + ); + + $result = $this->db->affected_rows(); + } } - else { + + if (empty($result)) { // just clear cache entry $this->set($msguid, false); } @@ -380,12 +390,17 @@ class kolab_storage_cache */ public function purge($type = null) { + if (!$this->ready) { + return true; + } + $this->_read_folder_data(); $result = $this->db->query( "DELETE FROM $this->cache_table WHERE folder_id=?", $this->folder_id ); + return $this->db->affected_rows($result); } @@ -396,6 +411,10 @@ class kolab_storage_cache */ public function rename($new_folder) { + if (!$this->ready) { + return; + } + $target = kolab_storage::get_folder($new_folder); // resolve new message UID in target folder @@ -443,21 +462,24 @@ class kolab_storage_cache } } } + // use IMAP else { - // extract object type from query parameter $filter = $this->_query2assoc($query); - // use 'list' for folder's default objects - if (is_array($this->index) && $filter['type'] == $this->type) { - $index = $this->index; - } - else { // search by object type + if ($filter['type']) { $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type']; - $index = $this->imap->search_once($this->folder->name, $search)->get(); + $index = $this->imap->search_once($this->folder->name, $search); + } + else { + $index = $this->imap->index($this->folder->name, null, null, true, true); } - // fetch all messages in $index from IMAP - $result = $uids ? $this->_fetch_uids($index, $filter['type']) : $this->_fetch($index, $filter['type']); + if ($index->is_error()) { + return null; + } + + $index = $index->get(); + $result = $uids ? $index : $this->_fetch($index, $filter['type']); // TODO: post-filter result according to query } @@ -483,8 +505,8 @@ class kolab_storage_cache */ public function count($query = array()) { - // cache is in sync, we can count records in local DB - if ($this->synched) { + // read from local cache DB (assume it to be synchronized) + if ($this->ready) { $this->_read_folder_data(); $sql_result = $this->db->query( @@ -500,16 +522,24 @@ class kolab_storage_cache $sql_arr = $this->db->fetch_assoc($sql_result); $count = intval($sql_arr['numrows']); } + // use IMAP else { - // search IMAP by object type $filter = $this->_query2assoc($query); - $ctype = kolab_format::KTYPE_PREFIX . $filter['type']; - $index = $this->imap->search_once($this->folder->name, 'UNDELETED HEADER X-Kolab-Type ' . $ctype); + + if ($filter['type']) { + $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type']; + $index = $this->imap->search_once($this->folder->name, $search); + } + else { + $index = $this->imap->index($this->folder->name, null, null, true, true); + } if ($index->is_error()) { return null; } + // TODO: post-filter result according to query + $count = $index->count(); } @@ -597,43 +627,6 @@ class kolab_storage_cache return $results; } - - /** - * Fetch object UIDs (aka message subjects) from IMAP - * - * @param array List of message UIDs to fetch - * @param string Requested object type or * for all - * @param string IMAP folder to read from - * @return array List of parsed Kolab objects - */ - protected function _fetch_uids($index, $type = null) - { - if (!$type) - $type = $this->folder->type; - - $this->bypass(true); - - $results = array(); - $headers = $this->imap->fetch_headers($this->folder->name, $index, false); - - $this->bypass(false); - - foreach ((array)$headers as $msguid => $headers) { - $object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']); - - // check object type header and abort on mismatch - if ($type != '*' && $object_type != $type) - return false; - - $uid = $headers->subject; - $this->uid2msg[$uid] = $msguid; - $results[] = $uid; - } - - return $results; - } - - /** * Helper method to convert the given Kolab object into a dataset to be written to cache */ @@ -789,7 +782,7 @@ class kolab_storage_cache protected function _read_folder_data() { // already done - if (!empty($this->folder_id)) + if (!empty($this->folder_id) || !$this->ready) return; $sql_arr = $this->db->fetch_assoc($this->db->query("SELECT folder_id, synclock, ctag FROM $this->folders_table WHERE resource=?", $this->resource_uri));