From 84615258affdbfdbfbd961d8748ec3327f3e0d71 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Tue, 15 Aug 2017 21:22:05 +0200 Subject: [PATCH] dbhub: Push databases using the POST method instead of PUT --- src/RemoteDatabase.cpp | 44 ++++++++++++++++++++++++++++-------------- src/RemoteDatabase.h | 8 +++++++- src/RemoteDock.cpp | 2 +- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/RemoteDatabase.cpp b/src/RemoteDatabase.cpp index dde66a0f..2caf4d12 100644 --- a/src/RemoteDatabase.cpp +++ b/src/RemoteDatabase.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "RemoteDatabase.h" #include "version.h" @@ -98,10 +99,6 @@ void RemoteDatabase::gotEncrypted(QNetworkReply* reply) void RemoteDatabase::gotReply(QNetworkReply* reply) { - // If this was a push database request, close the opened file - if(reply->property("type").toInt() == RequestTypePush) - delete reinterpret_cast(reply->property("file").value()); - // Check if request was successful if(reply->error() != QNetworkReply::NoError) { @@ -197,10 +194,6 @@ void RemoteDatabase::gotReply(QNetworkReply* reply) void RemoteDatabase::gotError(QNetworkReply* reply, const QList& errors) { - // If this was a push database request, close the opened file - if(reply->property("type").toInt() == RequestTypePush) - delete reinterpret_cast(reply->property("file").value()); - // Are there any errors in here that aren't about self-signed certificates and non-matching hostnames? bool serious_errors = false; foreach(const QSslError& error, errors) @@ -395,7 +388,7 @@ void RemoteDatabase::fetch(const QString& url, RequestType type, const QString& prepareProgressDialog(reply, false, url); } -void RemoteDatabase::push(const QString& filename, const QString& url, const QString& clientCert, +void RemoteDatabase::push(const QString& filename, const QString& url, const QString& clientCert, const QString& remotename, const QString& commitMessage, const QString& licence, bool isPublic) { // Check if network is accessible. If not, abort right here @@ -419,10 +412,12 @@ void RemoteDatabase::push(const QString& filename, const QString& url, const QSt request.setUrl(url); request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName()).arg(APP_VERSION).toUtf8()); - // Set custom headers for extra information about the commit - request.setRawHeader("commitmsg", commitMessage.toUtf8()); - request.setRawHeader("licence", licence.toUtf8()); - request.setRawHeader("public", isPublic ? "true" : "false"); + // Prepare HTTP multi part data containing all the information about the commit we're about to push + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + addPart(multipart, "file", file, remotename); + addPart(multipart, "commitmsg", commitMessage); + addPart(multipart, "licence", licence); + addPart(multipart, "public", isPublic ? "true" : "false"); // Set SSL configuration when trying to access a file via the HTTPS protocol bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; @@ -440,14 +435,33 @@ void RemoteDatabase::push(const QString& filename, const QString& url, const QSt clearAccessCache(clientCert); // Put database to remote server and save pending reply for future processing - QNetworkReply* reply = m_manager->put(request, file); + QNetworkReply* reply = m_manager->post(request, multipart); reply->setProperty("type", RequestTypePush); - reply->setProperty("file", qVariantFromValue(dynamic_cast(file))); + multipart->setParent(reply); // Delete the multi-part object along with the reply // Initialise the progress dialog for this request prepareProgressDialog(reply, true, url); } +void RemoteDatabase::addPart(QHttpMultiPart* multipart, const QString& name, const QString& value) +{ + QHttpPart part; + part.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(name)); + part.setBody(value.toUtf8()); + + multipart->append(part); +} + +void RemoteDatabase::addPart(QHttpMultiPart* multipart, const QString& name, QFile* file, const QString& filename) +{ + QHttpPart part; + part.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"; filename=\"%2\"").arg(name).arg(filename)); + part.setBodyDevice(file); + file->setParent(multipart); // Close the file and delete the file object as soon as the multi-part object is destroyed + + multipart->append(part); +} + void RemoteDatabase::localAssureOpened() { // This function should be called first in each RemoteDatabase::local* function. It assures the database for storing diff --git a/src/RemoteDatabase.h b/src/RemoteDatabase.h index 1a5bdc2c..9cec76a4 100644 --- a/src/RemoteDatabase.h +++ b/src/RemoteDatabase.h @@ -10,6 +10,8 @@ class QNetworkReply; class QSslError; class QProgressDialog; class QNetworkRequest; +class QHttpMultiPart; +class QFile; struct sqlite3; class RemoteDatabase : public QObject @@ -42,7 +44,7 @@ public: }; void fetch(const QString& url, RequestType type, const QString& clientCert = QString(), QVariant userdata = QVariant()); - void push(const QString& filename, const QString& url, const QString& clientCert, + void push(const QString& filename, const QString& url, const QString& clientCert, const QString& remotename, const QString& commitMessage = QString(), const QString& licence = QString(), bool isPublic = false); signals: @@ -72,6 +74,10 @@ private: void localAdd(QString filename, QString identity, const QUrl& url); QString localExists(const QUrl& url, QString identity); + // Helper functions for building multi-part HTTP requests + void addPart(QHttpMultiPart* multipart, const QString& name, const QString& value); + void addPart(QHttpMultiPart* multipart, const QString& name, QFile* file, const QString& filename); + // Before using a new client certificate we need to clear the access and authentication cache of the network manager // object. Otherwise Qt might reuse the old certificate if the requested URL has been used before. void clearAccessCache(const QString& clientCert); diff --git a/src/RemoteDock.cpp b/src/RemoteDock.cpp index 9df1501d..7c1d915d 100644 --- a/src/RemoteDock.cpp +++ b/src/RemoteDock.cpp @@ -110,7 +110,7 @@ void RemoteDock::pushDatabase() url.append(pushDialog.name()); // Push database - remoteDatabase.push(mainWindow->getDb().currentFile(), url, remoteModel->currentClientCertificate(), + remoteDatabase.push(mainWindow->getDb().currentFile(), url, remoteModel->currentClientCertificate(), pushDialog.name(), pushDialog.commitMessage(), pushDialog.licence(), pushDialog.isPublic()); }