diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 8d15e8da30211..4e06cacb4e5e2 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -643,8 +643,11 @@ void Folder::slotFilesLockReleased(const QSet &files) for (const auto &file : files) { const auto fileRecordPath = fileFromLocalPath(file); SyncJournalFileRecord rec; - const auto canUnlockFile = journalDb()->getFileRecord(fileRecordPath, &rec) - && rec.isValid() + const auto isFileRecordValid = journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid(); + if (isFileRecordValid) { + [[maybe_unused]] const auto result = _vfs->updateMetadata(path() + rec.path(), rec._modtime, rec._fileSize, rec._fileId); + } + const auto canUnlockFile = isFileRecordValid && rec._lockstate._locked && rec._lockstate._lockOwnerType == static_cast(SyncFileItem::LockOwnerType::UserLock) && rec._lockstate._lockOwnerId == _accountState->account()->davUser(); @@ -669,6 +672,18 @@ void Folder::slotFilesLockReleased(const QSet &files) } } +void Folder::slotFilesLockImposed(const QSet &files) +{ + qCDebug(lcFolder) << "Lock files detected for office files" << files; + for (const auto &file : files) { + const auto fileRecordPath = fileFromLocalPath(file); + SyncJournalFileRecord rec; + if (journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid()) { + [[maybe_unused]] const auto result = _vfs->updateMetadata(path() + rec.path(), rec._modtime, rec._fileSize, rec._fileId); + } + } +} + void Folder::implicitlyHydrateFile(const QString &relativepath) { qCInfo(lcFolder) << "Implicitly hydrate virtual file:" << relativepath; @@ -1375,6 +1390,7 @@ void Folder::registerFolderWatcher() if (_accountState->account()->capabilities().filesLockAvailable()) { connect(_folderWatcher.data(), &FolderWatcher::filesLockReleased, this, &Folder::slotFilesLockReleased); } + connect(_folderWatcher.data(), &FolderWatcher::filesLockImposed, this, &Folder::slotFilesLockImposed, Qt::UniqueConnection); _folderWatcher->init(path()); _folderWatcher->startNotificatonTest(path() + QLatin1String(".nextcloudsync.log")); } diff --git a/src/gui/folder.h b/src/gui/folder.h index cac47809ba112..7ca160cac7f78 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -350,6 +350,11 @@ public slots: */ void slotFilesLockReleased(const QSet &files); + /* + * Triggered when lock files were added + */ + void slotFilesLockImposed(const QSet &files); + /** * Mark a virtual file as being requested for download, and start a sync. * diff --git a/src/gui/folderwatcher.cpp b/src/gui/folderwatcher.cpp index b050af74cfc00..44b56d391ca5b 100644 --- a/src/gui/folderwatcher.cpp +++ b/src/gui/folderwatcher.cpp @@ -170,6 +170,7 @@ void FolderWatcher::changeDetected(const QStringList &paths) QSet changedPaths; QSet unlockedFiles; + QSet lockedFiles; for (int i = 0; i < paths.size(); ++i) { QString path = paths[i]; @@ -178,15 +179,21 @@ void FolderWatcher::changeDetected(const QStringList &paths) _testNotificationPath.clear(); } + const auto checkResult = checkIfFileIsLockOrUnlock(path); if (_shouldWatchForFileUnlocking) { - const auto unlockedFilePath = possiblyAddUnlockedFilePath(path); - if (!unlockedFilePath.isEmpty()) { - unlockedFiles.insert(unlockedFilePath); + if (checkResult.type == FileLockingInfo::Type::Unlocked && !checkResult.path.isEmpty()) { + unlockedFiles.insert(checkResult.path); } qCDebug(lcFolderWatcher) << "Unlocked files:" << unlockedFiles.values(); } + if (checkResult.type == FileLockingInfo::Type::Locked && !checkResult.path.isEmpty()) { + lockedFiles.insert(checkResult.path); + } + + qCDebug(lcFolderWatcher) << "Locked files:" << lockedFiles.values(); + // ------- handle ignores: if (pathIsIgnored(path)) { continue; @@ -199,6 +206,10 @@ void FolderWatcher::changeDetected(const QStringList &paths) emit filesLockReleased(unlockedFiles); } + if (!lockedFiles.isEmpty()) { + emit filesLockImposed(lockedFiles); + } + if (changedPaths.isEmpty()) { return; } @@ -214,12 +225,14 @@ void FolderWatcher::folderAccountCapabilitiesChanged() _shouldWatchForFileUnlocking = _folder->accountState()->account()->capabilities().filesLockAvailable(); } -QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path) +FolderWatcher::FileLockingInfo FolderWatcher::checkIfFileIsLockOrUnlock(const QString &path) const { qCDebug(lcFolderWatcher) << "Checking if it is a lock file:" << path; + + FileLockingInfo result; const auto pathSplit = path.split(QLatin1Char('/'), Qt::SkipEmptyParts); if (pathSplit.isEmpty()) { - return {}; + return result; } QString lockFilePatternFound; for (const auto &lockFilePattern : lockFilePatterns) { @@ -229,8 +242,8 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path) } } - if (lockFilePatternFound.isEmpty() || QFileInfo::exists(path)) { - return {}; + if (lockFilePatternFound.isEmpty()) { + return result; } qCDebug(lcFolderWatcher) << "Found a lock file with prefix:" << lockFilePatternFound << "in path:" << path; @@ -239,7 +252,7 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path) auto lockFilePathWithoutPrefixSplit = lockFilePathWitoutPrefix.split(QLatin1Char('.')); if (lockFilePathWithoutPrefixSplit.size() < 2) { - return {}; + return result; } auto extensionSanitized = lockFilePathWithoutPrefixSplit.takeLast().toStdString(); @@ -252,26 +265,26 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path) ); lockFilePathWithoutPrefixSplit.push_back(QString::fromStdString(extensionSanitized)); - auto unlockedFilePath = lockFilePathWithoutPrefixSplit.join(QLatin1Char('.')); - - if (!QFile::exists(unlockedFilePath)) { - unlockedFilePath.clear(); - qCDebug(lcFolderWatcher) << "Assumed unlocked file path" << unlockedFilePath << "does not exist. Going to try to find matching file"; - auto splitFilePath = unlockedFilePath.split(QLatin1Char('/')); - if (splitFilePath.size() > 1) { - const auto lockFileName = splitFilePath.takeLast(); - // some software will modify lock file name such that it does not correspond to original file (removing some symbols from the name, so we will search for a matching file - unlockedFilePath = findMatchingUnlockedFileInDir(splitFilePath.join(QLatin1Char('/')), lockFileName); - } + const auto lockFilePathWithoutPrefix = lockFilePathWithoutPrefixSplit.join(QLatin1Char('.')); + + qCDebug(lcFolderWatcher) << "Assumed locked/unlocked file path" << lockFilePathWithoutPrefix << "Going to try to find matching file"; + auto splitFilePath = lockFilePathWithoutPrefix.split(QLatin1Char('/')); + if (splitFilePath.size() > 1) { + const auto lockFileNameWithoutPrefix = splitFilePath.takeLast(); + // some software will modify lock file name such that it does not correspond to original file (removing some symbols from the name, so we will search + // for a matching file + result.path = findMatchingUnlockedFileInDir(splitFilePath.join(QLatin1Char('/')), lockFileNameWithoutPrefix); } - if (unlockedFilePath.isEmpty() || !QFile::exists(unlockedFilePath)) { - return {}; + if (result.path.isEmpty() || !QFile::exists(result.path)) { + result.path.clear(); + return result; } - return unlockedFilePath; + result.type = QFile::exists(path) ? FileLockingInfo::Type::Locked : FileLockingInfo::Type::Unlocked; + return result; } -QString FolderWatcher::findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName) +QString FolderWatcher::findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName) const { QString foundFilePath; const QDir dir(dirPath); diff --git a/src/gui/folderwatcher.h b/src/gui/folderwatcher.h index 0e7b50cee641f..f2d59f703d6dd 100644 --- a/src/gui/folderwatcher.h +++ b/src/gui/folderwatcher.h @@ -50,6 +50,13 @@ class Folder; class FolderWatcher : public QObject { Q_OBJECT + + struct FileLockingInfo { + enum class Type { Unset = -1, Locked, Unlocked }; + QString path; + Type type = Type::Unset; + }; + public: // Construct, connect signals, call init() explicit FolderWatcher(Folder *folder = nullptr); @@ -90,6 +97,11 @@ class FolderWatcher : public QObject */ void filesLockReleased(const QSet &files); + /* + * Emitted when lock files were added + */ + void filesLockImposed(const QSet &files); + /** * Emitted if some notifications were lost. * @@ -129,8 +141,8 @@ private slots: void appendSubPaths(QDir dir, QStringList& subPaths); - QString possiblyAddUnlockedFilePath(const QString &path); - QString findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName); + [[nodiscard]] FileLockingInfo checkIfFileIsLockOrUnlock(const QString &path) const; + [[nodiscard]] QString findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName) const; /* Check if the path should be igored by the FolderWatcher. */ [[nodiscard]] bool pathIsIgnored(const QString &path) const;