From 422f7933a47ac0f9f88fd1dc867693ab0c5525d2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 16 Feb 2024 18:19:54 +0100 Subject: [PATCH] Fix check for creation of a spaces folder in a spaces root Fixes: #11516 --- src/gui/folderman.cpp | 88 +++++++--- src/gui/folderman.h | 18 +- src/gui/folderwizard/folderwizard.cpp | 8 +- .../folderwizard/folderwizardlocalpath.cpp | 5 +- src/gui/guiutility.cpp | 40 ++++- src/gui/guiutility.h | 4 +- .../newwizard/setupwizardaccountbuilder.cpp | 2 +- .../accountconfiguredsetupwizardstate.cpp | 7 +- src/gui/owncloudgui.cpp | 2 +- test/testfolderman.cpp | 157 ++++++++++++------ 10 files changed, 226 insertions(+), 105 deletions(-) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 7cac581bfda..1dd2b41ff87 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -720,11 +720,31 @@ static QString canonicalPath(const QString &path) return selFile.canonicalFilePath(); } -static QString checkPathForSyncRootMarkingRecursive(const QString &path) +static QString checkPathForSyncRootMarkingRecursive(const QString &path, FolderMan::NewFolderType folderType, const QUuid &accountUuid) { - auto existingTag = Utility::getDirectorySyncRootMarking(path); - if (!existingTag.isEmpty()) { - return FolderMan::tr("Folder '%1' is already in use by application %2!").arg(path, existingTag); + std::pair existingTags = Utility::getDirectorySyncRootMarkings(path); + if (!existingTags.first.isEmpty()) { + if (existingTags.first != Theme::instance()->orgDomainName()) { + // another application uses this as spaces root folder + return FolderMan::tr("Folder '%1' is already in use by application %2!").arg(path, existingTags.first); + } + + // Looks good, it's our app, let's check the account tag: + switch (folderType) { + case FolderMan::NewFolderType::SpacesFolder: + if (existingTags.second == accountUuid) { + // Nice, that's what we like, the sync root for our account in our app. No error. + return {}; + } else { + return FolderMan::tr("Folder '%1' is already in use by another account.").arg(path); + } + + case FolderMan::NewFolderType::OC10SyncRoot: + [[fallthrough]]; + case FolderMan::NewFolderType::SpacesSyncRoot: + // It's our application but we don't want to create a spaces folder, so it must be another space root + return FolderMan::tr("Folder '%1' is already in use by another account.").arg(path); + } } QString parent = QFileInfo(path).path(); @@ -732,10 +752,10 @@ static QString checkPathForSyncRootMarkingRecursive(const QString &path) return {}; } - return checkPathForSyncRootMarkingRecursive(parent); + return checkPathForSyncRootMarkingRecursive(parent, folderType, accountUuid); } -QString FolderMan::checkPathValidityRecursive(const QString &path) +QString FolderMan::checkPathValidityRecursive(const QString &path, FolderMan::NewFolderType folderType, const QUuid &accountUuid) { if (path.isEmpty()) { return FolderMan::tr("No valid folder selected!"); @@ -750,31 +770,45 @@ QString FolderMan::checkPathValidityRecursive(const QString &path) return pathLenghtCheck.error(); } - const QFileInfo selFile(path); - if (numberOfSyncJournals(selFile.filePath()) != 0) { - return FolderMan::tr("The folder %1 is used in a folder sync connection!").arg(QDir::toNativeSeparators(selFile.filePath())); - } - - if (!selFile.exists()) { - const QString parentPath = selFile.path(); + const QFileInfo selectedPathInfo(path); + if (!selectedPathInfo.exists()) { + const QString parentPath = selectedPathInfo.path(); if (parentPath != path) { - return checkPathValidityRecursive(parentPath); + return checkPathValidityRecursive(parentPath, folderType, accountUuid); } return FolderMan::tr("The selected path does not exist!"); } - if (!selFile.isDir()) { + if (numberOfSyncJournals(selectedPathInfo.filePath()) != 0) { + return FolderMan::tr("The folder %1 is used in a folder sync connection!").arg(QDir::toNativeSeparators(selectedPathInfo.filePath())); + } + + // At this point we know there is no syncdb in the parent hyrarchy, check for spaces sync root. + + if (!selectedPathInfo.isDir()) { return FolderMan::tr("The selected path is not a folder!"); } - if (!selFile.isWritable()) { + if (!selectedPathInfo.isWritable()) { return FolderMan::tr("You have no permission to write to the selected folder!"); } - return checkPathForSyncRootMarkingRecursive(path); + return checkPathForSyncRootMarkingRecursive(path, folderType, accountUuid); } -QString FolderMan::checkPathValidityForNewFolder(const QString &path) const +/* + * OC10 folder: + * - sync root not in syncdb folder + * - sync root not in spaces root + * with spaces: + * - spaces sync root not in syncdb folder + * - spaces sync root not in another spaces sync root + * + * - space not in syncdb folder + * - space *can* be in sync root + * - space not in spaces sync root of other account (check with account uuid) + */ +QString FolderMan::checkPathValidityForNewFolder(const QString &path, NewFolderType folderType, const QUuid &accountUuid) const { // check if the local directory isn't used yet in another ownCloud sync const auto cs = Utility::fsCaseSensitivity(); @@ -789,25 +823,25 @@ QString FolderMan::checkPathValidityForNewFolder(const QString &path) const } if (FileSystem::isChildPathOf(folderDir, userDir)) { return tr("The local folder %1 already contains a folder used in a folder sync connection. " - "Please pick another one!") + "Please pick another local folder!") .arg(QDir::toNativeSeparators(path)); } if (FileSystem::isChildPathOf(userDir, folderDir)) { return tr("The local folder %1 is already contained in a folder used in a folder sync connection. " - "Please pick another one!") + "Please pick another local folder!") .arg(QDir::toNativeSeparators(path)); } } - const auto result = checkPathValidityRecursive(path); + const auto result = checkPathValidityRecursive(path, folderType, accountUuid); if (!result.isEmpty()) { - return tr("%1 Please pick another one!").arg(result); + return tr("%1 Please pick another local folder!").arg(result); } return {}; } -QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const QString &newFolder) +QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const QString &newFolder, FolderMan::NewFolderType folderType) { // reserve extra characters to allow appending of a number const QString normalisedPath = FileSystem::createPortableFileName(basePath, FileSystem::pathEscape(newFolder), std::string_view(" (100)").size()); @@ -825,8 +859,8 @@ QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const Q { QString folder = normalisedPath; for (int attempt = 2; attempt <= 100; ++attempt) { - if (!QFileInfo::exists(folder) - && FolderMan::instance()->checkPathValidityForNewFolder(folder).isEmpty()) { + // HvR: is this correct? + if (!QFileInfo::exists(folder) && FolderMan::instance()->checkPathValidityForNewFolder(folder, folderType, {}).isEmpty()) { return canonicalPath(folder); } folder = normalisedPath + QStringLiteral(" (%1)").arg(attempt); @@ -933,13 +967,13 @@ Folder *FolderMan::addFolderFromFolderWizardResult(const AccountStatePtr &accoun return f; } -QString FolderMan::suggestSyncFolder(const QUrl &server, const QString &displayName) +QString FolderMan::suggestSyncFolder(const QUrl &server, const QString &displayName, NewFolderType folderType) { QString folderName = tr("%1 - %2@%3").arg(Theme::instance()->appName(), displayName, server.host()); if (!Theme::instance()->multiAccount()) { folderName = Theme::instance()->appName(); } - return FolderMan::instance()->findGoodPathForNewSyncFolder(QDir::homePath(), folderName); + return FolderMan::instance()->findGoodPathForNewSyncFolder(QDir::homePath(), folderName, folderType); } bool FolderMan::prepareFolder(const QString &folder) diff --git a/src/gui/folderman.h b/src/gui/folderman.h index 7070c02610b..241498578e0 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -83,10 +83,20 @@ class FolderMan : public QObject { Q_OBJECT public: - static QString suggestSyncFolder(const QUrl &server, const QString &displayName); + /** + * For a new folder, the type guides what kind of checks are done to ensure the new folder is not embedded in an existing one. + * Or in case of a space folder, that if the new folder is in a Space sync root, it is the sync root of the same account. + */ + enum class NewFolderType { + OC10SyncRoot, + SpacesSyncRoot, + SpacesFolder, + }; + + static QString suggestSyncFolder(const QUrl &server, const QString &displayName, NewFolderType folderType); [[nodiscard]] static bool prepareFolder(const QString &folder); - static QString checkPathValidityRecursive(const QString &path); + static QString checkPathValidityRecursive(const QString &path, FolderMan::NewFolderType folderType, const QUuid &accountUuid); static std::unique_ptr createInstance(); ~FolderMan() override; @@ -177,7 +187,7 @@ class FolderMan : public QObject * * @returns an empty string if it is allowed, or an error if it is not allowed */ - QString checkPathValidityForNewFolder(const QString &path) const; + QString checkPathValidityForNewFolder(const QString &path, NewFolderType folderType, const QUuid &accountUuid) const; /** * Attempts to find a non-existing, acceptable path for creating a new sync folder. @@ -188,7 +198,7 @@ class FolderMan : public QObject * subfolder of ~ would be a good candidate. When that happens \a basePath * is returned. */ - static QString findGoodPathForNewSyncFolder(const QString &basePath, const QString &newFolder); + static QString findGoodPathForNewSyncFolder(const QString &basePath, const QString &newFolder, NewFolderType folderType); /** * While ignoring hidden files can theoretically be switched per folder, diff --git a/src/gui/folderwizard/folderwizard.cpp b/src/gui/folderwizard/folderwizard.cpp index e3618c1cd34..8a7d06f3d96 100644 --- a/src/gui/folderwizard/folderwizard.cpp +++ b/src/gui/folderwizard/folderwizard.cpp @@ -63,7 +63,8 @@ QString FolderWizardPrivate::formatWarnings(const QStringList &warnings, bool is QString FolderWizardPrivate::defaultSyncRoot() const { if (!_account->account()->hasDefaultSyncRoot()) { - return FolderMan::suggestSyncFolder(_account->account()->url(), _account->account()->davDisplayName()); + auto folderType = _account->supportsSpaces() ? FolderMan::NewFolderType::SpacesSyncRoot : FolderMan::NewFolderType::OC10SyncRoot; + return FolderMan::suggestSyncFolder(_account->account()->url(), _account->account()->davDisplayName(), folderType); } else { return _account->account()->defaultSyncRoot(); } @@ -96,12 +97,13 @@ FolderWizardPrivate::FolderWizardPrivate(FolderWizard *q, const AccountStatePtr QString FolderWizardPrivate::initialLocalPath() const { if (_account->supportsSpaces()) { - return FolderMan::findGoodPathForNewSyncFolder(defaultSyncRoot(), _spacesPage->selectedSpaceData(Spaces::SpacesModel::Columns::Name).toString()); + return FolderMan::findGoodPathForNewSyncFolder( + defaultSyncRoot(), _spacesPage->selectedSpaceData(Spaces::SpacesModel::Columns::Name).toString(), FolderMan::NewFolderType::SpacesSyncRoot); } // Split default sync root: const QFileInfo path(defaultSyncRoot()); - return FolderMan::findGoodPathForNewSyncFolder(path.path(), path.fileName()); + return FolderMan::findGoodPathForNewSyncFolder(path.path(), path.fileName(), FolderMan::NewFolderType::OC10SyncRoot); } QString FolderWizardPrivate::remotePath() const diff --git a/src/gui/folderwizard/folderwizardlocalpath.cpp b/src/gui/folderwizard/folderwizardlocalpath.cpp index d3bc9903ea4..cf9bb1b17ce 100644 --- a/src/gui/folderwizard/folderwizardlocalpath.cpp +++ b/src/gui/folderwizard/folderwizardlocalpath.cpp @@ -63,7 +63,10 @@ QString FolderWizardLocalPath::localPath() const bool FolderWizardLocalPath::isComplete() const { - QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(localPath()); + // HvR: is this correct? + auto folderType = folderWizardPrivate()->accountState()->supportsSpaces() ? FolderMan::NewFolderType::SpacesFolder : FolderMan::NewFolderType::OC10SyncRoot; + auto accountUuid = folderWizardPrivate()->accountState()->account()->uuid(); + QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(localPath(), folderType, accountUuid); bool isOk = errorStr.isEmpty(); QStringList warnStrings; diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp index 67adbe7346b..967a3ec7ac2 100644 --- a/src/gui/guiutility.cpp +++ b/src/gui/guiutility.cpp @@ -39,6 +39,11 @@ const QString dirTag() { return QStringLiteral("com.owncloud.spaces.app"); } + +const QString uuidTag() +{ + return QStringLiteral("com.owncloud.spaces.account-uuid"); +} } // anonymous namespace using namespace OCC; @@ -104,25 +109,39 @@ bool Utility::internetConnectionIsMetered() return false; } -void Utility::markDirectoryAsSyncRoot(const QString &path) +void Utility::markDirectoryAsSyncRoot(const QString &path, const QUuid &accountUuid) { - Q_ASSERT(getDirectorySyncRootMarking(path).isEmpty()); + Q_ASSERT(getDirectorySyncRootMarkings(path).first.isEmpty()); + Q_ASSERT(getDirectorySyncRootMarkings(path).second.isNull()); - auto result = FileSystem::Tags::set(path, dirTag(), Theme::instance()->orgDomainName().toUtf8()); - if (!result) { - qCWarning(lcGuiUtility) << QStringLiteral("Failed to set tag on '%1': %2").arg(path, result.error()) + auto result1 = FileSystem::Tags::set(path, dirTag(), Theme::instance()->orgDomainName().toUtf8()); + if (!result1) { + qCWarning(lcGuiUtility) << QStringLiteral("Failed to set tag on '%1': %2").arg(path, result1.error()) #ifdef Q_OS_WIN << QStringLiteral("(filesystem %1)").arg(FileSystem::fileSystemForPath(path)) #endif // Q_OS_WIN ; + return; + } + + auto result2 = FileSystem::Tags::set(path, uuidTag(), accountUuid.toString().toUtf8()); + if (!result2) { + qCWarning(lcGuiUtility) << QStringLiteral("Failed to set tag on '%1': %2").arg(path, result2.error()) +#ifdef Q_OS_WIN + << QStringLiteral("(filesystem %1)").arg(FileSystem::fileSystemForPath(path)) +#endif // Q_OS_WIN + ; + return; } } -QString Utility::getDirectorySyncRootMarking(const QString &path) +std::pair Utility::getDirectorySyncRootMarkings(const QString &path) { - auto existingValue = FileSystem::Tags::get(path, dirTag()); - if (existingValue.has_value()) { - return QString::fromUtf8(existingValue.value()); + auto existingDirTag = FileSystem::Tags::get(path, dirTag()); + auto existingUuidTag = FileSystem::Tags::get(path, uuidTag()); + + if (existingDirTag.has_value() && existingUuidTag.has_value()) { + return {QString::fromUtf8(existingDirTag.value()), QUuid::fromString(QString::fromUtf8(existingUuidTag.value()))}; } return {}; @@ -133,4 +152,7 @@ void Utility::unmarkDirectoryAsSyncRoot(const QString &path) if (!FileSystem::Tags::remove(path, dirTag())) { qCWarning(lcGuiUtility) << "Failed to remove tag on" << path; } + if (!FileSystem::Tags::remove(path, uuidTag())) { + qCWarning(lcGuiUtility) << "Failed to remove uuid tag on" << path; + } } diff --git a/src/gui/guiutility.h b/src/gui/guiutility.h index 92931016867..69e7bcd8496 100644 --- a/src/gui/guiutility.h +++ b/src/gui/guiutility.h @@ -53,8 +53,8 @@ namespace Utility { bool internetConnectionIsMetered(); - void markDirectoryAsSyncRoot(const QString &path); - QString getDirectorySyncRootMarking(const QString &path); + void markDirectoryAsSyncRoot(const QString &path, const QUuid &accountUuid); + std::pair getDirectorySyncRootMarkings(const QString &path); void unmarkDirectoryAsSyncRoot(const QString &path); } // namespace Utility } // namespace OCC diff --git a/src/gui/newwizard/setupwizardaccountbuilder.cpp b/src/gui/newwizard/setupwizardaccountbuilder.cpp index 5b2a3f341d6..a2fcb50fc1a 100644 --- a/src/gui/newwizard/setupwizardaccountbuilder.cpp +++ b/src/gui/newwizard/setupwizardaccountbuilder.cpp @@ -141,7 +141,7 @@ AccountPtr SetupWizardAccountBuilder::build() if (!QFileInfo::exists(_defaultSyncTargetDir)) { OC_ASSERT(QDir().mkpath(_defaultSyncTargetDir)); } - Utility::markDirectoryAsSyncRoot(_defaultSyncTargetDir); + Utility::markDirectoryAsSyncRoot(_defaultSyncTargetDir, newAccountPtr->uuid()); } return newAccountPtr; diff --git a/src/gui/newwizard/states/accountconfiguredsetupwizardstate.cpp b/src/gui/newwizard/states/accountconfiguredsetupwizardstate.cpp index 497bb167fe7..94aaeac4f5e 100644 --- a/src/gui/newwizard/states/accountconfiguredsetupwizardstate.cpp +++ b/src/gui/newwizard/states/accountconfiguredsetupwizardstate.cpp @@ -53,7 +53,9 @@ AccountConfiguredSetupWizardState::AccountConfiguredSetupWizardState(SetupWizard return _context->accountBuilder().serverUrl(); }(); - QString defaultSyncTargetDir = FolderMan::suggestSyncFolder(urlToSuggestSyncFolderFor, _context->accountBuilder().displayName()); + // We need some sync root, either for spaces, or for OC10. It's never a Space folder. + auto folderType = FolderMan::NewFolderType::SpacesSyncRoot; + QString defaultSyncTargetDir = FolderMan::suggestSyncFolder(urlToSuggestSyncFolderFor, _context->accountBuilder().displayName(), folderType); QString syncTargetDir = _context->accountBuilder().syncTargetDir(); if (syncTargetDir.isEmpty()) { @@ -86,7 +88,8 @@ void AccountConfiguredSetupWizardState::evaluatePage() return; } - QString invalidPathErrorMessage = FolderMan::checkPathValidityRecursive(syncTargetDir); + // Doesn't matter wether it's a spaces sync root or a oc10 sync root + QString invalidPathErrorMessage = FolderMan::checkPathValidityRecursive(syncTargetDir, FolderMan::NewFolderType::SpacesSyncRoot, {}); if (!invalidPathErrorMessage.isEmpty()) { Q_EMIT evaluationFailed(errorMessageTemplate.arg(invalidPathErrorMessage)); return; diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index a948fb3f147..53520da5381 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -93,7 +93,7 @@ void setUpInitialSyncFolder(AccountStatePtr accountStatePtr, bool useVfs) Utility::setupFavLink(localDir); for (const auto *space : spaces) { const QString name = space->displayName(); - const QString folderName = FolderMan::instance()->findGoodPathForNewSyncFolder(localDir, name); + const QString folderName = FolderMan::instance()->findGoodPathForNewSyncFolder(localDir, name, FolderMan::NewFolderType::SpacesFolder); auto folder = addFolder(folderName, {}, QUrl(space->drive().getRoot().getWebDavUrl()), space->drive().getRoot().getId(), name); folder->setPriority(space->priority()); // save the new priority diff --git a/test/testfolderman.cpp b/test/testfolderman.cpp index 9b4e74d9384..27594f6f523 100644 --- a/test/testfolderman.cpp +++ b/test/testfolderman.cpp @@ -57,27 +57,29 @@ private slots: QVERIFY(folderman->addFolder( newAccountState.get(), TestUtils::createDummyFolderDefinition(newAccountState->account(), dirPath + QStringLiteral("/ownCloud2")))); + const auto type = FolderMan::NewFolderType::OC10SyncRoot; + const QUuid uuid = {}; // those should be allowed // QString FolderMan::checkPathValidityForNewFolder(const QString& path, const QUrl &serverUrl, bool forNewDirectory) - QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/free")), QString()); - QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/free2/")), QString()); + QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/free"), type, uuid), QString()); + QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/free2/"), type, uuid), QString()); // Not an existing directory -> Ok - QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/bliblablu")), QString()); - QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/free/bliblablu")), QString()); + QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/bliblablu"), type, uuid), QString()); + QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/free/bliblablu"), type, uuid), QString()); // QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/bliblablu/some/more")), QString()); // A file -> Error - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/file.txt")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/file.txt"), type, uuid).isNull()); // The following both fail because they refer to the same account - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/"), type, uuid).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder/f")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath, type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder/f"), type, uuid).isNull()); #ifndef Q_OS_WIN // no links on windows, no permissions // make a bunch of links @@ -87,75 +89,75 @@ private slots: QVERIFY(QFile::link(dirPath + QStringLiteral("/sub/ownCloud1/folder"), dirPath + QStringLiteral("/link4"))); // Ok - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link1")).isNull()); - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2/free")).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link1"), type, uuid).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2/free"), type, uuid).isNull()); // Not Ok - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2"), type, uuid).isNull()); // link 3 points to an existing sync folder. To make it fail, the account must be the same - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3"), type, uuid).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link4")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3/folder")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link4"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3/folder"), type, uuid).isNull()); // test some non existing sub path (error) - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/some/sub/path")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/blublu")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder/g/h")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3/folder/neu_folder")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/some/sub/path"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/blublu"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/sub/ownCloud1/folder/g/h"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link3/folder/neu_folder"), type, uuid).isNull()); // Subfolder of links - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link1/subfolder")).isNull()); - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2/free/subfolder")).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link1/subfolder"), type, uuid).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/link2/free/subfolder"), type, uuid).isNull()); if (getuid() != 0) { // Should not have the rights - QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("/")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("/usr/bin/somefolder")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("/"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("/usr/bin/somefolder"), type, uuid).isNull()); } #endif -#ifdef Q_OS_WIN // drive-letter tests - if (!QFileInfo(QStringLiteral("v:/")).exists()) { - QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:/")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:/foo")).isNull()); + if (Utility::isWindows()) { // drive-letter tests + if (!QFileInfo(QStringLiteral("v:/")).exists()) { + QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:/"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(QStringLiteral("v:/foo"), type, uuid).isNull()); + } + if (QFileInfo(QStringLiteral("c:/")).isWritable()) { + QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:"), type, uuid).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:/"), type, uuid).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:/foo"), type, uuid).isNull()); + } } - if (QFileInfo(QStringLiteral("c:/")).isWritable()) { - QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:")).isNull()); - QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:/")).isNull()); - QVERIFY(folderman->checkPathValidityForNewFolder(QStringLiteral("c:/foo")).isNull()); - } -#endif // Invalid paths - QVERIFY(!folderman->checkPathValidityForNewFolder(QString()).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder({}, type, uuid).isNull()); // REMOVE ownCloud2 from the filesystem, but keep a folder sync'ed to it. QDir(dirPath + QStringLiteral("/ownCloud2/")).removeRecursively(); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/blublu")).isNull()); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/sub/subsub/sub")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/blublu"), type, uuid).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/ownCloud2/sub/subsub/sub"), type, uuid).isNull()); { // check for rejection of a directory with `.sync_*.db` QVERIFY(dir2.mkpath(QStringLiteral("db-check1"))); - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check1")).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check1"), type, uuid).isNull()); QFile f(dirPath + QStringLiteral("/db-check1/.sync_something.db")); QVERIFY(f.open(QFile::Truncate | QFile::WriteOnly)); f.close(); QVERIFY(QFileInfo::exists(dirPath + QStringLiteral("/db-check1/.sync_something.db"))); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check1")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check1"), type, uuid).isNull()); } { // check for rejection of a directory with `._sync_*.db` QVERIFY(dir2.mkpath(QStringLiteral("db-check2"))); - QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check2")).isNull()); + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check2"), type, uuid).isNull()); QFile f(dirPath + QStringLiteral("/db-check2/._sync_something.db")); QVERIFY(f.open(QFile::Truncate | QFile::WriteOnly)); f.close(); QVERIFY(QFileInfo::exists(dirPath + QStringLiteral("/db-check2/._sync_something.db"))); - QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check2")).isNull()); + QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/db-check2"), type, uuid).isNull()); } } @@ -183,28 +185,73 @@ private slots: newAccountState.get(), TestUtils::createDummyFolderDefinition(newAccountState->account(), dirPath + QStringLiteral("/ownCloud (2)/")))); // TEST + const auto folderType = FolderMan::NewFolderType::OC10SyncRoot; - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("oc")), dirPath + QStringLiteral("/oc")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud")), dirPath + QStringLiteral("/ownCloud (3)")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2")), dirPath + QStringLiteral("/ownCloud2 (2)")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud (2)")), dirPath + QStringLiteral("/ownCloud (2) (2)")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2/foo")), dirPath + QStringLiteral("/ownCloud2_foo")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2/bar")), dirPath + QStringLiteral("/ownCloud2_bar")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("sub")), dirPath + QStringLiteral("/sub (2)")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("oc"), folderType), dirPath + QStringLiteral("/oc")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud"), folderType), dirPath + QStringLiteral("/ownCloud (3)")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2"), folderType), dirPath + QStringLiteral("/ownCloud2 (2)")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud (2)"), folderType), dirPath + QStringLiteral("/ownCloud (2) (2)")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2/foo"), folderType), dirPath + QStringLiteral("/ownCloud2_foo")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2/bar"), folderType), dirPath + QStringLiteral("/ownCloud2_bar")); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("sub"), folderType), dirPath + QStringLiteral("/sub (2)")); // REMOVE ownCloud2 from the filesystem, but keep a folder sync'ed to it. // We should still not suggest this folder as a new folder. QDir(dirPath + QStringLiteral("/ownCloud (2)/")).removeRecursively(); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud")), dirPath + QStringLiteral("/ownCloud (3)")); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2")), QString(dirPath + QStringLiteral("/ownCloud2 (2)"))); - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud (2)")), QString(dirPath + QStringLiteral("/ownCloud (2) (2)"))); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud"), folderType), dirPath + QStringLiteral("/ownCloud (3)")); + QCOMPARE( + folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud2"), folderType), QString(dirPath + QStringLiteral("/ownCloud2 (2)"))); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("ownCloud (2)"), folderType), + QString(dirPath + QStringLiteral("/ownCloud (2) (2)"))); // make sure people can't do evil stuff - QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("../../../Bo/b")), QString(dirPath + QStringLiteral("/___Bo_b"))); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral("../../../Bo/b"), folderType), QString(dirPath + QStringLiteral("/___Bo_b"))); // normalise the name - QCOMPARE( - folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral(" Bo:*<>!b ")), QString(dirPath + QStringLiteral("/Bo____!b"))); + QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath, QStringLiteral(" Bo:*<>!b "), folderType), + QString(dirPath + QStringLiteral("/Bo____!b"))); + } + + void testSpacesSyncRootAndFolderCreation() + { + auto dir = TestUtils::createTempDir(); + QVERIFY(dir.isValid()); + QDir dir2(dir.path()); + + // Create a sync root for another account + QVERIFY(dir2.mkpath(QStringLiteral("AnotherSpacesSyncRoot"))); + const auto anotherUuid = QUuid::createUuid(); + Utility::markDirectoryAsSyncRoot(dir2.filePath(QStringLiteral("AnotherSpacesSyncRoot")), anotherUuid); + + FolderMan *folderman = TestUtils::folderMan(); + QCOMPARE(folderman, FolderMan::instance()); + + QString dirPath = dir2.canonicalPath(); + const auto ourUuid = QUuid::createUuid(); + + // Spaces Sync Root in another Spaces Sync Root should fail + QVERIFY(!folderman + ->checkPathValidityForNewFolder( + dirPath + QStringLiteral("/AnotherSpacesSyncRoot/OurSpacesSyncRoot"), FolderMan::NewFolderType::SpacesSyncRoot, ourUuid) + .isNull()); + // Spaces Sync Root one level up should be fine + QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + QStringLiteral("/OurSpacesSyncRoot"), FolderMan::NewFolderType::SpacesSyncRoot, ourUuid) + .isNull()); + + // Create the sync root so we can test Spaces Folder creation below + QVERIFY(dir2.mkpath(QStringLiteral("OurSpacesSyncRoot"))); + Utility::markDirectoryAsSyncRoot(dir2.filePath(QStringLiteral("OurSpacesSyncRoot")), ourUuid); + + // A folder for a Space in a sync root for another account should fail + QVERIFY(!folderman + ->checkPathValidityForNewFolder( + dirPath + QStringLiteral("/AnotherSpacesSyncRoot/OurSpacesFolder"), FolderMan::NewFolderType::SpacesFolder, ourUuid) + .isNull()); + // But in our sync root that should just be fine + QVERIFY( + folderman + ->checkPathValidityForNewFolder(dirPath + QStringLiteral("/OurSpacesSyncRoot/OurSpacesFolder"), FolderMan::NewFolderType::SpacesFolder, ourUuid) + .isNull()); } };