1 | /* |
2 | * Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org> |
3 | * Copyright (C) by Daniel Molkentin <danimo@owncloud.com> |
4 | * Copyright (C) by Klaas Freitag <freitag@owncloud.com> |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * for more details. |
15 | */ |
16 | |
17 | #ifndef MIRALL_FOLDER_H |
18 | #define MIRALL_FOLDER_H |
19 | |
20 | #include "syncresult.h" |
21 | #include "progressdispatcher.h" |
22 | #include "common/syncjournaldb.h" |
23 | #include "networkjobs.h" |
24 | |
25 | #include <csync.h> |
26 | |
27 | #include <QObject> |
28 | #include <QStringList> |
29 | #include <QUuid> |
30 | #include <set> |
31 | #include <chrono> |
32 | |
33 | class QThread; |
34 | class QSettings; |
35 | |
36 | namespace OCC { |
37 | |
38 | class SyncEngine; |
39 | class AccountState; |
40 | class SyncRunFileLog; |
41 | class FolderWatcher; |
42 | class LocalDiscoveryTracker; |
43 | |
44 | /** |
45 | * @brief The FolderDefinition class |
46 | * @ingroup gui |
47 | */ |
48 | class FolderDefinition |
49 | { |
50 | public: |
51 | FolderDefinition() |
52 | : paused(false) |
53 | , ignoreHiddenFiles(true) |
54 | { |
55 | } |
56 | |
57 | /// The name of the folder in the ui and internally |
58 | QString alias; |
59 | /// path on local machine |
60 | QString localPath; |
61 | /// path to the journal, usually relative to localPath |
62 | QString journalPath; |
63 | /// path on remote |
64 | QString targetPath; |
65 | /// whether the folder is paused |
66 | bool paused; |
67 | /// whether the folder syncs hidden files |
68 | bool ignoreHiddenFiles; |
69 | /// New files are downloaded as virtual files |
70 | bool useVirtualFiles = false; |
71 | /// The CLSID where this folder appears in registry for the Explorer navigation pane entry. |
72 | QUuid navigationPaneClsid; |
73 | |
74 | /// Saves the folder definition, creating a new settings group. |
75 | static void save(QSettings &settings, const FolderDefinition &folder); |
76 | |
77 | /// Reads a folder definition from a settings group with the name 'alias'. |
78 | static bool load(QSettings &settings, const QString &alias, |
79 | FolderDefinition *folder); |
80 | |
81 | /// The highest version in the settings that load() can read |
82 | static int maxSettingsVersion() { return 1; } |
83 | |
84 | /// Ensure / as separator and trailing /. |
85 | static QString prepareLocalPath(const QString &path); |
86 | |
87 | /// Ensure starting / and no ending /. |
88 | static QString prepareTargetPath(const QString &path); |
89 | |
90 | /// journalPath relative to localPath. |
91 | QString absoluteJournalPath() const; |
92 | |
93 | /// Returns the relative journal path that's appropriate for this folder and account. |
94 | QString defaultJournalPath(AccountPtr account); |
95 | }; |
96 | |
97 | /** |
98 | * @brief The Folder class |
99 | * @ingroup gui |
100 | */ |
101 | class Folder : public QObject |
102 | { |
103 | Q_OBJECT |
104 | |
105 | public: |
106 | Folder(const FolderDefinition &definition, AccountState *accountState, QObject *parent = 0L); |
107 | |
108 | ~Folder(); |
109 | |
110 | typedef QMap<QString, Folder *> Map; |
111 | typedef QMapIterator<QString, Folder *> MapIterator; |
112 | |
113 | /** |
114 | * The account the folder is configured on. |
115 | */ |
116 | AccountState *accountState() const { return _accountState.data(); } |
117 | |
118 | /** |
119 | * alias or nickname |
120 | */ |
121 | QString alias() const; |
122 | QString shortGuiRemotePathOrAppName() const; // since 2.0 we don't want to show aliases anymore, show the path instead |
123 | |
124 | /** |
125 | * short local path to display on the GUI (native separators) |
126 | */ |
127 | QString shortGuiLocalPath() const; |
128 | |
129 | /** |
130 | * canonical local folder path, always ends with / |
131 | */ |
132 | QString path() const; |
133 | |
134 | /** |
135 | * cleaned canonical folder path, like path() but never ends with a / |
136 | * |
137 | * Wrapper for QDir::cleanPath(path()) except for "Z:/", |
138 | * where it returns "Z:" instead of "Z:/". |
139 | */ |
140 | QString cleanPath() const; |
141 | |
142 | /** |
143 | * remote folder path |
144 | */ |
145 | QString remotePath() const; |
146 | |
147 | void setNavigationPaneClsid(const QUuid &clsid) { _definition.navigationPaneClsid = clsid; } |
148 | QUuid navigationPaneClsid() const { return _definition.navigationPaneClsid; } |
149 | |
150 | /** |
151 | * remote folder path with server url |
152 | */ |
153 | QUrl remoteUrl() const; |
154 | |
155 | /** |
156 | * switch sync on or off |
157 | */ |
158 | void setSyncPaused(bool); |
159 | |
160 | bool syncPaused() const; |
161 | |
162 | /** |
163 | * Returns true when the folder may sync. |
164 | */ |
165 | bool canSync() const; |
166 | |
167 | void prepareToSync(); |
168 | |
169 | /** |
170 | * True if the folder is busy and can't initiate |
171 | * a synchronization |
172 | */ |
173 | virtual bool isBusy() const; |
174 | |
175 | /** |
176 | * return the last sync result with error message and status |
177 | */ |
178 | SyncResult syncResult() const; |
179 | |
180 | /** |
181 | * This is called if the sync folder definition is removed. Do cleanups here. |
182 | */ |
183 | virtual void wipe(); |
184 | |
185 | void setSyncState(SyncResult::Status state); |
186 | |
187 | void setDirtyNetworkLimits(); |
188 | |
189 | /** |
190 | * Ignore syncing of hidden files or not. This is defined in the |
191 | * folder definition |
192 | */ |
193 | bool ignoreHiddenFiles(); |
194 | void setIgnoreHiddenFiles(bool ignore); |
195 | |
196 | // Used by the Socket API |
197 | SyncJournalDb *journalDb() { return &_journal; } |
198 | SyncEngine &syncEngine() { return *_engine; } |
199 | |
200 | RequestEtagJob *etagJob() { return _requestEtagJob; } |
201 | std::chrono::milliseconds msecSinceLastSync() const { return std::chrono::milliseconds(_timeSinceLastSyncDone.elapsed()); } |
202 | std::chrono::milliseconds msecLastSyncDuration() const { return _lastSyncDuration; } |
203 | int consecutiveFollowUpSyncs() const { return _consecutiveFollowUpSyncs; } |
204 | int consecutiveFailingSyncs() const { return _consecutiveFailingSyncs; } |
205 | |
206 | /// Saves the folder data in the account's settings. |
207 | void saveToSettings() const; |
208 | /// Removes the folder from the account's settings. |
209 | void removeFromSettings() const; |
210 | |
211 | /** |
212 | * Returns whether a file inside this folder should be excluded. |
213 | */ |
214 | bool isFileExcludedAbsolute(const QString &fullPath) const; |
215 | |
216 | /** |
217 | * Returns whether a file inside this folder should be excluded. |
218 | */ |
219 | bool isFileExcludedRelative(const QString &relativePath) const; |
220 | |
221 | /** Calls schedules this folder on the FolderMan after a short delay. |
222 | * |
223 | * This should be used in situations where a sync should be triggered |
224 | * because a local file was modified. Syncs don't upload files that were |
225 | * modified too recently, and this delay ensures the modification is |
226 | * far enough in the past. |
227 | * |
228 | * The delay doesn't reset with subsequent calls. |
229 | */ |
230 | void scheduleThisFolderSoon(); |
231 | |
232 | /** |
233 | * Migration: When this flag is true, this folder will save to |
234 | * the backwards-compatible 'Folders' section in the config file. |
235 | */ |
236 | void setSaveBackwardsCompatible(bool save); |
237 | |
238 | /** |
239 | * Sets up this folder's folderWatcher if possible. |
240 | * |
241 | * May be called several times. |
242 | */ |
243 | void registerFolderWatcher(); |
244 | |
245 | /** new files are downloaded as virtual files */ |
246 | bool useVirtualFiles() { return _definition.useVirtualFiles; } |
247 | |
248 | signals: |
249 | void syncStateChange(); |
250 | void syncStarted(); |
251 | void syncFinished(const SyncResult &result); |
252 | void progressInfo(const ProgressInfo &progress); |
253 | void newBigFolderDiscovered(const QString &); // A new folder bigger than the threshold was discovered |
254 | void syncPausedChanged(Folder *, bool paused); |
255 | void canSyncChanged(); |
256 | |
257 | /** |
258 | * Fires for each change inside this folder that wasn't caused |
259 | * by sync activity. |
260 | */ |
261 | void watchedFileChangedExternally(const QString &path); |
262 | |
263 | public slots: |
264 | |
265 | /** |
266 | * terminate the current sync run |
267 | */ |
268 | void slotTerminateSync(); |
269 | |
270 | // connected to the corresponding signals in the SyncEngine |
271 | void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *); |
272 | void slotAboutToRestoreBackup(bool *); |
273 | |
274 | |
275 | /** |
276 | * Starts a sync operation |
277 | * |
278 | * If the list of changed files is known, it is passed. |
279 | */ |
280 | void startSync(const QStringList &pathList = QStringList()); |
281 | |
282 | int slotDiscardDownloadProgress(); |
283 | int downloadInfoCount(); |
284 | int slotWipeErrorBlacklist(); |
285 | int errorBlackListEntryCount(); |
286 | |
287 | /** |
288 | * Triggered by the folder watcher when a file/dir in this folder |
289 | * changes. Needs to check whether this change should trigger a new |
290 | * sync run to be scheduled. |
291 | */ |
292 | void slotWatchedPathChanged(const QString &path); |
293 | |
294 | /** |
295 | * Mark a virtual file as being ready for download, and start a sync. |
296 | * relativePath is the patch to the file (including the extension) |
297 | */ |
298 | void downloadVirtualFile(const QString &relativepath); |
299 | |
300 | private slots: |
301 | void slotSyncStarted(); |
302 | void slotSyncFinished(bool); |
303 | |
304 | /** Adds a error message that's not tied to a specific item. |
305 | */ |
306 | void slotSyncError(const QString &message, ErrorCategory category = ErrorCategory::Normal); |
307 | |
308 | void slotCsyncUnavailable(); |
309 | |
310 | void slotTransmissionProgress(const ProgressInfo &pi); |
311 | void slotItemCompleted(const SyncFileItemPtr &); |
312 | |
313 | void slotRunEtagJob(); |
314 | void etagRetreived(const QString &); |
315 | void etagRetreivedFromSyncEngine(const QString &); |
316 | |
317 | void slotEmitFinishedDelayed(); |
318 | |
319 | void slotNewBigFolderDiscovered(const QString &, bool isExternal); |
320 | |
321 | void slotLogPropagationStart(); |
322 | |
323 | /** Adds this folder to the list of scheduled folders in the |
324 | * FolderMan. |
325 | */ |
326 | void slotScheduleThisFolder(); |
327 | |
328 | /** Ensures that the next sync performs a full local discovery. */ |
329 | void slotNextSyncFullLocalDiscovery(); |
330 | |
331 | /** Adjust sync result based on conflict data from IssuesWidget. |
332 | * |
333 | * This is pretty awkward, but IssuesWidget just keeps better track |
334 | * of conflicts across partial local discovery. |
335 | */ |
336 | void slotFolderConflicts(const QString &folder, const QStringList &conflictPaths); |
337 | |
338 | /** Warn users if they create a file or folder that is selective-sync excluded */ |
339 | void warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QStringRef &path); |
340 | |
341 | /** Warn users about an unreliable folder watcher */ |
342 | void slotWatcherUnreliable(const QString &message); |
343 | |
344 | private: |
345 | bool reloadExcludes(); |
346 | |
347 | void (); |
348 | |
349 | void checkLocalPath(); |
350 | |
351 | void setSyncOptions(); |
352 | |
353 | enum LogStatus { |
354 | LogStatusRemove, |
355 | LogStatusRename, |
356 | LogStatusMove, |
357 | LogStatusNew, |
358 | LogStatusError, |
359 | LogStatusConflict, |
360 | LogStatusUpdated |
361 | }; |
362 | |
363 | void createGuiLog(const QString &filename, LogStatus status, int count, |
364 | const QString &renameTarget = QString()); |
365 | |
366 | AccountStatePtr _accountState; |
367 | FolderDefinition _definition; |
368 | QString _canonicalLocalPath; // As returned with QFileInfo:canonicalFilePath. Always ends with "/" |
369 | |
370 | SyncResult _syncResult; |
371 | QScopedPointer<SyncEngine> _engine; |
372 | bool _csyncUnavail; |
373 | QPointer<RequestEtagJob> _requestEtagJob; |
374 | QString _lastEtag; |
375 | QElapsedTimer _timeSinceLastSyncDone; |
376 | QElapsedTimer _timeSinceLastSyncStart; |
377 | QElapsedTimer _timeSinceLastFullLocalDiscovery; |
378 | std::chrono::milliseconds _lastSyncDuration; |
379 | |
380 | /// The number of syncs that failed in a row. |
381 | /// Reset when a sync is successful. |
382 | int _consecutiveFailingSyncs; |
383 | |
384 | /// The number of requested follow-up syncs. |
385 | /// Reset when no follow-up is requested. |
386 | int _consecutiveFollowUpSyncs; |
387 | |
388 | SyncJournalDb _journal; |
389 | |
390 | QScopedPointer<SyncRunFileLog> _fileLog; |
391 | |
392 | QTimer _scheduleSelfTimer; |
393 | |
394 | /** |
395 | * When the same local path is synced to multiple accounts, only one |
396 | * of them can be stored in the settings in a way that's compatible |
397 | * with old clients that don't support it. This flag marks folders |
398 | * that shall be written in a backwards-compatible way, by being set |
399 | * on the *first* Folder instance that was configured for each local |
400 | * path. |
401 | */ |
402 | bool _saveBackwardsCompatible; |
403 | |
404 | /** |
405 | * Watches this folder's local directory for changes. |
406 | * |
407 | * Created by registerFolderWatcher(), triggers slotWatchedPathChanged() |
408 | */ |
409 | QScopedPointer<FolderWatcher> _folderWatcher; |
410 | |
411 | /** |
412 | * Keeps track of locally dirty files so we can skip local discovery sometimes. |
413 | */ |
414 | QScopedPointer<LocalDiscoveryTracker> _localDiscoveryTracker; |
415 | }; |
416 | } |
417 | |
418 | #endif |
419 | |