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
path: root/src
diff options
context:
space:
mode:
authoralex-z <blackslayer4@gmail.com>2021-12-20 12:13:48 +0300
committerallexzander (Rebase PR Action) <allexzander@users.noreply.github.com>2022-01-11 16:37:09 +0300
commitb7be10f712a5e4046f443460b95916abef9ebdae (patch)
treec58d8fa96eb19a0fda87edf7c7c7544762d000ed /src
parentfbe35538b667cf444645ed82ffa52f1a91a90809 (diff)
Ask server to recalculate checksum on validatin failure.
Signed-off-by: alex-z <blackslayer4@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/common/checksums.cpp19
-rw-r--r--src/common/checksums.h14
-rw-r--r--src/libsync/account.cpp6
-rw-r--r--src/libsync/account.h2
-rw-r--r--src/libsync/networkjobs.cpp19
-rw-r--r--src/libsync/networkjobs.h18
-rw-r--r--src/libsync/propagatedownload.cpp55
-rw-r--r--src/libsync/propagatedownload.h3
-rw-r--r--src/libsync/propagatorjobs.h1
9 files changed, 126 insertions, 11 deletions
diff --git a/src/common/checksums.cpp b/src/common/checksums.cpp
index 0c016ef4d..5cef55bfb 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."));
+ emit validationFailed(tr("The checksum header is malformed."), ChecksumHeaderMalformed);
return nullptr;
}
@@ -360,15 +360,28 @@ void ValidateChecksumHeader::start(std::unique_ptr<QIODevice> device, const QByt
calculator->start(std::move(device));
}
+QByteArray ValidateChecksumHeader::calculatedChecksumType() const
+{
+ return _calculatedChecksumType;
+}
+
+QByteArray ValidateChecksumHeader::calculatedChecksum() const
+{
+ return _calculatedChecksum;
+}
+
void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray &checksumType,
const QByteArray &checksum)
{
+ _calculatedChecksumType = checksumType;
+ _calculatedChecksum = checksum;
+
if (checksumType != _expectedChecksumType) {
- emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)));
+ emit validationFailed(tr("The checksum header contained an unknown checksum type \"%1\"").arg(QString::fromLatin1(_expectedChecksumType)), 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)));
+ 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);
return;
}
emit validated(checksumType, checksum);
diff --git a/src/common/checksums.h b/src/common/checksums.h
index 351fa745d..80716040c 100644
--- a/src/common/checksums.h
+++ b/src/common/checksums.h
@@ -140,6 +140,12 @@ class OCSYNC_EXPORT ValidateChecksumHeader : public QObject
{
Q_OBJECT
public:
+ enum FailureReason {
+ Success,
+ ChecksumHeaderMalformed,
+ ChecksumTypeUnknown,
+ ChecksumMismatch,
+ };
explicit ValidateChecksumHeader(QObject *parent = nullptr);
/**
@@ -161,9 +167,12 @@ public:
*/
void start(std::unique_ptr<QIODevice> device, const QByteArray &checksumHeader);
+ QByteArray calculatedChecksumType() const;
+ QByteArray calculatedChecksum() const;
+
signals:
void validated(const QByteArray &checksumType, const QByteArray &checksum);
- void validationFailed(const QString &errMsg);
+ void validationFailed(const QString &errMsg, FailureReason reason);
private slots:
void slotChecksumCalculated(const QByteArray &checksumType, const QByteArray &checksum);
@@ -173,6 +182,9 @@ private:
QByteArray _expectedChecksumType;
QByteArray _expectedChecksum;
+
+ QByteArray _calculatedChecksumType;
+ QByteArray _calculatedChecksum;
};
/**
diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp
index 537bd71e4..63a018709 100644
--- a/src/libsync/account.cpp
+++ b/src/libsync/account.cpp
@@ -58,6 +58,7 @@ using namespace QKeychain;
namespace {
constexpr int pushNotificationsReconnectInterval = 1000 * 60 * 2;
constexpr int usernamePrefillServerVersinMinSupportedMajor = 24;
+constexpr int checksumRecalculateRequestServerVersionMinSupportedMajor = 24;
}
namespace OCC {
@@ -635,6 +636,11 @@ bool Account::isUsernamePrefillSupported() const
return serverVersionInt() >= makeServerVersion(usernamePrefillServerVersinMinSupportedMajor, 0, 0);
}
+bool Account::isChecksumRecalculateRequestSupported() const
+{
+ return serverVersionInt() >= makeServerVersion(checksumRecalculateRequestServerVersionMinSupportedMajor, 0, 0);
+}
+
void Account::setServerVersion(const QString &version)
{
if (version == _serverVersion) {
diff --git a/src/libsync/account.h b/src/libsync/account.h
index 03e8a88cc..b1044f4d4 100644
--- a/src/libsync/account.h
+++ b/src/libsync/account.h
@@ -232,6 +232,8 @@ public:
bool isUsernamePrefillSupported() const;
+ bool isChecksumRecalculateRequestSupported() 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 f2ba661e4..5e29ab8c4 100644
--- a/src/libsync/networkjobs.cpp
+++ b/src/libsync/networkjobs.cpp
@@ -1084,6 +1084,25 @@ bool SimpleNetworkJob::finished()
return true;
}
+SimpleFileManipulationNetworkJob::SimpleFileManipulationNetworkJob(AccountPtr account, const QString &filePath, QObject *parent)
+ : AbstractNetworkJob(account, filePath, parent)
+{
+}
+
+QNetworkReply *SimpleFileManipulationNetworkJob::startRequest(
+ const QByteArray &verb, QNetworkRequest req, QIODevice *requestBody)
+{
+ const auto davUrlString = makeDavUrl(path()).toString();
+ auto reply = sendRequest(verb, makeDavUrl(path()), req, requestBody);
+ start();
+ return reply;
+}
+
+bool SimpleFileManipulationNetworkJob::finished()
+{
+ emit finishedSignal(reply());
+ return true;
+}
DeleteApiJob::DeleteApiJob(AccountPtr account, const QString &path, QObject *parent)
: AbstractNetworkJob(account, path, parent)
diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h
index 01cfcdedd..037379521 100644
--- a/src/libsync/networkjobs.h
+++ b/src/libsync/networkjobs.h
@@ -499,6 +499,24 @@ private slots:
};
/**
+ * @brief A basic file manipulation job
+ * @ingroup libsync
+ */
+class OWNCLOUDSYNC_EXPORT SimpleFileManipulationNetworkJob : public AbstractNetworkJob
+{
+ Q_OBJECT
+public:
+ explicit SimpleFileManipulationNetworkJob(AccountPtr account, const QString &filePath, QObject *parent = nullptr);
+
+ QNetworkReply *startRequest(const QByteArray &verb, QNetworkRequest req = QNetworkRequest(), QIODevice *requestBody = nullptr);
+
+signals:
+ void finishedSignal(QNetworkReply *reply);
+private slots:
+ bool finished() override;
+};
+
+/**
* @brief Runs a PROPFIND to figure out the private link url
*
* The numericFileId is used only to build the deprecatedPrivateLinkUrl
diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp
index 93a204d34..b3790916b 100644
--- a/src/libsync/propagatedownload.cpp
+++ b/src/libsync/propagatedownload.cpp
@@ -22,7 +22,6 @@
#include "common/utility.h"
#include "filesystem.h"
#include "propagatorjobs.h"
-#include <common/checksums.h>
#include <common/asserts.h>
#include <common/constants.h>
#include "clientsideencryptionjobs.h"
@@ -923,11 +922,55 @@ void PropagateDownloadFile::slotGetFinished()
validator->start(_tmpFile.fileName(), checksumHeader);
}
-void PropagateDownloadFile::slotChecksumFail(const QString &errMsg)
-{
- FileSystem::remove(_tmpFile.fileName());
- propagator()->_anotherSyncNeeded = true;
- done(SyncFileItem::SoftError, errMsg); // tr("The file downloaded with a broken checksum, will be redownloaded."));
+void PropagateDownloadFile::slotChecksumFail(const QString &errMsg, ValidateChecksumHeader::FailureReason reason)
+{
+ const auto processChecksumFailure = [this, errMsg]() {
+ FileSystem::remove(_tmpFile.fileName());
+ propagator()->_anotherSyncNeeded = true;
+ done(SyncFileItem::SoftError, errMsg); // tr("The file downloaded with a broken checksum, will be redownloaded."));
+ };
+
+ if (reason == ValidateChecksumHeader::FailureReason::ChecksumMismatch
+ && propagator()->account()->isChecksumRecalculateRequestSupported()) {
+ if (const auto validator = qobject_cast<ValidateChecksumHeader *>(sender())) {
+ const QByteArray calculatedChecksum(validator->calculatedChecksumType() + ':' + validator->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) {
+ if (reply->error() == QNetworkReply::NoError) {
+ const auto newChecksumFromServer = reply->rawHeader(checkSumHeaderC);
+ if (newChecksumFromServer == calculatedChecksum) {
+ const auto newChecksumFromServerSplit = newChecksumFromServer.split(':');
+ if (newChecksumFromServerSplit.size() > 1) {
+ transmissionChecksumValidated(
+ newChecksumFromServerSplit.first(), newChecksumFromServerSplit.last());
+ return;
+ }
+ }
+
+ qCCritical(lcPropagateDownload) << "Checksum recalculation has failed for file:" << reply->url()
+ << " " << checkSumHeaderC << " received is:" << newChecksumFromServer;
+ }
+
+ if (reply->error() != QNetworkReply::NoError) {
+ qCCritical(lcPropagateDownload)
+ << "Checksum recalculation has failed for file:" << reply->url()
+ << " Recalculation request has finished with error:" << reply->errorString();
+ }
+
+ processChecksumFailure();
+ });
+
+ qCWarning(lcPropagateDownload) << "Checksum validation has failed for file:" << fullRemotePathForFile
+ << " Requesting checksum recalculation on the server...";
+ QNetworkRequest req;
+ req.setRawHeader(checksumRecalculateOnServer, validator->calculatedChecksumType());
+ job->startRequest(QByteArrayLiteral("PATCH"), req);
+ return;
+ }
+ }
+
+ processChecksumFailure();
}
void PropagateDownloadFile::deleteExistingFolder()
diff --git a/src/libsync/propagatedownload.h b/src/libsync/propagatedownload.h
index ba9fff195..ce13f879c 100644
--- a/src/libsync/propagatedownload.h
+++ b/src/libsync/propagatedownload.h
@@ -17,6 +17,7 @@
#include "owncloudpropagator.h"
#include "networkjobs.h"
#include "clientsideencryption.h"
+#include <common/checksums.h>
#include <QBuffer>
#include <QFile>
@@ -235,7 +236,7 @@ private slots:
void abort(PropagatorJob::AbortType abortType) override;
void slotDownloadProgress(qint64, qint64);
- void slotChecksumFail(const QString &errMsg);
+ void slotChecksumFail(const QString &errMsg, ValidateChecksumHeader::FailureReason reason);
private:
void startAfterIsEncryptedIsChecked();
diff --git a/src/libsync/propagatorjobs.h b/src/libsync/propagatorjobs.h
index 1a1266865..1fa844378 100644
--- a/src/libsync/propagatorjobs.h
+++ b/src/libsync/propagatorjobs.h
@@ -26,6 +26,7 @@ namespace OCC {
*/
static const char checkSumHeaderC[] = "OC-Checksum";
static const char contentMd5HeaderC[] = "Content-MD5";
+static const char checksumRecalculateOnServer[] = "X-Recalculate-Hash";
/**
* @brief Declaration of the other propagation jobs