Welcome to mirror list, hosted at ThFree Co, Russian Federation.

folderman.h « gui « src - github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 444eedff869ae3be3eee9984644c2423f4845505 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/*
 * 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.
 */


#ifndef FOLDERMAN_H
#define FOLDERMAN_H

#include <QObject>
#include <QQueue>
#include <QList>

#include "folder.h"
#include "folderwatcher.h"
#include "navigationpanehelper.h"
#include "syncfileitem.h"

class TestFolderMan;
class TestCfApiShellExtensionsIPC;

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
    };

    ~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<Folder *> &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<PathValidityResult, QString> 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) 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<Folder *> 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();

    /** opens a file with default app, if the file is present **/
    void editFileLocally(const QString &userId, const QString &relPath, const QString &token);

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(Folder *);

    /**
     * Indicates when the schedule queue changes.
     */
    void scheduleQueueChanged();

    /**
     * Emitted whenever the list of configured folders changes.
     */
    void folderListChanged(const Folder::Map &);

    /**
     * Emitted once slotRemoveFoldersForAccount is done wiping
     */
    void wipeDone(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, RequestEtagJob *job);

    /** Wipe folder */
    void slotWipeFolderForAccount(AccountState *accountState);

private slots:
    void slotFolderSyncPaused(Folder *, bool paused);
    void slotFolderCanSyncChanged();
    void slotFolderSyncStarted();
    void slotFolderSyncFinished(const SyncResult &);

    void slotRunOneEtagJob();
    void slotEtagJobDestroyed(QObject *);

    // slot to take the next folder from queue and start syncing.
    void slotStartScheduledFolderSync();
    void slotEtagPollTimerTimeout();

    void slotAccountRemoved(AccountState *accountState);

    void slotRemoveFoldersForAccount(AccountState *accountState);

    // Wraps the Folder::syncStateChange() signal into the
    // FolderMan::folderSyncStateChange(Folder*) signal.
    void slotForwardFolderSyncStateChange();

    void slotServerVersionChanged(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 Folder::Map &);
    void slotProcessFilesPushNotification(Account *account);
    void slotConnectToPushNotifications(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> 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<Folder *> &folderMap);
    void runEtagJobIfPossible(Folder *folder);

    bool pushNotificationsFilesReady(Account *account);

    [[nodiscard]] bool isSwitchToVfsNeeded(const FolderDefinition &folderDefinition) const;

    QSet<Folder *> _disabledFolders;
    Folder::Map _folderMap;
    QString _folderConfigPath;
    Folder *_currentSyncFolder = nullptr;
    QPointer<Folder> _lastSyncFolder;
    bool _syncEnabled = true;

    /// Folder aliases from the settings that weren't read
    QSet<QString> _additionalBlockedFolderAliases;

    /// Starts regular etag query jobs
    QTimer _etagPollTimer;
    /// The currently running etag query
    QPointer<RequestEtagJob> _currentEtagJob;

    /// Watches files that couldn't be synced due to locks
    QScopedPointer<LockWatcher> _lockWatcher;

    /// Occasionally schedules folders
    QTimer _timeScheduler;

    /// Scheduled folders that should be synced as soon as possible
    QQueue<Folder *> _scheduledFolders;

    /// Picks the next scheduled folder and starts the sync
    QTimer _startScheduledSyncTimer;

    bool _nextSyncShouldStartImmediately = false;

    QScopedPointer<SocketApi> _socketApi;
    NavigationPaneHelper _navigationPaneHelper;

    bool _appRestartRequired = false;

    QMap<QString, QMetaObject::Connection> _localFileEditingSyncFinishedConnections;

    static FolderMan *_instance;
    explicit FolderMan(QObject *parent = nullptr);
    friend class OCC::Application;
    friend class ::TestFolderMan;
    friend class ::TestCfApiShellExtensionsIPC;
};

} // namespace OCC
#endif // FOLDERMAN_H