diff --git a/lib/kolab_sync_backend_state.php b/lib/kolab_sync_backend_state.php
index a85a738..bb3a9a6 100644
--- a/lib/kolab_sync_backend_state.php
+++ b/lib/kolab_sync_backend_state.php
@@ -1,204 +1,207 @@
|
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
/**
* Kolab backend class for the folder state storage
*/
class kolab_sync_backend_state extends kolab_sync_backend_common implements Syncroton_Backend_ISyncState
{
protected $table_name = 'syncroton_synckey';
protected $interface_name = 'Syncroton_Model_ISyncState';
/**
* Create new sync state of a folder
*
* @param Syncroton_Model_ISyncState $object State object
* @param bool $keep_previous_state Don't remove other states
*
* @return Syncroton_Model_SyncState
*/
public function create($object, $keep_previous_state = true)
{
$object = parent::create($object);
if ($keep_previous_state !== true) {
// remove all other synckeys
$this->_deleteOtherStates($object);
}
return $object;
}
/**
* Deletes states other than specified one
*/
protected function _deleteOtherStates(Syncroton_Model_ISyncState $state)
{
// remove all other synckeys
$where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($state->deviceId);
$where[] = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($state->type);
$where[] = $this->db->quote_identifier('counter') . ' <> ' . $this->db->quote($state->counter);
$this->db->query("DELETE FROM `{$this->table_name}` WHERE " . implode(' AND ', $where));
}
/**
* @see kolab_sync_backend_common::object_to_array()
*/
protected function object_to_array($object)
{
$data = parent::object_to_array($object);
if (is_array($object->pendingdata)) {
- $data['pendingdata'] = json_encode($object->pendingdata);
+ $data['pendingdata'] = gzdeflate(json_encode($object->pendingdata));
}
return $data;
}
/**
* @see kolab_sync_backend_common::get_object()
*/
protected function get_object($data)
{
$object = parent::get_object($data);
if ($object->pendingdata) {
- $object->pendingdata = json_decode($object->pendingdata, true);
+ $inflated = gzinflate($object->pendingdata);
+ // Inflation may fail for backward compatiblity
+ $data = $inflated ? $inflated : $object->pendingdata;
+ $object->pendingdata = json_decode($data, true);
}
return $object;
}
/**
* Returns the latest sync state
*
* @param Syncroton_Model_IDevice|string $deviceid Device object or identifier
* @param Syncroton_Model_IFolder|string $folderid Folder object or identifier
*
* @return Syncroton_Model_SyncState
*/
public function getSyncState($deviceid, $folderid)
{
$device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid;
$folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid;
$where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
$where[] = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id);
$select = $this->db->limitquery("SELECT * FROM `{$this->table_name}` WHERE " . implode(' AND ', $where)
. " ORDER BY `counter` DESC", 0, 1);
$state = $this->db->fetch_assoc($select);
if (empty($state)) {
throw new Syncroton_Exception_NotFound('SyncState not found');
}
return $this->get_object($state);
}
/**
* Delete all stored synckeys of given type
*
* @param Syncroton_Model_IDevice|string $deviceid Device object or identifier
* @param Syncroton_Model_IFolder|string $folderid Folder object or identifier
*/
public function resetState($deviceid, $folderid)
{
$device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid;
$folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid;
$where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
$where[] = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id);
$this->db->query("DELETE FROM `{$this->table_name}` WHERE " . implode(' AND ', $where));
}
/**
* Validates specified sync state by checking for existance of newer keys
*
* @param Syncroton_Model_IDevice|string $deviceid Device object or identifier
* @param Syncroton_Model_IFolder|string $folderid Folder object or identifier
* @param int $sync_key State key
*
* @return Syncroton_Model_SyncState
*/
public function validate($deviceid, $folderid, $sync_key)
{
$device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid;
$folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid;
$states = array();
// get sync data
// we'll get all records, thanks to this we'll be able to
// skip _deleteOtherStates() call below (one DELETE query less)
$where['device_id'] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
$where['type'] = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id);
$select = $this->db->query("SELECT * FROM `{$this->table_name}` WHERE " . implode(' AND ', $where));
while ($row = $this->db->fetch_assoc($select)) {
$states[$row['counter']] = $this->get_object($row);
}
// last state not found
if (empty($states) || empty($states[$sync_key])) {
return false;
}
$state = $states[$sync_key];
$next = max(array_keys($states));
$where = array();
$where['device_id'] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
$where['folder_id'] = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folder_id);
$where['is_deleted'] = $this->db->quote_identifier('is_deleted') . ' = 1';
// found more recent synckey => the last sync response got not received by the client
if ($next > $sync_key) {
$where['synckey'] = $this->db->quote_identifier('creation_synckey') . ' = ' . $this->db->quote($state->counter);
// undelete entries marked as deleted in syncroton_content table
$this->db->query("UPDATE `syncroton_content` SET `is_deleted` = 0 WHERE " . implode(' AND ', $where));
// remove entries added during latest sync in syncroton_content table
unset($where['is_deleted']);
$where['synckey'] = $this->db->quote_identifier('creation_synckey') . ' > ' . $this->db->quote($state->counter);
$this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where));
}
else {
// finaly delete all entries marked for removal in syncroton_content table
$this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where));
}
// remove all other synckeys
if (count($states) > 1) {
$this->_deleteOtherStates($state);
}
return $state;
}
}