1/*
2 * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15
16#ifndef FOLDERMAN_H
17#define FOLDERMAN_H
18
19#include <QObject>
20#include <QQueue>
21#include <QList>
22
23#include "folder.h"
24#include "folderwatcher.h"
25#include "navigationpanehelper.h"
26#include "syncfileitem.h"
27
28class TestFolderMan;
29
30namespace OCC {
31
32class Application;
33class SyncResult;
34class SocketApi;
35class LockWatcher;
36
37/**
38 * @brief The FolderMan class
39 * @ingroup gui
40 *
41 * The FolderMan knows about all loaded folders and is responsible for
42 * scheduling them when necessary.
43 *
44 * A folder is scheduled if:
45 * - The configured force-sync-interval has expired
46 * (_timeScheduler and slotScheduleFolderByTime())
47 *
48 * - A folder watcher receives a notification about a file change
49 * (_folderWatchers and Folder::slotWatchedPathChanged())
50 *
51 * - The folder etag on the server has changed
52 * (_etagPollTimer)
53 *
54 * - The locks of a monitored file are released
55 * (_lockWatcher and slotWatchedFileUnlocked())
56 *
57 * - There was a sync error or a follow-up sync is requested
58 * (_timeScheduler and slotScheduleFolderByTime()
59 * and Folder::slotSyncFinished())
60 */
61class FolderMan : public QObject
62{
63 Q_OBJECT
64public:
65 ~FolderMan();
66 static FolderMan *instance();
67
68 int setupFolders();
69 int setupFoldersMigration();
70
71 /**
72 * Returns a list of keys that can't be read because they are from
73 * future versions.
74 */
75 static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys);
76
77 OCC::Folder::Map map();
78
79 /** Adds a folder for an account, ensures the journal is gone and saves it in the settings.
80 */
81 Folder *addFolder(AccountState *accountState, const FolderDefinition &folderDefinition);
82
83 /** Removes a folder */
84 void removeFolder(Folder *);
85
86 /**
87 * Returns the folder which the file or directory stored in path is in
88 *
89 * Optionally, the path relative to the found folder is returned in
90 * relativePath.
91 */
92 Folder *folderForPath(const QString &path, QString *relativePath = nullptr);
93
94 /**
95 * returns a list of local files that exist on the local harddisk for an
96 * incoming relative server path. The method checks with all existing sync
97 * folders.
98 */
99 QStringList findFileInLocalFolders(const QString &relPath, const AccountPtr acc);
100
101 /** Returns the folder by alias or NULL if no folder with the alias exists. */
102 Folder *folder(const QString &);
103
104 /**
105 * Migrate accounts from owncloud < 2.0
106 * Creates a folder for a specific configuration, identified by alias.
107 */
108 Folder *setupFolderFromOldConfigFile(const QString &, AccountState *account);
109
110 /**
111 * Ensures that a given directory does not contain a sync journal file.
112 *
113 * @returns false if the journal could not be removed, true otherwise.
114 */
115 static bool ensureJournalGone(const QString &journalDbFile);
116
117 /** Creates a new and empty local directory. */
118 bool startFromScratch(const QString &);
119
120 /// Produce text for use in the tray tooltip
121 static QString trayTooltipStatusString(SyncResult::Status syncStatus, bool hasUnresolvedConflicts, bool paused);
122
123 /// Compute status summarizing multiple folders
124 static void trayOverallStatus(const QList<Folder *> &folders,
125 SyncResult::Status *status, bool *unresolvedConflicts);
126
127 // Escaping of the alias which is used in QSettings AND the file
128 // system, thus need to be escaped.
129 static QString escapeAlias(const QString &);
130 static QString unescapeAlias(const QString &);
131
132 SocketApi *socketApi();
133 NavigationPaneHelper &navigationPaneHelper() { return _navigationPaneHelper; }
134
135 /**
136 * Check if @a path is a valid path for a new folder considering the already sync'ed items.
137 * Make sure that this folder, or any subfolder is not sync'ed already.
138 *
139 * Note that different accounts are allowed to sync to the same folder.
140 *
141 * @returns an empty string if it is allowed, or an error if it is not allowed
142 */
143 QString checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl()) const;
144
145 /**
146 * Attempts to find a non-existing, acceptable path for creating a new sync folder.
147 *
148 * Uses \a basePath as the baseline. It'll return this path if it's acceptable.
149 *
150 * Note that this can fail. If someone syncs ~ and \a basePath is ~/ownCloud, no
151 * subfolder of ~ would be a good candidate. When that happens \a basePath
152 * is returned.
153 */
154 QString findGoodPathForNewSyncFolder(const QString &basePath, const QUrl &serverUrl) const;
155
156 /**
157 * While ignoring hidden files can theoretically be switched per folder,
158 * it's currently a global setting that users can only change for all folders
159 * at once.
160 * These helper functions can be removed once it's properly per-folder.
161 */
162 bool ignoreHiddenFiles() const;
163 void setIgnoreHiddenFiles(bool ignore);
164
165 /**
166 * Access to the current queue of scheduled folders.
167 */
168 QQueue<Folder *> scheduleQueue() const;
169
170 /**
171 * Access to the currently syncing folder.
172 */
173 Folder *currentSyncFolder() const;
174
175 /** Removes all folders */
176 int unloadAndDeleteAllFolders();
177
178 /**
179 * If enabled is set to false, no new folders will start to sync.
180 * The current one will finish.
181 */
182 void setSyncEnabled(bool);
183
184 /** Queues a folder for syncing. */
185 void scheduleFolder(Folder *);
186
187 /** Puts a folder in the very front of the queue. */
188 void scheduleFolderNext(Folder *);
189
190 /** Queues all folders for syncing. */
191 void scheduleAllFolders();
192
193 void setDirtyProxy();
194 void setDirtyNetworkLimits();
195
196 /**
197 * Terminates the current folder sync.
198 *
199 * It does not switch the folder to paused state.
200 */
201 void terminateSyncProcess();
202
203signals:
204 /**
205 * signal to indicate a folder has changed its sync state.
206 *
207 * Attention: The folder may be zero. Do a general update of the state then.
208 */
209 void folderSyncStateChange(Folder *);
210
211 /**
212 * Indicates when the schedule queue changes.
213 */
214 void scheduleQueueChanged();
215
216 /**
217 * Emitted whenever the list of configured folders changes.
218 */
219 void folderListChanged(const Folder::Map &);
220
221public slots:
222
223 /**
224 * Schedules folders of newly connected accounts, terminates and
225 * de-schedules folders of disconnected accounts.
226 */
227 void slotAccountStateChanged();
228
229 /**
230 * restart the client as soon as it is possible, ie. no folders syncing.
231 */
232 void slotScheduleAppRestart();
233
234 /**
235 * Triggers a sync run once the lock on the given file is removed.
236 *
237 * Automatically detemines the folder that's responsible for the file.
238 * See slotWatchedFileUnlocked().
239 */
240 void slotSyncOnceFileUnlocks(const QString &path);
241
242 // slot to schedule an ETag job (from Folder only)
243 void slotScheduleETagJob(const QString &alias, RequestEtagJob *job);
244
245private slots:
246 void slotFolderSyncPaused(Folder *, bool paused);
247 void slotFolderCanSyncChanged();
248 void slotFolderSyncStarted();
249 void slotFolderSyncFinished(const SyncResult &);
250
251 void slotRunOneEtagJob();
252 void slotEtagJobDestroyed(QObject *);
253
254 // slot to take the next folder from queue and start syncing.
255 void slotStartScheduledFolderSync();
256 void slotEtagPollTimerTimeout();
257
258 void slotRemoveFoldersForAccount(AccountState *accountState);
259
260 // Wraps the Folder::syncStateChange() signal into the
261 // FolderMan::folderSyncStateChange(Folder*) signal.
262 void slotForwardFolderSyncStateChange();
263
264 void slotServerVersionChanged(Account *account);
265
266 /**
267 * A file whose locks were being monitored has become unlocked.
268 *
269 * This schedules the folder for synchronization that contains
270 * the file with the given path.
271 */
272 void slotWatchedFileUnlocked(const QString &path);
273
274 /**
275 * Schedules folders whose time to sync has come.
276 *
277 * Either because a long time has passed since the last sync or
278 * because of previous failures.
279 */
280 void slotScheduleFolderByTime();
281
282private:
283 /** Adds a new folder, does not add it to the account settings and
284 * does not set an account on the new folder.
285 */
286 Folder *addFolderInternal(FolderDefinition folderDefinition,
287 AccountState *accountState);
288
289 /* unloads a folder object, does not delete it */
290 void unloadFolder(Folder *);
291
292 /** Will start a sync after a bit of delay. */
293 void startScheduledSyncSoon();
294
295 // finds all folder configuration files
296 // and create the folders
297 QString getBackupName(QString fullPathName) const;
298
299 // makes the folder known to the socket api
300 void registerFolderWithSocketApi(Folder *folder);
301
302 // restarts the application (Linux only)
303 void restartApplication();
304
305 void setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible, const QStringList &ignoreKeys);
306
307 QSet<Folder *> _disabledFolders;
308 Folder::Map _folderMap;
309 QString _folderConfigPath;
310 Folder *_currentSyncFolder;
311 QPointer<Folder> _lastSyncFolder;
312 bool _syncEnabled;
313
314 /// Folder aliases from the settings that weren't read
315 QSet<QString> _additionalBlockedFolderAliases;
316
317 /// Starts regular etag query jobs
318 QTimer _etagPollTimer;
319 /// The currently running etag query
320 QPointer<RequestEtagJob> _currentEtagJob;
321
322 /// Watches files that couldn't be synced due to locks
323 QScopedPointer<LockWatcher> _lockWatcher;
324
325 /// Occasionally schedules folders
326 QTimer _timeScheduler;
327
328 /// Scheduled folders that should be synced as soon as possible
329 QQueue<Folder *> _scheduledFolders;
330
331 /// Picks the next scheduled folder and starts the sync
332 QTimer _startScheduledSyncTimer;
333
334 QScopedPointer<SocketApi> _socketApi;
335 NavigationPaneHelper _navigationPaneHelper;
336
337 bool _appRestartRequired;
338
339 static FolderMan *_instance;
340 explicit FolderMan(QObject *parent = 0);
341 friend class OCC::Application;
342 friend class ::TestFolderMan;
343};
344
345} // namespace OCC
346#endif // FOLDERMAN_H
347