/* * Copyright (C) by Klaas Freitag * * 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. */ #ifndef FOLDERMAN_H #define FOLDERMAN_H #include #include #include #include "folder.h" #include "folderwatcher.h" #include "navigationpanehelper.h" #include "syncfileitem.h" class TestFolderMan; class TestCfApiShellExtensionsIPC; class TestShareModel; class ShareTestHelper; class EndToEndTestHelper; class TestSyncConflictsModel; namespace OCC { class Application; class SyncResult; class SocketApi; class LockWatcher; /** * @brief The FolderMan class * @ingroup gui * * The FolderMan knows about all loaded folders and is responsible for * scheduling them when necessary. * * A folder is scheduled if: * - The configured force-sync-interval has expired * (_timeScheduler and slotScheduleFolderByTime()) * * - A folder watcher receives a notification about a file change * (_folderWatchers and Folder::slotWatchedPathChanged()) * * - The folder etag on the server has changed * (_etagPollTimer) * * - The locks of a monitored file are released * (_lockWatcher and slotWatchedFileUnlocked()) * * - There was a sync error or a follow-up sync is requested * (_timeScheduler and slotScheduleFolderByTime() * and Folder::slotSyncFinished()) */ class FolderMan : public QObject { Q_OBJECT public: enum class PathValidityResult { Valid, ErrorRecursiveValidity, ErrorContainsFolder, ErrorContainedInFolder, ErrorNonEmptyFolder }; enum class GoodPathStrategy { AllowOnlyNewPath, AllowOverrideExistingPath, }; ~FolderMan() override; static FolderMan *instance(); int setupFolders(); int setupFoldersMigration(); /** * Returns a list of keys that can't be read because they are from * future versions. */ static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys); [[nodiscard]] const Folder::Map &map() const; /** Adds a folder for an account, ensures the journal is gone and saves it in the settings. */ Folder *addFolder(AccountState *accountState, const FolderDefinition &folderDefinition); /** Removes a folder */ void removeFolder(Folder *); /** Returns the folder which the file or directory stored in path is in */ Folder *folderForPath(const QString &path); /** * returns a list of local files that exist on the local harddisk for an * incoming relative server path. The method checks with all existing sync * folders. */ QStringList findFileInLocalFolders(const QString &relPath, const AccountPtr acc); /** Returns the folder by alias or \c nullptr if no folder with the alias exists. */ Folder *folder(const QString &); /** * Migrate accounts from owncloud < 2.0 * Creates a folder for a specific configuration, identified by alias. */ Folder *setupFolderFromOldConfigFile(const QString &, AccountState *account); /** * Ensures that a given directory does not contain a sync journal file. * * @returns false if the journal could not be removed, true otherwise. */ static bool ensureJournalGone(const QString &journalDbFile); /** Creates a new and empty local directory. */ bool startFromScratch(const QString &); /// Produce text for use in the tray tooltip static QString trayTooltipStatusString(SyncResult::Status syncStatus, bool hasUnresolvedConflicts, bool paused); /// Compute status summarizing multiple folders static void trayOverallStatus(const QList &folders, SyncResult::Status *status, bool *unresolvedConflicts); // Escaping of the alias which is used in QSettings AND the file // system, thus need to be escaped. static QString escapeAlias(const QString &); static QString unescapeAlias(const QString &); SocketApi *socketApi(); NavigationPaneHelper &navigationPaneHelper() { return _navigationPaneHelper; } /** * Check if @a path is a valid path for a new folder considering the already sync'ed items. * Make sure that this folder, or any subfolder is not sync'ed already. * * Note that different accounts are allowed to sync to the same folder. * * @returns an empty string and PathValidityResult::Valid if it is allowed, or an error if it is not allowed */ [[nodiscard]] QPair checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl()) const; /** * Attempts to find a non-existing, acceptable path for creating a new sync folder. * * Uses \a basePath as the baseline. It'll return this path if it's acceptable. * * Note that this can fail. If someone syncs ~ and \a basePath is ~/ownCloud, no * subfolder of ~ would be a good candidate. When that happens \a basePath * is returned. */ [[nodiscard]] QString findGoodPathForNewSyncFolder(const QString &basePath, const QUrl &serverUrl, GoodPathStrategy allowExisting) const; /** * While ignoring hidden files can theoretically be switched per folder, * it's currently a global setting that users can only change for all folders * at once. * These helper functions can be removed once it's properly per-folder. */ [[nodiscard]] bool ignoreHiddenFiles() const; void setIgnoreHiddenFiles(bool ignore); /** * Access to the current queue of scheduled folders. */ [[nodiscard]] QQueue scheduleQueue() const; /** * Access to the currently syncing folder. * * Note: This is only the folder that's currently syncing *as-scheduled*. There * may be externally-managed syncs such as from placeholder hydrations. * * See also isAnySyncRunning() */ [[nodiscard]] Folder *currentSyncFolder() const; /** * Returns true if any folder is currently syncing. * * This might be a FolderMan-scheduled sync, or a externally * managed sync like a placeholder hydration. */ [[nodiscard]] bool isAnySyncRunning() const; /** Removes all folders */ int unloadAndDeleteAllFolders(); /** * If enabled is set to false, no new folders will start to sync. * The current one will finish. */ void setSyncEnabled(bool); /** Queues a folder for syncing. */ void scheduleFolder(Folder *); /** Queues a folder for syncing that starts immediately. */ void scheduleFolderForImmediateSync(Folder *); /** Puts a folder in the very front of the queue. */ void scheduleFolderNext(Folder *); /** Queues all folders for syncing. */ void scheduleAllFolders(); void setDirtyProxy(); void setDirtyNetworkLimits(); /** removes current user from the share **/ void leaveShare(const QString &localFile); signals: /** * signal to indicate a folder has changed its sync state. * * Attention: The folder may be zero. Do a general update of the state then. */ void folderSyncStateChange(OCC::Folder *); /** * Indicates when the schedule queue changes. */ void scheduleQueueChanged(); /** * Emitted whenever the list of configured folders changes. */ void folderListChanged(const OCC::Folder::Map &); /** * Emitted once slotRemoveFoldersForAccount is done wiping */ void wipeDone(OCC::AccountState *account, bool success); public slots: /** * Schedules folders of newly connected accounts, terminates and * de-schedules folders of disconnected accounts. */ void slotAccountStateChanged(); /** * restart the client as soon as it is possible, ie. no folders syncing. */ void slotScheduleAppRestart(); /** * Triggers a sync run once the lock on the given file is removed. * * Automatically detemines the folder that's responsible for the file. * See slotWatchedFileUnlocked(). */ void slotSyncOnceFileUnlocks(const QString &path); // slot to schedule an ETag job (from Folder only) void slotScheduleETagJob(const QString &alias, OCC::RequestEtagJob *job); /** Wipe folder */ void slotWipeFolderForAccount(OCC::AccountState *accountState); void forceSyncForFolder(OCC::Folder *folder); void removeE2eFiles(const OCC::AccountPtr &account) const; private slots: void slotFolderSyncPaused(OCC::Folder *, bool paused); void slotFolderCanSyncChanged(); void slotFolderSyncStarted(); void slotFolderSyncFinished(const OCC::SyncResult &); void slotRunOneEtagJob(); void slotEtagJobDestroyed(QObject *); // slot to take the next folder from queue and start syncing. void slotStartScheduledFolderSync(); void slotEtagPollTimerTimeout(); void slotAccountRemoved(OCC::AccountState *accountState); void slotRemoveFoldersForAccount(OCC::AccountState *accountState); // Wraps the Folder::syncStateChange() signal into the // FolderMan::folderSyncStateChange(Folder*) signal. void slotForwardFolderSyncStateChange(); void slotServerVersionChanged(OCC::Account *account); /** * A file whose locks were being monitored has become unlocked. * * This schedules the folder for synchronization that contains * the file with the given path. */ void slotWatchedFileUnlocked(const QString &path); /** * Schedules folders whose time to sync has come. * * Either because a long time has passed since the last sync or * because of previous failures. */ void slotScheduleFolderByTime(); void slotSetupPushNotifications(const OCC::Folder::Map &); void slotProcessFilesPushNotification(OCC::Account *account); void slotConnectToPushNotifications(OCC::Account *account); private: /** Adds a new folder, does not add it to the account settings and * does not set an account on the new folder. */ Folder *addFolderInternal(FolderDefinition folderDefinition, AccountState *accountState, std::unique_ptr vfs); /* unloads a folder object, does not delete it */ void unloadFolder(Folder *); /** Will start a sync after a bit of delay. */ void startScheduledSyncSoon(); // finds all folder configuration files // and create the folders [[nodiscard]] QString getBackupName(QString fullPathName) const; // makes the folder known to the socket api void registerFolderWithSocketApi(Folder *folder); // restarts the application (Linux only) void restartApplication(); void setupFoldersHelper(QSettings &settings, AccountStatePtr account, const QStringList &ignoreKeys, bool backwardsCompatible, bool foldersWithPlaceholders); void runEtagJobsIfPossible(const QList &folderMap); void runEtagJobIfPossible(Folder *folder); bool pushNotificationsFilesReady(Account *account); [[nodiscard]] bool isSwitchToVfsNeeded(const FolderDefinition &folderDefinition) const; QSet _disabledFolders; Folder::Map _folderMap; QString _folderConfigPath; Folder *_currentSyncFolder = nullptr; QPointer _lastSyncFolder; bool _syncEnabled = true; /// Folder aliases from the settings that weren't read QSet _additionalBlockedFolderAliases; /// Starts regular etag query jobs QTimer _etagPollTimer; /// The currently running etag query QPointer _currentEtagJob; /// Watches files that couldn't be synced due to locks QScopedPointer _lockWatcher; /// Occasionally schedules folders QTimer _timeScheduler; /// Scheduled folders that should be synced as soon as possible QQueue _scheduledFolders; /// Picks the next scheduled folder and starts the sync QTimer _startScheduledSyncTimer; bool _nextSyncShouldStartImmediately = false; QScopedPointer _socketApi; NavigationPaneHelper _navigationPaneHelper; bool _appRestartRequired = false; static FolderMan *_instance; explicit FolderMan(QObject *parent = nullptr); friend class OCC::Application; friend class ::TestFolderMan; friend class ::TestSyncConflictsModel; friend class ::TestCfApiShellExtensionsIPC; friend class ::ShareTestHelper; friend class ::EndToEndTestHelper; }; } // namespace OCC #endif // FOLDERMAN_H