Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16228566
D4998.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D4998.diff
View Options
diff --git a/bin/resync.php b/bin/resync.php
new file mode 100755
--- /dev/null
+++ b/bin/resync.php
@@ -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");
+ }
+}
diff --git a/docs/SQL/mysql/2024101700.sql b/docs/SQL/mysql/2024101700.sql
new file mode 100644
--- /dev/null
+++ b/docs/SQL/mysql/2024101700.sql
@@ -0,0 +1 @@
+ALTER TABLE `syncroton_folder` ADD `resync` tinyint(1) DEFAULT NULL;
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
@@ -234,6 +234,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) {
diff --git a/lib/ext/Syncroton/Model/Folder.php b/lib/ext/Syncroton/Model/Folder.php
--- a/lib/ext/Syncroton/Model/Folder.php
+++ b/lib/ext/Syncroton/Model/Folder.php
@@ -34,6 +34,7 @@
'class' => ['type' => 'string'],
'creationTime' => ['type' => 'datetime'],
'lastfiltertype' => ['type' => 'number'],
+ 'resync' => ['type' => 'number'],
],
];
}
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
@@ -88,7 +88,7 @@
$select = $this->db->query('SELECT * FROM `' . $this->table_name . '` WHERE ' . implode(' AND ', $where));
$folder = $this->db->fetch_assoc($select);
- if (empty($folder)) {
+ if (empty($folder) || !empty($folder['resync'])) {
throw new Syncroton_Exception_NotFound('Folder not found');
}
@@ -117,6 +117,18 @@
Syncroton_Data_Factory::CLASS_TASKS,
];
+ // 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);
+ }
+
// Reset imap cache so we work with up-to-date folders list
rcube::get_instance()->get_storage()->clear_cache('mailboxes', true);
@@ -132,13 +144,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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 19, 5:26 PM (19 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
9890577
Default Alt Text
D4998.diff (7 KB)
Attached To
Mode
D4998: A way to force re-sync a folder
Attached
Detach File
Event Timeline
Log In to Comment