1 /* 2 * Copyright (C) by Christian Kamm <mail@ckamm.de> 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 #pragma once 15 16 #include <QObject> 17 #include <QScopedPointer> 18 #include <QSharedPointer> 19 20 #include <memory> 21 22 #include "assert.h" 23 #include "ocsynclib.h" 24 #include "result.h" 25 #include "syncfilestatus.h" 26 #include "pinstate.h" 27 28 typedef struct csync_file_stat_s csync_file_stat_t; 29 30 namespace OCC { 31 32 class Account; 33 typedef QSharedPointer<Account> AccountPtr; 34 class SyncJournalDb; 35 class VfsPrivate; 36 class SyncFileItem; 37 38 /** Collection of parameters for initializing a Vfs instance. */ 39 struct OCSYNC_EXPORT VfsSetupParams 40 { 41 /** The full path to the folder on the local filesystem 42 * 43 * Always ends with /. 44 */ 45 QString filesystemPath; 46 47 /** The path to the synced folder on the account 48 * 49 * Always ends with /. 50 */ 51 QString remotePath; 52 53 /// Account url, credentials etc for network calls 54 AccountPtr account; 55 56 /** Access to the sync folder's database. 57 * 58 * Note: The journal must live at least until the Vfs::stop() call. 59 */ 60 SyncJournalDb *journal = nullptr; 61 62 /// Strings potentially passed on to the platform 63 QString providerName; 64 QString providerVersion; 65 66 /** when registering with the system we might use 67 * a different presentaton to identify the accounts 68 */ 69 bool multipleAccountsRegistered = false; 70 }; 71 72 /** Interface describing how to deal with virtual/placeholder files. 73 * 74 * There are different ways of representing files locally that will only 75 * be filled with data (hydrated) on demand. One such way would be suffixed 76 * files, others could be FUSE based or use Windows CfAPI. 77 * 78 * This interface intends to decouple the sync algorithm and Folder from 79 * the details of how a particular VFS solution works. 80 * 81 * An instance is usually created through a plugin via the createVfsFromPlugin() 82 * function. 83 */ 84 class OCSYNC_EXPORT Vfs : public QObject 85 { 86 Q_OBJECT 87 88 public: 89 /** The kind of VFS in use (or no-VFS) 90 * 91 * Currently plugins and modes are one-to-one but that's not required. 92 */ 93 enum Mode 94 { 95 Off, 96 WithSuffix, 97 WindowsCfApi, 98 }; 99 Q_ENUM(Mode) 100 static QString modeToString(Mode mode); 101 static Optional<Mode> modeFromString(const QString &str); 102 103 static Result<bool, QString> checkAvailability(const QString &path); 104 105 enum class AvailabilityError 106 { 107 // Availability can't be retrieved due to db error 108 DbError, 109 // Availability not available since the item doesn't exist 110 NoSuchItem, 111 }; 112 using AvailabilityResult = Result<VfsItemAvailability, AvailabilityError>; 113 114 public: 115 explicit Vfs(QObject* parent = nullptr); 116 ~Vfs() override; 117 118 virtual Mode mode() const = 0; 119 120 /// For WithSuffix modes: the suffix (including the dot) 121 virtual QString fileSuffix() const = 0; 122 123 /// Access to the parameters the instance was start()ed with. params()124 const VfsSetupParams ¶ms() const { return _setupParams; } 125 126 127 /** Initializes interaction with the VFS provider. 128 * 129 * The plugin-specific work is done in startImpl(). 130 */ 131 void start(const VfsSetupParams ¶ms); 132 133 /// Stop interaction with VFS provider. Like when the client application quits. 134 virtual void stop() = 0; 135 136 /// Deregister the folder with the sync provider, like when a folder is removed. 137 virtual void unregisterFolder() = 0; 138 139 140 /** Whether the socket api should show pin state options 141 * 142 * Some plugins might provide alternate shell integration, making the normal 143 * context menu actions redundant. 144 */ 145 virtual bool socketApiPinStateActionsShown() const = 0; 146 147 /** Return true when download of a file's data is currently ongoing. 148 * 149 * See also the beginHydrating() and doneHydrating() signals. 150 */ 151 virtual bool isHydrating() const = 0; 152 153 /** Update placeholder metadata during discovery. 154 * 155 * If the remote metadata changes, the local placeholder's metadata should possibly 156 * change as well. 157 */ 158 virtual OC_REQUIRED_RESULT Result<void, QString> updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) = 0; 159 160 /// Create a new dehydrated placeholder. Called from PropagateDownload. 161 virtual OC_REQUIRED_RESULT Result<void, QString> createPlaceholder(const SyncFileItem &item) = 0; 162 163 /** Convert a hydrated placeholder to a dehydrated one. Called from PropagateDownlaod. 164 * 165 * This is different from delete+create because preserving some file metadata 166 * (like pin states) may be essential for some vfs plugins. 167 */ 168 virtual OC_REQUIRED_RESULT Result<void, QString> dehydratePlaceholder(const SyncFileItem &item) = 0; 169 170 /** Discovery hook: even unchanged files may need UPDATE_METADATA. 171 * 172 * For instance cfapi vfs wants local hydrated non-placeholder files to 173 * become hydrated placeholder files. 174 */ 175 virtual OC_REQUIRED_RESULT bool needsMetadataUpdate(const SyncFileItem &item) = 0; 176 177 /** Convert a new file to a hydrated placeholder. 178 * 179 * Some VFS integrations expect that every file, including those that have all 180 * the remote data, are "placeholders". This function is called by PropagateDownload 181 * to convert newly downloaded, fully hydrated files into placeholders. 182 * 183 * Implementations must make sure that calling this function on a file that already 184 * is a placeholder is acceptable. 185 * 186 * replacesFile can optionally contain a filesystem path to a placeholder that this 187 * new placeholder shall supersede, for rename-replace actions with new downloads, 188 * for example. 189 */ 190 virtual OC_REQUIRED_RESULT Result<void, QString> convertToPlaceholder( 191 const QString &filename, 192 const SyncFileItem &item, 193 const QString &replacesFile = QString()) = 0; 194 195 /// Determine whether the file at the given absolute path is a dehydrated placeholder. 196 virtual OC_REQUIRED_RESULT bool isDehydratedPlaceholder(const QString &filePath) = 0; 197 198 /** Similar to isDehydratedPlaceholder() but used from sync discovery. 199 * 200 * This function shall set stat->type if appropriate. 201 * It may rely on stat->path and stat_data (platform specific data). 202 * 203 * Returning true means that type was fully determined. 204 */ 205 virtual OC_REQUIRED_RESULT bool statTypeVirtualFile(csync_file_stat_t *stat, void *stat_data) = 0; 206 207 /** Sets the pin state for the item at a path. 208 * 209 * The pin state is set on the item and for all items below it. 210 * 211 * Usually this would forward to setting the pin state flag in the db table, 212 * but some vfs plugins will store the pin state in file attributes instead. 213 * 214 * folderPath is relative to the sync folder. Can be "" for root folder. 215 */ 216 virtual OC_REQUIRED_RESULT bool setPinState(const QString &folderPath, PinState state) = 0; 217 218 /** Returns the pin state of an item at a path. 219 * 220 * Usually backed by the db's effectivePinState() function but some vfs 221 * plugins will override it to retrieve the state from elsewhere. 222 * 223 * folderPath is relative to the sync folder. Can be "" for root folder. 224 * 225 * Returns none on retrieval error. 226 */ 227 virtual OC_REQUIRED_RESULT Optional<PinState> pinState(const QString &folderPath) = 0; 228 229 /** Returns availability status of an item at a path. 230 * 231 * The availability is a condensed user-facing version of PinState. See 232 * VfsItemAvailability for details. 233 * 234 * folderPath is relative to the sync folder. Can be "" for root folder. 235 */ 236 virtual OC_REQUIRED_RESULT AvailabilityResult availability(const QString &folderPath) = 0; 237 238 public slots: 239 /** Update in-sync state based on SyncFileStatusTracker signal. 240 * 241 * For some vfs plugins the icons aren't based on SocketAPI but rather on data shared 242 * via the vfs plugin. The connection to SyncFileStatusTracker allows both to be based 243 * on the same data. 244 */ 245 virtual void fileStatusChanged(const QString &systemFileName, SyncFileStatus fileStatus) = 0; 246 247 signals: 248 /// Emitted when a user-initiated hydration starts 249 void beginHydrating(); 250 /// Emitted when the hydration ends 251 void doneHydrating(); 252 253 protected: 254 /** Setup the plugin for the folder. 255 * 256 * For example, the VFS provider might monitor files to be able to start a file 257 * hydration (download of a file's remote contents) when the user wants to open 258 * it. 259 * 260 * Usually some registration needs to be done with the backend. This function 261 * should take care of it if necessary. 262 */ 263 virtual void startImpl(const VfsSetupParams ¶ms) = 0; 264 265 // Db-backed pin state handling. Derived classes may use it to implement pin states. 266 bool setPinStateInDb(const QString &folderPath, PinState state); 267 Optional<PinState> pinStateInDb(const QString &folderPath); 268 AvailabilityResult availabilityInDb(const QString &folderPath); 269 270 // the parameters passed to start() 271 VfsSetupParams _setupParams; 272 }; 273 274 /// Implementation of Vfs for Vfs::Off mode - does nothing 275 class OCSYNC_EXPORT VfsOff : public Vfs 276 { 277 Q_OBJECT 278 279 public: 280 VfsOff(QObject* parent = nullptr); 281 ~VfsOff() override; 282 mode()283 Mode mode() const override { return Vfs::Off; } 284 fileSuffix()285 QString fileSuffix() const override { return QString(); } 286 stop()287 void stop() override {} unregisterFolder()288 void unregisterFolder() override {} 289 socketApiPinStateActionsShown()290 bool socketApiPinStateActionsShown() const override { return false; } isHydrating()291 bool isHydrating() const override { return false; } 292 updateMetadata(const QString &,time_t,qint64,const QByteArray &)293 Result<void, QString> updateMetadata(const QString &, time_t, qint64, const QByteArray &) override { return {}; } createPlaceholder(const SyncFileItem &)294 Result<void, QString> createPlaceholder(const SyncFileItem &) override { return {}; } dehydratePlaceholder(const SyncFileItem &)295 Result<void, QString> dehydratePlaceholder(const SyncFileItem &) override { return {}; } convertToPlaceholder(const QString &,const SyncFileItem &,const QString &)296 Result<void, QString> convertToPlaceholder(const QString &, const SyncFileItem &, const QString &) override { return {}; } 297 needsMetadataUpdate(const SyncFileItem &)298 bool needsMetadataUpdate(const SyncFileItem &) override { return false; } isDehydratedPlaceholder(const QString &)299 bool isDehydratedPlaceholder(const QString &) override { return false; } statTypeVirtualFile(csync_file_stat_t *,void *)300 bool statTypeVirtualFile(csync_file_stat_t *, void *) override { return false; } 301 setPinState(const QString &,PinState)302 bool setPinState(const QString &, PinState) override { return true; } pinState(const QString &)303 Optional<PinState> pinState(const QString &) override { return PinState::AlwaysLocal; } availability(const QString &)304 AvailabilityResult availability(const QString &) override { return VfsItemAvailability::AlwaysLocal; } 305 306 public slots: fileStatusChanged(const QString &,SyncFileStatus)307 void fileStatusChanged(const QString &, SyncFileStatus) override {} 308 309 protected: startImpl(const VfsSetupParams &)310 void startImpl(const VfsSetupParams &) override {} 311 }; 312 313 /// Check whether the plugin for the mode is available. 314 OCSYNC_EXPORT bool isVfsPluginAvailable(Vfs::Mode mode); 315 316 /// Return the best available VFS mode. 317 OCSYNC_EXPORT Vfs::Mode bestAvailableVfsMode(); 318 319 /// Create a VFS instance for the mode, returns nullptr on failure. 320 OCSYNC_EXPORT std::unique_ptr<Vfs> createVfsFromPlugin(Vfs::Mode mode); 321 322 } // namespace OCC 323