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
33class QThread;
34class QSettings;
35
36namespace OCC {
37
38class SyncEngine;
39class AccountState;
40class SyncRunFileLog;
41class FolderWatcher;
42class LocalDiscoveryTracker;
43
44/**
45 * @brief The FolderDefinition class
46 * @ingroup gui
47 */
48class FolderDefinition
49{
50public:
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 */
101class Folder : public QObject
102{
103 Q_OBJECT
104
105public:
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
248signals:
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
263public 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
300private 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
344private:
345 bool reloadExcludes();
346
347 void showSyncResultPopup();
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