From c5dd2e89a1c7abe5a5ac67ce05bfdcb9e7b6b40b Mon Sep 17 00:00:00 2001 From: alex-z Date: Sat, 9 Mar 2024 22:17:27 +0100 Subject: [PATCH] E2EE. Fix root metadata fetching path for non-root remote sync folder. Refactoring. Stabilizing paths. Signed-off-by: alex-z --- src/common/utility.cpp | 10 +++- src/common/utility.h | 3 +- src/gui/filedetails/sharemodel.cpp | 3 ++ src/gui/folderman.cpp | 5 +- src/gui/sharemanager.cpp | 7 ++- src/gui/sharemanager.h | 4 +- src/gui/socketapi/socketapi.cpp | 8 +-- .../basepropagateremotedeleteencrypted.cpp | 10 ++-- src/libsync/discovery.cpp | 5 +- src/libsync/discoveryphase.cpp | 8 ++- src/libsync/discoveryphase.h | 2 + .../encryptedfoldermetadatahandler.cpp | 36 +++++++++---- src/libsync/encryptedfoldermetadatahandler.h | 5 +- src/libsync/encryptfolderjob.cpp | 10 ++-- src/libsync/foldermetadata.cpp | 12 +++-- src/libsync/foldermetadata.h | 4 +- src/libsync/propagatedownloadencrypted.cpp | 12 +---- src/libsync/propagateuploadencrypted.cpp | 27 +++------- src/libsync/updatee2eefoldermetadatajob.cpp | 5 +- .../updatee2eefolderusersmetadatajob.cpp | 51 +++++++++---------- .../updatee2eefolderusersmetadatajob.h | 4 +- src/libsync/updatemigratede2eemetadatajob.cpp | 13 ++--- src/libsync/updatemigratede2eemetadatajob.h | 4 +- src/libsync/vfs/cfapi/hydrationjob.cpp | 34 +++++-------- src/libsync/vfs/cfapi/hydrationjob.h | 6 +-- src/libsync/vfs/cfapi/vfs_cfapi.cpp | 2 +- test/testclientsideencryptionv2.cpp | 10 ++-- test/testsecurefiledrop.cpp | 6 +-- 28 files changed, 160 insertions(+), 146 deletions(-) diff --git a/src/common/utility.cpp b/src/common/utility.cpp index a2a315cadc5af..b95d40db24ed3 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -681,6 +681,12 @@ bool Utility::isCaseClashConflictFile(const QString &name) return bname.contains(QStringLiteral("(case clash from")); } +QString Utility::leadingSlashPath(const QString &path) +{ + static const auto slash = QLatin1Char('/'); + return !path.startsWith(slash) ? QString(slash + path) : path; +} + QString Utility::trailingSlashPath(const QString &path) { static const auto slash = QLatin1Char('/'); @@ -690,13 +696,13 @@ QString Utility::trailingSlashPath(const QString &path) QString Utility::noLeadingSlashPath(const QString &path) { static const auto slash = QLatin1Char('/'); - return path.startsWith(slash) ? path.mid(1) : path; + return path.size() > 1 && path.startsWith(slash) ? path.mid(1) : path; } QString Utility::noTrailingSlashPath(const QString &path) { static const auto slash = QLatin1Char('/'); - return path.endsWith(slash) ? path.chopped(1) : path; + return path.size() > 1 && path.endsWith(slash) ? path.chopped(1) : path; } QString Utility::fullRemotePathToRemoteSyncRootRelative(const QString &fullRemotePath, const QString &remoteSyncRoot) diff --git a/src/common/utility.h b/src/common/utility.h index c7b3668bdbcbf..d8c8a19927136 100644 --- a/src/common/utility.h +++ b/src/common/utility.h @@ -266,7 +266,8 @@ namespace Utility { * @brief Registers the desktop app as a handler for a custom URI to enable local editing */ OCSYNC_EXPORT void registerUriHandlerForLocalEditing(); - + + OCSYNC_EXPORT QString leadingSlashPath(const QString &path); OCSYNC_EXPORT QString trailingSlashPath(const QString &path); OCSYNC_EXPORT QString noLeadingSlashPath(const QString &path); OCSYNC_EXPORT QString noTrailingSlashPath(const QString &path); diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index b09c8c81783c5..d44c2837ef8c6 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -857,6 +857,9 @@ void ShareModel::slotDeleteE2EeShare(const SharePtr &share) const return; } + Q_ASSERT(folder->remotePath() == QStringLiteral("/") + || Utility::noLeadingSlashPath(share->path()).startsWith(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(folder->remotePath())))); + const auto removeE2eeShareJob = new UpdateE2eeFolderUsersMetadataJob(account, folder->journalDb(), folder->remotePath(), diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 36aedb2b29464..ffced3a51ccc0 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -1537,7 +1537,7 @@ void FolderMan::leaveShare(const QString &localFile) { const auto localFileNoTrailingSlash = localFile.endsWith('/') ? localFile.chopped(1) : localFile; if (const auto folder = FolderMan::instance()->folderForPath(localFileNoTrailingSlash)) { - const auto filePathRelative = QString(localFileNoTrailingSlash).remove(folder->path()); + const auto filePathRelative = Utility::noLeadingSlashPath(QString(localFileNoTrailingSlash).remove(folder->path())); SyncJournalFileRecord rec; if (folder->journalDb()->getFileRecord(filePathRelative, &rec) @@ -1551,8 +1551,7 @@ void FolderMan::leaveShare(const QString &localFile) folder->journalDb(), folder->remotePath(), UpdateE2eeFolderUsersMetadataJob::Remove, - //TODO: Might need to add a slash to "filePathRelative" once the server is working - filePathRelative, + folder->remotePathTrailingSlash() + filePathRelative, folder->accountState()->account()->davUser()); _removeE2eeShareJob->setParent(this); _removeE2eeShareJob->start(true); diff --git a/src/gui/sharemanager.cpp b/src/gui/sharemanager.cpp index 76bceee89d12e..62c39b4cdc752 100644 --- a/src/gui/sharemanager.cpp +++ b/src/gui/sharemanager.cpp @@ -488,7 +488,7 @@ void ShareManager::createShare(const QString &path, job->getSharedWithMe(); } -void ShareManager::createE2EeShareJob(const QString &path, +void ShareManager::createE2EeShareJob(const QString &fullRemotePath, const ShareePtr sharee, const Share::Permissions permissions, const QString &password) @@ -506,11 +506,14 @@ void ShareManager::createE2EeShareJob(const QString &path, return; } + Q_ASSERT(folder->remotePath() == QStringLiteral("/") || + Utility::noLeadingSlashPath(fullRemotePath).startsWith(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(folder->remotePath())))); + const auto createE2eeShareJob = new UpdateE2eeFolderUsersMetadataJob(_account, folder->journalDb(), folder->remotePath(), UpdateE2eeFolderUsersMetadataJob::Add, - path, + fullRemotePath, sharee->shareWith(), QSslCertificate{}, this); diff --git a/src/gui/sharemanager.h b/src/gui/sharemanager.h index 66844dec21e63..e68620e315500 100644 --- a/src/gui/sharemanager.h +++ b/src/gui/sharemanager.h @@ -416,7 +416,7 @@ class ShareManager : public QObject /** * Tell the manager to create and start new UpdateE2eeShareMetadataJob job * - * @param path The path of the share relative to the user folder on the server + * @param fullRemotePath The path of the share relative to the user folder on the server * @param shareType The type of share (TypeUser, TypeGroup, TypeRemote) * @param Permissions The share permissions * @param folderId The id for an E2EE folder @@ -425,7 +425,7 @@ class ShareManager : public QObject * On success the signal shareCreated is emitted * In case of a server error the serverError signal is emitted */ - void createE2EeShareJob(const QString &path, + void createE2EeShareJob(const QString &fullRemotePath, const ShareePtr sharee, const Share::Permissions permissions, const QString &password = ""); diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 69cd4127f5f5b..dfc70d067ea3e 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -550,13 +550,7 @@ void SocketApi::processEncryptRequest(const QString &localFile) auto path = rec._path; // Folder records have directory paths in Foo/Bar/ convention... // But EncryptFolderJob expects directory path Foo/Bar convention - auto choppedPath = path; - if (choppedPath.endsWith('/') && choppedPath != QStringLiteral("/")) { - choppedPath.chop(1); - } - if (choppedPath.startsWith('/') && choppedPath != QStringLiteral("/")) { - choppedPath = choppedPath.mid(1); - } + const auto choppedPath = Utility::noTrailingSlashPath(Utility::noLeadingSlashPath(path)); auto job = new OCC::EncryptFolderJob(account, folder->journalDb(), choppedPath, choppedPath, folder->remotePath(), rec.numericFileId()); job->setParent(this); diff --git a/src/libsync/basepropagateremotedeleteencrypted.cpp b/src/libsync/basepropagateremotedeleteencrypted.cpp index 8828a37252a1d..4baf8fa8cae61 100644 --- a/src/libsync/basepropagateremotedeleteencrypted.cpp +++ b/src/libsync/basepropagateremotedeleteencrypted.cpp @@ -57,17 +57,17 @@ void BasePropagateRemoteDeleteEncrypted::storeFirstErrorString(const QString &er void BasePropagateRemoteDeleteEncrypted::fetchMetadataForPath(const QString &path) { - qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Folder is encrypted, let's its metadata."; - _fullFolderRemotePath = _propagator->fullRemotePath(path); - + qCDebug(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Folder is encrypted, let's fetch its metadata."; + SyncJournalFileRecord rec; - if (!_propagator->_journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_fullFolderRemotePath, _propagator->remotePath()), &rec) || !rec.isValid()) { + if (!_propagator->_journal->getRootE2eFolderRecord(Utility::noLeadingSlashPath(path), &rec) || !rec.isValid()) { taskFailed(); return; } _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_propagator->account(), - _fullFolderRemotePath, + _propagator->fullRemotePath(path), + _propagator->remotePath(), _propagator->_journal, rec.path())); diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index a9fb2d63b2f14..461b4bbd1d5a4 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -2024,10 +2024,11 @@ void ProcessDirectoryJob::chopVirtualFileSuffix(QString &str) const DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery() { if (_dirItem && _dirItem->isEncrypted() && _dirItem->_encryptedFileName.isEmpty()) { - _discoveryData->_topLevelE2eeFolderPaths.insert(QLatin1Char('/') + _dirItem->_file); + _discoveryData->_topLevelE2eeFolderPaths.insert(_discoveryData->_remoteFolder + _dirItem->_file); } auto serverJob = new DiscoverySingleDirectoryJob(_discoveryData->_account, - _discoveryData->_remoteFolder + _currentFolder._server, + _currentFolder._server, + _discoveryData->_remoteFolder, _discoveryData->_topLevelE2eeFolderPaths, this); if (!_dirItem) { diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index ce492d9726299..fdf66e4058b02 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -371,13 +371,16 @@ void DiscoverySingleLocalDirectoryJob::run() { DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, + const QString &remoteRootFolderPath, const QSet &topLevelE2eeFolderPaths, QObject *parent) : QObject(parent) - , _subPath(path) + , _subPath(remoteRootFolderPath + path) + , _remoteRootFolderPath(remoteRootFolderPath) , _account(account) , _topLevelE2eeFolderPaths(topLevelE2eeFolderPaths) { + Q_ASSERT(!_remoteRootFolderPath.isEmpty()); } void DiscoverySingleDirectoryJob::start() @@ -692,8 +695,9 @@ void DiscoverySingleDirectoryJob::metadataReceived(const QJsonDocument &json, in } const auto e2EeFolderMetadata = new FolderMetadata(_account, + _remoteRootFolderPath, statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact), - RootEncryptedFolderInfo(topLevelFolderPath), + RootEncryptedFolderInfo(Utility::fullRemotePathToRemoteSyncRootRelative(topLevelFolderPath, _remoteRootFolderPath)), job->signature()); connect(e2EeFolderMetadata, &FolderMetadata::setupComplete, this, [this, e2EeFolderMetadata] { e2EeFolderMetadata->deleteLater(); diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 4d6dcea40644f..89012df25f878 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -145,6 +145,7 @@ class DiscoverySingleDirectoryJob : public QObject public: explicit DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, + const QString &remoteRootFolderPath, /* TODO for topLevelE2eeFolderPaths, from review: I still do not get why giving the whole QSet instead of just the parent of the folder we are in sounds to me like it would be much more efficient to just have the e2ee parent folder that we are inside*/ @@ -179,6 +180,7 @@ private slots: QVector _results; QString _subPath; + QString _remoteRootFolderPath; QByteArray _firstEtag; QByteArray _fileId; QByteArray _localFileId; diff --git a/src/libsync/encryptedfoldermetadatahandler.cpp b/src/libsync/encryptedfoldermetadatahandler.cpp index 412fe6e0e6623..65a394646cc8c 100644 --- a/src/libsync/encryptedfoldermetadatahandler.cpp +++ b/src/libsync/encryptedfoldermetadatahandler.cpp @@ -32,17 +32,22 @@ Q_LOGGING_CATEGORY(lcFetchAndUploadE2eeFolderMetadataJob, "nextcloud.sync.propag namespace OCC { EncryptedFolderMetadataHandler::EncryptedFolderMetadataHandler(const AccountPtr &account, - const QString &folderPath, + const QString &folderFullRemotePath, + const QString &remoteFolderRoot, SyncJournalDb *const journalDb, const QString &pathForTopLevelFolder, QObject *parent) : QObject(parent) , _account(account) - , _folderPath(folderPath) , _journalDb(journalDb) + , _folderFullRemotePath(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(folderFullRemotePath))) + , _remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot))) { - _rootEncryptedFolderInfo = RootEncryptedFolderInfo( - RootEncryptedFolderInfo::createRootPath(folderPath, pathForTopLevelFolder)); + Q_ASSERT(!_remoteFolderRoot.isEmpty()); + Q_ASSERT(_remoteFolderRoot == QStringLiteral("/") || _folderFullRemotePath.startsWith(_remoteFolderRoot)); + + const auto folderRelativePath = Utility::fullRemotePathToRemoteSyncRootRelative(_folderFullRemotePath, _remoteFolderRoot); + _rootEncryptedFolderInfo = RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(folderRelativePath, pathForTopLevelFolder)); } void EncryptedFolderMetadataHandler::fetchMetadata(const FetchMode fetchMode) @@ -53,9 +58,22 @@ void EncryptedFolderMetadataHandler::fetchMetadata(const FetchMode fetchMode) void EncryptedFolderMetadataHandler::fetchMetadata(const RootEncryptedFolderInfo &rootEncryptedFolderInfo, const FetchMode fetchMode) { + Q_ASSERT(!rootEncryptedFolderInfo.path.isEmpty()); + if (rootEncryptedFolderInfo.path.isEmpty()) { + qCWarning(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for" << _folderFullRemotePath << ". Invalid rootEncryptedFolderInfo!"; + emit fetchFinished(-1, tr("Error fetching metadata.")); + return; + } + _rootEncryptedFolderInfo = rootEncryptedFolderInfo; if (_rootEncryptedFolderInfo.path.isEmpty()) { - qCWarning(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for" << _folderPath << ". Invalid _rootEncryptedFolderInfo!"; + qCWarning(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for" << _folderFullRemotePath << ". Invalid _rootEncryptedFolderInfo!"; + emit fetchFinished(-1, tr("Error fetching metadata.")); + return; + } + if (_remoteFolderRoot != QStringLiteral("/") && !_folderFullRemotePath.startsWith(_remoteFolderRoot)) { + qCWarning(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for" << _folderFullRemotePath + << " and remote root" << _remoteFolderRoot << ". Invalid _remoteFolderRoot or _folderFullRemotePath!"; emit fetchFinished(-1, tr("Error fetching metadata.")); return; } @@ -99,7 +117,7 @@ void EncryptedFolderMetadataHandler::startFetchMetadata() void EncryptedFolderMetadataHandler::fetchFolderEncryptedId() { qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Folder is encrypted, let's get the Id from it."; - const auto job = new LsColJob(_account, _folderPath); + const auto job = new LsColJob(_account, _folderFullRemotePath); job->setProperties({"resourcetype", "http://owncloud.org/ns:fileid"}); connect(job, &LsColJob::directoryListingSubfolders, this, &EncryptedFolderMetadataHandler::slotFolderEncryptedIdReceived); connect(job, &LsColJob::finishedWithError, this, &EncryptedFolderMetadataHandler::slotFolderEncryptedIdError); @@ -167,17 +185,17 @@ void EncryptedFolderMetadataHandler::slotMetadataReceived(const QJsonDocument &j if (statusCode != 200 && statusCode != 404) { // neither successfully fetched, nor a folder without a metadata, fail further logic - qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for folder" << _folderPath; + qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error fetching metadata for folder" << _folderFullRemotePath; emit fetchFinished(statusCode, tr("Error fetching metadata.")); return; } const auto rawMetadata = statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact); - const auto metadata(QSharedPointer::create(_account, rawMetadata, _rootEncryptedFolderInfo, job->signature())); + const auto metadata(QSharedPointer::create(_account, _remoteFolderRoot, rawMetadata, _rootEncryptedFolderInfo, job->signature())); connect(metadata.data(), &FolderMetadata::setupComplete, this, [this, metadata] { if (!metadata->isValid()) { - qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error parsing or decrypting metadata for folder" << _folderPath; + qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error parsing or decrypting metadata for folder" << _folderFullRemotePath; emit fetchFinished(-1, tr("Error parsing or decrypting metadata.")); return; } diff --git a/src/libsync/encryptedfoldermetadatahandler.h b/src/libsync/encryptedfoldermetadatahandler.h index dcecce5c70eba..cb5bb1f08d78b 100644 --- a/src/libsync/encryptedfoldermetadatahandler.h +++ b/src/libsync/encryptedfoldermetadatahandler.h @@ -50,7 +50,7 @@ class OWNCLOUDSYNC_EXPORT EncryptedFolderMetadataHandler }; Q_ENUM(UnlockFolderWithResult); - explicit EncryptedFolderMetadataHandler(const AccountPtr &account, const QString &folderPath, SyncJournalDb *const journalDb, const QString &pathForTopLevelFolder, QObject *parent = nullptr); + explicit EncryptedFolderMetadataHandler(const AccountPtr &account, const QString &folderPath, const QString &remoteFolderRoot, SyncJournalDb *const journalDb, const QString &pathForTopLevelFolder, QObject *parent = nullptr); [[nodiscard]] QSharedPointer folderMetadata() const; @@ -101,8 +101,9 @@ public: signals: private: AccountPtr _account; - QString _folderPath; QPointer _journalDb; + QString _folderFullRemotePath; + QString _remoteFolderRoot; QByteArray _folderId; QByteArray _folderToken; diff --git a/src/libsync/encryptfolderjob.cpp b/src/libsync/encryptfolderjob.cpp index dba7c8593f8fd..295e24dd0c60f 100644 --- a/src/libsync/encryptfolderjob.cpp +++ b/src/libsync/encryptfolderjob.cpp @@ -37,8 +37,10 @@ EncryptFolderJob::EncryptFolderJob(const AccountPtr &account, SyncJournalDb *jou { SyncJournalFileRecord rec; const auto currentPath = !_pathNonEncrypted.isEmpty() ? _pathNonEncrypted : _path; + const auto currentPathRelative = Utility::fullRemotePathToRemoteSyncRootRelative(currentPath, _remoteSyncRootPath); + const QString fullRemotePath = Utility::trailingSlashPath(Utility::noLeadingSlashPath(_remoteSyncRootPath)) + currentPathRelative; [[maybe_unused]] const auto result = _journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(currentPath, _remoteSyncRootPath), &rec); - _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(account, _path, _journal, rec.path())); + _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(account, fullRemotePath, _remoteSyncRootPath, _journal, rec.path())); } void EncryptFolderJob::slotSetEncryptionFlag() @@ -102,16 +104,18 @@ void EncryptFolderJob::slotEncryptionFlagError(const QByteArray &fileId, void EncryptFolderJob::uploadMetadata() { const auto currentPath = !_pathNonEncrypted.isEmpty() ? _pathNonEncrypted : _path; + const auto currentPathRelative = Utility::fullRemotePathToRemoteSyncRootRelative(currentPath, _remoteSyncRootPath); SyncJournalFileRecord rec; - if (!_journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(currentPath, _remoteSyncRootPath), &rec)) { + if (!_journal->getRootE2eFolderRecord(currentPathRelative, &rec)) { emit finished(Error, EncryptionStatusEnums::ItemEncryptionStatus::NotEncrypted); return; } const auto emptyMetadata(QSharedPointer::create( _account, + _remoteSyncRootPath, QByteArray{}, - RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(currentPath, rec.path())), + RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(currentPathRelative, rec.path())), QByteArray{})); connect(emptyMetadata.data(), &FolderMetadata::setupComplete, this, [this, emptyMetadata] { diff --git a/src/libsync/foldermetadata.cpp b/src/libsync/foldermetadata.cpp index a01a1898250fb..4374093325244 100644 --- a/src/libsync/foldermetadata.cpp +++ b/src/libsync/foldermetadata.cpp @@ -60,21 +60,25 @@ bool FolderMetadata::EncryptedFile::isDirectory() const return mimetype.isEmpty() || mimetype == QByteArrayLiteral("inode/directory") || mimetype == QByteArrayLiteral("httpd/unix-directory"); } -FolderMetadata::FolderMetadata(AccountPtr account, FolderType folderType) - : _account(account), +FolderMetadata::FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType) : + _account(account), + _remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot))), _isRootEncryptedFolder(folderType == FolderType::Root) { + Q_ASSERT(!_remoteFolderRoot.isEmpty()); qCInfo(lcCseMetadata()) << "Setting up an Empty Metadata"; initEmptyMetadata(); } FolderMetadata::FolderMetadata(AccountPtr account, + const QString &remoteFolderRoot, const QByteArray &metadata, const RootEncryptedFolderInfo &rootEncryptedFolderInfo, const QByteArray &signature, QObject *parent) : QObject(parent) , _account(account) + , _remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot))) , _initialMetadata(metadata) , _isRootEncryptedFolder(rootEncryptedFolderInfo.path == QStringLiteral("/")) , _metadataKeyForEncryption(rootEncryptedFolderInfo.keyForEncryption) @@ -82,6 +86,7 @@ FolderMetadata::FolderMetadata(AccountPtr account, , _keyChecksums(rootEncryptedFolderInfo.keyChecksums) , _initialSignature(signature) { + Q_ASSERT(!_remoteFolderRoot.isEmpty()); setupVersionFromExistingMetadata(metadata); const auto doc = QJsonDocument::fromJson(metadata); @@ -987,7 +992,8 @@ bool FolderMetadata::moveFromFileDropToFiles() void FolderMetadata::startFetchRootE2eeFolderMetadata(const QString &path) { - _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_account, path, nullptr, "/")); + Q_ASSERT(!_remoteFolderRoot.isEmpty()); + _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_account, Utility::trailingSlashPath(_remoteFolderRoot) + path, _remoteFolderRoot, nullptr, "/")); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, diff --git a/src/libsync/foldermetadata.h b/src/libsync/foldermetadata.h index 2a7bbc919e925..0cdba5236effd 100644 --- a/src/libsync/foldermetadata.h +++ b/src/libsync/foldermetadata.h @@ -93,13 +93,14 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject }; Q_ENUM(FolderType) - FolderMetadata(AccountPtr account, FolderType folderType = FolderType::Nested); + FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType = FolderType::Nested); /* * construct metadata based on RootEncryptedFolderInfo * as per E2EE V2, the encryption key and users that have access are only stored in root(top-level) encrypted folder's metadata * see: https://github.com/nextcloud/end_to_end_encryption_rfc/blob/v2.1/RFC.md */ FolderMetadata(AccountPtr account, + const QString &remoteFolderRoot, const QByteArray &metadata, const RootEncryptedFolderInfo &rootEncryptedFolderInfo, const QByteArray &signature, @@ -197,6 +198,7 @@ private slots: private: AccountPtr _account; + QString _remoteFolderRoot; QByteArray _initialMetadata; bool _isRootEncryptedFolder = false; diff --git a/src/libsync/propagatedownloadencrypted.cpp b/src/libsync/propagatedownloadencrypted.cpp index bb77c8a3e6f9c..568aa911a91fe 100644 --- a/src/libsync/propagatedownloadencrypted.cpp +++ b/src/libsync/propagatedownloadencrypted.cpp @@ -15,14 +15,7 @@ PropagateDownloadEncrypted::PropagateDownloadEncrypted(OwncloudPropagator *propa , _item(item) , _info(_item->_file) { - const auto rootPath = [=]() { - const auto result = _propagator->remotePath(); - if (result.startsWith('/')) { - return result.mid(1); - } else { - return result; - } - }(); + const auto rootPath = Utility::noLeadingSlashPath(_propagator->remotePath()); const auto remoteFilename = _item->_encryptedFileName.isEmpty() ? _item->_file : _item->_encryptedFileName; const auto remotePath = QString(rootPath + remoteFilename); const auto remoteParentPath = remotePath.left(remotePath.lastIndexOf('/')); @@ -42,8 +35,7 @@ void PropagateDownloadEncrypted::start() emit failed(); return; } - _encryptedFolderMetadataHandler.reset( - new EncryptedFolderMetadataHandler(_propagator->account(), _remoteParentPath, _propagator->_journal, rec.path())); + _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_propagator->account(), _remoteParentPath, _propagator->remotePath(), _propagator->_journal, rec.path())); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, diff --git a/src/libsync/propagateuploadencrypted.cpp b/src/libsync/propagateuploadencrypted.cpp index e6fd284ccbbed..c64483ed6f1ed 100644 --- a/src/libsync/propagateuploadencrypted.cpp +++ b/src/libsync/propagateuploadencrypted.cpp @@ -20,27 +20,13 @@ Q_LOGGING_CATEGORY(lcPropagateUploadEncrypted, "nextcloud.sync.propagator.upload PropagateUploadEncrypted::PropagateUploadEncrypted(OwncloudPropagator *propagator, const QString &remoteParentPath, SyncFileItemPtr item, QObject *parent) : QObject(parent) , _propagator(propagator) - , _remoteParentPath(remoteParentPath) + , _remoteParentPath(Utility::noLeadingSlashPath(remoteParentPath)) , _item(item) { - const auto rootPath = [=]() { - const auto result = _propagator->remotePath(); - if (result.startsWith('/')) { - return result.mid(1); - } else { - return result; - } - }(); - _remoteParentAbsolutePath = [=] { - auto path = QString(rootPath + _remoteParentPath); - if (path.endsWith('/')) { - path.chop(1); - } - return path; - }(); + const auto rootPath = Utility::trailingSlashPath(Utility::noLeadingSlashPath(_propagator->remotePath())); + _remoteParentAbsolutePath = Utility::noTrailingSlashPath(rootPath + _remoteParentPath); } - void PropagateUploadEncrypted::start() { /* If the file is in a encrypted folder, which we know, we wouldn't be here otherwise, @@ -63,6 +49,7 @@ void PropagateUploadEncrypted::start() } _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_propagator->account(), _remoteParentAbsolutePath, + _propagator->remotePath(), _propagator->_journal, rec.path())); @@ -143,7 +130,7 @@ void PropagateUploadEncrypted::slotFetchMetadataJobFinished(int statusCode, cons encryptedFile.initializationVector = EncryptionHelper::generateRandom(16); - _item->_encryptedFileName = _remoteParentPath + QLatin1Char('/') + encryptedFile.encryptedFilename; + _item->_encryptedFileName = Utility::trailingSlashPath(_remoteParentPath) + encryptedFile.encryptedFilename; _item->_e2eEncryptionStatusRemote = metadata->existingMetadataEncryptionStatus(); _item->_e2eEncryptionServerCapability = EncryptionStatusEnums::fromEndToEndEncryptionApiVersion(_propagator->account()->capabilities().clientSideEncryptionVersion()); @@ -193,8 +180,8 @@ void PropagateUploadEncrypted::slotUploadMetadataFinished(int statusCode, const qCDebug(lcPropagateUploadEncrypted) << "Encrypted Info:" << outputInfo.path() << outputInfo.fileName() << outputInfo.size(); qCDebug(lcPropagateUploadEncrypted) << "Finalizing the upload part, now the actuall uploader will take over"; - emit finalized(outputInfo.path() + QLatin1Char('/') + outputInfo.fileName(), - _remoteParentPath + QLatin1Char('/') + outputInfo.fileName(), + emit finalized(Utility::trailingSlashPath(outputInfo.path()) + outputInfo.fileName(), + Utility::trailingSlashPath(_remoteParentPath) + outputInfo.fileName(), outputInfo.size()); } diff --git a/src/libsync/updatee2eefoldermetadatajob.cpp b/src/libsync/updatee2eefoldermetadatajob.cpp index 6d9267944acb0..5ea537c816261 100644 --- a/src/libsync/updatee2eefoldermetadatajob.cpp +++ b/src/libsync/updatee2eefoldermetadatajob.cpp @@ -32,8 +32,9 @@ namespace OCC { UpdateE2eeFolderMetadataJob::UpdateE2eeFolderMetadataJob(OwncloudPropagator *propagator, const SyncFileItemPtr &item, const QString &encryptedRemotePath) : PropagatorJob(propagator), _item(item), - _encryptedRemotePath(encryptedRemotePath) + _encryptedRemotePath(Utility::noLeadingSlashPath(propagator->fullRemotePath(encryptedRemotePath))) { + Q_ASSERT(propagator->remotePath() == QStringLiteral("/") || _encryptedRemotePath.startsWith(Utility::noLeadingSlashPath(propagator->remotePath()))); } void UpdateE2eeFolderMetadataJob::start() @@ -47,7 +48,7 @@ void UpdateE2eeFolderMetadataJob::start() return; } _encryptedFolderMetadataHandler.reset( - new EncryptedFolderMetadataHandler(propagator()->account(), _encryptedRemotePath, propagator()->_journal, rec.path())); + new EncryptedFolderMetadataHandler(propagator()->account(), _encryptedRemotePath, propagator()->remotePath() , propagator()->_journal, rec.path())); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, this, &UpdateE2eeFolderMetadataJob::slotFetchMetadataJobFinished); diff --git a/src/libsync/updatee2eefolderusersmetadatajob.cpp b/src/libsync/updatee2eefolderusersmetadatajob.cpp index e69bd81e351d8..58f54b5a35e00 100644 --- a/src/libsync/updatee2eefolderusersmetadatajob.cpp +++ b/src/libsync/updatee2eefolderusersmetadatajob.cpp @@ -28,28 +28,26 @@ UpdateE2eeFolderUsersMetadataJob::UpdateE2eeFolderUsersMetadataJob(const Account SyncJournalDb *journalDb, const QString &syncFolderRemotePath, const Operation operation, - const QString &path, + const QString &fullRemotePath, const QString &folderUserId, const QSslCertificate &certificate, QObject *parent) : QObject(parent) , _account(account) , _journalDb(journalDb) - , _syncFolderRemotePath(syncFolderRemotePath) + , _syncFolderRemotePath(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(syncFolderRemotePath))) , _operation(operation) - , _path(path) + , _fullRemotePath(Utility::noLeadingSlashPath(fullRemotePath)) , _folderUserId(folderUserId) , _folderUserCertificate(certificate) { - const auto pathSanitized = _path.startsWith(QLatin1Char('/')) ? _path.mid(1) : _path; - const auto folderPath = _syncFolderRemotePath + pathSanitized; - + Q_ASSERT(_syncFolderRemotePath == QStringLiteral("/") || _fullRemotePath.startsWith(_syncFolderRemotePath)); SyncJournalFileRecord rec; - if (!_journalDb->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_path, _syncFolderRemotePath), &rec) || !rec.isValid()) { - qCDebug(lcUpdateE2eeFolderUsersMetadataJob) << "Could not get root E2ee folder recort for path" << _path; + if (!_journalDb->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_fullRemotePath, _syncFolderRemotePath), &rec) || !rec.isValid()) { + qCDebug(lcUpdateE2eeFolderUsersMetadataJob) << "Could not get root E2ee folder recort for path" << _fullRemotePath; return; } - _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_account, folderPath, _journalDb, rec.path())); + _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_account, _fullRemotePath, _syncFolderRemotePath, _journalDb, rec.path())); } void UpdateE2eeFolderUsersMetadataJob::start(const bool keepLock) @@ -57,7 +55,7 @@ void UpdateE2eeFolderUsersMetadataJob::start(const bool keepLock) qCWarning(lcUpdateE2eeFolderUsersMetadataJob) << "[DEBUG_LEAVE_SHARE]: UpdateE2eeFolderUsersMetadataJob::start"; if (!_encryptedFolderMetadataHandler) { - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } @@ -68,7 +66,7 @@ void UpdateE2eeFolderUsersMetadataJob::start(const bool keepLock) } _keepLock = keepLock; if (_operation != Operation::Add && _operation != Operation::Remove && _operation != Operation::ReEncrypt) { - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } @@ -93,15 +91,14 @@ void UpdateE2eeFolderUsersMetadataJob::slotStartE2eeMetadataJobs() return; } - const auto pathSanitized = _path.startsWith(QLatin1Char('/')) ? _path.mid(1) : _path; - const auto folderPath = _syncFolderRemotePath + pathSanitized; + const auto folderPathRelative = Utility::fullRemotePathToRemoteSyncRootRelative(_fullRemotePath, _syncFolderRemotePath); SyncJournalFileRecord rec; - if (!_journalDb->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_path, _syncFolderRemotePath), &rec) || !rec.isValid()) { - emit finished(404, tr("Could not find root encrypted folder for folder %1").arg(_path)); + if (!_journalDb->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(folderPathRelative, _syncFolderRemotePath), &rec) || !rec.isValid()) { + emit finished(404, tr("Could not find root encrypted folder for folder %1").arg(_fullRemotePath)); return; } - const auto rootEncFolderInfo = RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(folderPath, rec.path()), _metadataKeyForEncryption, _metadataKeyForDecryption, _keyChecksums); + const auto rootEncFolderInfo = RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(folderPathRelative, rec.path()), _metadataKeyForEncryption, _metadataKeyForDecryption, _keyChecksums); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, this, &UpdateE2eeFolderUsersMetadataJob::slotFetchMetadataJobFinished); _encryptedFolderMetadataHandler->fetchMetadata(rootEncFolderInfo, EncryptedFolderMetadataHandler::FetchMode::AllowEmptyMetadata); @@ -113,12 +110,12 @@ void UpdateE2eeFolderUsersMetadataJob::slotFetchMetadataJobFinished(int statusCo if (statusCode != 200) { qCritical(lcUpdateE2eeFolderUsersMetadataJob) << "fetch metadata finished with error" << statusCode << message; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } if (!_encryptedFolderMetadataHandler->folderMetadata() || !_encryptedFolderMetadataHandler->folderMetadata()->isValid()) { - emit finished(403, tr("Could not add or remove a folder user %1, for folder %2").arg(_folderUserId).arg(_path)); + emit finished(403, tr("Could not add or remove a folder user %1, for folder %2").arg(_folderUserId).arg(_fullRemotePath)); return; } startUpdate(); @@ -128,14 +125,14 @@ void UpdateE2eeFolderUsersMetadataJob::startUpdate() { if (_operation == Operation::Invalid) { qCDebug(lcUpdateE2eeFolderUsersMetadataJob) << "Invalid operation"; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } if (_operation == Operation::Add || _operation == Operation::Remove) { if (!_encryptedFolderMetadataHandler->folderMetadata()) { qCDebug(lcUpdateE2eeFolderUsersMetadataJob) << "Metadata is null"; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } @@ -145,7 +142,7 @@ void UpdateE2eeFolderUsersMetadataJob::startUpdate() if (!result) { qCDebug(lcUpdateE2eeFolderUsersMetadataJob) << "Could not perform operation" << _operation << "on metadata"; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); return; } @@ -166,7 +163,7 @@ void UpdateE2eeFolderUsersMetadataJob::slotUpdateMetadataFinished(int code, cons qCDebug(lcUpdateE2eeFolderUsersMetadataJob()) << "Unlocking the folder."; unlockFolder(EncryptedFolderMetadataHandler::UnlockFolderWithResult::Failure); } else { - emit finished(code, tr("Error updating metadata for a folder %1").arg(_path) + QStringLiteral(":%1").arg(message)); + emit finished(code, tr("Error updating metadata for a folder %1").arg(_fullRemotePath) + QStringLiteral(":%1").arg(message)); } return; } @@ -198,16 +195,16 @@ void UpdateE2eeFolderUsersMetadataJob::scheduleSubJobs() unlockFolder(EncryptedFolderMetadataHandler::UnlockFolderWithResult::Failure); } else { qCWarning(lcUpdateE2eeFolderUsersMetadataJob()) << "Metadata is invalid."; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath)); } return; } - const auto pathInDb = _path.mid(_syncFolderRemotePath.size()); + const auto pathInDb = Utility::fullRemotePathToRemoteSyncRootRelative(_fullRemotePath, _syncFolderRemotePath); [[maybe_unused]] const auto result = _journalDb->getFilesBelowPath(pathInDb.toUtf8(), [this](const SyncJournalFileRecord &record) { if (record.isDirectory()) { const auto folderMetadata = _encryptedFolderMetadataHandler->folderMetadata(); - const auto subJob = new UpdateE2eeFolderUsersMetadataJob(_account, _journalDb, _syncFolderRemotePath, UpdateE2eeFolderUsersMetadataJob::ReEncrypt, QString::fromUtf8(record._e2eMangledName)); + const auto subJob = new UpdateE2eeFolderUsersMetadataJob(_account, _journalDb, _syncFolderRemotePath, UpdateE2eeFolderUsersMetadataJob::ReEncrypt, Utility::trailingSlashPath(_syncFolderRemotePath) + QString::fromUtf8(record._e2eMangledName)); subJob->setMetadataKeyForEncryption(folderMetadata->metadataKeyForEncryption()); subJob->setMetadataKeyForDecryption(folderMetadata->metadataKeyForDecryption()); subJob->setKeyChecksums(folderMetadata->keyChecksums()); @@ -257,7 +254,7 @@ void UpdateE2eeFolderUsersMetadataJob::slotSubJobFinished(int code, const QStrin Q_ASSERT(job); if (!job) { qCWarning(lcUpdateE2eeFolderUsersMetadataJob) << "slotSubJobFinished must be invoked by signal"; - emit finished(-1, tr("Error updating metadata for a folder %1").arg(_path) + QStringLiteral(":%1").arg(message)); + emit finished(-1, tr("Error updating metadata for a folder %1").arg(_fullRemotePath) + QStringLiteral(":%1").arg(message)); subJobsFinished(false); return; } @@ -346,7 +343,7 @@ void UpdateE2eeFolderUsersMetadataJob::setSubJobSyncItems(const QHash _journalDb; QString _syncFolderRemotePath; Operation _operation = Invalid; - QString _path; + QString _fullRemotePath; QString _folderUserId; QSslCertificate _folderUserCertificate; QByteArray _folderToken; diff --git a/src/libsync/updatemigratede2eemetadatajob.cpp b/src/libsync/updatemigratede2eemetadatajob.cpp index bfb61081f4ccc..1dbf5bdddf45e 100644 --- a/src/libsync/updatemigratede2eemetadatajob.cpp +++ b/src/libsync/updatemigratede2eemetadatajob.cpp @@ -30,13 +30,14 @@ namespace OCC { UpdateMigratedE2eeMetadataJob::UpdateMigratedE2eeMetadataJob(OwncloudPropagator *propagator, const SyncFileItemPtr &syncFileItem, - const QString &path, + const QString &fullRemotePath, const QString &folderRemotePath) : PropagatorJob(propagator) , _item(syncFileItem) - , _path(path) - , _folderRemotePath(folderRemotePath) + , _fullRemotePath(fullRemotePath) + , _folderRemotePath(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(folderRemotePath))) { + Q_ASSERT(_fullRemotePath == QStringLiteral("/") || _fullRemotePath.startsWith(_folderRemotePath)); } void UpdateMigratedE2eeMetadataJob::start() @@ -45,7 +46,7 @@ void UpdateMigratedE2eeMetadataJob::start() propagator()->_journal, _folderRemotePath, UpdateE2eeFolderUsersMetadataJob::Add, - _path, + _fullRemotePath, propagator()->account()->davUser(), propagator()->account()->e2e()->_certificate); updateMedatadaAndSubfoldersJob->setParent(this); @@ -83,9 +84,9 @@ PropagatorJob::JobParallelism UpdateMigratedE2eeMetadataJob::parallelism() const return PropagatorJob::JobParallelism::WaitForFinished; } -QString UpdateMigratedE2eeMetadataJob::path() const +QString UpdateMigratedE2eeMetadataJob::fullRemotePath() const { - return _path; + return _fullRemotePath; } void UpdateMigratedE2eeMetadataJob::addSubJobItem(const QString &key, const SyncFileItemPtr &syncFileItem) diff --git a/src/libsync/updatemigratede2eemetadatajob.h b/src/libsync/updatemigratede2eemetadatajob.h index dbc18aa77c013..bf15973f2790c 100644 --- a/src/libsync/updatemigratede2eemetadatajob.h +++ b/src/libsync/updatemigratede2eemetadatajob.h @@ -33,7 +33,7 @@ class OWNCLOUDSYNC_EXPORT UpdateMigratedE2eeMetadataJob : public PropagatorJob [[nodiscard]] JobParallelism parallelism() const override; - [[nodiscard]] QString path() const; + [[nodiscard]] QString fullRemotePath() const; void addSubJobItem(const QString &key, const SyncFileItemPtr &syncFileItem); @@ -43,7 +43,7 @@ private slots: private: SyncFileItemPtr _item; QHash _subJobItems; - QString _path; + QString _fullRemotePath; QString _folderRemotePath; }; diff --git a/src/libsync/vfs/cfapi/hydrationjob.cpp b/src/libsync/vfs/cfapi/hydrationjob.cpp index 2e215ff5776ef..32ef42ee1997a 100644 --- a/src/libsync/vfs/cfapi/hydrationjob.cpp +++ b/src/libsync/vfs/cfapi/hydrationjob.cpp @@ -43,14 +43,14 @@ void OCC::HydrationJob::setAccount(const AccountPtr &account) _account = account; } -QString OCC::HydrationJob::remotePath() const +QString OCC::HydrationJob::remoteSyncRootPath() const { - return _remotePath; + return _remoteSyncRootPath; } -void OCC::HydrationJob::setRemotePath(const QString &remotePath) +void OCC::HydrationJob::setRemoteSyncRootPath(const QString &path) { - _remotePath = remotePath; + _remoteSyncRootPath = Utility::noLeadingSlashPath(path); } QString OCC::HydrationJob::localPath() const @@ -137,10 +137,10 @@ void OCC::HydrationJob::start() { Q_ASSERT(_account); Q_ASSERT(_journal); - Q_ASSERT(!_remotePath.isEmpty() && !_localPath.isEmpty()); + Q_ASSERT(!_remoteSyncRootPath.isEmpty() && !_localPath.isEmpty()); Q_ASSERT(!_requestId.isEmpty() && !_folderPath.isEmpty()); - Q_ASSERT(_remotePath.endsWith('/')); + Q_ASSERT(_remoteSyncRootPath.endsWith('/')); Q_ASSERT(_localPath.endsWith('/')); Q_ASSERT(!_folderPath.startsWith('/')); @@ -305,7 +305,7 @@ void OCC::HydrationJob::slotFetchMetadataJobFinished(int statusCode, const QStri if (encryptedFileExactName == file.encryptedFilename) { qCDebug(lcHydration) << "Found matching encrypted metadata for file, starting download" << _requestId << _folderPath; _transferDataSocket = _transferDataServer->nextPendingConnection(); - _job = new GETEncryptedFileJob(_account, _remotePath + e2eMangledName(), _transferDataSocket, {}, {}, 0, file, this); + _job = new GETEncryptedFileJob(_account, Utility::trailingSlashPath(_remoteSyncRootPath) + e2eMangledName(), _transferDataSocket, {}, {}, 0, file, this); connect(qobject_cast(_job), &GETEncryptedFileJob::finishedSignal, this, &HydrationJob::onGetFinished); _job->start(); @@ -347,7 +347,7 @@ void OCC::HydrationJob::handleNewConnection() { qCInfo(lcHydration) << "Got new connection starting GETFileJob" << _requestId << _folderPath; _transferDataSocket = _transferDataServer->nextPendingConnection(); - _job = new GETFileJob(_account, _remotePath + _folderPath, _transferDataSocket, {}, {}, 0, this); + _job = new GETFileJob(_account, Utility::trailingSlashPath(_remoteSyncRootPath) + _folderPath, _transferDataSocket, {}, {}, 0, this); connect(_job, &GETFileJob::finishedSignal, this, &HydrationJob::onGetFinished); _job->start(); } @@ -356,25 +356,17 @@ void OCC::HydrationJob::handleNewConnectionForEncryptedFile() { // TODO: the following code is borrowed from PropagateDownloadEncrypted (should we factor it out and reuse? YES! Should we do it now? Probably not, as, this would imply modifying PropagateDownloadEncrypted, so we need a separate PR) qCInfo(lcHydration) << "Got new connection for encrypted file. Getting required info for decryption..."; - const auto rootPath = [=]() { - const auto result = _remotePath; - if (result.startsWith('/')) { - return result.mid(1); - } else { - return result; - } - }(); - const auto remoteFilename = e2eMangledName(); - const auto remotePath = QString(rootPath + remoteFilename); - const auto _remoteParentPath = remotePath.left(remotePath.lastIndexOf('/')); + const QString fullRemotePath = Utility::trailingSlashPath(_remoteSyncRootPath) + remoteFilename; + const auto containingFolderFullRemotePath = fullRemotePath.left(fullRemotePath.lastIndexOf('/')); SyncJournalFileRecord rec; - if (!_journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(_remoteParentPath, rootPath), &rec) || !rec.isValid()) { + if (!_journal->getRootE2eFolderRecord(Utility::fullRemotePathToRemoteSyncRootRelative(containingFolderFullRemotePath, _remoteSyncRootPath), &rec) || !rec.isValid()) { emitFinished(Error); return; } - _encryptedFolderMetadataHandler.reset(new EncryptedFolderMetadataHandler(_account, _remoteParentPath, _journal, rec.path())); + _encryptedFolderMetadataHandler.reset( + new EncryptedFolderMetadataHandler(_account, containingFolderFullRemotePath, _remoteSyncRootPath, _journal, rec.path())); connect(_encryptedFolderMetadataHandler.data(), &EncryptedFolderMetadataHandler::fetchFinished, this, diff --git a/src/libsync/vfs/cfapi/hydrationjob.h b/src/libsync/vfs/cfapi/hydrationjob.h index 27990c11a2ac5..7e8e449c657a2 100644 --- a/src/libsync/vfs/cfapi/hydrationjob.h +++ b/src/libsync/vfs/cfapi/hydrationjob.h @@ -46,8 +46,8 @@ class HydrationJob : public QObject AccountPtr account() const; void setAccount(const AccountPtr &account); - QString remotePath() const; - void setRemotePath(const QString &remotePath); + [[nodiscard]] QString remoteSyncRootPath() const; + void setRemoteSyncRootPath(const QString &path); QString localPath() const; void setLocalPath(const QString &localPath); @@ -99,7 +99,7 @@ private slots: void startServerAndWaitForConnections(); AccountPtr _account; - QString _remotePath; + QString _remoteSyncRootPath; QString _localPath; SyncJournalDb *_journal = nullptr; bool _isCancelled = false; diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.cpp b/src/libsync/vfs/cfapi/vfs_cfapi.cpp index 935cd8605fe4f..7e9e7bb8114ad 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.cpp +++ b/src/libsync/vfs/cfapi/vfs_cfapi.cpp @@ -445,7 +445,7 @@ void VfsCfApi::scheduleHydrationJob(const QString &requestId, const QString &fol auto job = new HydrationJob(this); job->setAccount(params().account); - job->setRemotePath(params().remotePath); + job->setRemoteSyncRootPath(params().remotePath); job->setLocalPath(params().filesystemPath); job->setJournal(params().journal); job->setRequestId(requestId); diff --git a/test/testclientsideencryptionv2.cpp b/test/testclientsideencryptionv2.cpp index 63554e2d23a40..3aa3066de02f8 100644 --- a/test/testclientsideencryptionv2.cpp +++ b/test/testclientsideencryptionv2.cpp @@ -96,7 +96,7 @@ private slots: void testInitializeNewRootFolderMetadataThenEncryptAndDecrypt() { - QScopedPointer metadata(new FolderMetadata(_account, FolderMetadata::FolderType::Root)); + QScopedPointer metadata(new FolderMetadata(_account, "/", FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupCompleteSpy(metadata.data(), &FolderMetadata::setupComplete); metadataSetupCompleteSpy.wait(); QCOMPARE(metadataSetupCompleteSpy.count(), 1); @@ -178,7 +178,7 @@ private slots: QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadataCopy)).toUtf8()); - QScopedPointer metadataFromJson(new FolderMetadata(_account, + QScopedPointer metadataFromJson(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); QSignalSpy metadataSetupExistingCompleteSpy(metadataFromJson.data(), &FolderMetadata::setupComplete); @@ -190,7 +190,7 @@ private slots: void testE2EeFolderMetadataSharing() { // instantiate empty metadata, add a file, and share with a second user "sharee" - QScopedPointer metadata(new FolderMetadata(_account, FolderMetadata::FolderType::Root)); + QScopedPointer metadata(new FolderMetadata(_account, "/", FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupCompleteSpy(metadata.data(), &FolderMetadata::setupComplete); metadataSetupCompleteSpy.wait(); QCOMPARE(metadataSetupCompleteSpy.count(), 1); @@ -280,7 +280,7 @@ private slots: QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadataCopy)).toUtf8()); - QScopedPointer metadataFromJsonForSecondUser(new FolderMetadata(_secondAccount, ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + QScopedPointer metadataFromJsonForSecondUser(new FolderMetadata(_secondAccount, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); QSignalSpy metadataSetupExistingCompleteSpy(metadataFromJsonForSecondUser.data(), &FolderMetadata::setupComplete); metadataSetupExistingCompleteSpy.wait(); QCOMPARE(metadataSetupExistingCompleteSpy.count(), 1); @@ -303,7 +303,7 @@ private slots: QJsonDocument ocsDocFromSecondUser = QJsonDocument::fromJson( QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadataFromSecondUser)).toUtf8()); - QScopedPointer metadataFromJsonForFirstUserToCheckCrossSharing(new FolderMetadata(_account, + QScopedPointer metadataFromJsonForFirstUserToCheckCrossSharing(new FolderMetadata(_account, "/", ocsDocFromSecondUser.toJson(), RootEncryptedFolderInfo::makeDefault(), signatureAfterSecondUserModification)); diff --git a/test/testsecurefiledrop.cpp b/test/testsecurefiledrop.cpp index c116e301fcb53..0eccc4d4805e4 100644 --- a/test/testsecurefiledrop.cpp +++ b/test/testsecurefiledrop.cpp @@ -79,7 +79,7 @@ private slots: _account->e2e()->_publicKey = publicKey; _account->e2e()->_privateKey = privateKey; - QScopedPointer metadata(new FolderMetadata(_account, FolderMetadata::FolderType::Root)); + QScopedPointer metadata(new FolderMetadata(_account, "/", FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupCompleteSpy(metadata.data(), &FolderMetadata::setupComplete); metadataSetupCompleteSpy.wait(); QCOMPARE(metadataSetupCompleteSpy.count(), 1); @@ -146,7 +146,7 @@ private slots: const auto signature = metadata->metadataSignature(); QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadata)).toUtf8()); - _parsedMetadataWithFileDrop.reset(new FolderMetadata(_account, ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + _parsedMetadataWithFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); QSignalSpy metadataWithFileDropSetupCompleteSpy(_parsedMetadataWithFileDrop.data(), &FolderMetadata::setupComplete); metadataWithFileDropSetupCompleteSpy.wait(); @@ -167,7 +167,7 @@ private slots: QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadata)).toUtf8()); - _parsedMetadataAfterProcessingFileDrop.reset(new FolderMetadata(_account, ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + _parsedMetadataAfterProcessingFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); QSignalSpy metadataAfterProcessingFileDropSetupCompleteSpy(_parsedMetadataAfterProcessingFileDrop.data(), &FolderMetadata::setupComplete); metadataAfterProcessingFileDropSetupCompleteSpy.wait();