From df5e604e6e3b0f0157c4188365c4d57a92499247 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Mon, 7 Nov 2016 23:38:51 +0100 Subject: [PATCH] dbhub: Add support for sending files NOt perfect yet but a start. --- src/MainWindow.cpp | 8 +- src/RemoteDatabase.cpp | 203 +++++++++++++++++++++++++++-------------- src/RemoteDatabase.h | 6 +- 3 files changed, 149 insertions(+), 68 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3d5390fa..a58b7482 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1394,6 +1394,7 @@ void MainWindow::activateFields(bool enable) ui->actionEncryption->setEnabled(enable && write); ui->buttonClearFilters->setEnabled(enable); ui->dockEdit->setEnabled(enable && write); + ui->actionSave_Remote->setEnabled(enable); } void MainWindow::browseTableHeaderClicked(int logicalindex) @@ -2152,7 +2153,12 @@ void MainWindow::on_actionOpen_Remote_triggered() void MainWindow::on_actionSave_Remote_triggered() { - QDesktopServices::openUrl(QUrl("https://dbhub.io")); + QString url = QInputDialog::getText(this, qApp->applicationName(), tr("Please enter the URL of the database file to save.")); + if(!url.isEmpty()) + { + QStringList certs = Settings::getSettingsValue("remote", "client_certificates").toStringList(); + m_remoteDb.pushDatabase(db.currentFile(), url, (certs.size() ? certs.at(0) : "")); + } } void MainWindow::on_actionWiki_triggered() diff --git a/src/RemoteDatabase.cpp b/src/RemoteDatabase.cpp index 0b36d68a..0c34184e 100644 --- a/src/RemoteDatabase.cpp +++ b/src/RemoteDatabase.cpp @@ -61,69 +61,6 @@ void RemoteDatabase::reloadSettings() // TODO Add support for proxies here } -void RemoteDatabase::fetchDatabase(const QString& url, const QString& clientCert) -{ - // Check if network is accessible. If not, abort right here - if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible) - { - QMessageBox::warning(0, qApp->applicationName(), tr("Error: The network is not accessible.")); - return; - } - - // Check if client cert exists - bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; - const QSslCertificate& cert = m_clientCertFiles[clientCert]; - if(https && cert.isNull()) - { - QMessageBox::warning(0, qApp->applicationName(), tr("Error: Invalid client certificate speicified.")); - return; - } - - // Build network request - QNetworkRequest request; - request.setUrl(url); - request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName()).arg(APP_VERSION).toUtf8()); - - // Set SSL configuration when trying to access a file via the HTTPS protocol - if(https) - { - // Load private key for the client certificate - QFile fileClientCert(clientCert); - fileClientCert.open(QFile::ReadOnly); - QSslKey clientKey(&fileClientCert, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - while(clientKey.isNull()) - { - // If the private key couldn't be read, we assume it's password protected. So ask the user for the correct password and try reading it - // again. If the user cancels the password dialog, abort the whole process. - QString password = QInputDialog::getText(0, qApp->applicationName(), tr("Please enter the passphrase for this client certificate in order to authenticate.")); - if(password.isEmpty()) - return; - clientKey = QSslKey(&fileClientCert, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, password.toUtf8()); - } - fileClientCert.close(); - - // Set client certificate (from the cache) and private key (just loaded) - m_sslConfiguration.setLocalCertificate(cert); - m_sslConfiguration.setPrivateKey(clientKey); - - // Apply SSL configuration - request.setSslConfiguration(m_sslConfiguration); - } - - // Fetch database and save pending reply. Note that we're only supporting one active download here at the moment. - m_currentReply = m_manager->get(request); - - // Initialise the progress dialog for this request - if(!m_progress) - m_progress = new QProgressDialog(); - m_progress->setWindowModality(Qt::ApplicationModal); - m_progress->setCancelButtonText(tr("Cancel")); - m_progress->setLabelText(tr("Downloading remote database from\n%1.").arg(url)); - m_progress->show(); - qApp->processEvents(); - connect(m_currentReply, &QNetworkReply::downloadProgress, this, &RemoteDatabase::updateProgress); -} - void RemoteDatabase::gotEncrypted(QNetworkReply* reply) { // Verify the server's certificate using our CA certs @@ -204,7 +141,7 @@ void RemoteDatabase::gotError(QNetworkReply* reply, const QList& erro reply->deleteLater(); } -void RemoteDatabase::updateProgress(qint64 bytesReceived, qint64 bytesTotal) +void RemoteDatabase::updateProgress(qint64 bytesTransmitted, qint64 bytesTotal) { // Update progress dialog if(bytesTotal == -1) @@ -213,14 +150,14 @@ void RemoteDatabase::updateProgress(qint64 bytesReceived, qint64 bytesTotal) m_progress->setMinimum(0); m_progress->setMaximum(0); m_progress->setValue(0); - } else if(bytesReceived == bytesTotal) { + } else if(bytesTransmitted == bytesTotal) { // The download has finished m_progress->hide(); } else { // It's still downloading and we know the current progress m_progress->setMinimum(0); m_progress->setMaximum(bytesTotal); - m_progress->setValue(bytesReceived); + m_progress->setValue(bytesTransmitted); } // Check if the Cancel button has been pressed @@ -237,3 +174,137 @@ const QList& RemoteDatabase::caCertificates() const static QList certs = m_sslConfiguration.caCertificates(); return certs; } + +bool RemoteDatabase::prepareSsl(QNetworkRequest* request, const QString& clientCert) +{ + // Check if client cert exists + const QSslCertificate& cert = m_clientCertFiles[clientCert]; + if(cert.isNull()) + { + QMessageBox::warning(0, qApp->applicationName(), tr("Error: Invalid client certificate speicified.")); + return false; + } + + // Load private key for the client certificate + QFile fileClientCert(clientCert); + fileClientCert.open(QFile::ReadOnly); + QSslKey clientKey(&fileClientCert, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + while(clientKey.isNull()) + { + // If the private key couldn't be read, we assume it's password protected. So ask the user for the correct password and try reading it + // again. If the user cancels the password dialog, abort the whole process. + QString password = QInputDialog::getText(0, qApp->applicationName(), tr("Please enter the passphrase for this client certificate in order to authenticate.")); + if(password.isEmpty()) + return false; + clientKey = QSslKey(&fileClientCert, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, password.toUtf8()); + } + fileClientCert.close(); + + // Set client certificate (from the cache) and private key (just loaded) + m_sslConfiguration.setLocalCertificate(cert); + m_sslConfiguration.setPrivateKey(clientKey); + + // Apply SSL configuration + request->setSslConfiguration(m_sslConfiguration); + + return true; +} + +void RemoteDatabase::prepareProgressDialog(bool upload, const QString& url) +{ + // Instantiate progress dialog and apply some basic settings + if(!m_progress) + m_progress = new QProgressDialog(); + m_progress->setWindowModality(Qt::ApplicationModal); + m_progress->setCancelButtonText(tr("Cancel")); + + // Set dialog text + if(upload) + m_progress->setLabelText(tr("Uploading remote database to\n%1.").arg(url)); + else + m_progress->setLabelText(tr("Downloading remote database from\n%1.").arg(url)); + + // Show dialog + m_progress->show(); + qApp->processEvents(); + + // Make sure the dialog is updated + if(upload) + connect(m_currentReply, &QNetworkReply::uploadProgress, this, &RemoteDatabase::updateProgress); + else + connect(m_currentReply, &QNetworkReply::downloadProgress, this, &RemoteDatabase::updateProgress); +} + +void RemoteDatabase::fetchDatabase(const QString& url, const QString& clientCert) +{ + // Check if network is accessible. If not, abort right here + if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible) + { + QMessageBox::warning(0, qApp->applicationName(), tr("Error: The network is not accessible.")); + return; + } + + // Build network request + QNetworkRequest request; + request.setUrl(url); + request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName()).arg(APP_VERSION).toUtf8()); + + // Set SSL configuration when trying to access a file via the HTTPS protocol + bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; + if(https) + { + // If configuring the SSL connection fails, abort the request here + if(!prepareSsl(&request, clientCert)) + return; + } + + // Fetch database and save pending reply. Note that we're only supporting one active download here at the moment. + m_currentReply = m_manager->get(request); + + // Initialise the progress dialog for this request + prepareProgressDialog(false, url); +} + +void RemoteDatabase::pushDatabase(const QString& filename, const QString& url, const QString& clientCert) +{ + // Check if network is accessible. If not, abort right here + if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible) + { + QMessageBox::warning(0, qApp->applicationName(), tr("Error: The network is not accessible.")); + return; + } + + // Open the file to send and check if it exists + QFile file(filename); + if(!file.open(QFile::ReadOnly)) + { + QMessageBox::warning(0, qApp->applicationName(), tr("Error: Cannot open the file for sending.")); + return; + } + + // Build network request + QNetworkRequest request; + request.setUrl(url); + request.setRawHeader("User-Agent", QString("%1 %2").arg(qApp->organizationName()).arg(APP_VERSION).toUtf8()); + + // Set SSL configuration when trying to access a file via the HTTPS protocol + bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; + if(https) + { + // If configuring the SSL connection fails, abort the request here + if(!prepareSsl(&request, clientCert)) + return; + } + + // Get file data + // TODO: Don't read the entire file here but directly pass the file handle to the put() call below in order + // to read larger files chunk by chunk. + QByteArray file_data = file.readAll(); + file.close(); + + // Fetch database and save pending reply. Note that we're only supporting one active download here at the moment. + m_currentReply = m_manager->put(request, file_data); + + // Initialise the progress dialog for this request + prepareProgressDialog(true, url); +} diff --git a/src/RemoteDatabase.h b/src/RemoteDatabase.h index 923484b0..f39248bf 100644 --- a/src/RemoteDatabase.h +++ b/src/RemoteDatabase.h @@ -9,6 +9,7 @@ class QString; class QNetworkReply; class QSslError; class QProgressDialog; +class QNetworkRequest; class RemoteDatabase : public QObject { @@ -24,6 +25,7 @@ public: const QMap& clientCertificates() const { return m_clientCertFiles; } void fetchDatabase(const QString& url, const QString& clientCert); + void pushDatabase(const QString& filename, const QString& url, const QString& clientCert); signals: void openFile(QString path); @@ -32,7 +34,9 @@ private: void gotEncrypted(QNetworkReply* reply); void gotReply(QNetworkReply* reply); void gotError(QNetworkReply* reply, const QList& errors); - void updateProgress(qint64 bytesReceived, qint64 bytesTotal); + void updateProgress(qint64 bytesTransmitted, qint64 bytesTotal); + bool prepareSsl(QNetworkRequest* request, const QString& clientCert); + void prepareProgressDialog(bool upload, const QString& url); QNetworkAccessManager* m_manager; QProgressDialog* m_progress;