summaryrefslogtreecommitdiff
path: root/lib/kolab_sync_data_email.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kolab_sync_data_email.php')
-rw-r--r--lib/kolab_sync_data_email.php133
1 files changed, 119 insertions, 14 deletions
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 99d1af5..381c3cf 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -805,6 +805,9 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
}
}
+ // get members of modified relations
+ $changed_msgs = $this->getChangesByRelations($folderid, $filter);
+
$result = $result_type == self::RESULT_COUNT ? 0 : array();
// no sorting for best performance
$sort_by = null;
@@ -830,6 +833,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
}
// We're in "get changes" mode
+ $modified = false;
if (isset($modseq_data)) {
$folder_data = $this->storage->folder_data($foldername);
$got_changes = true;
@@ -848,7 +852,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
// We can only get folder's HIGHESTMODSEQ value and store it for the next try
// Skip search if HIGHESTMODSEQ didn't change
if (!$got_changes || empty($modseq) || empty($modseq[$foldername])) {
- continue;
+ $modified = true;
}
$filter_str .= " MODSEQ " . ($modseq[$foldername] + 1);
@@ -865,25 +869,45 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
// $search = $this->storage->index($foldername, null, null, true, true);
// else
- $search = $this->storage->search_once($foldername, $filter_str);
+ if ($modified) {
+ $search = $this->storage->search_once($foldername, $filter_str);
+
+ if (!($search instanceof rcube_result_index) || $search->is_error()) {
+ throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
+ }
- if (!($search instanceof rcube_result_index) || $search->is_error()) {
- throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR);
+ switch ($result_type) {
+ case self::RESULT_COUNT:
+ $result += (int) $search->count();
+ break;
+
+ case self::RESULT_UID:
+ if ($uids = $search->get()) {
+ foreach ($uids as $idx => $uid) {
+ $uids[$idx] = $this->createMessageId($folder_id, $uid);
+ }
+ $result = array_merge($result, $uids);
+ }
+ break;
+ }
}
- switch ($result_type) {
- case self::RESULT_COUNT:
- $result += (int) $search->count();
- break;
+ // handle relation changes
+ if (!empty($changed_msgs)) {
+ $uids = $this->findRelationMembersInFolder($foldername, $changed_msgs, $filter);
- case self::RESULT_UID:
- if ($uids = $search->get()) {
+ switch ($result_type) {
+ case self::RESULT_COUNT:
+ $result += (int) count($uids);
+ break;
+
+ case self::RESULT_UID:
foreach ($uids as $idx => $uid) {
$uids[$idx] = $this->createMessageId($folder_id, $uid);
}
- $result = array_merge($result, $uids);
+ $result = array_unique(array_merge($result, $uids));
+ break;
}
- break;
}
}
@@ -906,12 +930,93 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
}
}
- // @TODO: tag objects modification detection
-
return $result;
}
/**
+ * Find members (messages) in specified folder
+ */
+ protected function findRelationMembersInFolder($foldername, $members, $filter)
+ {
+ foreach ($members as $member) {
+ // IMAP URI members
+ if ($url = kolab_storage_config::parse_member_url($member)) {
+ $result[$url['folder']][$url['uid']] = $url['params'];
+ }
+ }
+
+ // convert filter into one IMAP search string
+ $filter_str = 'ALL UNDELETED';
+ foreach ($filter as $idx => $filter_item) {
+ if (is_string($filter_item)) {
+ $filter_str .= ' ' . $filter_item;
+ }
+ }
+
+ $rcube = rcube::get_instance();
+ $storage = $rcube->get_storage();
+ $found = array();
+
+ // first find messages by UID
+ if (!empty($result[$foldername])) {
+ $index = $storage->search_once($foldername, 'UID '
+ . rcube_imap_generic::compressMessageSet(array_keys($result[$foldername])));
+ $found = $index->get();
+
+ // remove found messages from the $result
+ if (!empty($found)) {
+ $result[$foldername] = array_diff_key($result[$foldername], array_flip($found));
+
+ if (empty($result[$foldername])) {
+ unset($result[$foldername]);
+ }
+
+ // now apply the current filter to the found messages
+ $index = $storage->search_once($foldername, $filter_str . ' UID '
+ . rcube_imap_generic::compressMessageSet($found));
+ $found = $index->get();
+ }
+ }
+
+ // search by message parameters
+ if (!empty($result)) {
+ // @TODO: do this search in chunks (for e.g. 25 messages)?
+ $search = '';
+ $search_count = 0;
+
+ foreach ($result as $folder => $data) {
+ foreach ($data as $p) {
+ $search_params = array();
+ $search_count++;
+
+ foreach ($p as $key => $val) {
+ $key = strtoupper($key);
+ // don't search by subject, we don't want false-positives
+ if ($key != 'SUBJECT') {
+ $search_params[] = 'HEADER ' . $key . ' ' . rcube_imap_generic::escape($val);
+ }
+ }
+
+ $search .= ' (' . implode(' ', $search_params) . ')';
+ }
+ }
+
+ $search_str .= ' ' . str_repeat(' OR', $search_count-1) . $search;
+
+ // search messages in current folder
+ $search = $storage->search_once($foldername, $search_str);
+ $uids = $search->get();
+
+ if (!empty($uids)) {
+ // add UIDs into the result
+ $found = array_unique(array_merge($found, $uids));
+ }
+ }
+
+ return $found;
+ }
+
+ /**
* ActiveSync Search handler
*
* @param Syncroton_Model_StoreRequest $store Search query