2012-02-17 12:48:31 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 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 General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
2012-07-20 19:13:23 +04:00
|
|
|
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "folderman.h"
|
2014-11-10 01:25:57 +03:00
|
|
|
#include "configfile.h"
|
2014-07-11 02:31:24 +04:00
|
|
|
#include "folder.h"
|
|
|
|
#include "syncresult.h"
|
|
|
|
#include "theme.h"
|
2021-02-17 17:41:32 +03:00
|
|
|
#include "socketapi/socketapi.h"
|
2014-08-29 22:40:33 +04:00
|
|
|
#include "account.h"
|
2014-12-18 14:09:48 +03:00
|
|
|
#include "accountstate.h"
|
2015-04-09 17:19:17 +03:00
|
|
|
#include "accountmanager.h"
|
2015-03-11 12:51:36 +03:00
|
|
|
#include "filesystem.h"
|
2016-04-29 17:14:18 +03:00
|
|
|
#include "lockwatcher.h"
|
2017-09-01 19:11:43 +03:00
|
|
|
#include "common/asserts.h"
|
2022-07-25 19:57:18 +03:00
|
|
|
#include "gui/systray.h"
|
2021-01-12 11:23:41 +03:00
|
|
|
#include <pushnotifications.h>
|
2015-11-20 17:14:22 +03:00
|
|
|
#include <syncengine.h>
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2012-10-20 20:47:33 +04:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
#include <CoreServices/CoreServices.h>
|
|
|
|
#endif
|
|
|
|
|
2013-06-06 19:59:50 +04:00
|
|
|
#include <QMessageBox>
|
2012-05-21 18:48:49 +04:00
|
|
|
#include <QtCore>
|
2014-12-03 00:32:54 +03:00
|
|
|
#include <QMutableSetIterator>
|
|
|
|
#include <QSet>
|
2018-02-21 15:06:41 +03:00
|
|
|
#include <QNetworkProxy>
|
2012-05-21 18:48:49 +04:00
|
|
|
|
2022-10-29 03:19:35 +03:00
|
|
|
namespace {
|
2022-12-15 18:09:39 +03:00
|
|
|
constexpr auto settingsAccountsC = "Accounts";
|
|
|
|
constexpr auto settingsFoldersC = "Folders";
|
|
|
|
constexpr auto settingsVersionC = "version";
|
2022-11-07 20:33:39 +03:00
|
|
|
constexpr auto maxFoldersVersion = 1;
|
2022-10-29 03:19:35 +03:00
|
|
|
}
|
2018-05-02 16:40:54 +03:00
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2017-12-28 22:33:10 +03:00
|
|
|
Q_LOGGING_CATEGORY(lcFolderMan, "nextcloud.gui.folder.manager", QtInfoMsg)
|
2017-05-09 15:24:11 +03:00
|
|
|
|
2018-11-11 12:56:22 +03:00
|
|
|
FolderMan *FolderMan::_instance = nullptr;
|
2017-05-17 11:55:42 +03:00
|
|
|
|
2012-02-17 12:48:31 +04:00
|
|
|
FolderMan::FolderMan(QObject *parent)
|
2013-02-09 17:04:04 +04:00
|
|
|
: QObject(parent)
|
2016-04-29 17:14:18 +03:00
|
|
|
, _lockWatcher(new LockWatcher)
|
2017-10-04 14:49:42 +03:00
|
|
|
, _navigationPaneHelper(this)
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
2017-02-07 15:52:15 +03:00
|
|
|
ASSERT(!_instance);
|
2014-05-26 16:34:08 +04:00
|
|
|
_instance = this;
|
2014-07-15 15:34:32 +04:00
|
|
|
|
2016-03-18 20:31:01 +03:00
|
|
|
_socketApi.reset(new SocketApi);
|
2014-12-03 00:32:54 +03:00
|
|
|
|
2014-12-10 15:01:36 +03:00
|
|
|
ConfigFile cfg;
|
2018-01-24 00:51:25 +03:00
|
|
|
std::chrono::milliseconds polltime = cfg.remotePollInterval();
|
|
|
|
qCInfo(lcFolderMan) << "setting remote poll timer interval to" << polltime.count() << "msec";
|
|
|
|
_etagPollTimer.setInterval(polltime.count());
|
2017-09-20 11:14:48 +03:00
|
|
|
QObject::connect(&_etagPollTimer, &QTimer::timeout, this, &FolderMan::slotEtagPollTimerTimeout);
|
2014-12-03 00:32:54 +03:00
|
|
|
_etagPollTimer.start();
|
2014-12-18 14:09:48 +03:00
|
|
|
|
2015-01-16 14:23:15 +03:00
|
|
|
_startScheduledSyncTimer.setSingleShot(true);
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(&_startScheduledSyncTimer, &QTimer::timeout,
|
|
|
|
this, &FolderMan::slotStartScheduledFolderSync);
|
2015-01-16 14:23:15 +03:00
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
_timeScheduler.setInterval(5000);
|
|
|
|
_timeScheduler.setSingleShot(false);
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(&_timeScheduler, &QTimer::timeout,
|
|
|
|
this, &FolderMan::slotScheduleFolderByTime);
|
2016-10-18 17:04:25 +03:00
|
|
|
_timeScheduler.start();
|
|
|
|
|
2019-07-24 14:56:21 +03:00
|
|
|
connect(AccountManager::instance(), &AccountManager::removeAccountFolders,
|
2017-09-20 11:14:48 +03:00
|
|
|
this, &FolderMan::slotRemoveFoldersForAccount);
|
2016-04-29 17:14:18 +03:00
|
|
|
|
2021-03-09 12:56:17 +03:00
|
|
|
connect(AccountManager::instance(), &AccountManager::accountSyncConnectionRemoved,
|
|
|
|
this, &FolderMan::slotAccountRemoved);
|
|
|
|
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(_lockWatcher.data(), &LockWatcher::fileUnlocked,
|
|
|
|
this, &FolderMan::slotWatchedFileUnlocked);
|
2021-01-12 11:23:41 +03:00
|
|
|
|
|
|
|
connect(this, &FolderMan::folderListChanged, this, &FolderMan::slotSetupPushNotifications);
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2013-07-22 15:59:52 +04:00
|
|
|
FolderMan *FolderMan::instance()
|
|
|
|
{
|
|
|
|
return _instance;
|
|
|
|
}
|
|
|
|
|
2012-02-17 12:48:31 +04:00
|
|
|
FolderMan::~FolderMan()
|
|
|
|
{
|
2013-06-29 00:27:53 +04:00
|
|
|
qDeleteAll(_folderMap);
|
2018-11-11 12:56:22 +03:00
|
|
|
_instance = nullptr;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2020-07-23 13:40:58 +03:00
|
|
|
const OCC::Folder::Map &FolderMan::map() const
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
|
|
|
return _folderMap;
|
|
|
|
}
|
|
|
|
|
2015-04-28 16:13:39 +03:00
|
|
|
void FolderMan::unloadFolder(Folder *f)
|
2014-07-15 18:06:06 +04:00
|
|
|
{
|
2015-02-27 12:42:59 +03:00
|
|
|
if (!f) {
|
|
|
|
return;
|
|
|
|
}
|
2014-07-25 14:20:38 +04:00
|
|
|
|
2016-03-18 20:31:01 +03:00
|
|
|
_socketApi->slotUnregisterPath(f->alias());
|
2015-02-27 12:42:59 +03:00
|
|
|
|
2015-04-28 16:13:39 +03:00
|
|
|
_folderMap.remove(f->alias());
|
2015-06-17 16:55:48 +03:00
|
|
|
|
2017-09-20 11:14:48 +03:00
|
|
|
disconnect(f, &Folder::syncStarted,
|
|
|
|
this, &FolderMan::slotFolderSyncStarted);
|
|
|
|
disconnect(f, &Folder::syncFinished,
|
|
|
|
this, &FolderMan::slotFolderSyncFinished);
|
|
|
|
disconnect(f, &Folder::syncStateChange,
|
|
|
|
this, &FolderMan::slotForwardFolderSyncStateChange);
|
|
|
|
disconnect(f, &Folder::syncPausedChanged,
|
|
|
|
this, &FolderMan::slotFolderSyncPaused);
|
2017-09-20 12:48:13 +03:00
|
|
|
disconnect(&f->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged,
|
|
|
|
_socketApi.data(), &SocketApi::broadcastStatusPushMessage);
|
2017-09-20 11:14:48 +03:00
|
|
|
disconnect(f, &Folder::watchedFileChangedExternally,
|
|
|
|
&f->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::slotPathTouched);
|
2014-07-15 18:06:06 +04:00
|
|
|
}
|
|
|
|
|
2015-02-27 12:42:59 +03:00
|
|
|
int FolderMan::unloadAndDeleteAllFolders()
|
2013-06-03 19:26:48 +04:00
|
|
|
{
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
// clear the list of existing folders.
|
|
|
|
Folder::MapIterator i(_folderMap);
|
|
|
|
while (i.hasNext()) {
|
|
|
|
i.next();
|
2015-02-27 12:42:59 +03:00
|
|
|
Folder *f = i.value();
|
2015-04-28 16:13:39 +03:00
|
|
|
unloadFolder(f);
|
2015-02-27 12:42:59 +03:00
|
|
|
delete f;
|
2013-06-03 19:26:48 +04:00
|
|
|
cnt++;
|
|
|
|
}
|
2017-02-07 15:52:15 +03:00
|
|
|
ASSERT(_folderMap.isEmpty());
|
|
|
|
|
2018-11-11 12:56:22 +03:00
|
|
|
_lastSyncFolder = nullptr;
|
|
|
|
_currentSyncFolder = nullptr;
|
2016-10-18 17:04:25 +03:00
|
|
|
_scheduledFolders.clear();
|
2017-07-06 15:02:21 +03:00
|
|
|
emit folderListChanged(_folderMap);
|
2015-09-04 11:33:48 +03:00
|
|
|
emit scheduleQueueChanged();
|
2014-07-15 18:06:06 +04:00
|
|
|
|
2013-06-03 19:26:48 +04:00
|
|
|
return cnt;
|
|
|
|
}
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2017-10-09 13:06:11 +03:00
|
|
|
void FolderMan::registerFolderWithSocketApi(Folder *folder)
|
2014-01-11 23:35:16 +04:00
|
|
|
{
|
|
|
|
if (!folder)
|
|
|
|
return;
|
2016-12-06 15:45:01 +03:00
|
|
|
if (!QDir(folder->path()).exists())
|
|
|
|
return;
|
2014-01-11 23:35:16 +04:00
|
|
|
|
2014-07-25 14:20:38 +04:00
|
|
|
// register the folder with the socket API
|
2016-04-28 23:43:53 +03:00
|
|
|
if (folder->canSync())
|
|
|
|
_socketApi->slotRegisterPath(folder->alias());
|
2014-01-11 23:35:16 +04:00
|
|
|
}
|
|
|
|
|
2013-11-18 12:35:18 +04:00
|
|
|
int FolderMan::setupFolders()
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
2022-07-19 13:05:20 +03:00
|
|
|
Utility::registerUriHandlerForLocalEditing();
|
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
unloadAndDeleteAllFolders();
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2017-08-16 09:36:52 +03:00
|
|
|
auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
|
2015-04-24 11:18:33 +03:00
|
|
|
const auto accountsWithSettings = settings->childGroups();
|
|
|
|
if (accountsWithSettings.isEmpty()) {
|
2023-04-26 11:01:33 +03:00
|
|
|
const auto migratedFoldersCount = setupFoldersMigration();
|
|
|
|
if (migratedFoldersCount > 0) {
|
2015-08-06 13:49:18 +03:00
|
|
|
AccountManager::instance()->save(false); // don't save credentials, they had not been loaded from keychain
|
2015-07-31 19:31:54 +03:00
|
|
|
}
|
2023-04-26 11:01:33 +03:00
|
|
|
return migratedFoldersCount;
|
2015-04-24 11:18:33 +03:00
|
|
|
}
|
2013-02-10 14:00:22 +04:00
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Setup folders from settings file";
|
2014-05-26 16:34:08 +04:00
|
|
|
|
2023-08-30 19:34:35 +03:00
|
|
|
// this is done in Application::configVersionMigration
|
2023-08-27 23:19:04 +03:00
|
|
|
QStringList skipSettingsKeys;
|
|
|
|
backwardMigrationSettingsKeys(&skipSettingsKeys, &skipSettingsKeys);
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto accounts = AccountManager::instance()->accounts();
|
|
|
|
for (const auto &account : accounts) {
|
2015-04-24 11:18:33 +03:00
|
|
|
const auto id = account->account()->id();
|
|
|
|
if (!accountsWithSettings.contains(id)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
settings->beginGroup(id);
|
2016-11-22 17:30:12 +03:00
|
|
|
|
2018-05-14 15:37:48 +03:00
|
|
|
// The "backwardsCompatible" flag here is related to migrating old
|
|
|
|
// database locations
|
2023-04-26 11:01:33 +03:00
|
|
|
auto process = [&](const QString &groupName, const bool backwardsCompatible, const bool foldersWithPlaceholders) {
|
2018-05-14 15:37:48 +03:00
|
|
|
settings->beginGroup(groupName);
|
|
|
|
if (skipSettingsKeys.contains(settings->group())) {
|
|
|
|
// Should not happen: bad container keys should have been deleted
|
|
|
|
qCWarning(lcFolderMan) << "Folder structure" << groupName << "is too new, ignoring";
|
|
|
|
} else {
|
2018-10-29 14:31:39 +03:00
|
|
|
setupFoldersHelper(*settings, account, skipSettingsKeys, backwardsCompatible, foldersWithPlaceholders);
|
2018-05-14 15:37:48 +03:00
|
|
|
}
|
|
|
|
settings->endGroup();
|
|
|
|
};
|
2016-11-22 17:30:12 +03:00
|
|
|
|
2018-10-29 14:31:39 +03:00
|
|
|
process(QStringLiteral("Folders"), true, false);
|
2016-11-22 17:30:12 +03:00
|
|
|
|
2018-05-14 15:37:48 +03:00
|
|
|
// See Folder::saveToSettings for details about why these exists.
|
2018-10-29 14:31:39 +03:00
|
|
|
process(QStringLiteral("Multifolders"), false, false);
|
|
|
|
process(QStringLiteral("FoldersWithPlaceholders"), false, true);
|
2018-05-02 13:38:03 +03:00
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
settings->endGroup(); // <account>
|
|
|
|
}
|
2012-02-17 14:11:18 +04:00
|
|
|
|
2015-12-08 16:37:37 +03:00
|
|
|
emit folderListChanged(_folderMap);
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2022-10-24 18:53:40 +03:00
|
|
|
for (const auto folder : qAsConst(_folderMap)) {
|
2021-12-14 13:26:11 +03:00
|
|
|
folder->processSwitchedToVirtualFiles();
|
|
|
|
}
|
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
return _folderMap.size();
|
|
|
|
}
|
|
|
|
|
2018-10-29 14:31:39 +03:00
|
|
|
void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account, const QStringList &ignoreKeys, bool backwardsCompatible, bool foldersWithPlaceholders)
|
2016-11-22 17:30:12 +03:00
|
|
|
{
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto settingsChildGroups = settings.childGroups();
|
|
|
|
for (const auto &folderAlias : settingsChildGroups) {
|
2018-05-14 15:37:48 +03:00
|
|
|
// Skip folders with too-new version
|
|
|
|
settings.beginGroup(folderAlias);
|
|
|
|
if (ignoreKeys.contains(settings.group())) {
|
|
|
|
qCInfo(lcFolderMan) << "Folder" << folderAlias << "is too new, ignoring";
|
|
|
|
_additionalBlockedFolderAliases.insert(folderAlias);
|
|
|
|
settings.endGroup();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
settings.endGroup();
|
|
|
|
|
2016-11-22 17:30:12 +03:00
|
|
|
FolderDefinition folderDefinition;
|
2018-12-20 13:24:41 +03:00
|
|
|
settings.beginGroup(folderAlias);
|
2016-11-22 17:30:12 +03:00
|
|
|
if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
|
2017-06-16 16:43:21 +03:00
|
|
|
auto defaultJournalPath = folderDefinition.defaultJournalPath(account->account());
|
|
|
|
|
2016-11-23 12:40:17 +03:00
|
|
|
// Migration: Old settings don't have journalPath
|
|
|
|
if (folderDefinition.journalPath.isEmpty()) {
|
2017-06-16 16:43:21 +03:00
|
|
|
folderDefinition.journalPath = defaultJournalPath;
|
2019-09-26 18:47:01 +03:00
|
|
|
}
|
|
|
|
|
2020-11-05 19:18:13 +03:00
|
|
|
// Migration #2: journalPath might be absolute (in DataAppDir most likely) move it back to the root of local tree
|
|
|
|
if (folderDefinition.journalPath.at(0) != QChar('.')) {
|
|
|
|
QFile oldJournal(folderDefinition.journalPath);
|
|
|
|
QFile oldJournalShm(folderDefinition.journalPath + QStringLiteral("-shm"));
|
|
|
|
QFile oldJournalWal(folderDefinition.journalPath + QStringLiteral("-wal"));
|
2019-09-26 20:09:40 +03:00
|
|
|
|
2019-10-27 19:25:50 +03:00
|
|
|
folderDefinition.journalPath = defaultJournalPath;
|
2019-09-26 20:09:40 +03:00
|
|
|
|
|
|
|
socketApi()->slotUnregisterPath(folderAlias);
|
|
|
|
auto settings = account->settings();
|
|
|
|
|
2020-07-12 01:08:46 +03:00
|
|
|
auto journalFileMoveSuccess = true;
|
2020-07-12 11:13:14 +03:00
|
|
|
// Due to db logic can't be sure which of these file exist.
|
|
|
|
if (oldJournal.exists()) {
|
2020-11-05 19:18:13 +03:00
|
|
|
journalFileMoveSuccess &= oldJournal.rename(folderDefinition.localPath + "/" + folderDefinition.journalPath);
|
2020-07-12 11:13:14 +03:00
|
|
|
}
|
|
|
|
if (oldJournalShm.exists()) {
|
2020-11-05 19:18:13 +03:00
|
|
|
journalFileMoveSuccess &= oldJournalShm.rename(folderDefinition.localPath + "/" + folderDefinition.journalPath + QStringLiteral("-shm"));
|
2020-07-12 11:13:14 +03:00
|
|
|
}
|
|
|
|
if (oldJournalWal.exists()) {
|
2020-11-05 19:18:13 +03:00
|
|
|
journalFileMoveSuccess &= oldJournalWal.rename(folderDefinition.localPath + "/" + folderDefinition.journalPath + QStringLiteral("-wal"));
|
2020-07-12 11:13:14 +03:00
|
|
|
}
|
2020-07-12 01:08:46 +03:00
|
|
|
|
|
|
|
if (!journalFileMoveSuccess) {
|
2020-11-05 19:18:13 +03:00
|
|
|
qCWarning(lcFolderMan) << "Wasn't able to move 3.0 syncjournal database files to new location. One-time loss off sync settings possible.";
|
2020-07-12 01:08:46 +03:00
|
|
|
} else {
|
|
|
|
qCInfo(lcFolderMan) << "Successfully migrated syncjournal database.";
|
|
|
|
}
|
|
|
|
|
2018-11-15 11:45:14 +03:00
|
|
|
auto vfs = createVfsFromPlugin(folderDefinition.virtualFilesMode);
|
2018-11-13 13:46:26 +03:00
|
|
|
if (!vfs && folderDefinition.virtualFilesMode != Vfs::Off) {
|
|
|
|
qCWarning(lcFolderMan) << "Could not load plugin for mode" << folderDefinition.virtualFilesMode;
|
|
|
|
}
|
|
|
|
|
2023-04-26 11:15:49 +03:00
|
|
|
const auto folder = addFolderInternal(folderDefinition, account.data(), std::move(vfs));
|
|
|
|
folder->saveToSettings();
|
2019-09-26 20:09:40 +03:00
|
|
|
|
2020-09-16 10:54:36 +03:00
|
|
|
continue;
|
2017-06-16 16:43:21 +03:00
|
|
|
}
|
|
|
|
|
2018-12-18 12:10:19 +03:00
|
|
|
// Migration: ._ files sometimes can't be created.
|
|
|
|
// So if the configured journalPath has a dot-underscore ("._sync_*.db")
|
2017-06-16 16:43:21 +03:00
|
|
|
// but the current default doesn't have the underscore, switch to the
|
2018-12-18 12:10:19 +03:00
|
|
|
// new default if no db exists yet.
|
2017-06-16 16:43:21 +03:00
|
|
|
if (folderDefinition.journalPath.startsWith("._sync_")
|
2018-12-18 12:10:19 +03:00
|
|
|
&& defaultJournalPath.startsWith(".sync_")
|
|
|
|
&& !QFile::exists(folderDefinition.absoluteJournalPath())) {
|
2017-06-16 16:43:21 +03:00
|
|
|
folderDefinition.journalPath = defaultJournalPath;
|
2016-11-23 12:40:17 +03:00
|
|
|
}
|
2017-06-16 16:43:21 +03:00
|
|
|
|
2016-11-23 12:40:17 +03:00
|
|
|
// Migration: If an old db is found, move it to the new name.
|
|
|
|
if (backwardsCompatible) {
|
|
|
|
SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
|
|
|
|
}
|
|
|
|
|
2021-11-04 13:20:16 +03:00
|
|
|
const auto switchToVfs = isSwitchToVfsNeeded(folderDefinition);
|
|
|
|
if (switchToVfs) {
|
|
|
|
folderDefinition.virtualFilesMode = bestAvailableVfsMode();
|
|
|
|
}
|
|
|
|
|
2018-11-15 11:45:14 +03:00
|
|
|
auto vfs = createVfsFromPlugin(folderDefinition.virtualFilesMode);
|
2018-11-21 14:23:08 +03:00
|
|
|
if (!vfs) {
|
2018-11-13 13:46:26 +03:00
|
|
|
// TODO: Must do better error handling
|
|
|
|
qFatal("Could not load plugin");
|
|
|
|
}
|
|
|
|
|
2023-04-26 11:15:49 +03:00
|
|
|
if (const auto folder = addFolderInternal(std::move(folderDefinition), account.data(), std::move(vfs))) {
|
2021-11-04 13:20:16 +03:00
|
|
|
if (switchToVfs) {
|
2023-04-26 11:15:49 +03:00
|
|
|
folder->switchToVirtualFiles();
|
2021-11-04 13:20:16 +03:00
|
|
|
}
|
2018-12-20 13:24:41 +03:00
|
|
|
// Migrate the old "usePlaceholders" setting to the root folder pin state
|
2022-12-15 18:09:39 +03:00
|
|
|
if (settings.value(QLatin1String(settingsVersionC), 1).toInt() == 1
|
2018-12-20 13:24:41 +03:00
|
|
|
&& settings.value(QLatin1String("usePlaceholders"), false).toBool()) {
|
2020-07-17 15:28:16 +03:00
|
|
|
qCInfo(lcFolderMan) << "Migrate: From usePlaceholders to PinState::OnlineOnly";
|
2023-04-26 11:15:49 +03:00
|
|
|
folder->setRootPinState(PinState::OnlineOnly);
|
2018-12-20 13:24:41 +03:00
|
|
|
}
|
|
|
|
|
2016-11-23 12:40:17 +03:00
|
|
|
// Migration: Mark folders that shall be saved in a backwards-compatible way
|
2018-10-29 14:31:39 +03:00
|
|
|
if (backwardsCompatible)
|
2023-04-26 11:15:49 +03:00
|
|
|
folder->setSaveBackwardsCompatible(true);
|
2018-10-29 14:31:39 +03:00
|
|
|
if (foldersWithPlaceholders)
|
2023-04-26 11:15:49 +03:00
|
|
|
folder->setSaveInFoldersWithPlaceholders();
|
2018-12-20 13:24:41 +03:00
|
|
|
|
2023-04-26 11:15:49 +03:00
|
|
|
scheduleFolder(folder);
|
|
|
|
emit folderSyncStateChange(folder);
|
2016-11-22 17:30:12 +03:00
|
|
|
}
|
|
|
|
}
|
2018-12-20 13:24:41 +03:00
|
|
|
settings.endGroup();
|
2016-11-22 17:30:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
int FolderMan::setupFoldersMigration()
|
|
|
|
{
|
|
|
|
ConfigFile cfg;
|
|
|
|
QDir storageDir(cfg.configPath());
|
2022-11-07 20:33:39 +03:00
|
|
|
_folderConfigPath = cfg.configPath();
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2023-04-30 13:41:19 +03:00
|
|
|
const auto legacyConfigPath = ConfigFile::discoveredLegacyConfigPath();
|
|
|
|
const auto configPath = legacyConfigPath.isEmpty() ? _folderConfigPath : legacyConfigPath;
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2023-04-30 13:41:19 +03:00
|
|
|
qCInfo(lcFolderMan) << "Setup folders from " << configPath << "(migration)";
|
|
|
|
|
|
|
|
QDir dir(configPath);
|
2023-08-30 19:34:35 +03:00
|
|
|
// We need to include hidden files just in case the alias starts with '.'
|
2015-04-24 11:18:33 +03:00
|
|
|
dir.setFilter(QDir::Files | QDir::Hidden);
|
2023-08-30 19:34:35 +03:00
|
|
|
// Exclude previous backed up configs e.g. oc.cfg.backup_20230831_133749_4.0.0
|
|
|
|
// only need the current config in use by the legacy application
|
2023-08-27 23:19:04 +03:00
|
|
|
const auto dirFiles = dir.entryList({"*.cfg"});
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
// Migrate all folders for each account found in legacy config file(s)
|
|
|
|
const auto legacyAccounts = AccountManager::instance()->accounts();
|
2023-04-30 13:30:55 +03:00
|
|
|
for (const auto &fileName : dirFiles) {
|
2023-09-12 20:35:34 +03:00
|
|
|
for (const auto &accountState : legacyAccounts) {
|
|
|
|
const auto fullFilePath = dir.filePath(fileName);
|
2023-09-20 19:09:51 +03:00
|
|
|
setupLegacyFolder(fullFilePath, accountState.data());
|
2023-09-12 20:35:34 +03:00
|
|
|
}
|
2012-07-25 17:00:18 +04:00
|
|
|
}
|
2014-03-26 22:30:13 +04:00
|
|
|
|
2015-12-08 16:37:37 +03:00
|
|
|
emit folderListChanged(_folderMap);
|
2014-03-26 22:30:13 +04:00
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
// return the number of valid folders.
|
|
|
|
return _folderMap.size();
|
2018-05-02 16:40:54 +03:00
|
|
|
}
|
|
|
|
|
2018-05-14 15:37:48 +03:00
|
|
|
void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys)
|
2018-05-02 16:40:54 +03:00
|
|
|
{
|
|
|
|
auto settings = ConfigFile::settingsWithGroup(QLatin1String("Accounts"));
|
|
|
|
|
|
|
|
auto processSubgroup = [&](const QString &name) {
|
|
|
|
settings->beginGroup(name);
|
2023-04-26 11:12:04 +03:00
|
|
|
const auto foldersVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
|
2018-05-02 16:40:54 +03:00
|
|
|
if (foldersVersion <= maxFoldersVersion) {
|
2023-04-26 11:12:04 +03:00
|
|
|
for (const auto &folderAlias : settings->childGroups()) {
|
2018-05-02 16:40:54 +03:00
|
|
|
settings->beginGroup(folderAlias);
|
2023-04-26 11:12:04 +03:00
|
|
|
const auto folderVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
|
2018-05-02 16:40:54 +03:00
|
|
|
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
|
2018-05-14 15:37:48 +03:00
|
|
|
ignoreKeys->append(settings->group());
|
2018-05-02 16:40:54 +03:00
|
|
|
}
|
|
|
|
settings->endGroup();
|
|
|
|
}
|
|
|
|
} else {
|
2018-05-14 15:37:48 +03:00
|
|
|
deleteKeys->append(settings->group());
|
2018-05-02 16:40:54 +03:00
|
|
|
}
|
|
|
|
settings->endGroup();
|
|
|
|
};
|
|
|
|
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto settingsChildGroups = settings->childGroups();
|
|
|
|
for (const auto &accountId : settingsChildGroups) {
|
2018-05-02 16:40:54 +03:00
|
|
|
settings->beginGroup(accountId);
|
|
|
|
processSubgroup("Folders");
|
|
|
|
processSubgroup("Multifolders");
|
|
|
|
processSubgroup("FoldersWithPlaceholders");
|
|
|
|
settings->endGroup();
|
|
|
|
}
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
2014-01-20 18:12:26 +04:00
|
|
|
|
2016-09-02 13:29:21 +03:00
|
|
|
bool FolderMan::ensureJournalGone(const QString &journalDbFile)
|
2013-06-06 19:59:50 +04:00
|
|
|
{
|
2016-09-02 16:51:13 +03:00
|
|
|
// remove the old journal file
|
2016-09-02 13:29:21 +03:00
|
|
|
while (QFile::exists(journalDbFile) && !QFile::remove(journalDbFile)) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcFolderMan) << "Could not remove old db file at" << journalDbFile;
|
2018-11-11 12:56:22 +03:00
|
|
|
int ret = QMessageBox::warning(nullptr, tr("Could not reset folder state"),
|
2021-07-28 22:35:13 +03:00
|
|
|
tr("An old sync journal \"%1\" was found, "
|
2013-06-06 19:59:50 +04:00
|
|
|
"but could not be removed. Please make sure "
|
|
|
|
"that no application is currently using it.")
|
2016-09-02 13:29:21 +03:00
|
|
|
.arg(QDir::fromNativeSeparators(QDir::cleanPath(journalDbFile))),
|
2013-06-06 19:59:50 +04:00
|
|
|
QMessageBox::Retry | QMessageBox::Abort);
|
|
|
|
if (ret == QMessageBox::Abort) {
|
2013-06-06 23:43:05 +04:00
|
|
|
return false;
|
2013-06-06 19:59:50 +04:00
|
|
|
}
|
|
|
|
}
|
2013-06-06 23:43:05 +04:00
|
|
|
return true;
|
2013-06-06 19:59:50 +04:00
|
|
|
}
|
|
|
|
|
2012-10-17 17:13:55 +04:00
|
|
|
#define SLASH_TAG QLatin1String("__SLASH__")
|
|
|
|
#define BSLASH_TAG QLatin1String("__BSLASH__")
|
|
|
|
#define QMARK_TAG QLatin1String("__QMARK__")
|
|
|
|
#define PERCENT_TAG QLatin1String("__PERCENT__")
|
|
|
|
#define STAR_TAG QLatin1String("__STAR__")
|
|
|
|
#define COLON_TAG QLatin1String("__COLON__")
|
|
|
|
#define PIPE_TAG QLatin1String("__PIPE__")
|
|
|
|
#define QUOTE_TAG QLatin1String("__QUOTE__")
|
|
|
|
#define LT_TAG QLatin1String("__LESS_THAN__")
|
|
|
|
#define GT_TAG QLatin1String("__GREATER_THAN__")
|
2012-11-22 15:31:02 +04:00
|
|
|
#define PAR_O_TAG QLatin1String("__PAR_OPEN__")
|
|
|
|
#define PAR_C_TAG QLatin1String("__PAR_CLOSE__")
|
2017-05-17 11:55:42 +03:00
|
|
|
|
2014-08-11 17:09:17 +04:00
|
|
|
QString FolderMan::escapeAlias(const QString &alias)
|
2012-10-17 17:13:55 +04:00
|
|
|
{
|
|
|
|
QString a(alias);
|
|
|
|
|
|
|
|
a.replace(QLatin1Char('/'), SLASH_TAG);
|
|
|
|
a.replace(QLatin1Char('\\'), BSLASH_TAG);
|
|
|
|
a.replace(QLatin1Char('?'), QMARK_TAG);
|
|
|
|
a.replace(QLatin1Char('%'), PERCENT_TAG);
|
|
|
|
a.replace(QLatin1Char('*'), STAR_TAG);
|
|
|
|
a.replace(QLatin1Char(':'), COLON_TAG);
|
|
|
|
a.replace(QLatin1Char('|'), PIPE_TAG);
|
|
|
|
a.replace(QLatin1Char('"'), QUOTE_TAG);
|
|
|
|
a.replace(QLatin1Char('<'), LT_TAG);
|
|
|
|
a.replace(QLatin1Char('>'), GT_TAG);
|
2012-11-22 15:31:02 +04:00
|
|
|
a.replace(QLatin1Char('['), PAR_O_TAG);
|
|
|
|
a.replace(QLatin1Char(']'), PAR_C_TAG);
|
2012-10-17 17:13:55 +04:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2014-09-03 18:12:21 +04:00
|
|
|
SocketApi *FolderMan::socketApi()
|
|
|
|
{
|
2016-03-18 20:31:01 +03:00
|
|
|
return this->_socketApi.data();
|
2014-09-03 18:12:21 +04:00
|
|
|
}
|
|
|
|
|
2015-09-02 17:05:58 +03:00
|
|
|
QString FolderMan::unescapeAlias(const QString &alias)
|
2012-10-17 17:13:55 +04:00
|
|
|
{
|
|
|
|
QString a(alias);
|
|
|
|
|
|
|
|
a.replace(SLASH_TAG, QLatin1String("/"));
|
|
|
|
a.replace(BSLASH_TAG, QLatin1String("\\"));
|
|
|
|
a.replace(QMARK_TAG, QLatin1String("?"));
|
|
|
|
a.replace(PERCENT_TAG, QLatin1String("%"));
|
|
|
|
a.replace(STAR_TAG, QLatin1String("*"));
|
|
|
|
a.replace(COLON_TAG, QLatin1String(":"));
|
|
|
|
a.replace(PIPE_TAG, QLatin1String("|"));
|
|
|
|
a.replace(QUOTE_TAG, QLatin1String("\""));
|
|
|
|
a.replace(LT_TAG, QLatin1String("<"));
|
|
|
|
a.replace(GT_TAG, QLatin1String(">"));
|
2012-11-22 15:31:02 +04:00
|
|
|
a.replace(PAR_O_TAG, QLatin1String("["));
|
|
|
|
a.replace(PAR_C_TAG, QLatin1String("]"));
|
2012-10-17 17:13:55 +04:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2023-09-20 19:09:51 +03:00
|
|
|
void FolderMan::setupLegacyFolder(const QString &fileNamePath, AccountState *accountState)
|
2015-05-12 17:48:19 +03:00
|
|
|
{
|
2023-04-30 13:41:19 +03:00
|
|
|
qCInfo(lcFolderMan) << " ` -> setting up:" << fileNamePath;
|
|
|
|
QString escapedFileNamePath(fileNamePath);
|
2015-10-05 07:21:19 +03:00
|
|
|
// check the unescaped variant (for the case when the filename comes out
|
|
|
|
// of the directory listing). If the file does not exist, escape the
|
2012-10-17 17:13:55 +04:00
|
|
|
// file and try again.
|
2023-04-30 13:41:19 +03:00
|
|
|
QFileInfo cfgFile(fileNamePath);
|
2012-10-17 17:13:55 +04:00
|
|
|
|
|
|
|
if (!cfgFile.exists()) {
|
|
|
|
// try the escaped variant.
|
2023-04-30 13:41:19 +03:00
|
|
|
escapedFileNamePath = escapeAlias(fileNamePath);
|
|
|
|
cfgFile.setFile(_folderConfigPath, escapedFileNamePath);
|
2012-10-17 17:13:55 +04:00
|
|
|
}
|
|
|
|
if (!cfgFile.isReadable()) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcFolderMan) << "Cannot read folder definition for alias " << cfgFile.filePath();
|
2023-08-27 23:19:04 +03:00
|
|
|
return;
|
2012-10-17 17:13:55 +04:00
|
|
|
}
|
|
|
|
|
2023-04-30 13:41:19 +03:00
|
|
|
QSettings settings(escapedFileNamePath, QSettings::IniFormat);
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << " -> file path: " << settings.fileName();
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2012-11-22 15:31:29 +04:00
|
|
|
// Check if the filename is equal to the group setting. If not, use the group
|
|
|
|
// name as an alias.
|
2022-12-15 16:32:48 +03:00
|
|
|
const auto groups = settings.childGroups();
|
2022-11-07 20:33:39 +03:00
|
|
|
if (groups.isEmpty()) {
|
|
|
|
qCWarning(lcFolderMan) << "empty file:" << cfgFile.filePath();
|
2023-08-27 23:19:04 +03:00
|
|
|
return;
|
2022-11-07 20:33:39 +03:00
|
|
|
}
|
2012-11-22 15:31:29 +04:00
|
|
|
|
2022-11-07 20:33:39 +03:00
|
|
|
if (!accountState) {
|
|
|
|
qCCritical(lcFolderMan) << "can't create folder without an account";
|
2023-08-27 23:19:04 +03:00
|
|
|
return;
|
2012-11-22 15:31:29 +04:00
|
|
|
}
|
|
|
|
|
2022-12-15 18:09:39 +03:00
|
|
|
settings.beginGroup(settingsAccountsC);
|
2023-09-12 20:35:34 +03:00
|
|
|
qCDebug(lcFolderMan) << "try to migrate accountId:" << accountState->account()->id();
|
|
|
|
settings.beginGroup(accountState->account()->id());
|
|
|
|
settings.beginGroup(settingsFoldersC);
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
if (settings.childGroups().isEmpty()) {
|
|
|
|
qCDebug(lcFolderMan) << "there are no legacy folders for accountId:" << accountState->account()->id();
|
|
|
|
return;
|
|
|
|
}
|
2012-02-17 14:11:18 +04:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
const auto childGroups = settings.childGroups();
|
|
|
|
for (const auto &alias : childGroups) {
|
|
|
|
settings.beginGroup(alias);
|
|
|
|
qCDebug(lcFolderMan) << "try to migrate folder alias:" << alias;
|
2012-02-17 12:48:31 +04:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
const auto path = settings.value(QLatin1String("localPath")).toString();
|
|
|
|
const auto targetPath = settings.value(QLatin1String("targetPath")).toString();
|
|
|
|
const auto journalPath = settings.value(QLatin1String("journalPath")).toString();
|
|
|
|
const auto paused = settings.value(QLatin1String("paused"), false).toBool();
|
|
|
|
const auto ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), false).toBool();
|
2013-02-10 17:57:57 +04:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
if (path.isEmpty()) {
|
|
|
|
qCDebug(lcFolderMan) << "localPath is empty";
|
|
|
|
settings.endGroup();
|
|
|
|
continue;
|
|
|
|
}
|
2022-11-07 20:33:39 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
if (targetPath.isEmpty()) {
|
|
|
|
qCDebug(lcFolderMan) << "targetPath is empty";
|
|
|
|
settings.endGroup();
|
|
|
|
continue;
|
|
|
|
}
|
2022-11-07 20:33:39 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
if (journalPath.isEmpty()) {
|
|
|
|
qCDebug(lcFolderMan) << "journalPath is empty";
|
|
|
|
settings.endGroup();
|
|
|
|
continue;
|
|
|
|
}
|
2022-11-07 20:33:39 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
FolderDefinition folderDefinition;
|
|
|
|
folderDefinition.alias = alias;
|
|
|
|
folderDefinition.localPath = path;
|
|
|
|
folderDefinition.targetPath = targetPath;
|
|
|
|
folderDefinition.journalPath = journalPath;
|
|
|
|
folderDefinition.paused = paused;
|
|
|
|
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles;
|
|
|
|
|
|
|
|
if (const auto folder = addFolderInternal(folderDefinition, accountState, std::make_unique<VfsOff>())) {
|
|
|
|
auto ok = true;
|
|
|
|
auto legacyBlacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
|
|
|
|
&ok);
|
|
|
|
if (!ok) {
|
2023-09-16 22:42:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "There was a problem retrieving the database selective sync for " << folder;
|
2023-09-12 20:35:34 +03:00
|
|
|
}
|
2023-08-28 00:32:05 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
legacyBlacklist << settings.value(QLatin1String("blackList")).toStringList();
|
|
|
|
if (!legacyBlacklist.isEmpty()) {
|
|
|
|
qCInfo(lcFolderMan) << "Legacy selective sync list found:" << legacyBlacklist;
|
|
|
|
for (const auto &legacyFolder : legacyBlacklist) {
|
|
|
|
folder->migrateBlackListPath(legacyFolder);
|
2022-11-07 20:33:39 +03:00
|
|
|
}
|
2023-09-12 20:35:34 +03:00
|
|
|
settings.remove(QLatin1String("blackList"));
|
|
|
|
}
|
2022-11-07 20:33:39 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
folder->saveToSettings();
|
2023-04-26 15:27:43 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
qCInfo(lcFolderMan) << "Migrated!" << folder->path();
|
|
|
|
settings.sync();
|
2023-08-27 23:19:04 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
if (!folder) {
|
|
|
|
continue;
|
2022-11-07 20:33:39 +03:00
|
|
|
}
|
2014-12-18 14:09:48 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
scheduleFolder(folder);
|
|
|
|
emit folderSyncStateChange(folder);
|
2015-06-17 15:53:58 +03:00
|
|
|
}
|
|
|
|
|
2022-11-07 20:33:39 +03:00
|
|
|
settings.endGroup();
|
2015-05-21 13:22:50 +03:00
|
|
|
}
|
2023-04-26 15:27:43 +03:00
|
|
|
|
2023-09-12 20:35:34 +03:00
|
|
|
settings.endGroup();
|
|
|
|
settings.endGroup();
|
|
|
|
|
2023-08-27 23:19:04 +03:00
|
|
|
return;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2016-03-02 13:06:03 +03:00
|
|
|
void FolderMan::slotFolderSyncPaused(Folder *f, bool paused)
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
2015-01-16 14:23:15 +03:00
|
|
|
if (!f) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCCritical(lcFolderMan) << "slotFolderSyncPaused called with empty folder";
|
2015-01-16 14:23:15 +03:00
|
|
|
return;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2015-01-16 14:23:15 +03:00
|
|
|
if (!paused) {
|
|
|
|
_disabledFolders.remove(f);
|
2016-10-19 12:03:13 +03:00
|
|
|
scheduleFolder(f);
|
2015-01-16 14:23:15 +03:00
|
|
|
} else {
|
|
|
|
_disabledFolders.insert(f);
|
2012-04-30 10:56:56 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 23:43:53 +03:00
|
|
|
void FolderMan::slotFolderCanSyncChanged()
|
|
|
|
{
|
2020-05-18 21:54:23 +03:00
|
|
|
auto *f = qobject_cast<Folder *>(sender());
|
2019-07-24 14:56:21 +03:00
|
|
|
ASSERT(f);
|
2016-04-28 23:43:53 +03:00
|
|
|
if (f->canSync()) {
|
|
|
|
_socketApi->slotRegisterPath(f->alias());
|
|
|
|
} else {
|
|
|
|
_socketApi->slotUnregisterPath(f->alias());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-21 14:50:19 +04:00
|
|
|
Folder *FolderMan::folder(const QString &alias)
|
|
|
|
{
|
|
|
|
if (!alias.isEmpty()) {
|
|
|
|
if (_folderMap.contains(alias)) {
|
|
|
|
return _folderMap[alias];
|
|
|
|
}
|
|
|
|
}
|
2018-11-11 12:56:22 +03:00
|
|
|
return nullptr;
|
2012-02-21 14:50:19 +04:00
|
|
|
}
|
|
|
|
|
2016-10-19 12:03:13 +03:00
|
|
|
void FolderMan::scheduleAllFolders()
|
2013-02-19 20:24:10 +04:00
|
|
|
{
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto folderMapValues = _folderMap.values();
|
|
|
|
for (Folder *f : folderMapValues) {
|
2015-07-01 12:39:57 +03:00
|
|
|
if (f && f->canSync()) {
|
2016-10-19 12:03:13 +03:00
|
|
|
scheduleFolder(f);
|
2013-08-05 15:34:36 +04:00
|
|
|
}
|
2013-02-19 20:24:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:46:12 +03:00
|
|
|
void FolderMan::forceSyncForFolder(Folder *folder)
|
|
|
|
{
|
|
|
|
// Terminate and reschedule any running sync
|
|
|
|
for (const auto folderInMap : map()) {
|
|
|
|
if (folderInMap->isSyncRunning()) {
|
|
|
|
folderInMap->slotTerminateSync();
|
|
|
|
scheduleFolder(folderInMap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
folder->slotWipeErrorBlacklist(); // issue #6757
|
2022-11-09 18:43:08 +03:00
|
|
|
folder->setSyncPaused(false);
|
|
|
|
|
2022-10-24 14:46:12 +03:00
|
|
|
// Insert the selected folder at the front of the queue
|
|
|
|
scheduleFolderNext(folder);
|
|
|
|
}
|
|
|
|
|
2022-11-11 20:32:12 +03:00
|
|
|
void FolderMan::removeE2eFiles(const AccountPtr &account) const
|
|
|
|
{
|
|
|
|
Q_ASSERT(account->e2e()->_mnemonic.isEmpty());
|
|
|
|
for (const auto folder : map()) {
|
|
|
|
if(folder->accountState()->account()->id() == account->id()) {
|
|
|
|
folder->removeLocalE2eFiles();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-17 13:12:00 +03:00
|
|
|
void FolderMan::slotScheduleAppRestart()
|
|
|
|
{
|
|
|
|
_appRestartRequired = true;
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Application restart requested!";
|
2015-07-17 13:12:00 +03:00
|
|
|
}
|
|
|
|
|
2016-04-29 17:14:18 +03:00
|
|
|
void FolderMan::slotSyncOnceFileUnlocks(const QString &path)
|
|
|
|
{
|
|
|
|
_lockWatcher->addFile(path);
|
|
|
|
}
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
/*
|
|
|
|
* if a folder wants to be synced, it calls this slot and is added
|
|
|
|
* to the queue. The slot to actually start a sync is called afterwards.
|
|
|
|
*/
|
2016-10-19 12:03:13 +03:00
|
|
|
void FolderMan::scheduleFolder(Folder *f)
|
2012-03-22 19:22:08 +04:00
|
|
|
{
|
2015-01-16 14:23:15 +03:00
|
|
|
if (!f) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCCritical(lcFolderMan) << "slotScheduleSync called with null folder";
|
2014-05-15 21:29:40 +04:00
|
|
|
return;
|
|
|
|
}
|
2015-04-28 16:13:39 +03:00
|
|
|
auto alias = f->alias();
|
2014-09-17 15:02:11 +04:00
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Schedule folder " << alias << " to sync!";
|
2012-03-22 19:22:08 +04:00
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
if (!_scheduledFolders.contains(f)) {
|
2015-09-04 11:33:48 +03:00
|
|
|
if (!f->canSync()) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Folder is not ready to sync, not scheduled!";
|
2016-03-18 20:31:01 +03:00
|
|
|
_socketApi->slotUpdateFolderView(f);
|
2014-09-17 15:02:11 +04:00
|
|
|
return;
|
2014-01-11 23:35:16 +04:00
|
|
|
}
|
2015-09-04 11:33:48 +03:00
|
|
|
f->prepareToSync();
|
|
|
|
emit folderSyncStateChange(f);
|
2016-10-18 17:04:25 +03:00
|
|
|
_scheduledFolders.enqueue(f);
|
2015-09-04 11:33:48 +03:00
|
|
|
emit scheduleQueueChanged();
|
2012-03-29 12:13:19 +04:00
|
|
|
} else {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Sync for folder " << alias << " already scheduled, do not enqueue!";
|
2012-03-29 12:13:19 +04:00
|
|
|
}
|
2014-09-17 15:02:11 +04:00
|
|
|
|
2015-01-16 14:23:15 +03:00
|
|
|
startScheduledSyncSoon();
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
|
|
|
|
2022-09-06 18:03:31 +03:00
|
|
|
void FolderMan::scheduleFolderForImmediateSync(Folder *f)
|
|
|
|
{
|
|
|
|
_nextSyncShouldStartImmediately = true;
|
|
|
|
scheduleFolder(f);
|
|
|
|
}
|
|
|
|
|
2016-11-25 16:23:56 +03:00
|
|
|
void FolderMan::scheduleFolderNext(Folder *f)
|
|
|
|
{
|
|
|
|
auto alias = f->alias();
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Schedule folder " << alias << " to sync! Front-of-queue.";
|
2016-11-25 16:23:56 +03:00
|
|
|
|
|
|
|
if (!f->canSync()) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Folder is not ready to sync, not scheduled!";
|
2016-11-25 16:23:56 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_scheduledFolders.removeAll(f);
|
|
|
|
|
|
|
|
f->prepareToSync();
|
|
|
|
emit folderSyncStateChange(f);
|
|
|
|
_scheduledFolders.prepend(f);
|
|
|
|
emit scheduleQueueChanged();
|
|
|
|
|
|
|
|
startScheduledSyncSoon();
|
|
|
|
}
|
|
|
|
|
2014-12-03 16:56:46 +03:00
|
|
|
void FolderMan::slotScheduleETagJob(const QString & /*alias*/, RequestEtagJob *job)
|
2014-12-03 00:32:54 +03:00
|
|
|
{
|
2017-09-20 11:14:48 +03:00
|
|
|
QObject::connect(job, &QObject::destroyed, this, &FolderMan::slotEtagJobDestroyed);
|
2014-12-03 00:32:54 +03:00
|
|
|
QMetaObject::invokeMethod(this, "slotRunOneEtagJob", Qt::QueuedConnection);
|
2014-12-03 16:56:46 +03:00
|
|
|
// maybe: add to queue
|
2014-12-03 00:32:54 +03:00
|
|
|
}
|
|
|
|
|
2014-12-03 16:56:46 +03:00
|
|
|
void FolderMan::slotEtagJobDestroyed(QObject * /*o*/)
|
2014-12-03 00:32:54 +03:00
|
|
|
{
|
2014-12-03 16:56:46 +03:00
|
|
|
// _currentEtagJob is automatically cleared
|
|
|
|
// maybe: remove from queue
|
2014-12-03 00:32:54 +03:00
|
|
|
QMetaObject::invokeMethod(this, "slotRunOneEtagJob", Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::slotRunOneEtagJob()
|
|
|
|
{
|
|
|
|
if (_currentEtagJob.isNull()) {
|
2019-09-09 21:06:32 +03:00
|
|
|
Folder *folder = nullptr;
|
2020-09-27 14:19:08 +03:00
|
|
|
for (Folder *f : qAsConst(_folderMap)) {
|
2014-12-03 00:32:54 +03:00
|
|
|
if (f->etagJob()) {
|
2023-06-02 14:55:08 +03:00
|
|
|
// Caveat: always grabs the first folder with a job, but we think this is Ok for now and avoids us having a separate queue.
|
2014-12-03 00:32:54 +03:00
|
|
|
_currentEtagJob = f->etagJob();
|
2016-01-05 17:11:46 +03:00
|
|
|
folder = f;
|
2014-12-03 00:32:54 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_currentEtagJob.isNull()) {
|
2017-05-09 15:24:11 +03:00
|
|
|
//qCDebug(lcFolderMan) << "No more remote ETag check jobs to schedule.";
|
2015-07-17 13:12:00 +03:00
|
|
|
|
|
|
|
/* now it might be a good time to check for restarting... */
|
2020-11-26 19:19:20 +03:00
|
|
|
if (!isAnySyncRunning() && _appRestartRequired) {
|
2015-07-17 13:12:00 +03:00
|
|
|
restartApplication();
|
|
|
|
}
|
2014-12-03 00:32:54 +03:00
|
|
|
} else {
|
2017-05-09 15:24:11 +03:00
|
|
|
qCDebug(lcFolderMan) << "Scheduling" << folder->remoteUrl().toString() << "to check remote ETag";
|
2014-12-03 00:32:54 +03:00
|
|
|
_currentEtagJob->start(); // on destroy/end it will continue the queue via slotEtagJobDestroyed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-01 12:39:57 +03:00
|
|
|
void FolderMan::slotAccountStateChanged()
|
|
|
|
{
|
2020-05-18 21:54:23 +03:00
|
|
|
auto *accountState = qobject_cast<AccountState *>(sender());
|
2015-07-01 12:39:57 +03:00
|
|
|
if (!accountState) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QString accountName = accountState->account()->displayName();
|
|
|
|
|
2016-04-28 23:43:53 +03:00
|
|
|
if (accountState->isConnected()) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Account" << accountName << "connected, scheduling its folders";
|
2015-07-01 12:39:57 +03:00
|
|
|
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto folderMapValues = _folderMap.values();
|
|
|
|
for (Folder *f : folderMapValues) {
|
2015-07-01 12:39:57 +03:00
|
|
|
if (f
|
|
|
|
&& f->canSync()
|
|
|
|
&& f->accountState() == accountState) {
|
2016-10-19 12:03:13 +03:00
|
|
|
scheduleFolder(f);
|
2015-07-01 12:39:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Account" << accountName << "disconnected or paused, "
|
2015-07-01 12:39:57 +03:00
|
|
|
"terminating or descheduling sync folders";
|
|
|
|
|
2020-11-26 19:19:20 +03:00
|
|
|
foreach (Folder *f, _folderMap.values()) {
|
|
|
|
if (f
|
|
|
|
&& f->isSyncRunning()
|
|
|
|
&& f->accountState() == accountState) {
|
|
|
|
f->slotTerminateSync();
|
|
|
|
}
|
2015-07-01 12:39:57 +03:00
|
|
|
}
|
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
QMutableListIterator<Folder *> it(_scheduledFolders);
|
2015-07-01 12:39:57 +03:00
|
|
|
while (it.hasNext()) {
|
|
|
|
Folder *f = it.next();
|
|
|
|
if (f->accountState() == accountState) {
|
|
|
|
it.remove();
|
|
|
|
}
|
|
|
|
}
|
2015-09-04 11:33:48 +03:00
|
|
|
emit scheduleQueueChanged();
|
2015-07-01 12:39:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-05 07:21:19 +03:00
|
|
|
// only enable or disable foldermans will schedule and do syncs.
|
2014-08-19 16:03:41 +04:00
|
|
|
// this is not the same as Pause and Resume of folders.
|
2013-02-10 14:00:22 +04:00
|
|
|
void FolderMan::setSyncEnabled(bool enabled)
|
|
|
|
{
|
2016-10-18 17:04:25 +03:00
|
|
|
if (!_syncEnabled && enabled && !_scheduledFolders.isEmpty()) {
|
2015-10-05 07:21:19 +03:00
|
|
|
// We have things in our queue that were waiting for the connection to come back on.
|
2015-01-16 14:23:15 +03:00
|
|
|
startScheduledSyncSoon();
|
2013-08-14 14:59:56 +04:00
|
|
|
}
|
2013-02-10 14:00:22 +04:00
|
|
|
_syncEnabled = enabled;
|
2014-08-19 16:03:41 +04:00
|
|
|
// force a redraw in case the network connect status changed
|
2022-10-24 18:53:40 +03:00
|
|
|
emit folderSyncStateChange(nullptr);
|
2013-02-10 14:00:22 +04:00
|
|
|
}
|
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
void FolderMan::startScheduledSyncSoon()
|
2015-01-16 14:23:15 +03:00
|
|
|
{
|
|
|
|
if (_startScheduledSyncTimer.isActive()) {
|
|
|
|
return;
|
|
|
|
}
|
2016-10-18 17:04:25 +03:00
|
|
|
if (_scheduledFolders.empty()) {
|
2015-01-16 14:23:15 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-11-26 19:19:20 +03:00
|
|
|
if (isAnySyncRunning()) {
|
2015-01-16 14:23:15 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
qint64 msDelay = 100; // 100ms minimum delay
|
2015-01-16 14:23:15 +03:00
|
|
|
qint64 msSinceLastSync = 0;
|
|
|
|
|
|
|
|
// Require a pause based on the duration of the last sync run.
|
2015-04-28 16:13:39 +03:00
|
|
|
if (Folder *lastFolder = _lastSyncFolder) {
|
2018-01-24 00:51:25 +03:00
|
|
|
msSinceLastSync = lastFolder->msecSinceLastSync().count();
|
2015-01-16 14:23:15 +03:00
|
|
|
|
|
|
|
// 1s -> 1.5s pause
|
|
|
|
// 10s -> 5s pause
|
|
|
|
// 1min -> 12s pause
|
|
|
|
// 1h -> 90s pause
|
2018-01-24 00:51:25 +03:00
|
|
|
qint64 pause = qSqrt(lastFolder->msecLastSyncDuration().count()) / 20.0 * 1000.0;
|
2015-01-16 14:23:15 +03:00
|
|
|
msDelay = qMax(msDelay, pause);
|
|
|
|
}
|
|
|
|
|
2015-01-16 14:45:12 +03:00
|
|
|
// Delays beyond one minute seem too big, particularly since there
|
|
|
|
// could be things later in the queue that shouldn't be punished by a
|
|
|
|
// long delay!
|
2015-01-16 14:23:15 +03:00
|
|
|
msDelay = qMin(msDelay, 60 * 1000ll);
|
|
|
|
|
2015-01-28 13:23:20 +03:00
|
|
|
// Time since the last sync run counts against the delay
|
2015-01-16 14:23:15 +03:00
|
|
|
msDelay = qMax(1ll, msDelay - msSinceLastSync);
|
2015-01-28 13:23:20 +03:00
|
|
|
|
2022-09-06 18:03:31 +03:00
|
|
|
if (_nextSyncShouldStartImmediately) {
|
|
|
|
_nextSyncShouldStartImmediately = false;
|
|
|
|
qCInfo(lcFolderMan) << "Next sync is marked to start immediately, so setting the delay to '0'";
|
|
|
|
msDelay = 0;
|
|
|
|
}
|
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Starting the next scheduled sync in" << (msDelay / 1000) << "seconds";
|
2015-01-16 14:23:15 +03:00
|
|
|
_startScheduledSyncTimer.start(msDelay);
|
|
|
|
}
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
/*
|
|
|
|
* slot to start folder syncs.
|
|
|
|
* It is either called from the slot where folders enqueue themselves for
|
|
|
|
* syncing or after a folder sync was finished.
|
|
|
|
*/
|
2014-09-17 15:02:11 +04:00
|
|
|
void FolderMan::slotStartScheduledFolderSync()
|
2012-03-22 19:22:08 +04:00
|
|
|
{
|
2020-11-26 19:19:20 +03:00
|
|
|
if (isAnySyncRunning()) {
|
2022-10-24 18:53:40 +03:00
|
|
|
for (auto f : qAsConst(_folderMap)) {
|
2020-11-26 19:19:20 +03:00
|
|
|
if (f->isSyncRunning())
|
|
|
|
qCInfo(lcFolderMan) << "Currently folder " << f->remoteUrl().toString() << " is running, wait for finish!";
|
|
|
|
}
|
2012-03-22 19:22:08 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-10 14:00:22 +04:00
|
|
|
if (!_syncEnabled) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "FolderMan: Syncing is disabled, no scheduling.";
|
2013-02-10 14:00:22 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCDebug(lcFolderMan) << "folderQueue size: " << _scheduledFolders.count();
|
2016-10-18 17:04:25 +03:00
|
|
|
if (_scheduledFolders.isEmpty()) {
|
2015-01-16 14:23:15 +03:00
|
|
|
return;
|
|
|
|
}
|
2014-01-11 23:35:16 +04:00
|
|
|
|
2015-11-11 12:56:19 +03:00
|
|
|
// Find the first folder in the queue that can be synced.
|
2018-11-11 12:56:22 +03:00
|
|
|
Folder *folder = nullptr;
|
2016-10-18 17:04:25 +03:00
|
|
|
while (!_scheduledFolders.isEmpty()) {
|
2016-11-22 12:53:15 +03:00
|
|
|
Folder *g = _scheduledFolders.dequeue();
|
|
|
|
if (g->canSync()) {
|
|
|
|
folder = g;
|
2015-11-11 12:56:19 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-04 11:33:48 +03:00
|
|
|
emit scheduleQueueChanged();
|
2014-01-11 23:35:16 +04:00
|
|
|
|
2015-01-16 14:23:15 +03:00
|
|
|
// Start syncing this folder!
|
2016-11-22 12:53:15 +03:00
|
|
|
if (folder) {
|
2016-12-06 15:45:01 +03:00
|
|
|
// Safe to call several times, and necessary to try again if
|
|
|
|
// the folder path didn't exist previously.
|
2017-10-09 13:06:11 +03:00
|
|
|
folder->registerFolderWatcher();
|
|
|
|
registerFolderWithSocketApi(folder);
|
2016-12-06 15:45:01 +03:00
|
|
|
|
2016-11-22 12:53:15 +03:00
|
|
|
_currentSyncFolder = folder;
|
|
|
|
folder->startSync(QStringList());
|
2012-03-22 19:22:08 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-12 11:23:41 +03:00
|
|
|
bool FolderMan::pushNotificationsFilesReady(Account *account)
|
|
|
|
{
|
|
|
|
const auto pushNotifications = account->pushNotifications();
|
|
|
|
const auto pushFilesAvailable = account->capabilities().availablePushNotifications() & PushNotificationType::Files;
|
|
|
|
|
|
|
|
return pushFilesAvailable && pushNotifications && pushNotifications->isReady();
|
|
|
|
}
|
|
|
|
|
2021-11-04 13:20:16 +03:00
|
|
|
bool FolderMan::isSwitchToVfsNeeded(const FolderDefinition &folderDefinition) const
|
|
|
|
{
|
|
|
|
auto result = false;
|
|
|
|
if (ENFORCE_VIRTUAL_FILES_SYNC_FOLDER &&
|
|
|
|
folderDefinition.virtualFilesMode != bestAvailableVfsMode() &&
|
|
|
|
folderDefinition.virtualFilesMode == Vfs::Off &&
|
|
|
|
OCC::Theme::instance()->showVirtualFilesOption()) {
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-12-03 00:32:54 +03:00
|
|
|
void FolderMan::slotEtagPollTimerTimeout()
|
|
|
|
{
|
2021-01-12 11:23:41 +03:00
|
|
|
qCInfo(lcFolderMan) << "Etag poll timer timeout";
|
2014-12-03 00:32:54 +03:00
|
|
|
|
2021-01-12 11:23:41 +03:00
|
|
|
const auto folderMapValues = _folderMap.values();
|
|
|
|
|
|
|
|
qCInfo(lcFolderMan) << "Folders to sync:" << folderMapValues.size();
|
|
|
|
|
|
|
|
QList<Folder *> foldersToRun;
|
|
|
|
|
|
|
|
// Some folders need not to be checked because they use the push notifications
|
|
|
|
std::copy_if(folderMapValues.begin(), folderMapValues.end(), std::back_inserter(foldersToRun), [this](Folder *folder) -> bool {
|
|
|
|
const auto account = folder->accountState()->account();
|
|
|
|
return !pushNotificationsFilesReady(account.data());
|
|
|
|
});
|
|
|
|
|
|
|
|
qCInfo(lcFolderMan) << "Number of folders that don't use push notifications:" << foldersToRun.size();
|
|
|
|
|
|
|
|
runEtagJobsIfPossible(foldersToRun);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::runEtagJobsIfPossible(const QList<Folder *> &folderMap)
|
|
|
|
{
|
|
|
|
for (auto folder : folderMap) {
|
|
|
|
runEtagJobIfPossible(folder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::runEtagJobIfPossible(Folder *folder)
|
|
|
|
{
|
|
|
|
const ConfigFile cfg;
|
|
|
|
const auto polltime = cfg.remotePollInterval();
|
|
|
|
|
|
|
|
qCInfo(lcFolderMan) << "Run etag job on folder" << folder;
|
|
|
|
|
|
|
|
if (!folder) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (folder->isSyncRunning()) {
|
|
|
|
qCInfo(lcFolderMan) << "Can not run etag job: Sync is running";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_scheduledFolders.contains(folder)) {
|
2023-06-02 14:55:08 +03:00
|
|
|
qCInfo(lcFolderMan) << "Can not run etag job: Folder is already scheduled";
|
2021-01-12 11:23:41 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_disabledFolders.contains(folder)) {
|
|
|
|
qCInfo(lcFolderMan) << "Can not run etag job: Folder is disabled";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (folder->etagJob() || folder->isBusy() || !folder->canSync()) {
|
|
|
|
qCInfo(lcFolderMan) << "Can not run etag job: Folder is busy";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// When not using push notifications, make sure polltime is reached
|
|
|
|
if (!pushNotificationsFilesReady(folder->accountState()->account().data())) {
|
|
|
|
if (folder->msecSinceLastSync() < polltime) {
|
|
|
|
qCInfo(lcFolderMan) << "Can not run etag job: Polltime not reached";
|
|
|
|
return;
|
2014-12-03 00:32:54 +03:00
|
|
|
}
|
|
|
|
}
|
2021-01-12 11:23:41 +03:00
|
|
|
|
|
|
|
QMetaObject::invokeMethod(folder, "slotRunEtagJob", Qt::QueuedConnection);
|
2014-12-03 00:32:54 +03:00
|
|
|
}
|
|
|
|
|
2021-03-09 12:56:17 +03:00
|
|
|
void FolderMan::slotAccountRemoved(AccountState *accountState)
|
|
|
|
{
|
2022-09-08 14:21:30 +03:00
|
|
|
QVector<Folder *> foldersToRemove;
|
2021-03-09 12:56:17 +03:00
|
|
|
for (const auto &folder : qAsConst(_folderMap)) {
|
|
|
|
if (folder->accountState() == accountState) {
|
2022-09-08 14:21:30 +03:00
|
|
|
foldersToRemove.push_back(folder);
|
2021-03-09 12:56:17 +03:00
|
|
|
}
|
|
|
|
}
|
2022-09-08 14:21:30 +03:00
|
|
|
for (const auto &folder : qAsConst(foldersToRemove)) {
|
|
|
|
removeFolder(folder);
|
|
|
|
}
|
2021-03-09 12:56:17 +03:00
|
|
|
}
|
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
void FolderMan::slotRemoveFoldersForAccount(AccountState *accountState)
|
|
|
|
{
|
2015-04-28 16:13:39 +03:00
|
|
|
QVarLengthArray<Folder *, 16> foldersToRemove;
|
2014-12-18 14:09:48 +03:00
|
|
|
Folder::MapIterator i(_folderMap);
|
|
|
|
while (i.hasNext()) {
|
|
|
|
i.next();
|
|
|
|
Folder *folder = i.value();
|
|
|
|
if (folder->accountState() == accountState) {
|
2015-04-28 16:13:39 +03:00
|
|
|
foldersToRemove.append(folder);
|
2014-12-18 14:09:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 14:19:08 +03:00
|
|
|
for (const auto &f : qAsConst(foldersToRemove)) {
|
2016-10-19 12:03:13 +03:00
|
|
|
removeFolder(f);
|
2014-12-18 14:09:48 +03:00
|
|
|
}
|
2017-07-23 21:58:00 +03:00
|
|
|
emit folderListChanged(_folderMap);
|
2014-12-18 14:09:48 +03:00
|
|
|
}
|
|
|
|
|
2015-06-17 16:55:48 +03:00
|
|
|
void FolderMan::slotForwardFolderSyncStateChange()
|
|
|
|
{
|
2020-05-18 21:54:23 +03:00
|
|
|
if (auto *f = qobject_cast<Folder *>(sender())) {
|
2015-06-17 16:55:48 +03:00
|
|
|
emit folderSyncStateChange(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 13:59:36 +03:00
|
|
|
void FolderMan::slotServerVersionChanged(Account *account)
|
|
|
|
{
|
|
|
|
// Pause folders if the server version is unsupported
|
|
|
|
if (account->serverVersionUnsupported()) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcFolderMan) << "The server version is unsupported:" << account->serverVersion()
|
2016-03-02 13:59:36 +03:00
|
|
|
<< "pausing all folders on the account";
|
|
|
|
|
2020-09-27 14:19:08 +03:00
|
|
|
for (auto &f : qAsConst(_folderMap)) {
|
2016-03-02 13:59:36 +03:00
|
|
|
if (f->accountState()->account().data() == account) {
|
|
|
|
f->setSyncPaused(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
void FolderMan::slotWatchedFileUnlocked(const QString &path)
|
2016-04-29 17:14:18 +03:00
|
|
|
{
|
|
|
|
if (Folder *f = folderForPath(path)) {
|
2018-11-01 14:25:12 +03:00
|
|
|
// Treat this equivalently to the file being reported by the file watcher
|
2019-12-16 18:33:41 +03:00
|
|
|
f->slotWatchedPathChanged(path, Folder::ChangeReason::UnLock);
|
2016-10-18 17:04:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::slotScheduleFolderByTime()
|
|
|
|
{
|
2020-09-27 14:19:08 +03:00
|
|
|
for (const auto &f : qAsConst(_folderMap)) {
|
2016-10-18 17:04:25 +03:00
|
|
|
// Never schedule if syncing is disabled or when we're currently
|
|
|
|
// querying the server for etags
|
|
|
|
if (!f->canSync() || f->etagJob()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto msecsSinceSync = f->msecSinceLastSync();
|
|
|
|
|
|
|
|
// Possibly it's just time for a new sync run
|
2018-01-24 00:51:25 +03:00
|
|
|
bool forceSyncIntervalExpired = msecsSinceSync > ConfigFile().forceSyncInterval();
|
2016-10-18 17:04:25 +03:00
|
|
|
if (forceSyncIntervalExpired) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Scheduling folder" << f->alias()
|
2018-01-24 00:51:25 +03:00
|
|
|
<< "because it has been" << msecsSinceSync.count() << "ms "
|
2016-10-18 17:04:25 +03:00
|
|
|
<< "since the last sync";
|
|
|
|
|
2016-10-19 12:03:13 +03:00
|
|
|
scheduleFolder(f);
|
2016-10-18 17:04:25 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-06 15:18:59 +03:00
|
|
|
// Retry a couple of times after failure; or regularly if requested
|
|
|
|
bool syncAgain =
|
|
|
|
(f->consecutiveFailingSyncs() > 0 && f->consecutiveFailingSyncs() < 3)
|
|
|
|
|| f->syncEngine().isAnotherSyncNeeded() == DelayedFollowUp;
|
2018-01-24 00:51:25 +03:00
|
|
|
auto syncAgainDelay = std::chrono::seconds(10); // 10s for the first retry-after-fail
|
2016-10-18 17:04:25 +03:00
|
|
|
if (f->consecutiveFailingSyncs() > 1)
|
2018-01-24 00:51:25 +03:00
|
|
|
syncAgainDelay = std::chrono::seconds(60); // 60s for each further attempt
|
|
|
|
if (syncAgain && msecsSinceSync > syncAgainDelay) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Scheduling folder" << f->alias()
|
2016-12-06 15:18:59 +03:00
|
|
|
<< ", the last" << f->consecutiveFailingSyncs() << "syncs failed"
|
|
|
|
<< ", anotherSyncNeeded" << f->syncEngine().isAnotherSyncNeeded()
|
|
|
|
<< ", last status:" << f->syncResult().statusString()
|
2018-01-24 00:51:25 +03:00
|
|
|
<< ", time since last sync:" << msecsSinceSync.count();
|
2016-10-18 17:04:25 +03:00
|
|
|
|
2016-10-19 12:03:13 +03:00
|
|
|
scheduleFolder(f);
|
2016-10-18 17:04:25 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we want to retry failing syncs or another-sync-needed runs more often?
|
2016-04-29 17:14:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 19:19:20 +03:00
|
|
|
bool FolderMan::isAnySyncRunning() const
|
|
|
|
{
|
|
|
|
if (_currentSyncFolder)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (auto f : _folderMap) {
|
|
|
|
if (f->isSyncRunning())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-02-17 12:48:31 +04:00
|
|
|
void FolderMan::slotFolderSyncStarted()
|
|
|
|
{
|
2020-11-26 19:19:20 +03:00
|
|
|
auto f = qobject_cast<Folder *>(sender());
|
|
|
|
ASSERT(f);
|
|
|
|
if (!f)
|
|
|
|
return;
|
|
|
|
|
2017-05-16 18:42:57 +03:00
|
|
|
qCInfo(lcFolderMan, ">========== Sync started for folder [%s] of account [%s] with remote [%s]",
|
2020-11-26 19:19:20 +03:00
|
|
|
qPrintable(f->shortGuiLocalPath()),
|
|
|
|
qPrintable(f->accountState()->account()->displayName()),
|
|
|
|
qPrintable(f->remoteUrl().toString()));
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2012-03-22 19:22:08 +04:00
|
|
|
/*
|
|
|
|
* a folder indicates that its syncing is finished.
|
|
|
|
* Start the next sync after the system had some milliseconds to breath.
|
2014-10-09 17:50:29 +04:00
|
|
|
* This delay is particularly useful to avoid late file change notifications
|
|
|
|
* (that we caused ourselves by syncing) from triggering another spurious sync.
|
2012-03-22 19:22:08 +04:00
|
|
|
*/
|
2012-02-17 12:48:31 +04:00
|
|
|
void FolderMan::slotFolderSyncFinished(const SyncResult &)
|
|
|
|
{
|
2020-11-26 19:19:20 +03:00
|
|
|
auto f = qobject_cast<Folder *>(sender());
|
|
|
|
ASSERT(f);
|
|
|
|
if (!f)
|
|
|
|
return;
|
2012-03-29 12:13:19 +04:00
|
|
|
|
2020-11-26 19:19:20 +03:00
|
|
|
qCInfo(lcFolderMan, "<========== Sync finished for folder [%s] of account [%s] with remote [%s]",
|
|
|
|
qPrintable(f->shortGuiLocalPath()),
|
|
|
|
qPrintable(f->accountState()->account()->displayName()),
|
|
|
|
qPrintable(f->remoteUrl().toString()));
|
2014-01-11 23:35:16 +04:00
|
|
|
|
2020-11-26 19:19:20 +03:00
|
|
|
if (f == _currentSyncFolder) {
|
|
|
|
_lastSyncFolder = _currentSyncFolder;
|
|
|
|
_currentSyncFolder = nullptr;
|
|
|
|
}
|
|
|
|
if (!isAnySyncRunning())
|
|
|
|
startScheduledSyncSoon();
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2015-04-24 11:18:33 +03:00
|
|
|
Folder *FolderMan::addFolder(AccountState *accountState, const FolderDefinition &folderDefinition)
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
2016-11-23 12:40:17 +03:00
|
|
|
// Choose a db filename
|
|
|
|
auto definition = folderDefinition;
|
|
|
|
definition.journalPath = definition.defaultJournalPath(accountState->account());
|
|
|
|
|
|
|
|
if (!ensureJournalGone(definition.absoluteJournalPath())) {
|
2018-11-11 12:56:22 +03:00
|
|
|
return nullptr;
|
2015-04-24 11:18:33 +03:00
|
|
|
}
|
2014-12-11 14:28:01 +03:00
|
|
|
|
2018-11-15 11:45:14 +03:00
|
|
|
auto vfs = createVfsFromPlugin(folderDefinition.virtualFilesMode);
|
2018-11-21 14:23:08 +03:00
|
|
|
if (!vfs) {
|
2018-11-13 13:46:26 +03:00
|
|
|
qCWarning(lcFolderMan) << "Could not load plugin for mode" << folderDefinition.virtualFilesMode;
|
2020-12-10 21:52:58 +03:00
|
|
|
return nullptr;
|
2018-11-13 13:46:26 +03:00
|
|
|
}
|
|
|
|
|
2018-11-15 11:45:14 +03:00
|
|
|
auto folder = addFolderInternal(definition, accountState, std::move(vfs));
|
2016-11-23 12:40:17 +03:00
|
|
|
|
|
|
|
// Migration: The first account that's configured for a local folder shall
|
|
|
|
// be saved in a backwards-compatible way.
|
2020-09-29 19:32:48 +03:00
|
|
|
const auto folderList = FolderMan::instance()->map();
|
2021-07-23 18:41:02 +03:00
|
|
|
const auto oneAccountOnly = std::none_of(folderList.cbegin(), folderList.cend(), [folder](const auto *other) {
|
2020-09-27 14:19:08 +03:00
|
|
|
return other != folder && other->cleanPath() == folder->cleanPath();
|
|
|
|
});
|
|
|
|
|
2016-11-23 12:40:17 +03:00
|
|
|
folder->setSaveBackwardsCompatible(oneAccountOnly);
|
2016-09-02 13:29:21 +03:00
|
|
|
|
2016-03-16 21:07:40 +03:00
|
|
|
if (folder) {
|
2018-11-13 13:46:26 +03:00
|
|
|
folder->setSaveBackwardsCompatible(oneAccountOnly);
|
2015-06-17 15:54:39 +03:00
|
|
|
folder->saveToSettings();
|
2015-12-08 16:37:37 +03:00
|
|
|
emit folderSyncStateChange(folder);
|
|
|
|
emit folderListChanged(_folderMap);
|
2015-06-17 15:53:58 +03:00
|
|
|
}
|
2017-10-04 14:49:42 +03:00
|
|
|
|
|
|
|
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
|
2015-04-27 15:34:39 +03:00
|
|
|
return folder;
|
2015-04-24 11:18:33 +03:00
|
|
|
}
|
2014-12-11 14:28:01 +03:00
|
|
|
|
2018-11-13 13:46:26 +03:00
|
|
|
Folder *FolderMan::addFolderInternal(
|
|
|
|
FolderDefinition folderDefinition,
|
|
|
|
AccountState *accountState,
|
2018-11-15 11:45:14 +03:00
|
|
|
std::unique_ptr<Vfs> vfs)
|
2015-04-24 11:18:33 +03:00
|
|
|
{
|
2016-04-26 17:09:39 +03:00
|
|
|
auto alias = folderDefinition.alias;
|
|
|
|
int count = 0;
|
2018-05-14 15:37:48 +03:00
|
|
|
while (folderDefinition.alias.isEmpty()
|
|
|
|
|| _folderMap.contains(folderDefinition.alias)
|
|
|
|
|| _additionalBlockedFolderAliases.contains(folderDefinition.alias)) {
|
2016-04-26 17:09:39 +03:00
|
|
|
// There is already a folder configured with this name and folder names need to be unique
|
2023-11-14 21:20:51 +03:00
|
|
|
folderDefinition.alias = QString::number(alias.toInt() + (++count));
|
2016-04-26 17:09:39 +03:00
|
|
|
}
|
|
|
|
|
2018-11-15 11:45:14 +03:00
|
|
|
auto folder = new Folder(folderDefinition, accountState, std::move(vfs), this);
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2020-08-03 18:09:08 +03:00
|
|
|
if (_navigationPaneHelper.showInExplorerNavigationPane() && folderDefinition.navigationPaneClsid.isNull()) {
|
|
|
|
folder->setNavigationPaneClsid(QUuid::createUuid());
|
|
|
|
folder->saveToSettings();
|
|
|
|
}
|
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Adding folder to Folder Map " << folder << folder->alias();
|
2015-04-24 11:18:33 +03:00
|
|
|
_folderMap[folder->alias()] = folder;
|
2015-06-17 16:55:48 +03:00
|
|
|
if (folder->syncPaused()) {
|
2015-04-24 11:18:33 +03:00
|
|
|
_disabledFolders.insert(folder);
|
|
|
|
}
|
|
|
|
|
2015-06-17 16:55:48 +03:00
|
|
|
// See matching disconnects in unloadFolder().
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(folder, &Folder::syncStarted, this, &FolderMan::slotFolderSyncStarted);
|
|
|
|
connect(folder, &Folder::syncFinished, this, &FolderMan::slotFolderSyncFinished);
|
|
|
|
connect(folder, &Folder::syncStateChange, this, &FolderMan::slotForwardFolderSyncStateChange);
|
|
|
|
connect(folder, &Folder::syncPausedChanged, this, &FolderMan::slotFolderSyncPaused);
|
|
|
|
connect(folder, &Folder::canSyncChanged, this, &FolderMan::slotFolderCanSyncChanged);
|
2017-09-20 12:48:13 +03:00
|
|
|
connect(&folder->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged,
|
|
|
|
_socketApi.data(), &SocketApi::broadcastStatusPushMessage);
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(folder, &Folder::watchedFileChangedExternally,
|
|
|
|
&folder->syncEngine().syncFileStatusTracker(), &SyncFileStatusTracker::slotPathTouched);
|
2015-04-24 11:18:33 +03:00
|
|
|
|
2017-10-09 13:06:11 +03:00
|
|
|
folder->registerFolderWatcher();
|
|
|
|
registerFolderWithSocketApi(folder);
|
2015-04-24 11:18:33 +03:00
|
|
|
return folder;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2014-07-01 18:24:14 +04:00
|
|
|
Folder *FolderMan::folderForPath(const QString &path)
|
2013-10-03 19:04:55 +04:00
|
|
|
{
|
2014-07-14 17:13:28 +04:00
|
|
|
QString absolutePath = QDir::cleanPath(path) + QLatin1Char('/');
|
2013-10-03 19:04:55 +04:00
|
|
|
|
2020-09-29 19:32:48 +03:00
|
|
|
const auto folders = this->map().values();
|
2020-09-27 14:19:08 +03:00
|
|
|
const auto it = std::find_if(folders.cbegin(), folders.cend(), [absolutePath](const auto *folder) {
|
2015-05-05 18:34:01 +03:00
|
|
|
const QString folderPath = folder->cleanPath() + QLatin1Char('/');
|
2020-09-27 14:19:08 +03:00
|
|
|
return absolutePath.startsWith(folderPath, (Utility::isWindows() || Utility::isMac()) ? Qt::CaseInsensitive : Qt::CaseSensitive);
|
|
|
|
});
|
2014-07-14 17:13:28 +04:00
|
|
|
|
2020-09-27 14:19:08 +03:00
|
|
|
return it != folders.cend() ? *it : nullptr;
|
2013-10-03 19:04:55 +04:00
|
|
|
}
|
|
|
|
|
2023-08-03 10:34:39 +03:00
|
|
|
void FolderMan::addFolderToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType list)
|
2023-07-05 12:16:36 +03:00
|
|
|
{
|
|
|
|
const auto folder = folderForPath(path);
|
|
|
|
if (!folder) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-05 21:47:05 +03:00
|
|
|
const QString folderPath = folder->cleanPath() + QLatin1Char('/');
|
|
|
|
const auto relPath = path.mid(folderPath.length());
|
2023-08-03 10:34:39 +03:00
|
|
|
|
|
|
|
switch (list) {
|
|
|
|
case SyncJournalDb::SelectiveSyncListType::SelectiveSyncWhiteList:
|
|
|
|
folder->whitelistPath(relPath);
|
|
|
|
break;
|
|
|
|
case SyncJournalDb::SelectiveSyncListType::SelectiveSyncBlackList:
|
|
|
|
folder->blacklistPath(relPath);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
2023-07-05 12:16:36 +03:00
|
|
|
}
|
|
|
|
|
2023-08-03 10:34:39 +03:00
|
|
|
void FolderMan::whitelistFolderPath(const QString &path)
|
2023-07-05 12:16:36 +03:00
|
|
|
{
|
2023-08-03 10:34:39 +03:00
|
|
|
addFolderToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncListType::SelectiveSyncWhiteList);
|
|
|
|
}
|
2023-07-05 12:16:36 +03:00
|
|
|
|
2023-08-03 10:34:39 +03:00
|
|
|
void FolderMan::blacklistFolderPath(const QString &path)
|
|
|
|
{
|
|
|
|
addFolderToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncListType::SelectiveSyncBlackList);
|
2023-07-05 12:16:36 +03:00
|
|
|
}
|
|
|
|
|
2015-11-24 16:24:18 +03:00
|
|
|
QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const AccountPtr acc)
|
2015-11-16 17:04:59 +03:00
|
|
|
{
|
|
|
|
QStringList re;
|
|
|
|
|
2019-01-25 09:46:16 +03:00
|
|
|
// We'll be comparing against Folder::remotePath which always starts with /
|
|
|
|
QString serverPath = relPath;
|
|
|
|
if (!serverPath.startsWith('/'))
|
|
|
|
serverPath.prepend('/');
|
|
|
|
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto mapValues = map().values();
|
|
|
|
for (Folder *folder : mapValues) {
|
2020-06-10 04:47:49 +03:00
|
|
|
if (acc && folder->accountState()->account() != acc) {
|
2015-11-24 16:24:18 +03:00
|
|
|
continue;
|
|
|
|
}
|
2023-09-08 12:11:43 +03:00
|
|
|
if (!serverPath.startsWith(folder->remotePathTrailingSlash()))
|
2019-01-25 09:46:16 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
QString path = folder->cleanPath() + '/';
|
|
|
|
path += serverPath.midRef(folder->remotePathTrailingSlash().length());
|
2015-11-16 17:04:59 +03:00
|
|
|
if (QFile::exists(path)) {
|
|
|
|
re.append(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return re;
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:03:13 +03:00
|
|
|
void FolderMan::removeFolder(Folder *f)
|
2012-02-17 12:48:31 +04:00
|
|
|
{
|
2015-02-27 12:42:59 +03:00
|
|
|
if (!f) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCCritical(lcFolderMan) << "Can not remove null folder";
|
2015-02-27 12:42:59 +03:00
|
|
|
return;
|
2012-03-29 12:13:19 +04:00
|
|
|
}
|
|
|
|
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Removing " << f->alias();
|
2013-06-12 14:41:32 +04:00
|
|
|
|
2020-11-26 19:19:20 +03:00
|
|
|
const bool currentlyRunning = f->isSyncRunning();
|
2015-02-27 12:42:59 +03:00
|
|
|
if (currentlyRunning) {
|
|
|
|
// abort the sync now
|
2020-11-26 19:19:20 +03:00
|
|
|
f->slotTerminateSync();
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2016-10-18 17:04:25 +03:00
|
|
|
if (_scheduledFolders.removeAll(f) > 0) {
|
2015-09-04 11:33:48 +03:00
|
|
|
emit scheduleQueueChanged();
|
|
|
|
}
|
2014-03-10 15:22:06 +04:00
|
|
|
|
2015-02-27 12:42:59 +03:00
|
|
|
f->setSyncPaused(true);
|
2019-01-25 15:16:32 +03:00
|
|
|
f->wipeForRemoval();
|
2014-07-15 18:06:06 +04:00
|
|
|
|
2015-02-27 12:42:59 +03:00
|
|
|
// remove the folder configuration
|
2015-04-24 11:18:33 +03:00
|
|
|
f->removeFromSettings();
|
2014-07-15 18:06:06 +04:00
|
|
|
|
2015-04-28 16:13:39 +03:00
|
|
|
unloadFolder(f);
|
2015-09-18 15:34:01 +03:00
|
|
|
if (currentlyRunning) {
|
|
|
|
// We want to schedule the next folder once this is done
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(f, &Folder::syncFinished,
|
|
|
|
this, &FolderMan::slotFolderSyncFinished);
|
2015-09-18 15:34:01 +03:00
|
|
|
// Let the folder delete itself when done.
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(f, &Folder::syncFinished, f, &QObject::deleteLater);
|
2015-09-18 15:34:01 +03:00
|
|
|
} else {
|
2015-02-27 12:42:59 +03:00
|
|
|
delete f;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
2017-07-06 15:02:21 +03:00
|
|
|
|
2017-10-04 14:49:42 +03:00
|
|
|
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
|
|
|
|
|
2017-07-06 15:02:21 +03:00
|
|
|
emit folderListChanged(_folderMap);
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
|
|
|
|
2014-09-15 15:49:13 +04:00
|
|
|
QString FolderMan::getBackupName(QString fullPathName) const
|
2013-05-26 15:56:51 +04:00
|
|
|
{
|
2014-09-15 15:49:13 +04:00
|
|
|
if (fullPathName.endsWith("/"))
|
|
|
|
fullPathName.chop(1);
|
|
|
|
|
2013-05-26 15:56:51 +04:00
|
|
|
if (fullPathName.isEmpty())
|
2017-12-08 12:58:55 +03:00
|
|
|
return QString();
|
2013-05-26 15:56:51 +04:00
|
|
|
|
2015-07-29 13:23:33 +03:00
|
|
|
QString newName = fullPathName + tr(" (backup)");
|
2013-05-26 15:56:51 +04:00
|
|
|
QFileInfo fi(newName);
|
2015-08-03 10:17:45 +03:00
|
|
|
int cnt = 2;
|
2013-05-26 15:56:51 +04:00
|
|
|
do {
|
|
|
|
if (fi.exists()) {
|
2015-07-29 13:23:33 +03:00
|
|
|
newName = fullPathName + tr(" (backup %1)").arg(cnt++);
|
2013-05-26 15:56:51 +04:00
|
|
|
fi.setFile(newName);
|
|
|
|
}
|
|
|
|
} while (fi.exists());
|
|
|
|
|
|
|
|
return newName;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FolderMan::startFromScratch(const QString &localFolder)
|
|
|
|
{
|
2014-09-05 12:13:00 +04:00
|
|
|
if (localFolder.isEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-05-26 15:56:51 +04:00
|
|
|
|
|
|
|
QFileInfo fi(localFolder);
|
2014-09-05 12:13:00 +04:00
|
|
|
QDir parentDir(fi.dir());
|
|
|
|
QString folderName = fi.fileName();
|
|
|
|
|
|
|
|
// Adjust for case where localFolder ends with a /
|
|
|
|
if (fi.isDir()) {
|
|
|
|
folderName = parentDir.dirName();
|
|
|
|
parentDir.cdUp();
|
|
|
|
}
|
2013-06-03 17:47:07 +04:00
|
|
|
|
2014-09-05 12:13:00 +04:00
|
|
|
if (fi.exists()) {
|
|
|
|
// It exists, but is empty -> just reuse it.
|
|
|
|
if (fi.isDir() && fi.dir().count() == 0) {
|
2017-05-09 15:24:11 +03:00
|
|
|
qCDebug(lcFolderMan) << "startFromScratch: Directory is empty!";
|
2013-06-03 17:47:07 +04:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-24 13:12:02 +04:00
|
|
|
// Disconnect the socket api from the database to avoid that locking of the
|
|
|
|
// db file does not allow to move this dir.
|
2016-03-18 20:31:01 +03:00
|
|
|
Folder *f = folderForPath(localFolder);
|
|
|
|
if (f) {
|
|
|
|
if (localFolder.startsWith(f->path())) {
|
|
|
|
_socketApi->slotUnregisterPath(f->alias());
|
2014-10-24 13:36:01 +04:00
|
|
|
}
|
2016-03-18 20:31:01 +03:00
|
|
|
f->journalDb()->close();
|
|
|
|
f->slotTerminateSync(); // Normally it should not be running, but viel hilft viel
|
2014-10-24 13:12:02 +04:00
|
|
|
}
|
|
|
|
|
2014-09-05 12:13:00 +04:00
|
|
|
// Make a backup of the folder/file.
|
|
|
|
QString newName = getBackupName(parentDir.absoluteFilePath(folderName));
|
2015-03-11 12:51:36 +03:00
|
|
|
QString renameError;
|
|
|
|
if (!FileSystem::rename(fi.absoluteFilePath(), newName, &renameError)) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcFolderMan) << "startFromScratch: Could not rename" << fi.absoluteFilePath()
|
2015-03-11 12:51:36 +03:00
|
|
|
<< "to" << newName << "error:" << renameError;
|
2014-09-05 12:13:00 +04:00
|
|
|
return false;
|
2013-05-26 15:56:51 +04:00
|
|
|
}
|
|
|
|
}
|
2014-09-05 12:13:00 +04:00
|
|
|
|
|
|
|
if (!parentDir.mkdir(fi.absoluteFilePath())) {
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcFolderMan) << "startFromScratch: Could not mkdir" << fi.absoluteFilePath();
|
2014-09-05 12:13:00 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2013-05-26 15:56:51 +04:00
|
|
|
}
|
|
|
|
|
2019-07-24 14:56:21 +03:00
|
|
|
void FolderMan::slotWipeFolderForAccount(AccountState *accountState)
|
|
|
|
{
|
|
|
|
QVarLengthArray<Folder *, 16> foldersToRemove;
|
|
|
|
Folder::MapIterator i(_folderMap);
|
|
|
|
while (i.hasNext()) {
|
|
|
|
i.next();
|
|
|
|
Folder *folder = i.value();
|
|
|
|
if (folder->accountState() == accountState) {
|
|
|
|
foldersToRemove.append(folder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool success = false;
|
2020-09-27 14:19:08 +03:00
|
|
|
for (const auto &f : qAsConst(foldersToRemove)) {
|
2019-07-24 14:56:21 +03:00
|
|
|
if (!f) {
|
|
|
|
qCCritical(lcFolderMan) << "Can not remove null folder";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qCInfo(lcFolderMan) << "Removing " << f->alias();
|
|
|
|
|
|
|
|
const bool currentlyRunning = (_currentSyncFolder == f);
|
|
|
|
if (currentlyRunning) {
|
|
|
|
// abort the sync now
|
2020-11-26 19:19:20 +03:00
|
|
|
_currentSyncFolder->slotTerminateSync();
|
2019-07-24 14:56:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_scheduledFolders.removeAll(f) > 0) {
|
|
|
|
emit scheduleQueueChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
// wipe database
|
2019-01-25 15:16:32 +03:00
|
|
|
f->wipeForRemoval();
|
2019-07-24 14:56:21 +03:00
|
|
|
|
|
|
|
// wipe data
|
|
|
|
QDir userFolder(f->path());
|
|
|
|
if (userFolder.exists()) {
|
|
|
|
success = userFolder.removeRecursively();
|
|
|
|
if (!success) {
|
|
|
|
qCWarning(lcFolderMan) << "Failed to remove existing folder " << f->path();
|
|
|
|
} else {
|
|
|
|
qCInfo(lcFolderMan) << "wipe: Removed file " << f->path();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
success = true;
|
|
|
|
qCWarning(lcFolderMan) << "folder does not exist, can not remove.";
|
|
|
|
}
|
|
|
|
|
|
|
|
f->setSyncPaused(true);
|
|
|
|
|
|
|
|
// remove the folder configuration
|
|
|
|
f->removeFromSettings();
|
|
|
|
|
|
|
|
unloadFolder(f);
|
|
|
|
if (currentlyRunning) {
|
|
|
|
delete f;
|
|
|
|
}
|
|
|
|
|
|
|
|
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit folderListChanged(_folderMap);
|
|
|
|
emit wipeDone(accountState, success);
|
|
|
|
}
|
|
|
|
|
2018-01-05 11:44:20 +03:00
|
|
|
void FolderMan::setDirtyProxy()
|
2013-10-14 18:11:15 +04:00
|
|
|
{
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto folderMapValues = _folderMap.values();
|
2022-11-15 13:52:25 +03:00
|
|
|
for (const auto folder : folderMapValues) {
|
|
|
|
if (folder) {
|
|
|
|
if (folder->accountState() && folder->accountState()->account()
|
|
|
|
&& folder->accountState()->account()->networkAccessManager()) {
|
2015-10-05 07:21:19 +03:00
|
|
|
// Need to do this so we do not use the old determined system proxy
|
2022-11-15 13:52:25 +03:00
|
|
|
folder->accountState()->account()->networkAccessManager()->setProxy(
|
2015-03-25 14:25:18 +03:00
|
|
|
QNetworkProxy(QNetworkProxy::DefaultProxy));
|
|
|
|
}
|
2014-01-20 18:12:26 +04:00
|
|
|
}
|
2013-10-14 18:11:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 20:29:50 +04:00
|
|
|
void FolderMan::setDirtyNetworkLimits()
|
|
|
|
{
|
2022-10-24 18:53:40 +03:00
|
|
|
const auto folderMapValues = _folderMap.values();
|
2022-11-15 13:52:25 +03:00
|
|
|
for (auto folder : folderMapValues) {
|
2014-01-31 20:29:50 +04:00
|
|
|
// set only in busy folders. Otherwise they read the config anyway.
|
2022-11-15 13:52:25 +03:00
|
|
|
if (folder && folder->isBusy()) {
|
|
|
|
folder->setDirtyNetworkLimits();
|
2014-01-31 20:29:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-14 18:11:15 +04:00
|
|
|
|
2022-10-21 17:11:29 +03:00
|
|
|
void FolderMan::leaveShare(const QString &localFile)
|
|
|
|
{
|
|
|
|
if (const auto folder = FolderMan::instance()->folderForPath(localFile)) {
|
|
|
|
const auto filePathRelative = QString(localFile).remove(folder->path());
|
|
|
|
|
|
|
|
const auto leaveShareJob = new SimpleApiJob(folder->accountState()->account(), folder->accountState()->account()->davPath() + filePathRelative);
|
|
|
|
leaveShareJob->setVerb(SimpleApiJob::Verb::Delete);
|
|
|
|
connect(leaveShareJob, &SimpleApiJob::resultReceived, this, [this, folder](int statusCode) {
|
|
|
|
Q_UNUSED(statusCode)
|
|
|
|
scheduleFolder(folder);
|
|
|
|
});
|
|
|
|
leaveShareJob->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 16:11:20 +03:00
|
|
|
void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
|
|
|
|
SyncResult::Status *status, bool *unresolvedConflicts)
|
2013-07-22 14:10:13 +04:00
|
|
|
{
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Undefined;
|
|
|
|
*unresolvedConflicts = false;
|
2013-07-22 14:10:13 +04:00
|
|
|
|
2013-12-20 18:13:33 +04:00
|
|
|
int cnt = folders.count();
|
|
|
|
|
|
|
|
// if one folder: show the state of the one folder.
|
2015-10-05 07:21:19 +03:00
|
|
|
// if more folders:
|
2013-12-20 18:13:33 +04:00
|
|
|
// if one of them has an error -> show error
|
|
|
|
// if one is paused, but others ok, show ok
|
|
|
|
// do not show "problem" in the tray
|
|
|
|
//
|
|
|
|
if (cnt == 1) {
|
|
|
|
Folder *folder = folders.at(0);
|
|
|
|
if (folder) {
|
2018-01-23 16:11:20 +03:00
|
|
|
auto syncResult = folder->syncResult();
|
2014-08-19 15:58:20 +04:00
|
|
|
if (folder->syncPaused()) {
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Paused;
|
2014-08-19 15:58:20 +04:00
|
|
|
} else {
|
2018-01-23 16:11:20 +03:00
|
|
|
SyncResult::Status syncStatus = syncResult.status();
|
2014-08-19 15:58:20 +04:00
|
|
|
switch (syncStatus) {
|
|
|
|
case SyncResult::Undefined:
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Error;
|
2014-08-19 15:58:20 +04:00
|
|
|
break;
|
|
|
|
case SyncResult::Problem: // don't show the problem icon in tray.
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Success;
|
2014-08-19 15:58:20 +04:00
|
|
|
break;
|
2018-01-23 16:11:20 +03:00
|
|
|
default:
|
|
|
|
*status = syncStatus;
|
2014-08-19 15:58:20 +04:00
|
|
|
break;
|
|
|
|
}
|
2013-12-20 18:13:33 +04:00
|
|
|
}
|
2018-01-23 16:11:20 +03:00
|
|
|
*unresolvedConflicts = syncResult.hasUnresolvedConflicts();
|
2013-12-20 18:13:33 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int errorsSeen = 0;
|
|
|
|
int goodSeen = 0;
|
2015-07-01 12:39:57 +03:00
|
|
|
int abortOrPausedSeen = 0;
|
2013-12-20 18:13:33 +04:00
|
|
|
int runSeen = 0;
|
|
|
|
|
2020-09-27 14:19:08 +03:00
|
|
|
for (const Folder *folder : qAsConst(folders)) {
|
2018-01-23 16:11:20 +03:00
|
|
|
SyncResult folderResult = folder->syncResult();
|
2014-08-19 15:58:20 +04:00
|
|
|
if (folder->syncPaused()) {
|
2015-07-01 12:39:57 +03:00
|
|
|
abortOrPausedSeen++;
|
2014-08-19 15:58:20 +04:00
|
|
|
} else {
|
|
|
|
SyncResult::Status syncStatus = folderResult.status();
|
|
|
|
|
|
|
|
switch (syncStatus) {
|
|
|
|
case SyncResult::Undefined:
|
|
|
|
case SyncResult::NotYetStarted:
|
|
|
|
break;
|
2015-07-01 13:30:18 +03:00
|
|
|
case SyncResult::SyncPrepare:
|
2014-08-19 15:58:20 +04:00
|
|
|
case SyncResult::SyncRunning:
|
|
|
|
runSeen++;
|
|
|
|
break;
|
|
|
|
case SyncResult::Problem: // don't show the problem icon in tray.
|
|
|
|
case SyncResult::Success:
|
|
|
|
goodSeen++;
|
|
|
|
break;
|
|
|
|
case SyncResult::Error:
|
|
|
|
case SyncResult::SetupError:
|
|
|
|
errorsSeen++;
|
|
|
|
break;
|
|
|
|
case SyncResult::SyncAbortRequested:
|
|
|
|
case SyncResult::Paused:
|
2015-07-01 12:39:57 +03:00
|
|
|
abortOrPausedSeen++;
|
2014-08-19 15:58:20 +04:00
|
|
|
// no default case on purpose, check compiler warnings
|
|
|
|
}
|
2013-12-20 18:13:33 +04:00
|
|
|
}
|
2018-01-23 16:11:20 +03:00
|
|
|
if (folderResult.hasUnresolvedConflicts())
|
|
|
|
*unresolvedConflicts = true;
|
2013-12-20 18:13:33 +04:00
|
|
|
}
|
|
|
|
if (errorsSeen > 0) {
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Error;
|
|
|
|
} else if (abortOrPausedSeen > 0 && abortOrPausedSeen == cnt) {
|
2013-12-20 18:13:33 +04:00
|
|
|
// only if all folders are paused
|
2018-01-23 16:11:20 +03:00
|
|
|
*status = SyncResult::Paused;
|
|
|
|
} else if (runSeen > 0) {
|
|
|
|
*status = SyncResult::SyncRunning;
|
|
|
|
} else if (goodSeen > 0) {
|
|
|
|
*status = SyncResult::Success;
|
2013-07-22 14:10:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 16:11:20 +03:00
|
|
|
QString FolderMan::trayTooltipStatusString(
|
|
|
|
SyncResult::Status syncStatus, bool hasUnresolvedConflicts, bool paused)
|
2013-07-22 14:10:13 +04:00
|
|
|
{
|
|
|
|
QString folderMessage;
|
2017-01-25 13:28:18 +03:00
|
|
|
switch (syncStatus) {
|
2013-07-22 14:10:13 +04:00
|
|
|
case SyncResult::Undefined:
|
2022-06-23 12:13:18 +03:00
|
|
|
folderMessage = tr("Undefined state.");
|
2013-07-22 14:10:13 +04:00
|
|
|
break;
|
|
|
|
case SyncResult::NotYetStarted:
|
2015-10-05 07:21:19 +03:00
|
|
|
folderMessage = tr("Waiting to start syncing.");
|
2013-07-22 14:10:13 +04:00
|
|
|
break;
|
|
|
|
case SyncResult::SyncPrepare:
|
|
|
|
folderMessage = tr("Preparing for sync.");
|
|
|
|
break;
|
|
|
|
case SyncResult::SyncRunning:
|
|
|
|
folderMessage = tr("Sync is running.");
|
|
|
|
break;
|
|
|
|
case SyncResult::Success:
|
2018-01-23 16:11:20 +03:00
|
|
|
case SyncResult::Problem:
|
|
|
|
if (hasUnresolvedConflicts) {
|
2021-03-23 12:33:17 +03:00
|
|
|
folderMessage = tr("Sync finished with unresolved conflicts.");
|
2018-01-23 16:11:20 +03:00
|
|
|
} else {
|
2022-06-23 12:13:18 +03:00
|
|
|
folderMessage = tr("Last sync was successful.");
|
2018-01-23 16:11:20 +03:00
|
|
|
}
|
2013-07-22 14:10:13 +04:00
|
|
|
break;
|
|
|
|
case SyncResult::Error:
|
|
|
|
break;
|
|
|
|
case SyncResult::SetupError:
|
2022-06-23 12:13:18 +03:00
|
|
|
folderMessage = tr("Setup error.");
|
2013-07-22 14:10:13 +04:00
|
|
|
break;
|
2013-10-03 18:21:54 +04:00
|
|
|
case SyncResult::SyncAbortRequested:
|
2022-06-23 12:13:18 +03:00
|
|
|
folderMessage = tr("Sync request was cancelled.");
|
2013-10-03 18:21:54 +04:00
|
|
|
break;
|
2013-12-20 18:13:33 +04:00
|
|
|
case SyncResult::Paused:
|
|
|
|
folderMessage = tr("Sync is paused.");
|
|
|
|
break;
|
2013-07-22 14:10:13 +04:00
|
|
|
// no default case on purpose, check compiler warnings
|
|
|
|
}
|
2014-08-19 15:58:20 +04:00
|
|
|
if (paused) {
|
2013-08-05 15:34:36 +04:00
|
|
|
// sync is disabled.
|
|
|
|
folderMessage = tr("%1 (Sync is paused)").arg(folderMessage);
|
|
|
|
}
|
2013-07-22 14:10:13 +04:00
|
|
|
return folderMessage;
|
2012-02-17 12:48:31 +04:00
|
|
|
}
|
2013-07-22 14:10:13 +04:00
|
|
|
|
2018-07-16 13:46:03 +03:00
|
|
|
static QString checkPathValidityRecursive(const QString &path)
|
2015-07-13 15:35:19 +03:00
|
|
|
{
|
2015-07-28 13:14:52 +03:00
|
|
|
if (path.isEmpty()) {
|
2018-07-16 13:46:03 +03:00
|
|
|
return FolderMan::tr("No valid folder selected!");
|
2015-07-28 13:14:52 +03:00
|
|
|
}
|
|
|
|
|
2020-10-22 18:12:00 +03:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
Utility::NtfsPermissionLookupRAII ntfs_perm;
|
|
|
|
#endif
|
|
|
|
const QFileInfo selFile(path);
|
2015-07-13 15:35:19 +03:00
|
|
|
|
2015-07-28 13:14:52 +03:00
|
|
|
if (!selFile.exists()) {
|
2019-03-14 11:12:46 +03:00
|
|
|
QString parentPath = selFile.dir().path();
|
|
|
|
if (parentPath != path)
|
|
|
|
return checkPathValidityRecursive(parentPath);
|
|
|
|
return FolderMan::tr("The selected path does not exist!");
|
2015-07-28 13:14:52 +03:00
|
|
|
}
|
2015-07-13 15:35:19 +03:00
|
|
|
|
|
|
|
if (!selFile.isDir()) {
|
2018-07-16 13:46:03 +03:00
|
|
|
return FolderMan::tr("The selected path is not a folder!");
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
|
|
|
|
2023-03-09 15:05:19 +03:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
if (!selFile.isWritable()) {
|
|
|
|
// isWritable() doesn't cover all NTFS permissions
|
|
|
|
// try creating and removing a test file, and make sure it is excluded from sync
|
|
|
|
if (!Utility::canCreateFileInPath(path)) {
|
|
|
|
return FolderMan::tr("You have no permission to write to the selected folder!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2015-07-13 15:35:19 +03:00
|
|
|
if (!selFile.isWritable()) {
|
2018-07-16 13:46:03 +03:00
|
|
|
return FolderMan::tr("You have no permission to write to the selected folder!");
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
2023-03-09 15:05:19 +03:00
|
|
|
#endif
|
|
|
|
return {};
|
2018-07-16 13:46:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// QFileInfo::canonicalPath returns an empty string if the file does not exist.
|
|
|
|
// This function also works with files that does not exist and resolve the symlinks in the
|
|
|
|
// parent directories.
|
|
|
|
static QString canonicalPath(const QString &path)
|
|
|
|
{
|
|
|
|
QFileInfo selFile(path);
|
|
|
|
if (!selFile.exists()) {
|
2019-02-18 16:32:47 +03:00
|
|
|
const auto parentPath = selFile.dir().path();
|
|
|
|
|
|
|
|
// It's possible for the parentPath to match the path
|
2022-12-08 17:45:45 +03:00
|
|
|
// (possibly we've arrived at a non-existent drive root on Windows)
|
2019-02-18 16:32:47 +03:00
|
|
|
// and recursing would be fatal.
|
|
|
|
if (parentPath == path) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return canonicalPath(parentPath) + '/' + selFile.fileName();
|
2018-07-16 13:46:03 +03:00
|
|
|
}
|
|
|
|
return selFile.canonicalFilePath();
|
|
|
|
}
|
|
|
|
|
2022-09-08 13:00:13 +03:00
|
|
|
QPair<FolderMan::PathValidityResult, QString> FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl) const
|
2018-07-16 13:46:03 +03:00
|
|
|
{
|
2022-09-08 13:00:13 +03:00
|
|
|
QPair<FolderMan::PathValidityResult, QString> result;
|
|
|
|
|
2022-09-08 14:21:30 +03:00
|
|
|
const auto recursiveValidity = checkPathValidityRecursive(path);
|
2020-11-26 17:03:11 +03:00
|
|
|
if (!recursiveValidity.isEmpty()) {
|
|
|
|
qCDebug(lcFolderMan) << path << recursiveValidity;
|
2022-09-08 14:21:30 +03:00
|
|
|
result.first = FolderMan::PathValidityResult::ErrorRecursiveValidity;
|
2022-09-08 13:00:13 +03:00
|
|
|
result.second = recursiveValidity;
|
|
|
|
return result;
|
2020-11-26 17:03:11 +03:00
|
|
|
}
|
2015-07-13 15:35:19 +03:00
|
|
|
|
|
|
|
// check if the local directory isn't used yet in another ownCloud sync
|
2016-10-12 15:50:10 +03:00
|
|
|
Qt::CaseSensitivity cs = Qt::CaseSensitive;
|
|
|
|
if (Utility::fsCasePreserving()) {
|
|
|
|
cs = Qt::CaseInsensitive;
|
|
|
|
}
|
2015-07-13 15:35:19 +03:00
|
|
|
|
2018-07-16 13:46:03 +03:00
|
|
|
const QString userDir = QDir::cleanPath(canonicalPath(path)) + '/';
|
2015-07-13 15:35:19 +03:00
|
|
|
for (auto i = _folderMap.constBegin(); i != _folderMap.constEnd(); ++i) {
|
2020-05-18 21:54:23 +03:00
|
|
|
auto *f = static_cast<Folder *>(i.value());
|
2018-07-16 13:46:03 +03:00
|
|
|
QString folderDir = QDir::cleanPath(canonicalPath(f->path())) + '/';
|
2015-07-13 15:35:19 +03:00
|
|
|
|
2018-07-16 13:46:03 +03:00
|
|
|
bool differentPaths = QString::compare(folderDir, userDir, cs) != 0;
|
|
|
|
if (differentPaths && folderDir.startsWith(userDir, cs)) {
|
2022-09-08 14:21:30 +03:00
|
|
|
result.first = FolderMan::PathValidityResult::ErrorContainsFolder;
|
2022-09-08 13:00:13 +03:00
|
|
|
result.second = tr("The local folder %1 already contains a folder used in a folder sync connection. "
|
|
|
|
"Please pick another one!")
|
|
|
|
.arg(QDir::toNativeSeparators(path));
|
|
|
|
return result;
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
|
|
|
|
2018-07-16 13:46:03 +03:00
|
|
|
if (differentPaths && userDir.startsWith(folderDir, cs)) {
|
2022-09-08 14:21:30 +03:00
|
|
|
result.first = FolderMan::PathValidityResult::ErrorContainedInFolder;
|
2022-09-08 13:00:13 +03:00
|
|
|
result.second = tr("The local folder %1 is already contained in a folder used in a folder sync connection. "
|
2015-09-08 14:37:57 +03:00
|
|
|
"Please pick another one!")
|
2016-10-07 17:23:13 +03:00
|
|
|
.arg(QDir::toNativeSeparators(path));
|
2022-09-08 13:00:13 +03:00
|
|
|
return result;
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
|
|
|
|
2023-06-02 14:55:08 +03:00
|
|
|
// if both paths are equal, the server url needs to be different
|
2016-10-12 19:16:53 +03:00
|
|
|
// otherwise it would mean that a new connection from the same local folder
|
|
|
|
// to the same account is added which is not wanted. The account must differ.
|
2018-07-16 13:46:03 +03:00
|
|
|
if (serverUrl.isValid() && !differentPaths) {
|
2016-09-30 15:08:00 +03:00
|
|
|
QUrl folderUrl = f->accountState()->account()->url();
|
|
|
|
QString user = f->accountState()->account()->credentials()->user();
|
|
|
|
folderUrl.setUserName(user);
|
|
|
|
|
|
|
|
if (serverUrl == folderUrl) {
|
2022-09-08 14:21:30 +03:00
|
|
|
result.first = FolderMan::PathValidityResult::ErrorNonEmptyFolder;
|
2022-09-08 13:00:13 +03:00
|
|
|
result.second = tr("There is already a sync from the server to this local folder. "
|
2016-09-30 15:08:00 +03:00
|
|
|
"Please pick another local folder!");
|
2022-09-08 13:00:13 +03:00
|
|
|
return result;
|
2016-09-30 15:08:00 +03:00
|
|
|
}
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-08 13:00:13 +03:00
|
|
|
return result;
|
2015-07-13 15:35:19 +03:00
|
|
|
}
|
|
|
|
|
2023-03-24 14:29:41 +03:00
|
|
|
QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const QUrl &serverUrl, GoodPathStrategy allowExisting) const
|
2017-03-20 16:28:32 +03:00
|
|
|
{
|
|
|
|
QString folder = basePath;
|
|
|
|
|
|
|
|
// If the parent folder is a sync folder or contained in one, we can't
|
|
|
|
// possibly find a valid sync folder inside it.
|
|
|
|
// Example: Someone syncs their home directory. Then ~/foobar is not
|
|
|
|
// going to be an acceptable sync folder path for any value of foobar.
|
|
|
|
QString parentFolder = QFileInfo(folder).dir().canonicalPath();
|
|
|
|
if (FolderMan::instance()->folderForPath(parentFolder)) {
|
|
|
|
// Any path with that parent is going to be unacceptable,
|
|
|
|
// so just keep it as-is.
|
|
|
|
return basePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
int attempt = 1;
|
|
|
|
forever {
|
2023-03-24 14:29:41 +03:00
|
|
|
const auto isGood = FolderMan::instance()->checkPathValidityForNewFolder(folder, serverUrl).second.isEmpty() &&
|
|
|
|
(allowExisting == GoodPathStrategy::AllowOverrideExistingPath || !QFileInfo::exists(folder));
|
2017-03-20 16:28:32 +03:00
|
|
|
if (isGood) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count attempts and give up eventually
|
|
|
|
attempt++;
|
|
|
|
if (attempt > 100) {
|
|
|
|
return basePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
folder = basePath + QString::number(attempt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
2015-08-10 12:03:57 +03:00
|
|
|
bool FolderMan::ignoreHiddenFiles() const
|
|
|
|
{
|
|
|
|
if (_folderMap.empty()) {
|
2018-08-28 12:24:36 +03:00
|
|
|
// Currently no folders in the manager -> return default
|
|
|
|
return false;
|
2015-08-10 12:03:57 +03:00
|
|
|
}
|
2018-08-28 12:24:36 +03:00
|
|
|
// Since the hiddenFiles settings is the same for all folders, just return the settings of the first folder
|
2015-08-10 12:03:57 +03:00
|
|
|
return _folderMap.begin().value()->ignoreHiddenFiles();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::setIgnoreHiddenFiles(bool ignore)
|
|
|
|
{
|
|
|
|
// Note that the setting will revert to 'true' if all folders
|
|
|
|
// are deleted...
|
2020-09-27 14:19:08 +03:00
|
|
|
for (Folder *folder : qAsConst(_folderMap)) {
|
2015-08-10 12:03:57 +03:00
|
|
|
folder->setIgnoreHiddenFiles(ignore);
|
|
|
|
folder->saveToSettings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-04 11:33:48 +03:00
|
|
|
QQueue<Folder *> FolderMan::scheduleQueue() const
|
|
|
|
{
|
2016-10-18 17:04:25 +03:00
|
|
|
return _scheduledFolders;
|
2015-09-04 11:33:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Folder *FolderMan::currentSyncFolder() const
|
|
|
|
{
|
|
|
|
return _currentSyncFolder;
|
|
|
|
}
|
|
|
|
|
2015-07-17 13:12:00 +03:00
|
|
|
void FolderMan::restartApplication()
|
|
|
|
{
|
|
|
|
if (Utility::isLinux()) {
|
|
|
|
// restart:
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcFolderMan) << "Restarting application NOW, PID" << qApp->applicationPid() << "is ending.";
|
2015-07-17 13:12:00 +03:00
|
|
|
qApp->quit();
|
|
|
|
QStringList args = qApp->arguments();
|
|
|
|
QString prg = args.takeFirst();
|
|
|
|
|
|
|
|
QProcess::startDetached(prg, args);
|
|
|
|
} else {
|
2017-05-09 15:24:11 +03:00
|
|
|
qCDebug(lcFolderMan) << "On this platform we do not restart.";
|
2015-07-17 13:12:00 +03:00
|
|
|
}
|
|
|
|
}
|
2015-07-13 15:35:19 +03:00
|
|
|
|
2021-01-12 11:23:41 +03:00
|
|
|
void FolderMan::slotSetupPushNotifications(const Folder::Map &folderMap)
|
|
|
|
{
|
|
|
|
for (auto folder : folderMap) {
|
|
|
|
const auto account = folder->accountState()->account();
|
|
|
|
|
|
|
|
// See if the account already provides the PushNotifications object and if yes connect to it.
|
|
|
|
// If we can't connect at this point, the signals will be connected in slotPushNotificationsReady()
|
|
|
|
// after the PushNotification object emitted the ready signal
|
|
|
|
slotConnectToPushNotifications(account.data());
|
|
|
|
connect(account.data(), &Account::pushNotificationsReady, this, &FolderMan::slotConnectToPushNotifications, Qt::UniqueConnection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::slotProcessFilesPushNotification(Account *account)
|
|
|
|
{
|
|
|
|
qCInfo(lcFolderMan) << "Got files push notification for account" << account;
|
|
|
|
|
2022-10-24 18:53:40 +03:00
|
|
|
for (auto folder : qAsConst(_folderMap)) {
|
2021-01-12 11:23:41 +03:00
|
|
|
// Just run on the folders that belong to this account
|
|
|
|
if (folder->accountState()->account() != account) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
qCInfo(lcFolderMan) << "Schedule folder" << folder << "for sync";
|
|
|
|
scheduleFolder(folder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FolderMan::slotConnectToPushNotifications(Account *account)
|
|
|
|
{
|
|
|
|
const auto pushNotifications = account->pushNotifications();
|
|
|
|
|
|
|
|
if (pushNotificationsFilesReady(account)) {
|
|
|
|
qCInfo(lcFolderMan) << "Push notifications ready";
|
|
|
|
connect(pushNotifications, &PushNotifications::filesChanged, this, &FolderMan::slotProcessFilesPushNotification, Qt::UniqueConnection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
} // namespace OCC
|