Properly set skip_deleted and threading on Kolab storage IMAP operations (T1145)
Summary: Fixes race-conditions between Kolab folders and Roundcube core where skip_deleted/threading could be set for operations outside of Kolab plugins, causing e.g. inability to see \Deleted messages. Fixes T1145. Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Maniphest Tasks: T1145 Differential Revision: https://git.kolab.org/D112
This commit is contained in:
parent
ba78c23364
commit
9d883ed07d
3 changed files with 51 additions and 28 deletions
|
@ -86,11 +86,7 @@ class kolab_storage
|
|||
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
|
||||
|
||||
if (self::$ready) {
|
||||
// set imap options
|
||||
self::$imap->set_options(array(
|
||||
'skip_deleted' => true,
|
||||
'threading' => false,
|
||||
));
|
||||
// do nothing
|
||||
}
|
||||
else if (!class_exists('kolabformat')) {
|
||||
rcube::raise_error(array(
|
||||
|
|
|
@ -186,7 +186,9 @@ class kolab_storage_cache
|
|||
|
||||
if (!$this->ready) {
|
||||
// kolab cache is disabled, synchronize IMAP mailbox cache only
|
||||
$this->imap_mode(true);
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
$this->imap_mode(false);
|
||||
}
|
||||
else {
|
||||
// read cached folder metadata
|
||||
|
@ -204,7 +206,7 @@ class kolab_storage_cache
|
|||
$this->_sync_lock();
|
||||
|
||||
// disable messages cache if configured to do so
|
||||
$this->bypass(true);
|
||||
$this->imap_mode(true);
|
||||
|
||||
// synchronize IMAP mailbox cache
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
|
@ -212,6 +214,8 @@ class kolab_storage_cache
|
|||
// compare IMAP index with object cache index
|
||||
$imap_index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||
|
||||
$this->imap_mode(false);
|
||||
|
||||
// determine objects to fetch or to invalidate
|
||||
if (!$imap_index->is_error()) {
|
||||
$imap_index = $imap_index->get();
|
||||
|
@ -261,8 +265,6 @@ class kolab_storage_cache
|
|||
}
|
||||
}
|
||||
|
||||
$this->bypass(false);
|
||||
|
||||
// remove lock
|
||||
$this->_sync_unlock();
|
||||
}
|
||||
|
@ -603,6 +605,8 @@ class kolab_storage_cache
|
|||
else {
|
||||
$filter = $this->_query2assoc($query);
|
||||
|
||||
$this->imap_mode(true);
|
||||
|
||||
if ($filter['type']) {
|
||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, $search);
|
||||
|
@ -611,6 +615,8 @@ class kolab_storage_cache
|
|||
$index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||
}
|
||||
|
||||
$this->imap_mode(false);
|
||||
|
||||
if ($index->is_error()) {
|
||||
$this->check_error();
|
||||
if ($uids) {
|
||||
|
@ -670,6 +676,8 @@ class kolab_storage_cache
|
|||
else {
|
||||
$filter = $this->_query2assoc($query);
|
||||
|
||||
$this->imap_mode(true);
|
||||
|
||||
if ($filter['type']) {
|
||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, $search);
|
||||
|
@ -678,6 +686,8 @@ class kolab_storage_cache
|
|||
$index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||
}
|
||||
|
||||
$this->imap_mode(false);
|
||||
|
||||
if ($index->is_error()) {
|
||||
$this->check_error();
|
||||
return null;
|
||||
|
@ -1136,13 +1146,31 @@ class kolab_storage_cache
|
|||
}
|
||||
|
||||
/**
|
||||
* Bypass Roundcube messages cache.
|
||||
* Roundcube cache duplicates information already stored in kolab_cache.
|
||||
* Set Roundcube storage options and bypass messages cache.
|
||||
*
|
||||
* @param bool $disable True disables, False enables messages cache
|
||||
* We use skip_deleted and threading settings specific to Kolab,
|
||||
* we have to change these global settings only temporarily.
|
||||
* Roundcube cache duplicates information already stored in kolab_cache,
|
||||
* that's why we can disable it for better performance.
|
||||
*
|
||||
* @param bool $force True to start Kolab mode, False to stop it.
|
||||
*/
|
||||
public function bypass($disable = false)
|
||||
public function imap_mode($force = false)
|
||||
{
|
||||
// remember current IMAP settings
|
||||
if ($force) {
|
||||
$this->imap_options = array(
|
||||
'skip_deleted' => $this->imap->get_option('skip_deleted'),
|
||||
'threading' => $this->imap->get_threading(),
|
||||
);
|
||||
}
|
||||
|
||||
// re-set IMAP settings
|
||||
$this->imap->set_threading($force ? false : $this->imap_options['threading']);
|
||||
$this->imap->set_options(array(
|
||||
'skip_deleted' => $force ? true : $this->imap_options['skip_deleted'],
|
||||
));
|
||||
|
||||
// if kolab cache is disabled do nothing
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
|
@ -1158,7 +1186,7 @@ class kolab_storage_cache
|
|||
|
||||
if ($messages_cache) {
|
||||
// handle recurrent (multilevel) bypass() calls
|
||||
if ($disable) {
|
||||
if ($force) {
|
||||
$this->cache_bypassed += 1;
|
||||
if ($this->cache_bypassed > 1) {
|
||||
return;
|
||||
|
@ -1174,7 +1202,7 @@ class kolab_storage_cache
|
|||
switch ($cache_bypass) {
|
||||
case 2:
|
||||
// Disable messages cache completely
|
||||
$this->imap->set_messages_caching(!$disable);
|
||||
$this->imap->set_messages_caching(!$force);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
@ -1182,7 +1210,7 @@ class kolab_storage_cache
|
|||
// Default mode is both (MODE_INDEX | MODE_MESSAGE)
|
||||
$mode = rcube_imap_cache::MODE_INDEX;
|
||||
|
||||
if (!$disable) {
|
||||
if (!$force) {
|
||||
$mode |= rcube_imap_cache::MODE_MESSAGE;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
function __construct($name, $type = null, $type_annotation = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->imap->set_options(array('skip_deleted' => true));
|
||||
$this->set_folder($name, $type, $type_annotation);
|
||||
}
|
||||
|
||||
|
@ -448,9 +447,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
|
||||
$this->imap->set_folder($folder);
|
||||
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
$message = new rcube_message($msguid);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
|
||||
// Message doesn't exist?
|
||||
if (empty($message->headers)) {
|
||||
|
@ -698,9 +697,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
|
||||
if ($old_uid) {
|
||||
// delete old message
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
$this->imap->delete_message($old_uid, $object['_mailbox']);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
}
|
||||
|
||||
// insert/update message in cache
|
||||
|
@ -796,7 +795,7 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
$msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object);
|
||||
$success = false;
|
||||
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
|
||||
if ($msguid && $expunge) {
|
||||
$success = $this->imap->delete_message($msguid, $this->name);
|
||||
|
@ -805,7 +804,7 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
$success = $this->imap->set_flag($msguid, 'DELETED', $this->name);
|
||||
}
|
||||
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
|
||||
if ($success) {
|
||||
$this->cache->set($msguid, false);
|
||||
|
@ -824,9 +823,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
}
|
||||
|
||||
$this->cache->purge();
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
$result = $this->imap->clear_folder($this->name);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -844,9 +843,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
}
|
||||
|
||||
if ($msguid = $this->cache->uid2msguid($uid, true)) {
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
$result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
|
||||
if ($result) {
|
||||
return $msguid;
|
||||
|
@ -873,9 +872,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
|||
$target_folder = kolab_storage::get_folder($target_folder);
|
||||
|
||||
if ($msguid = $this->cache->uid2msguid($uid)) {
|
||||
$this->cache->bypass(true);
|
||||
$this->cache->imap_mode(true);
|
||||
$result = $this->imap->move_message($msguid, $target_folder->name, $this->name);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->imap_mode(false);
|
||||
|
||||
if ($result) {
|
||||
$new_uid = ($copyuid = $this->imap->conn->data['COPYUID']) ? $copyuid[1] : null;
|
||||
|
|
Loading…
Add table
Reference in a new issue