Ver código fonte

Checksum validation PropagateDownload unit tests.

Signed-off-by: alex-z <blackslayer4@gmail.com>
alex-z 4 anos atrás
pai
commit
190d278fd4

+ 5 - 3
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);

+ 4 - 1
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);

+ 5 - 0
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) {

+ 2 - 0
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; }

+ 3 - 3
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;

+ 2 - 2
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);
 

+ 12 - 13
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();

+ 2 - 1
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();

+ 4 - 1
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;

+ 47 - 3
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