Projects
Kolab:16:Testing:Candidate
kolab-syncroton
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 61
View file
kolab-syncroton.spec
Changed
@@ -43,7 +43,7 @@ %global upstream_version 2.4.2 Name: kolab-syncroton -Version: 2.4.2.33 +Version: 2.4.2.34 Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware
View file
debian.changelog
Changed
@@ -1,4 +1,4 @@ -kolab-syncroton (2.4.2.33-0~kolab1) unstable; urgency=low +kolab-syncroton (2.4.2.34-0~kolab1) unstable; urgency=low * Release version 2.4.2
View file
kolab-syncroton-2.4.2.tar.gz/bin/analyzelogs.php
Changed
@@ -37,6 +37,7 @@ require_once "Syncroton/Command/Sync.php"; require_once "Syncroton/Command/Ping.php"; require_once "Syncroton/Command/MoveItems.php"; +require_once "Syncroton/Command/FolderSync.php"; $filename = $argv1; @@ -45,7 +46,8 @@ // Split up the log files into chunks that hopefully match the commands $parts = preg_split("/\.*\: " . preg_quote("DEBUG Syncroton_Server::handle::65 REQUEST METHOD: POST", '/') . "/", $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); -function getStatusConstants($classname) { +function getStatusConstants($classname) +{ $reflect = new ReflectionClass($classname); $result = $reflect->getConstants(); $result = array_filter($result, function ($val) { @@ -55,7 +57,11 @@ return $result; } -function explainStatus($command, $status) { +function explainStatus($command, $status) +{ + if (!$status) { + return "none"; + } switch ($command) { case "Ping": $result = getStatusConstants("Syncroton_Command_Ping"); @@ -66,8 +72,11 @@ case "MoveItems": $result = getStatusConstants("Syncroton_Command_MoveItems"); return $result$status ?? "Unknown"; + case "FolderSync": + $result = getStatusConstants("Syncroton_Command_FolderSync"); + return $result$status ?? "Unknown"; } - return "Unkonwn"; + return "Unknown command"; } foreach ($parts as $part) { @@ -82,7 +91,7 @@ $statusExplained = explainStatus($command, $status); - print(" Command: ". str_pad($command, 10) . str_pad("\tStatus: $status ($statusExplained)", 45) . "\tTimestamp: $timestamp\n"); + print(" Command: " . str_pad($command, 10) . str_pad("\tStatus: $status ($statusExplained)", 45) . "\tTimestamp: $timestamp\n"); if ($command == "Sync") { // Find collections within this sync // 25-Sep-2024 09:16:35.347730: INFO Syncroton_Command_Sync::handle::221 SyncKey is 7301 Class: Email CollectionId: 38b950ebd62cd9a66929c89615d0fc04 @@ -115,19 +124,19 @@ // Detect entries that are being added from the client if (preg_match_all('/found (.*) entries to be added on server/', $part, $matches)) { foreach ($matches0 ?? as $match) { - print(" " . $match. "\n"); + print(" " . $match . "\n"); } } if (preg_match_all('/found (.*) entries to be updated on server/', $part, $matches)) { foreach ($matches0 ?? as $match) { - print(" " . $match. "\n"); + print(" " . $match . "\n"); } } if (preg_match_all('/found (.*) entries to be deleted on server/', $part, $matches)) { foreach ($matches0 ?? as $match) { - print(" " . $match. "\n"); + print(" " . $match . "\n"); } } } @@ -138,4 +147,3 @@ //TODO on Sync: //* Reason for interruption } -
View file
kolab-syncroton-2.4.2.tar.gz/bin/inspect.php
Changed
@@ -110,8 +110,8 @@ 'ssl' => 'verify_peer_name' => false, 'verify_peer' => false, - 'allow_self_signed' => true - + 'allow_self_signed' => true, + , ; ini_set('display_errors', 1); @@ -233,13 +233,15 @@ var_export($result); } -function println($output) { +function println($output) +{ print("{$output}\n"); } -function filterType($value) { +function filterType($value) +{ if (!$value) { - return "No filter"; + return "No filter"; } switch($value) { case 0: return "No filter"; @@ -255,11 +257,13 @@ return "Unknown value: $value"; } -function getContentUids($db, $device_id, $folder_id) { +function getContentUids($db, $device_id, $folder_id) +{ $contentSelect = $db->query( "SELECT contentid FROM `syncroton_content`" . " WHERE `device_id` = ? AND `folder_id` = ? AND `is_deleted` = 0", - $device_id, $folder_id + $device_id, + $folder_id ); $contentUids = ; @@ -269,7 +273,8 @@ return $contentUids; } -function getImapUids($imap, $folder, $lastfiltertype) { +function getImapUids($imap, $folder, $lastfiltertype) +{ $imap->select($folder); $index = $imap->search($folder, 'ALL UNDELETED ' . filterTypeToIMAPSearch($lastfiltertype), true); if (!$index->is_error()) {
View file
kolab-syncroton-2.4.2.tar.gz/bin/resync.php
Added
@@ -0,0 +1,116 @@ +#!/usr/bin/php +<?php +/* + +--------------------------------------------------------------------------+ + | Kolab Sync (ActiveSync for Kolab) | + | | + | Copyright (C) 2024, Apheleia IT AG <contact@apheleia-it.ch> | + | | + | 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 <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@apheleia-it.ch> | + +--------------------------------------------------------------------------+ +*/ + +define('RCUBE_INSTALL_PATH', realpath(dirname(__FILE__) . '/../') . '/'); +define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/plugins/'); + +// Define include path +$include_path = RCUBE_INSTALL_PATH . 'lib' . PATH_SEPARATOR; +$include_path .= RCUBE_INSTALL_PATH . 'lib/ext' . PATH_SEPARATOR; +$include_path .= ini_get('include_path'); +set_include_path($include_path); + +// include composer autoloader (if available) +if (@file_exists(RCUBE_INSTALL_PATH . 'vendor/autoload.php')) { + require RCUBE_INSTALL_PATH . 'vendor/autoload.php'; +} + +// include global functions from Roundcube Framework +require_once 'Roundcube/bootstrap.php'; + +$opts = rcube_utils::get_opt( + 'o' => 'owner', + 'f' => 'folder', + 'd' => 'deviceid', + 't' => 'devicetype', // e.g. WindowsOutlook15 or iPhone +); + +$rcube = \rcube::get_instance(); +$db = $rcube->get_dbh(); + +if (empty($opts'owner')) { + rcube::raise_error("Owner not specified (--owner).", false, true); +} +if (empty($opts'folder')) { + rcube::raise_error("Folder name not specified (--folder).", false, true); +} + +$select = $db->query( + "SELECT `user_id` FROM `users` WHERE `username` = ? ORDER BY `user_id` DESC", + \strtolower($opts'owner') +); + +if ($data = $db->fetch_assoc($select)) { + $userid = $data'user_id'; +} else { + rcube::raise_error("User not found in Roundcube database.", false, true); +} + +$devices = ; +if (!empty($opts'deviceid')) { + $select = $db->query( + "SELECT `id` FROM `syncroton_device` WHERE `owner_id` = ? AND `deviceid` = ?", + $userid, + $opts'deviceid' + ); + while ($record = $db->fetch_assoc($select)) { + $devices = $record'id'; + } +} elseif (!empty($opts'devicetype')) { + $select = $db->query( + "SELECT `id` FROM `syncroton_device` WHERE `owner_id` = ? AND `devicetype` = ?", + $userid, + $opts'devicetype' + ); + while ($record = $db->fetch_assoc($select)) { + $devices = $record'id'; + } +} else { + $select = $db->query("SELECT `id` FROM `syncroton_device` WHERE `owner_id` = ?", $userid); + while ($record = $db->fetch_assoc($select)) { + $devices = $record'id'; + } +} + +if (empty($devices)) { + rcube::raise_error("Device not found.", false, true); +} + +// TODO: Support not only top-level folders + +$select = $db->query( + "SELECT `id`, `displayname`, `folderid` FROM `syncroton_folder`" + . " WHERE `device_id` IN (" . $db->array2list($devices) . ")" + . " AND `parentid` = '0' AND `displayname` = " . $db->quote($opts'folder') +); + +while ($record = $db->fetch_assoc($select)) { + if (!empty($opts'dry-run')) { + print("DRY-RUN {$record'displayname'} ({$record'id'}:{$record'folderid'})\n"); + } else { + $db->query("UPDATE `syncroton_folder` SET `resync` = 1 WHERE id = ?", $record'id'); + print("{$record'displayname'} ({$record'id'}:{$record'folderid'})\n"); + } +}
View file
kolab-syncroton-2.4.2.tar.gz/docs/SQL/mysql.initial.sql
Changed
@@ -44,6 +44,7 @@ `displayname` varchar(254) NOT NULL, `type` int(11) NOT NULL, `creation_time` datetime NOT NULL, + `creation_synckey` int(11) NOT NULL DEFAULT '0', `lastfiltertype` int(11) DEFAULT NULL, `supportedfields` longblob DEFAULT NULL, PRIMARY KEY (`id`), @@ -115,4 +116,4 @@ PRIMARY KEY(`name`) ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -INSERT INTO `system` (`name`, `value`) VALUES ('syncroton-version', '2024031100'); +INSERT INTO `system` (`name`, `value`) VALUES ('syncroton-version', '2024102300');
View file
kolab-syncroton-2.4.2.tar.gz/docs/SQL/mysql/2024101700.sql
Added
@@ -0,0 +1,1 @@ +ALTER TABLE `syncroton_folder` ADD `resync` tinyint(1) DEFAULT NULL;
View file
kolab-syncroton-2.4.2.tar.gz/docs/SQL/mysql/2024102300.sql
Added
@@ -0,0 +1,1 @@ +ALTER TABLE `syncroton_folder` ADD `creation_synckey` int(11) NOT NULL DEFAULT '0';
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Backend/IFolder.php
Changed
@@ -32,9 +32,10 @@ * * @param Syncroton_Model_Device|string $deviceId * @param string $class + * @param int $syncKey * @return array */ - public function getFolderState($deviceId, $class); + public function getFolderState($deviceId, $class, $syncKey); /** * delete all stored folderId's for given device
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/FolderCreate.php
Changed
@@ -100,6 +100,7 @@ $this->_folder->class = $folder->class; $this->_folder->deviceId = $this->_device->id; $this->_folder->creationTime = $this->_syncTimeStamp; + $this->_folder->creationSynckey = $this->_syncState->counter; // Check if the folder already exists to avoid a duplicate insert attempt in db try {
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/FolderSync.php
Changed
@@ -94,6 +94,9 @@ } 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->_syncStateBackend->resetState($this->_device, 'FolderSync'); } } @@ -143,7 +146,7 @@ $serverFolders = $dataController->getAllFolders(); // retrieve all folders sent to client - $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class); + $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class, $this->_syncState->counter); if ($this->_syncState->counter > 0) { // retrieve all folders changed since last sync @@ -194,6 +197,7 @@ } else { $add = $serverFolders$serverFolderId; $add->creationTime = $this->_syncTimeStamp; + $add->creationSynckey = $this->_syncState->counter + 1; $add->deviceId = $this->_device->id; unset($add->id); } @@ -234,6 +238,12 @@ } } + // Handle folders set for forced re-sync, we'll send a delete action to the client, + // 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); })); + $serverFoldersIds = array_diff($serverFoldersIds, $forceDeleteIds); + // calculate deleted entries $serverDiff = array_diff($clientFoldersIds, $serverFoldersIds); foreach ($serverDiff as $serverFolderId) { @@ -262,7 +272,12 @@ // store folder in backend if (empty($folder->id)) { - $this->_folderBackend->create($folder); + try { + $this->_folderBackend->create($folder); + } catch(Exception $zdse) { + //This can happen if we rerun a previous sync-key + $this->_folderBackend->update($folder); + } } } @@ -281,10 +296,10 @@ $this->_folderBackend->delete($folder); } - if (empty($this->_syncState->id)) { - $this->_syncStateBackend->create($this->_syncState); - } else { - $this->_syncStateBackend->update($this->_syncState); + // 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 + $this->_syncStateBackend->create($this->_syncState, true); } return $this->_outputDom;
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Command/Ping.php
Changed
@@ -124,7 +124,7 @@ do { // take a break to save battery lifetime call_user_func($sleepCallback); - sleep(Syncroton_Registry::getPingTimeout()); + sleep(min(Syncroton_Registry::getPingTimeout(), $lifeTime)); // make sure the connection is still alive, abort otherwise if (connection_aborted()) {
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/Folder.php
Changed
@@ -33,7 +33,9 @@ 'ownerId' => 'type' => 'string', 'class' => 'type' => 'string', 'creationTime' => 'type' => 'datetime', + 'creationSynckey' => 'type' => 'number', 'lastfiltertype' => 'type' => 'number', + 'resync' => 'type' => 'number', , ; }
View file
kolab-syncroton-2.4.2.tar.gz/lib/ext/Syncroton/Model/IFolder.php
Changed
@@ -21,6 +21,7 @@ * @property string $parentId * @property string $displayName * @property DateTime $creationTime + * @property int $creationSynckey * @property int $lastfiltertype * @property int $type */
View file
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_backend_folder.php
Changed
@@ -50,15 +50,19 @@ * * @param Syncroton_Model_Device|string $deviceid Device object or identifier * @param string $class Class name + * @param int $syncKey Sync key * * @return array List of object identifiers */ - public function getFolderState($deviceid, $class) + public function getFolderState($deviceid, $class, $syncKey = null) { $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('class') . ' = ' . $this->db->quote($class); + if ($syncKey) { + $where = $this->db->quote_identifier('creation_synckey') . ' < ' . $this->db->quote($syncKey + 1); + } $select = $this->db->query('SELECT * FROM `' . $this->table_name . '` WHERE ' . implode(' AND ', $where)); $result = ; @@ -88,8 +92,11 @@ $select = $this->db->query('SELECT * FROM `' . $this->table_name . '` WHERE ' . implode(' AND ', $where)); $folder = $this->db->fetch_assoc($select); + if (!empty($folder'resync')) { + throw new Syncroton_Exception_NotFound("Folder $folderid not found because of resync"); + } if (empty($folder)) { - throw new Syncroton_Exception_NotFound('Folder not found'); + throw new Syncroton_Exception_NotFound("Folder $folderid not found"); } return $this->get_object($folder); @@ -120,6 +127,18 @@ // Reset imap cache so we work with up-to-date folders list rcube::get_instance()->get_storage()->clear_cache('mailboxes', true); + // Retrieve all folders already sent to the client + $select = $this->db->query("SELECT * FROM `{$this->table_name}` WHERE `device_id` = ?", $device->id); + + while ($folder = $this->db->fetch_assoc($select)) { + if (!empty($folder'resync')) { + // Folder re-sync requested + return true; + } + + $client_folders$folder'folderid' = $this->get_object($folder); + } + foreach ($folder_classes as $class) { try { // retrieve all folders available in data backend @@ -132,13 +151,6 @@ } } - // retrieve all folders sent to the client - $select = $this->db->query("SELECT * FROM `{$this->table_name}` WHERE `device_id` = ?", $device->id); - - while ($folder = $this->db->fetch_assoc($select)) { - $client_folders$folder'folderid' = $this->get_object($folder); - } - ksort($client_folders); ksort($server_folders);
View file
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_data_email.php
Changed
@@ -674,7 +674,7 @@ $result = ; if (!is_array($list)) { - throw new Syncroton_Exception_NotFound('Folder not found'); + throw new Syncroton_Exception_NotFound("Folder $folder_id not found: no folders available"); } // device supports multiple folders? @@ -691,7 +691,7 @@ } if (empty($result)) { - throw new Syncroton_Exception_NotFound('Folder not found'); + throw new Syncroton_Exception_NotFound("Folder $folder_id not found."); } return $result; @@ -1137,7 +1137,7 @@ $message = $this->getObject($fileReference); if (!$message) { - throw new Syncroton_Exception_NotFound('Message not found'); + throw new Syncroton_Exception_NotFound("Message $fileReference not found"); } $part = $message->mime_parts$part_id;
View file
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_logger.php
Changed
@@ -161,8 +161,8 @@ } foreach ($params as $key => $val) { - if ($val = $_GET$val) { - $device$key = $val; + if (isset($_GET$val)) { + $device$key = $_GET$val; } }
View file
kolab-syncroton-2.4.2.tar.gz/lib/kolab_sync_storage.php
Changed
@@ -1966,7 +1966,7 @@ //If we had a collision before if (isset($this->relations$folderid$synctime . "-1")) { - return $this->relations$folderid$synctime. "-1"; + return $this->relations$folderid$synctime . "-1"; } if (!isset($this->relations$folderid$synctime)) { $rcube = rcube::get_instance();
View file
kolab-syncroton-2.4.2.tar.gz/tests/Sync/FoldersTest.php
Changed
@@ -3,16 +3,230 @@ class FoldersTest extends Tests\SyncTestCase { /** - * Test FolderSync command + * Cleanup folders */ - public function testFolderSync() + public function setUp(): void { // Note: We essentially assume the test account is in an initial state, extra folders may break tests // Anyway, we first remove folders that might have been created during tests in this file $this->deleteTestFolder('Test Folder', 'mail'); + $this->deleteTestFolder('NewFolder', 'mail'); + $this->deleteTestFolder('NewFolder2', 'mail'); $this->deleteTestFolder('Test Folder New', 'mail'); $this->deleteTestFolder('Test Contacts Folder', 'contact'); $this->deleteTestFolder('Test Contacts New', 'contact'); + parent::setUp(); + } + + /** + * Test FolderSync command + */ + public function testFolderSyncBasic() + { + $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); + $this->assertSame('1', $xpath->query("//ns:FolderSync/ns:SyncKey")->item(0)->nodeValue); + // We expect some folders to exist (dont' know how many) + $this->assertTrue(intval($xpath->query("//ns:FolderSync/ns:Changes/ns:Count")->item(0)->nodeValue) > 2); + + $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('1', $xpath->query("//ns:FolderSync/ns:SyncKey")->item(0)->nodeValue); + // No changes on second sync + $this->assertSame(strval(0), $xpath->query("//ns:FolderSync/ns:Changes/ns:Count")->item(0)->nodeValue); + + + //Clear the creation_synckey (that's the migration scenario) + //Shouldn't trigger a change + $rcube = \rcube::get_instance(); + $db = $rcube->get_dbh(); + $result = $db->query( + "UPDATE `syncroton_folder` SET `creation_synckey` = null", + ); + + $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->printDom($dom); + $this->assertSame('1', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue); + $this->assertSame('1', $xpath->query("//ns:FolderSync/ns:SyncKey")->item(0)->nodeValue); + // No changes on second sync + $this->assertSame(strval(0), $xpath->query("//ns:FolderSync/ns:Changes/ns:Count")->item(0)->nodeValue); + } + + /** + * Test invalid sync key + */ + public function testFolderInvalidSyncKey() + { + $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>999</SyncKey> + </FolderSync> + EOF; + + $response = $this->request($request, 'FolderSync'); + + $this->assertEquals(200, $response->getStatusCode()); + + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + + $this->assertSame('9', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue); + } + + + /** + * Test synckey reuse + */ + public function testSyncKeyResend() + { + $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); + + //Resend the same synckey + $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); + + //And now make sure we can still move on + $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>2</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); + + //Now add another folder + $this->createTestFolder("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>2</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('3', $xpath->query("//ns:FolderSync/ns:SyncKey")->item(0)->nodeValue); + $this->assertSame(strval(1), $xpath->query("//ns:FolderSync/ns:Changes/ns:Count")->item(0)->nodeValue); + + //And finally make sure we can't go back two synckeys (because that has been cleaned up meanwhile) + $this->createTestFolder("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>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('9', $xpath->query("//ns:FolderSync/ns:Status")->item(0)->nodeValue); + + // Cleanup for the other tests + $this->deleteTestFolder('NewFolder', 'mail'); + $this->deleteTestFolder('NewFolder2', 'mail'); + } + + /** + * Test FolderSync command + */ + public function testFolderSync() + { $request = <<<EOF <?xml version="1.0" encoding="utf-8"?>
View file
kolab-syncroton-2.4.2.tar.gz/tests/Sync/MoveItemsTest.php
Changed
@@ -205,6 +205,7 @@ // Test with multi-folder support enabled self::$deviceType = 'iphone'; + // @phpstan-ignore-next-line $davFolder = $this->isStorageDriver('kolab') ? 'Contacts' : 'Addressbook'; $this->emptyTestFolder($davFolder, 'contact'); $this->deleteTestFolder($folderName = 'Test Contacts Folder', 'contact');
View file
kolab-syncroton-2.4.2.tar.gz/tests/Sync/PingTest.php
Added
@@ -0,0 +1,102 @@ +<?php + +class PingTest extends Tests\SyncTestCase +{ + + /** + * Test Ping command + */ + public function testFolderSyncBasic() + { + $request = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Ping xmlns="uri:Ping"> + <HeartbeatInterval>900</HeartbeatInterval> + <Folders> + <Folder> + <Id>38b950ebd62cd9a66929c89615d0fc04</Id> + <Class>Email</Class> + </Folder> + </Folders> + </Ping> + EOF; + + $response = $this->request($request, 'Ping'); + + $this->assertEquals(200, $response->getStatusCode()); + + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + $this->printDom($dom); + + //Initially we know no folders + $this->assertSame('7', $xpath->query("//ns:Ping/ns:Status")->item(0)->nodeValue); + + + //We discover folders with a foldersync + $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()); + + //Now we get to the actual ping + $request = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Ping xmlns="uri:Ping"> + <HeartbeatInterval>0</HeartbeatInterval> + <Folders> + <Folder> + <Id>38b950ebd62cd9a66929c89615d0fc04</Id> + <Class>Email</Class> + </Folder> + </Folders> + </Ping> + EOF; + + $response = $this->request($request, 'Ping'); + $this->assertEquals(200, $response->getStatusCode()); + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + // $this->printDom($dom); + //Initially we know no folders + $this->assertSame('2', $xpath->query("//ns:Ping/ns:Status")->item(0)->nodeValue); + } + + /** + * Test Ping command + */ + public function testUnknownFolder() + { + $request = <<<EOF + <?xml version="1.0" encoding="utf-8"?> + <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> + <Ping xmlns="uri:Ping"> + <HeartbeatInterval>900</HeartbeatInterval> + <Folders> + <Folder> + <Id>foobar</Id> + <Class>Email</Class> + </Folder> + </Folders> + </Ping> + EOF; + + $response = $this->request($request, 'Ping'); + + $this->assertEquals(200, $response->getStatusCode()); + + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + // $this->printDom($dom); + + $this->assertSame('7', $xpath->query("//ns:Ping/ns:Status")->item(0)->nodeValue); + } +}
View file
kolab-syncroton-2.4.2.tar.gz/tests/Sync/Sync/RelationsTest.php
Changed
@@ -4,8 +4,8 @@ class RelationsTest extends \Tests\SyncTestCase { - - protected function initialSyncRequest($folderId) { + protected function initialSyncRequest($folderId) + { $request = <<<EOF <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> @@ -21,7 +21,8 @@ return $this->request($request, 'Sync'); } - protected function syncRequest($syncKey, $folderId, $windowSize = null) { + protected function syncRequest($syncKey, $folderId, $windowSize = null) + { $request = <<<EOF <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> @@ -77,7 +78,7 @@ $this->assertSame('Email', $xpath->query("//ns:Sync/ns:Collections/ns:Collection/ns:Class")->item(0)->nodeValue); $this->assertSame($folderId, $xpath->query("//ns:Sync/ns:Collections/ns:Collection/ns:CollectionId")->item(0)->nodeValue); - // First we append + // First we append $uid1 = $this->appendMail('INBOX', 'mail.sync1'); $uid2 = $this->appendMail('INBOX', 'mail.sync2'); $this->appendMail('INBOX', 'mail.sync1', 'sync1' => 'sync3'); @@ -184,7 +185,7 @@ $this->assertSame(0, $xpath->query("{$root}/ns:ApplicationData/Email:Categories")->count()); //FIXME this currently fails because we omit the empty categories element // $this->assertSame("", $xpath->query("{$root}/ns:ApplicationData/Email:Categories")->item(0)->nodeValue); - + // Assert the db state $result = $db->query( @@ -209,4 +210,3 @@ return $syncKey; } } -
View file
kolab-syncroton-2.4.2.tar.gz/tests/SyncTestCase.php
Changed
@@ -178,8 +178,6 @@ } else { exit("Folder is missing"); } - - return; } $dav = $this->getDavStorage(); @@ -224,6 +222,27 @@ } /** + * Create a folder + */ + protected function createTestFolder($name, $type) + { + // Create IMAP folders + if ($type == 'mail' || $this->isStorageDriver('kolab')) { + $imap = $this->getImapStorage(); + //TODO set type if not mail + $imap->create_folder($name, true); + + $metadata = ; + $metadata'FOLDER' = ; + $metadata'FOLDER'self::$deviceId = ; + $metadata'FOLDER'self::$deviceId'S' = '1'; + $imap->set_metadata($name, '/private/vendor/kolab/activesync' => json_encode($metadata)); + + return; + } + } + + /** * Remove all objects from a folder */ protected function emptyTestFolder($name, $type) @@ -394,6 +413,14 @@ return $xpath; } + /** + * Pretty print the DOM + */ + protected function printDom($dom) + { + $dom->formatOutput = true; + print($dom->saveXML()); + } /** * adapter for phpunit < 9
View file
kolab-syncroton.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 1:2.4.2.33-1~kolab1 +Version: 1:2.4.2.34-1~kolab1 Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Uploaders: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Homepage: http://www.kolab.org/
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.