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'));
|
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
|
||||||
|
|
||||||
if (self::$ready) {
|
if (self::$ready) {
|
||||||
// set imap options
|
// do nothing
|
||||||
self::$imap->set_options(array(
|
|
||||||
'skip_deleted' => true,
|
|
||||||
'threading' => false,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
else if (!class_exists('kolabformat')) {
|
else if (!class_exists('kolabformat')) {
|
||||||
rcube::raise_error(array(
|
rcube::raise_error(array(
|
||||||
|
|
|
@ -186,7 +186,9 @@ class kolab_storage_cache
|
||||||
|
|
||||||
if (!$this->ready) {
|
if (!$this->ready) {
|
||||||
// kolab cache is disabled, synchronize IMAP mailbox cache only
|
// kolab cache is disabled, synchronize IMAP mailbox cache only
|
||||||
|
$this->imap_mode(true);
|
||||||
$this->imap->folder_sync($this->folder->name);
|
$this->imap->folder_sync($this->folder->name);
|
||||||
|
$this->imap_mode(false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// read cached folder metadata
|
// read cached folder metadata
|
||||||
|
@ -204,7 +206,7 @@ class kolab_storage_cache
|
||||||
$this->_sync_lock();
|
$this->_sync_lock();
|
||||||
|
|
||||||
// disable messages cache if configured to do so
|
// disable messages cache if configured to do so
|
||||||
$this->bypass(true);
|
$this->imap_mode(true);
|
||||||
|
|
||||||
// synchronize IMAP mailbox cache
|
// synchronize IMAP mailbox cache
|
||||||
$this->imap->folder_sync($this->folder->name);
|
$this->imap->folder_sync($this->folder->name);
|
||||||
|
@ -212,6 +214,8 @@ class kolab_storage_cache
|
||||||
// compare IMAP index with object cache index
|
// compare IMAP index with object cache index
|
||||||
$imap_index = $this->imap->index($this->folder->name, null, null, true, true);
|
$imap_index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||||
|
|
||||||
|
$this->imap_mode(false);
|
||||||
|
|
||||||
// determine objects to fetch or to invalidate
|
// determine objects to fetch or to invalidate
|
||||||
if (!$imap_index->is_error()) {
|
if (!$imap_index->is_error()) {
|
||||||
$imap_index = $imap_index->get();
|
$imap_index = $imap_index->get();
|
||||||
|
@ -261,8 +265,6 @@ class kolab_storage_cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->bypass(false);
|
|
||||||
|
|
||||||
// remove lock
|
// remove lock
|
||||||
$this->_sync_unlock();
|
$this->_sync_unlock();
|
||||||
}
|
}
|
||||||
|
@ -603,6 +605,8 @@ class kolab_storage_cache
|
||||||
else {
|
else {
|
||||||
$filter = $this->_query2assoc($query);
|
$filter = $this->_query2assoc($query);
|
||||||
|
|
||||||
|
$this->imap_mode(true);
|
||||||
|
|
||||||
if ($filter['type']) {
|
if ($filter['type']) {
|
||||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
||||||
$index = $this->imap->search_once($this->folder->name, $search);
|
$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);
|
$index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->imap_mode(false);
|
||||||
|
|
||||||
if ($index->is_error()) {
|
if ($index->is_error()) {
|
||||||
$this->check_error();
|
$this->check_error();
|
||||||
if ($uids) {
|
if ($uids) {
|
||||||
|
@ -670,6 +676,8 @@ class kolab_storage_cache
|
||||||
else {
|
else {
|
||||||
$filter = $this->_query2assoc($query);
|
$filter = $this->_query2assoc($query);
|
||||||
|
|
||||||
|
$this->imap_mode(true);
|
||||||
|
|
||||||
if ($filter['type']) {
|
if ($filter['type']) {
|
||||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type'];
|
||||||
$index = $this->imap->search_once($this->folder->name, $search);
|
$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);
|
$index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->imap_mode(false);
|
||||||
|
|
||||||
if ($index->is_error()) {
|
if ($index->is_error()) {
|
||||||
$this->check_error();
|
$this->check_error();
|
||||||
return null;
|
return null;
|
||||||
|
@ -1136,13 +1146,31 @@ class kolab_storage_cache
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bypass Roundcube messages cache.
|
* Set Roundcube storage options and bypass messages cache.
|
||||||
* Roundcube cache duplicates information already stored in kolab_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 kolab cache is disabled do nothing
|
||||||
if (!$this->enabled) {
|
if (!$this->enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -1158,7 +1186,7 @@ class kolab_storage_cache
|
||||||
|
|
||||||
if ($messages_cache) {
|
if ($messages_cache) {
|
||||||
// handle recurrent (multilevel) bypass() calls
|
// handle recurrent (multilevel) bypass() calls
|
||||||
if ($disable) {
|
if ($force) {
|
||||||
$this->cache_bypassed += 1;
|
$this->cache_bypassed += 1;
|
||||||
if ($this->cache_bypassed > 1) {
|
if ($this->cache_bypassed > 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -1174,7 +1202,7 @@ class kolab_storage_cache
|
||||||
switch ($cache_bypass) {
|
switch ($cache_bypass) {
|
||||||
case 2:
|
case 2:
|
||||||
// Disable messages cache completely
|
// Disable messages cache completely
|
||||||
$this->imap->set_messages_caching(!$disable);
|
$this->imap->set_messages_caching(!$force);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1182,7 +1210,7 @@ class kolab_storage_cache
|
||||||
// Default mode is both (MODE_INDEX | MODE_MESSAGE)
|
// Default mode is both (MODE_INDEX | MODE_MESSAGE)
|
||||||
$mode = rcube_imap_cache::MODE_INDEX;
|
$mode = rcube_imap_cache::MODE_INDEX;
|
||||||
|
|
||||||
if (!$disable) {
|
if (!$force) {
|
||||||
$mode |= rcube_imap_cache::MODE_MESSAGE;
|
$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)
|
function __construct($name, $type = null, $type_annotation = null)
|
||||||
{
|
{
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
$this->imap->set_options(array('skip_deleted' => true));
|
|
||||||
$this->set_folder($name, $type, $type_annotation);
|
$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->imap->set_folder($folder);
|
||||||
|
|
||||||
$this->cache->bypass(true);
|
$this->cache->imap_mode(true);
|
||||||
$message = new rcube_message($msguid);
|
$message = new rcube_message($msguid);
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
|
|
||||||
// Message doesn't exist?
|
// Message doesn't exist?
|
||||||
if (empty($message->headers)) {
|
if (empty($message->headers)) {
|
||||||
|
@ -698,9 +697,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
||||||
|
|
||||||
if ($old_uid) {
|
if ($old_uid) {
|
||||||
// delete old message
|
// delete old message
|
||||||
$this->cache->bypass(true);
|
$this->cache->imap_mode(true);
|
||||||
$this->imap->delete_message($old_uid, $object['_mailbox']);
|
$this->imap->delete_message($old_uid, $object['_mailbox']);
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert/update message in cache
|
// 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);
|
$msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object);
|
||||||
$success = false;
|
$success = false;
|
||||||
|
|
||||||
$this->cache->bypass(true);
|
$this->cache->imap_mode(true);
|
||||||
|
|
||||||
if ($msguid && $expunge) {
|
if ($msguid && $expunge) {
|
||||||
$success = $this->imap->delete_message($msguid, $this->name);
|
$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);
|
$success = $this->imap->set_flag($msguid, 'DELETED', $this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
|
|
||||||
if ($success) {
|
if ($success) {
|
||||||
$this->cache->set($msguid, false);
|
$this->cache->set($msguid, false);
|
||||||
|
@ -824,9 +823,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache->purge();
|
$this->cache->purge();
|
||||||
$this->cache->bypass(true);
|
$this->cache->imap_mode(true);
|
||||||
$result = $this->imap->clear_folder($this->name);
|
$result = $this->imap->clear_folder($this->name);
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
@ -844,9 +843,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($msguid = $this->cache->uid2msguid($uid, true)) {
|
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);
|
$result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name);
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
return $msguid;
|
return $msguid;
|
||||||
|
@ -873,9 +872,9 @@ class kolab_storage_folder extends kolab_storage_folder_api
|
||||||
$target_folder = kolab_storage::get_folder($target_folder);
|
$target_folder = kolab_storage::get_folder($target_folder);
|
||||||
|
|
||||||
if ($msguid = $this->cache->uid2msguid($uid)) {
|
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);
|
$result = $this->imap->move_message($msguid, $target_folder->name, $this->name);
|
||||||
$this->cache->bypass(false);
|
$this->cache->imap_mode(false);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$new_uid = ($copyuid = $this->imap->conn->data['COPYUID']) ? $copyuid[1] : null;
|
$new_uid = ($copyuid = $this->imap->conn->data['COPYUID']) ? $copyuid[1] : null;
|
||||||
|
|
Loading…
Add table
Reference in a new issue