From c3f745fa766055a69f8a3254fadd85334ce2b70e Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 14 Aug 2018 13:35:04 +0200 Subject: [PATCH] Conflict handling: add the OC-ConflictBasePath header Issue #6702 --- src/common/syncjournaldb.cpp | 17 ++++++++++++++--- src/common/syncjournalfilerecord.h | 8 ++++++++ src/libsync/owncloudpropagator.cpp | 1 + src/libsync/propagatedownload.cpp | 5 +++-- src/libsync/propagateupload.cpp | 2 ++ src/libsync/syncengine.cpp | 3 ++- test/testsyncconflict.cpp | 20 ++++++++++++++++---- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index ce6cc234b..ee961862f 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -741,6 +741,15 @@ bool SyncJournalDb::updateMetadataTableStructure() commitInternal("update database structure: add contentChecksum col for uploadinfo"); } + if (!tableColumns("conflicts").contains("basePath")) { + SqlQuery query(_db); + query.prepare("ALTER TABLE conflicts ADD COLUMN basePath TEXT;"); + if (!query.exec()) { + sqlFail("updateMetadataTableStructure: add basePath column", query); + re = false; + } + } + if (true) { SqlQuery query(_db); query.prepare("CREATE INDEX IF NOT EXISTS metadata_e2e_id ON metadata(e2eMangledName);"); @@ -1978,13 +1987,14 @@ void SyncJournalDb::setConflictRecord(const ConflictRecord &record) auto &query = _setConflictRecordQuery; ASSERT(query.initOrReset(QByteArrayLiteral( "INSERT OR REPLACE INTO conflicts " - "(path, baseFileId, baseModtime, baseEtag) " - "VALUES (?1, ?2, ?3, ?4);"), + "(path, baseFileId, baseModtime, baseEtag, basePath) " + "VALUES (?1, ?2, ?3, ?4, ?5);"), _db)); query.bindValue(1, record.path); query.bindValue(2, record.baseFileId); query.bindValue(3, record.baseModtime); query.bindValue(4, record.baseEtag); + query.bindValue(5, record.basePath); ASSERT(query.exec()); } @@ -1996,7 +2006,7 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path) if (!checkConnect()) return entry; auto &query = _getConflictRecordQuery; - ASSERT(query.initOrReset(QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag FROM conflicts WHERE path=?1;"), _db)); + ASSERT(query.initOrReset(QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag, basePath FROM conflicts WHERE path=?1;"), _db)); query.bindValue(1, path); ASSERT(query.exec()); if (!query.next()) @@ -2006,6 +2016,7 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path) entry.baseFileId = query.baValue(0); entry.baseModtime = query.int64Value(1); entry.baseEtag = query.baValue(2); + entry.basePath = query.baValue(3); return entry; } diff --git a/src/common/syncjournalfilerecord.h b/src/common/syncjournalfilerecord.h index 91e7fb870..e1a9b32ff 100644 --- a/src/common/syncjournalfilerecord.h +++ b/src/common/syncjournalfilerecord.h @@ -139,6 +139,14 @@ public: */ QByteArray baseEtag; + /** + * The path of the original file + * + * maybe be empty if not available + */ + QByteArray basePath; + + bool isValid() const { return !path.isEmpty(); } }; } diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index d5dbdcfd7..59b0ae59e 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -691,6 +691,7 @@ bool OwncloudPropagator::createConflict(const SyncFileItemPtr &item, ConflictRecord conflictRecord; conflictRecord.path = conflictFileName.toUtf8(); conflictRecord.baseModtime = item->_previousModtime; + conflictRecord.basePath = item->_file.toUtf8(); SyncJournalFileRecord baseRecord; if (_journal->getFileRecord(item->_originalFile, &baseRecord) && baseRecord.isValid()) { diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 8eca4ff67..e54e8e7e6 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -714,10 +714,11 @@ void PropagateDownloadFile::slotGetFinished() // the database yet!) if (job->reply()->rawHeader("OC-Conflict") == "1") { _conflictRecord.path = _item->_file.toUtf8(); + _conflictRecord.basePath = job->reply()->rawHeader("OC-ConflictBasePath"); _conflictRecord.baseFileId = job->reply()->rawHeader("OC-ConflictBaseFileId"); - _conflictRecord.baseEtag = _job->reply()->rawHeader("OC-ConflictBaseEtag"); + _conflictRecord.baseEtag = job->reply()->rawHeader("OC-ConflictBaseEtag"); - auto mtimeHeader = _job->reply()->rawHeader("OC-ConflictBaseMtime"); + auto mtimeHeader = job->reply()->rawHeader("OC-ConflictBaseMtime"); if (!mtimeHeader.isEmpty()) _conflictRecord.baseModtime = mtimeHeader.toLongLong(); diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index dfe19dac5..e7767465a 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -731,6 +731,8 @@ QMap PropagateUploadFileCommon::headers() auto conflictRecord = propagator()->_journal->conflictRecord(_item->_file.toUtf8()); if (conflictRecord.isValid()) { headers["OC-Conflict"] = "1"; + if (!conflictRecord.basePath.isEmpty()) + headers["OC-ConflictBasePath"] = conflictRecord.basePath; if (!conflictRecord.baseFileId.isEmpty()) headers["OC-ConflictBaseFileId"] = conflictRecord.baseFileId; if (conflictRecord.baseModtime != -1) diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 84337cee6..4871d8183 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -278,9 +278,10 @@ void SyncEngine::conflictRecordMaintenance() if (!conflictRecordPaths.contains(bapath)) { ConflictRecord record; record.path = bapath; + auto basePath = Utility::conflictFileBaseNameFromPattern(bapath); + record.basePath = basePath; // Determine fileid of target file - auto basePath = Utility::conflictFileBaseNameFromPattern(bapath); SyncJournalFileRecord baseRecord; if (_journal->getFileRecord(basePath, &baseRecord) && baseRecord.isValid()) { record.baseFileId = baseRecord._fileId; diff --git a/test/testsyncconflict.cpp b/test/testsyncconflict.cpp index 9bed0e01b..ffdd20cac 100644 --- a/test/testsyncconflict.cpp +++ b/test/testsyncconflict.cpp @@ -100,11 +100,15 @@ private slots: QMap conflictMap; fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *) -> QNetworkReply * { if (op == QNetworkAccessManager::PutOperation) { - auto baseFileId = request.rawHeader("OC-ConflictBaseFileId"); - if (!baseFileId.isEmpty()) { + if (request.rawHeader("OC-Conflict") == "1") { + auto baseFileId = request.rawHeader("OC-ConflictBaseFileId"); auto components = request.url().toString().split('/'); QString conflictFile = components.mid(components.size() - 2).join('/'); conflictMap[baseFileId] = conflictFile; + [&] { + QVERIFY(!baseFileId.isEmpty()); + QCOMPARE(request.rawHeader("OC-ConflictBasePath"), Utility::conflictFileBaseNameFromPattern(conflictFile.toUtf8())); + }(); } } return nullptr; @@ -146,11 +150,15 @@ private slots: QMap conflictMap; fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *) -> QNetworkReply * { if (op == QNetworkAccessManager::PutOperation) { - auto baseFileId = request.rawHeader("OC-ConflictBaseFileId"); - if (!baseFileId.isEmpty()) { + if (request.rawHeader("OC-Conflict") == "1") { + auto baseFileId = request.rawHeader("OC-ConflictBaseFileId"); auto components = request.url().toString().split('/'); QString conflictFile = components.mid(components.size() - 2).join('/'); conflictMap[baseFileId] = conflictFile; + [&] { + QVERIFY(!baseFileId.isEmpty()); + QCOMPARE(request.rawHeader("OC-ConflictBasePath"), Utility::conflictFileBaseNameFromPattern(conflictFile.toUtf8())); + }(); } } return nullptr; @@ -165,6 +173,7 @@ private slots: ConflictRecord conflictRecord; conflictRecord.path = conflictName.toUtf8(); conflictRecord.baseFileId = a1FileId; + conflictRecord.basePath = "A/a1"; fakeFolder.syncJournal().setConflictRecord(conflictRecord); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); @@ -216,6 +225,7 @@ private slots: auto conflictRecord = fakeFolder.syncJournal().conflictRecord("A/a1 (conflicted copy 1234)"); QVERIFY(conflictRecord.isValid()); QCOMPARE(conflictRecord.baseFileId, fakeFolder.remoteModifier().find("A/a1")->fileId); + QCOMPARE(conflictRecord.basePath, "A/a1"); // Now with server headers QObject parent; @@ -227,6 +237,7 @@ private slots: reply->setRawHeader("OC-ConflictBaseFileId", a2FileId); reply->setRawHeader("OC-ConflictBaseMtime", "1234"); reply->setRawHeader("OC-ConflictBaseEtag", "etag"); + reply->setRawHeader("OC-ConflictBasePath", "A/original"); return reply; } return nullptr; @@ -239,6 +250,7 @@ private slots: QCOMPARE(conflictRecord.baseFileId, a2FileId); QCOMPARE(conflictRecord.baseModtime, 1234); QCOMPARE(conflictRecord.baseEtag, QByteArray("etag")); + QCOMPARE(conflictRecord.basePath, QByteArray("A/original")); } // Check that conflict records are removed when the file is gone -- 2.30.2