3 commits - docs/SQL lib/ext lib/kolab_sync_backend.php lib/kolab_sync_data_email.php lib/kolab_sync_data.php

Aleksander Machniak machniak at kolabsys.com
Thu Jan 17 14:01:59 CET 2013


 docs/SQL/mysql.initial.sql         |   10 ++++++
 docs/SQL/mysql/2013011600.sql      |    9 ++++++
 lib/ext/Syncroton/Command/Sync.php |    2 -
 lib/kolab_sync_backend.php         |   55 ++++++++++++++++++++++++++++++++++++-
 lib/kolab_sync_data.php            |   53 +++++++++++++++++++++++------------
 lib/kolab_sync_data_email.php      |   55 ++++++++++++++++++++++++++++++-------
 6 files changed, 154 insertions(+), 30 deletions(-)

New commits:
commit 54c82e3a0e3c2c6d877a78d098cdefca7a3186f1
Author: Aleksander Machniak <alec at alec.pl>
Date:   Thu Jan 17 13:59:06 2013 +0100

    Fix regression where \Seen flag wasn't set on sent messages

diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 6a67b34..9a3c336 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -618,7 +618,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
             $sent_folder = kolab_sync::get_instance()->config->get('sent_mbox');
 
             if (strlen($sent_folder) && $this->storage->folder_exists($sent_folder)) {
-                return $this->storage->save_message($sent_folder, $message->source());
+                return $this->storage->save_message($sent_folder, $message->source(), '', false, array('SEEN'));
             }
         }
     }


