diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index cee0a8d7204e..024d50d18a1e 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -100,6 +100,8 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), &SyncEngine::aboutToRemoveAllFiles, this, &Folder::slotAboutToRemoveAllFiles); + connect(_engine.data(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + this, &Folder::slotNeedToRemoveRemnantsReadOnlyFolders); connect(_engine.data(), &SyncEngine::transmissionProgress, this, &Folder::slotTransmissionProgress); connect(_engine.data(), &SyncEngine::itemCompleted, this, &Folder::slotItemCompleted); @@ -1653,6 +1655,27 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, std::functio msgBox->open(); } +void Folder::slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback) +{ + auto listOfFolders = QStringList{}; + for (const auto &oneFolder : folders) { + listOfFolders.push_back(oneFolder->_file); + } + + qCInfo(lcFolder()) << "will delete invalid read-only folders:" << listOfFolders.join(", "); + + setSyncPaused(true); + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(true); + setSyncPaused(false); + _lastEtag.clear(); + slotScheduleThisFolder(); +} + void Folder::removeLocalE2eFiles() { qCDebug(lcFolder) << "Removing local E2EE files"; diff --git a/src/gui/folder.h b/src/gui/folder.h index eea99e144778..8f2e5265a10d 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -333,6 +333,10 @@ public slots: // connected to the corresponding signals in the SyncEngine void slotAboutToRemoveAllFiles(OCC::SyncFileItem::Direction, std::function callback); + void slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback); + /** * Starts a sync operation * diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index ecd0b72c5024..b783c22c5811 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -1783,12 +1783,14 @@ bool ProcessDirectoryJob::checkPermissions(const OCC::SyncFileItemPtr &item) #if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 qCWarning(lcDisco) << "unexpected new folder in a read-only folder will be made read-write" << localPath; FileSystem::setFolderPermissions(localPath, FileSystem::FolderPermissions::ReadWrite); + emit _discoveryData->remnantReadOnlyFolderDiscovered(item); #endif return false; } else if (!item->isDirectory() && !perms.hasPermission(RemotePermissions::CanAddFile)) { qCWarning(lcDisco) << "checkForPermission: ERROR" << item->_file; item->_instruction = CSYNC_INSTRUCTION_ERROR; item->_errorString = tr("Not allowed because you don't have permission to add files in that folder"); + emit _discoveryData->remnantReadOnlyFolderDiscovered(item); return false; } break; @@ -1989,6 +1991,7 @@ int ProcessDirectoryJob::processSubJobs(int nbJobs) #if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 qCWarning(lcDisco) << "unexpected new folder in a read-only folder will be made read-write" << localPath; FileSystem::setFolderPermissions(localPath, FileSystem::FolderPermissions::ReadWrite); + emit _discoveryData->remnantReadOnlyFolderDiscovered(_dirItem); #endif } diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 89012df25f87..7d53f1336d69 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -357,6 +357,7 @@ class DiscoveryPhase : public QObject void addErrorToGui(const SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category); + void remnantReadOnlyFolderDiscovered(const OCC::SyncFileItemPtr &item); private slots: void slotItemDiscovered(const OCC::SyncFileItemPtr &item); }; diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 8675507d3299..8dbf191b8748 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -273,6 +273,12 @@ bool FileSystem::removeRecursively(const QString &path, const std::function= MAC_OS_X_VERSION_10_15 + const auto fileInfo = QFileInfo{di.filePath()}; + const auto parentFolderPath = fileInfo.dir().absolutePath(); + const auto parentPermissionsHandler = FileSystem::FilePermissionsRestore{parentFolderPath, FileSystem::FolderPermissions::ReadWrite}; +#endif removeOk = FileSystem::remove(di.filePath(), &removeError); if (removeOk) { if (onDeleted) @@ -289,6 +295,12 @@ bool FileSystem::removeRecursively(const QString &path, const std::function= MAC_OS_X_VERSION_10_15 + const auto fileInfo = QFileInfo{path}; + const auto parentFolderPath = fileInfo.dir().absolutePath(); + const auto parentPermissionsHandler = FileSystem::FilePermissionsRestore{parentFolderPath, FileSystem::FolderPermissions::ReadWrite}; + FileSystem::setFolderPermissions(path, FileSystem::FolderPermissions::ReadWrite); +#endif allRemoved = QDir().rmdir(path); if (allRemoved) { if (onDeleted) diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 2c140d101958..592078dda140 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -635,6 +635,8 @@ void SyncEngine::startSync() _progressInfo->_status = ProgressInfo::Discovery; emit transmissionProgress(*_progressInfo); + _remnantReadOnlyFolders.clear(); + _discoveryPhase.reset(new DiscoveryPhase); _discoveryPhase->_leadingAndTrailingSpacesFilesAllowed = _leadingAndTrailingSpacesFilesAllowed; _discoveryPhase->_account = _account; @@ -688,6 +690,7 @@ void SyncEngine::startSync() connect(_discoveryPhase.data(), &DiscoveryPhase::finished, this, &SyncEngine::slotDiscoveryFinished); connect(_discoveryPhase.data(), &DiscoveryPhase::silentlyExcluded, _syncFileStatusTracker.data(), &SyncFileStatusTracker::slotAddSilentlyExcluded); + connect(_discoveryPhase.data(), &DiscoveryPhase::remnantReadOnlyFolderDiscovered, this, &SyncEngine::remnantReadOnlyFolderDiscovered); ProcessDirectoryJob *discoveryJob = nullptr; @@ -803,102 +806,6 @@ void SyncEngine::slotDiscoveryFinished() _progressInfo->_status = ProgressInfo::Reconcile; emit transmissionProgress(*_progressInfo); - // qCInfo(lcEngine) << "Permissions of the root folder: " << _csync_ctx->remote.root_perms.toString(); - auto finish = [this]{ - auto databaseFingerprint = _journal->dataFingerprint(); - // If databaseFingerprint is empty, this means that there was no information in the database - // (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint) - if (!databaseFingerprint.isEmpty() && _discoveryPhase - && _discoveryPhase->_dataFingerprint != databaseFingerprint) { - qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint; - restoreOldFiles(_syncItems); - } - - if (_discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { - slotScheduleFilesDelayedSync(); - } else if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { - _anotherSyncNeeded = ImmediateFollowUp; - } - - if (!_discoveryPhase->_filesUnscheduleSync.empty()) { - slotUnscheduleFilesDelayedSync(); - } - - if (_discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { - for (const auto &item : qAsConst(_syncItems)) { - if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) { - item->_instruction = CSYNC_INSTRUCTION_IGNORE; - } - } - _anotherSyncNeeded = ImmediateFollowUp; - } - - Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end())); - - qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms"; - - _localDiscoveryPaths.clear(); - - // To announce the beginning of the sync - emit aboutToPropagate(_syncItems); - - qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms"; - - // it's important to do this before ProgressInfo::start(), to announce start of new sync - _progressInfo->_status = ProgressInfo::Propagation; - emit transmissionProgress(*_progressInfo); - _progressInfo->startEstimateUpdates(); - - // post update phase script: allow to tweak stuff by a custom script in debug mode. - if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) { - #ifndef NDEBUG - const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT"); - - qCDebug(lcEngine) << "Post Update Script: " << script; - auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); - if (scriptArgs.size() > 0) { - const auto scriptExecutable = scriptArgs.takeFirst(); - QProcess::execute(scriptExecutable, scriptArgs); - } -#else - qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG"; - #endif - } - - // do a database commit - _journal->commit(QStringLiteral("post treewalk")); - - _propagator = QSharedPointer( - new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList)); - _propagator->setSyncOptions(_syncOptions); - connect(_propagator.data(), &OwncloudPropagator::itemCompleted, - this, &SyncEngine::slotItemCompleted); - connect(_propagator.data(), &OwncloudPropagator::progress, - this, &SyncEngine::slotProgress); - connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection); - connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile); - connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile); - connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage); - connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage); - connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem); - - // apply the network limits to the propagator - setNetworkLimits(_uploadLimit, _downloadLimit); - - deleteStaleDownloadInfos(_syncItems); - deleteStaleUploadInfos(_syncItems); - deleteStaleErrorBlacklistEntries(_syncItems); - _journal->commit(QStringLiteral("post stale entry removal")); - - // Emit the started signal only after the propagator has been set up. - if (_needsUpdate) - Q_EMIT started(); - - _propagator->start(std::move(_syncItems)); - - qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms"; - }; - const auto displayDialog = ConfigFile().promptDeleteFiles() && !_syncOptions.isCmd(); if (!_hasNoneFiles && _hasRemoveFile && displayDialog) { qCInfo(lcEngine) << "All the files are going to be changed, asking the user"; @@ -909,27 +816,18 @@ void SyncEngine::slotDiscoveryFinished() } } - QPointer guard = new QObject(); - QPointer self = this; - auto callback = [this, self, finish, guard](bool cancel) -> void { - // use a guard to ensure its only called once... - // qpointer to self to ensure we still exist - if (!guard || !self) { - return; - } - guard->deleteLater(); - if (cancel) { - qCInfo(lcEngine) << "User aborted sync"; - finalize(false); - return; - } else { - finish(); - } - }; - emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); + promptUserBeforePropagation([this, side](auto &&callback){ + emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); + }); return; } - finish(); + + if (!_remnantReadOnlyFolders.isEmpty()) { + handleRemnantReadOnlyFolders(); + return; + } + + finishSync(); } void SyncEngine::slotCleanPollsJobAborted(const QString &error, const ErrorCategory errorCategory) @@ -1106,6 +1004,138 @@ void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems) } } +void SyncEngine::cancelSyncOrContinue(bool cancel) +{ + if (cancel) { + qCInfo(lcEngine) << "User aborted sync"; + finalize(false); + } else { + finishSync(); + } +} + +void SyncEngine::finishSync() +{ + auto databaseFingerprint = _journal->dataFingerprint(); + // If databaseFingerprint is empty, this means that there was no information in the database + // (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint) + if (!databaseFingerprint.isEmpty() && _discoveryPhase + && _discoveryPhase->_dataFingerprint != databaseFingerprint) { + qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint; + restoreOldFiles(_syncItems); + } + + if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { + slotScheduleFilesDelayedSync(); + } else if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { + _anotherSyncNeeded = ImmediateFollowUp; + } + + if (_discoveryPhase && !_discoveryPhase->_filesUnscheduleSync.empty()) { + slotUnscheduleFilesDelayedSync(); + } + + if (_discoveryPhase && _discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { + for (const auto &item : qAsConst(_syncItems)) { + if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) { + // item->_instruction = CSYNC_INSTRUCTION_IGNORE; + } + } + _anotherSyncNeeded = ImmediateFollowUp; + } + + Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end())); + + qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms"; + + _localDiscoveryPaths.clear(); + + // To announce the beginning of the sync + emit aboutToPropagate(_syncItems); + + qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms"; + + // it's important to do this before ProgressInfo::start(), to announce start of new sync + _progressInfo->_status = ProgressInfo::Propagation; + emit transmissionProgress(*_progressInfo); + _progressInfo->startEstimateUpdates(); + + // post update phase script: allow to tweak stuff by a custom script in debug mode. + if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) { +#ifndef NDEBUG + const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT"); + + qCDebug(lcEngine) << "Post Update Script: " << script; + auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); + if (scriptArgs.size() > 0) { + const auto scriptExecutable = scriptArgs.takeFirst(); + QProcess::execute(scriptExecutable, scriptArgs); + } +#else + qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG"; +#endif + } + + // do a database commit + _journal->commit(QStringLiteral("post treewalk")); + + _propagator = QSharedPointer( + new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList)); + _propagator->setSyncOptions(_syncOptions); + connect(_propagator.data(), &OwncloudPropagator::itemCompleted, + this, &SyncEngine::slotItemCompleted); + connect(_propagator.data(), &OwncloudPropagator::progress, + this, &SyncEngine::slotProgress); + connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection); + connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile); + connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile); + connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage); + connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage); + connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem); + + // apply the network limits to the propagator + setNetworkLimits(_uploadLimit, _downloadLimit); + + deleteStaleDownloadInfos(_syncItems); + deleteStaleUploadInfos(_syncItems); + deleteStaleErrorBlacklistEntries(_syncItems); + _journal->commit(QStringLiteral("post stale entry removal")); + + // Emit the started signal only after the propagator has been set up. + if (_needsUpdate) + Q_EMIT started(); + + _propagator->start(std::move(_syncItems)); + + qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms"; +} + +void SyncEngine::handleRemnantReadOnlyFolders() +{ + promptUserBeforePropagation([this](auto &&callback) { + emit aboutToRemoveRemnantsReadOnlyFolders(_remnantReadOnlyFolders, _localPath, callback); + }); +} + +template +void SyncEngine::promptUserBeforePropagation(T &&lambda) +{ + QPointer guard = new QObject(); + QPointer self = this; + auto callback = [this, self, guard](bool cancel) -> void { + // use a guard to ensure its only called once... + // qpointer to self to ensure we still exist + if (!guard || !self) { + return; + } + guard->deleteLater(); + + cancelSyncOrContinue(cancel); + }; + + lambda(callback); +} + void SyncEngine::slotAddTouchedFile(const QString &fn) { QElapsedTimer now; @@ -1491,6 +1521,11 @@ void SyncEngine::slotCleanupScheduledSyncTimers() } } +void SyncEngine::remnantReadOnlyFolderDiscovered(const SyncFileItemPtr &item) +{ + _remnantReadOnlyFolders.push_back(item); +} + void SyncEngine::slotUnscheduleFilesDelayedSync() { if (!_discoveryPhase || _discoveryPhase->_filesUnscheduleSync.empty()) { diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index af234725cf2d..b0b26c5804bf 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -184,6 +184,10 @@ public slots: */ void aboutToRemoveAllFiles(OCC::SyncFileItem::Direction direction, std::function f); + void aboutToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function f); + // A new folder was discovered and was not synced because of the confirmation feature void newBigFolder(const QString &folder, bool isExternal); @@ -235,6 +239,8 @@ private slots: void slotUnscheduleFilesDelayedSync(); void slotCleanupScheduledSyncTimers(); + void remnantReadOnlyFolderDiscovered(const OCC::SyncFileItemPtr &item); + private: // Some files need a sync run to be executed at a specified time after // their status is scheduled to change (e.g. lock status will expire in @@ -354,6 +360,15 @@ private slots: */ void restoreOldFiles(SyncFileItemVector &syncItems); + void cancelSyncOrContinue(bool cancel); + + void finishSync(); + + void handleRemnantReadOnlyFolders(); + + template + void promptUserBeforePropagation(T &&lambda); + // true if there is at least one file which was not changed on the server bool _hasNoneFiles = false; @@ -399,6 +414,8 @@ private slots: QVector> _scheduledSyncTimers; SingleItemDiscoveryOptions _singleItemDiscoveryOptions; + + QList _remnantReadOnlyFolders; }; } diff --git a/test/testpermissions.cpp b/test/testpermissions.cpp index 1e2ab899b7ab..bdef17baee75 100644 --- a/test/testpermissions.cpp +++ b/test/testpermissions.cpp @@ -59,12 +59,22 @@ SyncFileItemPtr findDiscoveryItem(const SyncFileItemVector &spy, const QString & bool itemInstruction(const ItemCompletedSpy &spy, const QString &path, const SyncInstructions instr) { auto item = spy.findItem(path); + const auto checkHelper = [item, instr]() { + QCOMPARE(item->_instruction, instr); + }; + + checkHelper(); return item->_instruction == instr; } bool discoveryInstruction(const SyncFileItemVector &spy, const QString &path, const SyncInstructions instr) { auto item = findDiscoveryItem(spy, path); + const auto checkHelper = [item, instr]() { + QCOMPARE(item->_instruction, instr); + }; + + checkHelper(); return item->_instruction == instr; } @@ -85,6 +95,14 @@ private slots: FakeFolder fakeFolder{ FileInfo() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + qDebug() << "aboutToRemoveRemnantsReadOnlyFolders called"; + Q_UNUSED(folders); + Q_UNUSED(localPath); + callback(false); + }); + // Some of this test depends on the order of discovery. With threading // that order becomes effectively random, but we want to make sure to test // all cases and thus disable threading. @@ -401,6 +419,13 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + Q_UNUSED(folders) + Q_UNUSED(localPath) + callback(false); + }); + // Some of this test depends on the order of discovery. With threading // that order becomes effectively random, but we want to make sure to test // all cases and thus disable threading. @@ -520,6 +545,14 @@ private slots: void testAllowedMoveForbiddenDelete() { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + // Some of this test depends on the order of discovery. With threading // that order becomes effectively random, but we want to make sure to test // all cases and thus disable threading. @@ -568,6 +601,15 @@ private slots: void testParentMoveNotAllowedChildrenRestored() { FakeFolder fakeFolder{FileInfo{}}; + + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &lm = fakeFolder.localModifier(); auto &rm = fakeFolder.remoteModifier(); rm.mkdir("forbidden-move"); @@ -620,6 +662,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder"); @@ -637,6 +687,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readWriteFolder"); @@ -655,6 +713,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("testFolder"); @@ -691,6 +757,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("testFolder"); @@ -751,6 +825,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder"); @@ -781,6 +863,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder"); @@ -813,6 +903,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder"); @@ -848,6 +946,14 @@ private slots: { FakeFolder fakeFolder{FileInfo{}}; + QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + [&](const QList &folders, const QString &localPath, std::function callback) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + callback(false); + }); + auto &remote = fakeFolder.remoteModifier(); remote.mkdir("readOnlyFolder");