Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralex-z <blackslayer4@gmail.com>2021-12-20 15:59:08 +0300
committerallexzander (Rebase PR Action) <allexzander@users.noreply.github.com>2022-01-11 16:37:09 +0300
commit190d278fd4302cbb963338b4e224b326834ef0e2 (patch)
treeeffaa94fc8d61c58d8b0364a058fe87bfac93ab1
parent5b0e2d8ed071d6fb7d0ff9152c089df093740bee (diff)
Checksum validation PropagateDownload unit tests.
Signed-off-by: alex-z <blackslayer4@gmail.com>
-rw-r--r--src/common/checksums.cpp8
-rw-r--r--src/common/checksums.h5
-rw-r--r--src/libsync/account.cpp5
-rw-r--r--src/libsync/account.h2
-rw-r--r--src/libsync/networkjobs.cpp6
-rw-r--r--src/libsync/networkjobs.h4
-rw-r--r--src/libsync/propagatedownload.cpp25
-rw-r--r--src/libsync/propagatedownload.h3
-rw-r--r--test/testchecksumvalidator.cpp5
-rw-r--r--test/testsyncengine.cpp50
10 files changed, 86 insertions, 27 deletions
diff --git a/src/common/checksums.cpp b/src/common/checksums.cpp
index 5cef55bfb..a298e5d2f 100644
--- a/src/common/checksums.cpp
+++ b/src/common/checksums.cpp
@@ -337,7 +337,7 @@ ComputeChecksum *ValidateChecksumHeader::prepareStart(const QByteArray &checksum
if (!parseChecksumHeader(checksumHeader, &_expectedChecksumType, &_expectedChecksum)) {
qCWarning(lcChecksums) << "Checksum header malformed:" << checksumHeader;
- emit validationFailed(tr("The checksum header is malformed."), ChecksumHeaderMalformed);
+ emit validationFailed(tr("The checksum header is malformed."), _calculatedChecksumType, _calculatedChecksum, ChecksumHeaderMalformed);
return nullptr;
}
@@ -377,11 +377,13 @@ void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray &checksumTy
_calculatedChecksum = checksum;
if (checksumType != _expectedChecksumType) {
- emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)), ChecksumTypeUnknown);
+ emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)),
+ _calculatedChecksumType, _calculatedChecksum, ChecksumTypeUnknown);
return;
}
if (checksum != _expectedChecksum) {
- emit validationFailed(tr(R"(The downloaded file does not match the checksum, it will be resumed. "%1" != "%2")").arg(QString::fromUtf8(_expectedChecksum), QString::fromUtf8(checksum)), ChecksumMismatch);
+ emit validationFailed(tr(R"(The downloaded file does not match the checksum, it will be resumed. "%1" != "%2")").arg(QString::fromUtf8(_expectedChecksum), QString::fromUtf8(checksum)),
+ _calculatedChecksumType, _calculatedChecksum, ChecksumMismatch);
return;
}
emit validated(checksumType, checksum);
diff --git a/src/common/checksums.h b/src/common/checksums.h
index 80716040c..8ade282c7 100644
--- a/src/common/checksums.h
+++ b/src/common/checksums.h
@@ -146,6 +146,8 @@ public:
ChecksumTypeUnknown,
ChecksumMismatch,
};
+ Q_ENUM(FailureReason)
+
explicit ValidateChecksumHeader(QObject *parent = nullptr);
/**
@@ -172,7 +174,8 @@ public:
signals:
void validated(const QByteArray &checksumType, const QByteArray &checksum);
- void validationFailed(const QString &errMsg, FailureReason reason);
+ void validationFailed(const QString &errMsg, const QByteArray &calculatedChecksumType,
+ const QByteArray &calculatedChecksum, ValidateChecksumHeader::FailureReason reason);
private slots:
void slotChecksumCalculated(const QByteArray &checksumType, const QByteArray &checksum);
diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp
index 16d9bf4b2..35aa36ce4 100644
--- a/src/libsync/account.cpp
+++ b/src/libsync/account.cpp
@@ -641,6 +641,11 @@ bool Account::isChecksumRecalculateRequestSupported() const
return serverVersionInt() >= makeServerVersion(checksumRecalculateRequestServerVersionMinSupportedMajor, 0, 0);
}
+int Account::checksumRecalculateServerVersionMinSupportedMajor() const
+{
+ return checksumRecalculateRequestServerVersionMinSupportedMajor;
+}
+
void Account::setServerVersion(const QString &version)
{
if (version == _serverVersion) {
diff --git a/src/libsync/account.h b/src/libsync/account.h
index b1044f4d4..a80eff079 100644
--- a/src/libsync/account.h
+++ b/src/libsync/account.h
@@ -234,6 +234,8 @@ public:
bool isChecksumRecalculateRequestSupported() const;
+ int checksumRecalculateServerVersionMinSupportedMajor() const;
+
/** True when the server connection is using HTTP2 */
bool isHttp2Supported() { return _http2Supported; }
void setHttp2Supported(bool value) { _http2Supported = value; }
diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp
index 5e29ab8c4..c515b9a37 100644
--- a/src/libsync/networkjobs.cpp
+++ b/src/libsync/networkjobs.cpp
@@ -1084,12 +1084,12 @@ bool SimpleNetworkJob::finished()
return true;
}
-SimpleFileManipulationNetworkJob::SimpleFileManipulationNetworkJob(AccountPtr account, const QString &filePath, QObject *parent)
+SimpleFileJob::SimpleFileJob(AccountPtr account, const QString &filePath, QObject *parent)
: AbstractNetworkJob(account, filePath, parent)
{
}
-QNetworkReply *SimpleFileManipulationNetworkJob::startRequest(
+QNetworkReply *SimpleFileJob::startRequest(
const QByteArray &verb, QNetworkRequest req, QIODevice *requestBody)
{
const auto davUrlString = makeDavUrl(path()).toString();
@@ -1098,7 +1098,7 @@ QNetworkReply *SimpleFileManipulationNetworkJob::startRequest(
return reply;
}
-bool SimpleFileManipulationNetworkJob::finished()
+bool SimpleFileJob::finished()
{
emit finishedSignal(reply());
return true;
diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h
index 037379521..d85464db7 100644
--- a/src/libsync/networkjobs.h
+++ b/src/libsync/networkjobs.h
@@ -502,11 +502,11 @@ private slots:
* @brief A basic file manipulation job
* @ingroup libsync
*/
-class OWNCLOUDSYNC_EXPORT SimpleFileManipulationNetworkJob : public AbstractNetworkJob
+class OWNCLOUDSYNC_EXPORT SimpleFileJob : public AbstractNetworkJob
{
Q_OBJECT
public:
- explicit SimpleFileManipulationNetworkJob(AccountPtr account, const QString &filePath, QObject *parent = nullptr);
+ explicit SimpleFileJob(AccountPtr account, const QString &filePath, QObject *parent = nullptr);
QNetworkReply *startRequest(const QByteArray &verb, QNetworkRequest req = QNetworkRequest(), QIODevice *requestBody = nullptr);
diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp
index b3790916b..35337669c 100644
--- a/src/libsync/propagatedownload.cpp
+++ b/src/libsync/propagatedownload.cpp
@@ -922,7 +922,8 @@ void PropagateDownloadFile::slotGetFinished()
validator->start(_tmpFile.fileName(), checksumHeader);
}
-void PropagateDownloadFile::slotChecksumFail(const QString &errMsg, ValidateChecksumHeader::FailureReason reason)
+void PropagateDownloadFile::slotChecksumFail(const QString &errMsg,
+ const QByteArray &calculatedChecksumType, const QByteArray &calculatedChecksum, ValidateChecksumHeader::FailureReason reason)
{
const auto processChecksumFailure = [this, errMsg]() {
FileSystem::remove(_tmpFile.fileName());
@@ -932,24 +933,23 @@ void PropagateDownloadFile::slotChecksumFail(const QString &errMsg, ValidateChec
if (reason == ValidateChecksumHeader::FailureReason::ChecksumMismatch
&& propagator()->account()->isChecksumRecalculateRequestSupported()) {
- if (const auto validator = qobject_cast<ValidateChecksumHeader *>(sender())) {
- const QByteArray calculatedChecksum(validator->calculatedChecksumType() + ':' + validator->calculatedChecksum());
+ const QByteArray calculatedChecksumHeader(calculatedChecksumType + ':' + calculatedChecksum);
const QString fullRemotePathForFile(propagator()->fullRemotePath(_isEncrypted ? _item->_encryptedFileName : _item->_file));
- auto *job = new SimpleFileManipulationNetworkJob(propagator()->account(), fullRemotePathForFile);
- QObject::connect(job, &SimpleFileManipulationNetworkJob::finishedSignal, this, [this, calculatedChecksum, processChecksumFailure](QNetworkReply *reply) {
+ auto *job = new SimpleFileJob(propagator()->account(), fullRemotePathForFile);
+ QObject::connect(job, &SimpleFileJob::finishedSignal, this, [this, calculatedChecksumHeader, processChecksumFailure](QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) {
- const auto newChecksumFromServer = reply->rawHeader(checkSumHeaderC);
- if (newChecksumFromServer == calculatedChecksum) {
- const auto newChecksumFromServerSplit = newChecksumFromServer.split(':');
- if (newChecksumFromServerSplit.size() > 1) {
+ const auto newChecksumHeaderFromServer = reply->rawHeader(checkSumHeaderC);
+ if (newChecksumHeaderFromServer == calculatedChecksumHeader) {
+ const auto newChecksumHeaderFromServerSplit = newChecksumHeaderFromServer.split(':');
+ if (newChecksumHeaderFromServerSplit.size() > 1) {
transmissionChecksumValidated(
- newChecksumFromServerSplit.first(), newChecksumFromServerSplit.last());
+ newChecksumHeaderFromServerSplit.first(), newChecksumHeaderFromServerSplit.last());
return;
}
}
qCCritical(lcPropagateDownload) << "Checksum recalculation has failed for file:" << reply->url()
- << " " << checkSumHeaderC << " received is:" << newChecksumFromServer;
+ << " " << checkSumHeaderC << " received is:" << newChecksumHeaderFromServer;
}
if (reply->error() != QNetworkReply::NoError) {
@@ -964,10 +964,9 @@ void PropagateDownloadFile::slotChecksumFail(const QString &errMsg, ValidateChec
qCWarning(lcPropagateDownload) << "Checksum validation has failed for file:" << fullRemotePathForFile
<< " Requesting checksum recalculation on the server...";
QNetworkRequest req;
- req.setRawHeader(checksumRecalculateOnServer, validator->calculatedChecksumType());
+ req.setRawHeader(checksumRecalculateOnServer, calculatedChecksumType);
job->startRequest(QByteArrayLiteral("PATCH"), req);
return;
- }
}
processChecksumFailure();
diff --git a/src/libsync/propagatedownload.h b/src/libsync/propagatedownload.h
index ce13f879c..28a128a2d 100644
--- a/src/libsync/propagatedownload.h
+++ b/src/libsync/propagatedownload.h
@@ -236,7 +236,8 @@ private slots:
void abort(PropagatorJob::AbortType abortType) override;
void slotDownloadProgress(qint64, qint64);
- void slotChecksumFail(const QString &errMsg, ValidateChecksumHeader::FailureReason reason);
+ void slotChecksumFail(const QString &errMsg, const QByteArray &calculatedChecksumType,
+ const QByteArray &calculatedChecksum, ValidateChecksumHeader::FailureReason reason);
private:
void startAfterIsEncryptedIsChecked();
diff --git a/test/testchecksumvalidator.cpp b/test/testchecksumvalidator.cpp
index f797e0eb2..addca54bb 100644
--- a/test/testchecksumvalidator.cpp
+++ b/test/testchecksumvalidator.cpp
@@ -43,8 +43,11 @@ using namespace OCC::Utility;
_successDown = true;
}
- void slotDownError(const QString &errMsg, ValidateChecksumHeader::FailureReason reason)
+ void slotDownError(const QString &errMsg, const QByteArray &calculatedChecksumType,
+ const QByteArray &calculatedChecksum, ValidateChecksumHeader::FailureReason reason)
{
+ Q_UNUSED(calculatedChecksumType);
+ Q_UNUSED(calculatedChecksum);
QCOMPARE(_expectedError, errMsg);
QCOMPARE(_expectedFailureReason, reason);
_errorSeen = true;
diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp
index 328e813ee..856cf560e 100644
--- a/test/testsyncengine.cpp
+++ b/test/testsyncengine.cpp
@@ -8,6 +8,7 @@
#include <QtTest>
#include "syncenginetestutils.h"
#include <syncengine.h>
+#include <propagatorjobs.h>
using namespace OCC;
@@ -551,16 +552,27 @@ private slots:
QObject parent;
QByteArray checksumValue;
+ QByteArray checksumValueRecalculated;
QByteArray contentMd5Value;
+ bool isChecksumRecalculateSupported = false;
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *) -> QNetworkReply * {
if (op == QNetworkAccessManager::GetOperation) {
auto reply = new FakeGetReply(fakeFolder.remoteModifier(), op, request, &parent);
if (!checksumValue.isNull())
- reply->setRawHeader("OC-Checksum", checksumValue);
+ reply->setRawHeader(OCC::checkSumHeaderC, checksumValue);
if (!contentMd5Value.isNull())
- reply->setRawHeader("Content-MD5", contentMd5Value);
+ reply->setRawHeader(OCC::contentMd5HeaderC, contentMd5Value);
return reply;
+ } else if (op == QNetworkAccessManager::CustomOperation) {
+ if (request.hasRawHeader(OCC::checksumRecalculateOnServer)) {
+ if (!isChecksumRecalculateSupported) {
+ return new FakeErrorReply(op, request, &parent, 402);
+ }
+ auto reply = new FakeGetReply(fakeFolder.remoteModifier(), op, request, &parent);
+ reply->setRawHeader(OCC::checkSumHeaderC, checksumValueRecalculated);
+ return reply;
+ }
}
return nullptr;
});
@@ -575,8 +587,11 @@ private slots:
fakeFolder.remoteModifier().create("A/a4", 16, 'A');
QVERIFY(!fakeFolder.syncOnce());
+ const QByteArray matchedSha1Checksum(QByteArrayLiteral("SHA1:19b1928d58a2030d08023f3d7054516dbc186f20"));
+ const QByteArray mismatchedSha1Checksum(matchedSha1Checksum.chopped(1));
+
// Good OC-Checksum
- checksumValue = "SHA1:19b1928d58a2030d08023f3d7054516dbc186f20"; // printf 'A%.0s' {1..16} | sha1sum -
+ checksumValue = matchedSha1Checksum; // printf 'A%.0s' {1..16} | sha1sum -
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
checksumValue = QByteArray();
@@ -610,6 +625,35 @@ private slots:
checksumValue = "Unsupported:XXXX SHA1:19b1928d58a2030d08023f3d7054516dbc186f20 Invalid:XxX";
QVERIFY(fakeFolder.syncOnce()); // The supported SHA1 checksum is valid now, so the file are downloaded
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ // Begin Test mismatch recalculation---------------------------------------------------------------------------------
+
+ const auto prevServerVersion = fakeFolder.account()->serverVersion();
+ fakeFolder.account()->setServerVersion(QString("%1.0.0").arg(fakeFolder.account()->checksumRecalculateServerVersionMinSupportedMajor()));
+
+ // Mismatched OC-Checksum and X-Recalculate-Hash is not supported -> sync must fail
+ isChecksumRecalculateSupported = false;
+ checksumValue = mismatchedSha1Checksum;
+ checksumValueRecalculated = matchedSha1Checksum;
+ fakeFolder.remoteModifier().create("A/a9", 16, 'A');
+ QVERIFY(!fakeFolder.syncOnce());
+
+ // Mismatched OC-Checksum and X-Recalculate-Hash is supported, but, recalculated checksum is again mismatched -> sync must fail
+ isChecksumRecalculateSupported = true;
+ checksumValue = mismatchedSha1Checksum;
+ checksumValueRecalculated = mismatchedSha1Checksum;
+ QVERIFY(!fakeFolder.syncOnce());
+
+ // Mismatched OC-Checksum and X-Recalculate-Hash is supported, and, recalculated checksum is a match -> sync must succeed
+ isChecksumRecalculateSupported = true;
+ checksumValue = mismatchedSha1Checksum;
+ checksumValueRecalculated = matchedSha1Checksum;
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+ checksumValue = QByteArray();
+
+ fakeFolder.account()->setServerVersion(prevServerVersion);
+ // End Test mismatch recalculation-----------------------------------------------------------------------------------
}
// Tests the behavior of invalid filename detection