commit 2bec11daf4ffe957a156199d7a3ce279615472c4
Author: Aleksander Machniak <alec at alec.pl>
Date:   Thu Jan 17 13:56:01 2013 +0100

    Fix detection of changes in email messages (e.g. \Seen flag update) (Bug #1169)

diff --git a/docs/SQL/mysql.initial.sql b/docs/SQL/mysql.initial.sql
index c0581a5..9bae2e1 100644
--- a/docs/SQL/mysql.initial.sql
+++ b/docs/SQL/mysql.initial.sql
@@ -134,3 +134,13 @@ CREATE TABLE IF NOT EXISTS `syncroton_data_folder` (
     `parent_id` varchar(40) DEFAULT NULL,
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `syncroton_modseq` (
+    `device_id` varchar(40) NOT NULL,
+    `folder_id` varchar(40) NOT NULL,
+    `synctime` varchar(14) NOT NULL,
+    `data` longblob,
+    PRIMARY KEY (`device_id`,`folder_id`,`synctime`),
+    KEY `syncroton_modseq::device_id` (`device_id`),
+    CONSTRAINT `syncroton_modseq::device_id--syncroton_device::id` FOREIGN KEY (`device_id`) REFERENCES `syncroton_device` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
diff --git a/docs/SQL/mysql/2013011600.sql b/docs/SQL/mysql/2013011600.sql
new file mode 100644
index 0000000..914f750
--- /dev/null
+++ b/docs/SQL/mysql/2013011600.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS `syncroton_modseq` (
+    `device_id` varchar(40) NOT NULL,
+    `folder_id` varchar(40) NOT NULL,
+    `synctime` varchar(14) NOT NULL,
+    `data` longblob,
+    PRIMARY KEY (`device_id`,`folder_id`,`synctime`),
+    KEY `syncroton_modseq::device_id` (`device_id`),
+    CONSTRAINT `syncroton_modseq::device_id--syncroton_device::id` FOREIGN KEY (`device_id`) REFERENCES `syncroton_device` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
diff --git a/lib/ext/Syncroton/Command/Sync.php b/lib/ext/Syncroton/Command/Sync.php
index b59bae6..c5da42b 100644
--- a/lib/ext/Syncroton/Command/Sync.php
+++ b/lib/ext/Syncroton/Command/Sync.php
@@ -491,7 +491,7 @@ class Syncroton_Command_Sync extends Syncroton_Command_Wbxml
                         if (($now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) {
                             continue;
                         }
-
+                        
                         $dataController = Syncroton_Data_Factory::factory($collectionData->folder->class , $this->_device, $this->_syncTimeStamp);
                         
                         $hasChanges = $dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState);
diff --git a/lib/kolab_sync_backend.php b/lib/kolab_sync_backend.php
index 25c2fb1..7424a32 100644
--- a/lib/kolab_sync_backend.php
+++ b/lib/kolab_sync_backend.php
@@ -682,7 +682,6 @@ class kolab_sync_backend
             }
         }
 */
-
         // get all folders of specified type
         $folderdata = $this->folder_meta();
 
@@ -710,6 +709,60 @@ class kolab_sync_backend
     }
 
     /**
+     */
+    public function modseq_set($deviceid, $folderid, $synctime, $data)
+    {
+        $synctime = $synctime->format('Ymdhis');
+        $rcube    = rcube::get_instance();
+        $db       = $rcube->get_dbh();
+
+        $this->modseq[$deviceid][$folderid][$synctime] = $data;
+
+        $data = json_encode($data);
+
+        $db->query("UPDATE syncroton_modseq"
+            ." SET data = ?"
+            ." WHERE device_id = ? AND folder_id = ? AND synctime = ?",
+            $data, $deviceid, $folderid, $synctime);
+
+        if (!$db->affected_rows()) {
+            $db->query("INSERT INTO syncroton_modseq (device_id, folder_id, synctime, data)"
+                ." VALUES (?, ?, ?, ?)",
+                $deviceid, $folderid, $synctime, $data);
+        }
+    }
+
+    public function modseq_get($deviceid, $folderid, $synctime)
+    {
+        $synctime = $synctime->format('Ymdhis');
+
+        if (!isset($this->modseq[$deviceid]) || !isset($this->modseq[$deviceid][$folderid])
+            || !isset($this->modseq[$deviceid][$synctime])
+        ) {
+            $rcube = rcube::get_instance();
+            $db    = $rcube->get_dbh();
+
+            $db->limitquery("SELECT data, synctime FROM syncroton_modseq"
+                ." WHERE device_id = ? AND folder_id = ? AND synctime <= ?"
+                ." ORDER BY synctime DESC",
+                0, 2, $deviceid, $folderid, $synctime);
+
+            if ($row = $db->fetch_assoc()) {
+                $this->modseq[$deviceid][$folderid][$synctime] = json_decode($row['data']);
+
+                // Cleanup: remove old records (older than 12 hours from the last one)
+                if ($row = $db->fetch_assoc()) {
+                    $db->query("DELETE FROM syncroton_modseq"
+                        ." WHERE device_id = ? AND folder_id = ? AND synctime < ?",
+                    $deviceid, $folderid, $row['synctime'] - 86400);
+                }
+            }
+        }
+
+        return @$this->modseq[$deviceid][$folderid][$synctime];
+    }
+
+    /**
      * Compares two arrays
      *
      * @param array $array1
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 769d472..6a67b34 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -751,10 +751,12 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
         // convert filter into one IMAP search string
         foreach ($filter as $idx => $filter_item) {
             if (is_array($filter_item)) {
-                // @TODO
-                // convert 'changed' entries into IMAP search string
-                // for now we just return empty result
-                return $result_type == self::RESULT_COUNT ? 0 : array();
+                // This is a request for changes since list time
+                // we'll use HIGHESTMODSEQ value from the last Sync
+                if ($filter_item[0] == 'changed' && $filter_item[1] == '>') {
+                    $modseq = (array) $this->backend->modseq_get($this->device->id, $folderid, $filter_item[2]);
+                    $modseq_data = array();
+                }
             }
             else {
                 $filter_str .= ' ' . $filter_item;
@@ -765,15 +767,43 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
         // no sorting for best performance
         $sort_by = null;
 
-        foreach ($folders as $folderid) {
-            $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
+        foreach ($folders as $folder_id) {
+            $foldername = $this->backend->folder_id2name($folder_id, $this->device->deviceid);
 
             if ($foldername === null) {
                 continue;
             }
 
-//           $this->storage->set_folder($foldername);
-            $this->storage->folder_sync($foldername);
+            $this->storage->set_folder($foldername);
+
+            // Syncronize folder (if it wasn't synced in this request already)
+            if ($this->lastsync_folder != $foldername
+                || $this->lastsync_time < time() - Syncroton_Registry::getPingTimeout()
+            ) {
+                $this->storage->folder_sync($foldername);
+            }
+
+            $this->lastsync_folder = $foldername;
+            $this->lastsync_time   = time();
+
+            // We're in "get changes" mode
+            if (isset($modseq_data)) {
+                $folder_data = $this->storage->folder_data($foldername);
+                if ($folder_data['HIGHESTMODSEQ']) {
+                    $modseq_data[$foldername] = $folder_data['HIGHESTMODSEQ'];
+                    if ($modseq_data[$foldername] != $modseq[$foldername]) {
+                        $modseq_update = true;
+                    }
+                }
+
+                // If previous HIGHESTMODSEQ doesn't exist we can't get changes
+                // We can only get folder's HIGHESTMODSEQ value and store it for the next try
+                if (empty($modseq) || empty($modseq[$foldername])) {
+                    continue;
+                }
+
+                $filter_str .= " MODSEQ " . ($modseq[$foldername] + 1);
+            }
 
             $search = $this->storage->search_once($foldername, $filter_str);
 
@@ -789,7 +819,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
             case self::RESULT_UID:
                 if ($uids = $search->get()) {
                     foreach ($uids as $idx => $uid) {
-                        $uids[$idx] = $this->createMessageId($folderid, $uid);
+                        $uids[$idx] = $this->createMessageId($folder_id, $uid);
                     }
                     $result = array_merge($result, $uids);
                 }
@@ -804,6 +834,11 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
             }
         }
 
+        if (!empty($modseq_update)) {
+            $this->backend->modseq_set($this->device->id, $folderid,
+                $this->syncTimeStamp, $modseq_data);
+        }
+
         return $result;
     }
 


commit f191c1b177119bfed98a01841e6a01abd38edcd4
Author: Aleksander Machniak <alec at alec.pl>
Date:   Tue Jan 15 09:49:32 2013 +0100

    Performance improvement in hasChanges()

diff --git a/lib/kolab_sync_data.php b/lib/kolab_sync_data.php
index 1f2e384..82d6547 100644
--- a/lib/kolab_sync_data.php
+++ b/lib/kolab_sync_data.php
@@ -570,7 +570,6 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
         return $result;
     }
 
-
     /**
      * get count of all entries available on the server
      *
@@ -587,36 +586,54 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
         return $result;
     }
 
-
+    /**
+     * Returns number of changed objects in the backend folder
+     *
+     * @param Syncroton_Backend_IContent $contentBackend
+     * @param Syncroton_Model_IFolder    $folder
+     * @param Syncroton_Model_ISyncState $syncState
+     *
+     * @return int
+     */
     public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
     {
         $allClientEntries = $contentBackend->getFolderState($this->device, $folder);
+        $allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
+        $changedEntries   = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync);
+        $addedEntries     = array_diff($allServerEntries, $allClientEntries);
+        $deletedEntries   = array_diff($allClientEntries, $allServerEntries);
+
+        return count($addedEntries) + count($deletedEntries) + $changedEntries;
+    }
+
+    /**
+     * Returns true if any data got modified in the backend folder
+     *
+     * @param Syncroton_Backend_IContent $contentBackend
+     * @param Syncroton_Model_IFolder    $folder
+     * @param Syncroton_Model_ISyncState $syncState
+     *
+     * @return bool
+     */
+    public function hasChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
+    {
+        if ($this->getChangedEntriesCount($folder->serverId, $syncState->lastsync)) {
+            return true;
+        }
+
+        $allClientEntries = $contentBackend->getFolderState($this->device, $folder);
 
         // @TODO: Consider looping over all folders here, not in getServerEntries() and
-        // getChangedEntriesCount(). This way we could break the loop and not check all folders (see @TODO below)
+        // getChangedEntriesCount(). This way we could break the loop and not check all folders
         // or at least skip redundant cache sync of the same folder
         $allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
 
         $addedEntries   = array_diff($allServerEntries, $allClientEntries);
         $deletedEntries = array_diff($allClientEntries, $allServerEntries);
 
-        // @TODO: Count is needed only for GetItemEstimate command
-        // in Ping command we need only information that anything is changed/added/deleted
-        // so in case when count($addedEntries) + count($deletedEntries) != 0 we don't need
-        // to count changed entries here. We could also revert the order in such case
-        // and execute getChangedEntriesCount() before
-        $changedEntries = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync);
-
-        return count($addedEntries) + count($deletedEntries) + $changedEntries;
+        return count($addedEntries) > 0 || count($deletedEntries) > 0;
     }
 
-
-    public function hasChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
-    {
-        return !!$this->getCountOfChanges($contentBackend, $folder, $syncState);
-    }
-
-
     /**
      * Fetches the entry from the backend
      */





More information about the commits mailing list