Page MenuHomePhorge

D5044.1775270834.diff
No OneTemporary

Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None

D5044.1775270834.diff

diff --git a/lib/ext/Syncroton/Backend/Folder.php b/lib/ext/Syncroton/Backend/Folder.php
--- a/lib/ext/Syncroton/Backend/Folder.php
+++ b/lib/ext/Syncroton/Backend/Folder.php
@@ -134,4 +134,9 @@
return parent::_toCamelCase($string, $ucFirst);
}
}
+
+ public function exists($deviceid, $folderid);
+ {
+ return false; // not implemented
+ }
}
diff --git a/lib/ext/Syncroton/Backend/IFolder.php b/lib/ext/Syncroton/Backend/IFolder.php
--- a/lib/ext/Syncroton/Backend/IFolder.php
+++ b/lib/ext/Syncroton/Backend/IFolder.php
@@ -53,4 +53,14 @@
* @return bool True if folders hierarchy changed, False otherwise
*/
public function hasHierarchyChanges($device);
+
+ /**
+ * Check if the folder already exists
+ *
+ * @param Syncroton_Model_Device|string $deviceid Device object or identifier
+ * @param string $folderid Folder identifier
+ *
+ * @return bool true if it exists
+ */
+ public function exists($deviceid, $folderid);
}
diff --git a/lib/ext/Syncroton/Command/FolderSync.php b/lib/ext/Syncroton/Command/FolderSync.php
--- a/lib/ext/Syncroton/Command/FolderSync.php
+++ b/lib/ext/Syncroton/Command/FolderSync.php
@@ -68,6 +68,11 @@
*/
protected $_syncKey;
+ /**
+ * @var bool
+ */
+ protected $_syncKeyReused = false;
+
/**
* Parse FolderSync request
*/
@@ -92,11 +97,19 @@
$this->_syncStateBackend->resetState($this->_device, 'FolderSync');
return;
+ } else {
+ // The synckey that is sent to us should already be existing, because we create it at the end,
+ // however, the next one shouldn't
+ if ($this->_syncStateBackend->haveNext($this->_device, 'FolderSync', $syncKey)) {
+ $this->_syncKeyReused = true;
+ if ($this->_logger instanceof Zend_Log) {
+ $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " already known synckey $syncKey provided");
+ }
+ }
}
-
if (!($this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $syncKey)) instanceof Syncroton_Model_SyncState) {
if ($this->_logger instanceof Zend_Log) {
- $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalidating sync state");
+ $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey $syncKey provided, invalidating sync state");
}
$this->_syncStateBackend->resetState($this->_device, 'FolderSync');
}
@@ -243,6 +256,11 @@
// but because the folder is still existing and subscribed on the backend it should
// "immediately" be added again (and re-synced).
$forceDeleteIds = array_keys(array_filter($clientFolders, function ($f) { return !empty($f->resync); }));
+ if (!empty($forceDeleteIds)) {
+ if ($this->_logger instanceof Zend_Log) {
+ $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " forcing resync of: " . var_export($forceDeleteIds, true));
+ }
+ }
$serverFoldersIds = array_diff($serverFoldersIds, $forceDeleteIds);
// calculate deleted entries
@@ -254,14 +272,14 @@
$folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_SUCCESS));
+ $newSyncKey = $this->_syncState->counter;
$count = count($adds) + count($updates) + count($deletes);
if ($count > 0) {
- $this->_syncState->counter++;
- $this->_syncState->lastsync = $this->_syncTimeStamp;
+ $newSyncKey++;
}
// create xml output
- $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
+ $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $newSyncKey));
$changes = $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Changes'));
$changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Count', $count));
@@ -271,12 +289,11 @@
$folder->appendXML($add, $this->_device);
- // store folder in backend
- if (empty($folder->id)) {
- try {
+ if (!$this->_syncKeyReused && empty($folder->id)) {
+ // The folder could exist in the backend if we e.g. delete the same name and then recreate,
+ // or disable/reenable for syncing.
+ if (!$this->_folderBackend->exists($this->_device->id, $folder->serverId)) {
$this->_folderBackend->create($folder);
- } catch (Exception $zdse) {
- //This can happen if we rerun a previous sync-key
}
}
}
@@ -285,7 +302,6 @@
$update = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Update'));
$folder->appendXML($update, $this->_device);
-
$this->_folderBackend->update($folder);
}
@@ -296,9 +312,12 @@
$this->_folderBackend->delete($folder);
}
- // Only create this syncstate if it isn't already existing (which happens if we a sync key is re-sent)
- if (!$this->_syncStateBackend->haveNext($this->_device, 'FolderSync', $this->_syncState->counter - 1)) {
- // Keep previous sync states in case a sync key is re-sent
+ if ($this->_syncState->counter != $newSyncKey) {
+ $this->_syncState->counter = $newSyncKey;
+ $this->_syncState->lastsync = $this->_syncTimeStamp;
+ // Keep previous sync states in case a sync key is re-sent.
+ // We always insert because deleteOtherStates is executed from _syncStateBackend->validate,
+ // which means we remove and re-insert the latest state on key resend.
$this->_syncStateBackend->create($this->_syncState, true); // @phpstan-ignore-line
}
diff --git a/lib/kolab_sync_backend_folder.php b/lib/kolab_sync_backend_folder.php
--- a/lib/kolab_sync_backend_folder.php
+++ b/lib/kolab_sync_backend_folder.php
@@ -102,6 +102,26 @@
return $this->get_object($folder);
}
+ /**
+ * Check if the folder already exists
+ *
+ * @param Syncroton_Model_Device|string $deviceid Device object or identifier
+ * @param string $folderid Folder identifier
+ *
+ * @return bool true if it exists
+ */
+ public function exists($deviceid, $folderid)
+ {
+ $device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid;
+
+ $where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
+ $where[] = $this->db->quote_identifier('folderid') . ' = ' . $this->db->quote($folderid);
+
+ $select = $this->db->query('SELECT 1 FROM `' . $this->table_name . '` WHERE ' . implode(' AND ', $where));
+ $folder = $this->db->fetch_assoc($select);
+ return !empty($folder);
+ }
+
/**
* Find out if the folder hierarchy changed since the last FolderSync
*
diff --git a/tests/Sync/FoldersTest.php b/tests/Sync/FoldersTest.php
--- a/tests/Sync/FoldersTest.php
+++ b/tests/Sync/FoldersTest.php
@@ -214,6 +214,52 @@
$this->assertSame('9', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue);
}
+ /**
+ * This test recreates a previously deleted folder.
+ * Currently similar to disabling/reenabling a folder for sync, but should perhaps be tested separately
+ *
+ * @depends testSyncKeyResend
+ */
+ public function testRecreatePreviousFolder()
+ {
+ $this->deleteTestFolder('NewFolder', 'mail');
+ $this->deleteTestFolder('NewFolder2', 'mail');
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy">
+ <SyncKey>0</SyncKey>
+ </FolderSync>
+ EOF;
+ $response = $this->request($request, 'FolderSync');
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = $this->fromWbxml($response->getBody());
+ $xpath = $this->xpath($dom);
+ $this->assertSame('1', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue);
+
+ //Now change something
+ $this->createTestFolder("NewFolder", "mail");
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy">
+ <SyncKey>1</SyncKey>
+ </FolderSync>
+ EOF;
+
+ $response = $this->request($request, 'FolderSync');
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = $this->fromWbxml($response->getBody());
+ $xpath = $this->xpath($dom);
+ $this->assertSame('1', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue);
+ $this->assertSame('2', $xpath->query("//ns:FolderSync/ns:SyncKey")->item(0)->nodeValue);
+ $this->assertSame(strval(1), $xpath->query("//ns:FolderSync/ns:Changes/ns:Count")->item(0)->nodeValue);
+
+ // Cleanup for the other tests
+ $this->deleteTestFolder('NewFolder', 'mail');
+ $this->deleteTestFolder('NewFolder2', 'mail');
+ }
+
/**
* Test FolderSync command
*/

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 4, 2:47 AM (3 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822932
Default Alt Text
D5044.1775270834.diff (9 KB)

Event Timeline