diff src/main.cpp @ 142:36c9cb759326

Implement simple SQLite database backup at program exit using Qt HTTP/HTTPS and a PHP script on the remote server. Needs more work, testing and better error handling.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 24 Aug 2017 15:44:33 +0300
parents 783417da6da3
children 75a4faa219a9
line wrap: on
line diff
--- a/src/main.cpp	Thu Aug 24 13:16:09 2017 +0300
+++ b/src/main.cpp	Thu Aug 24 15:44:33 2017 +0300
@@ -11,7 +11,6 @@
 #include <QSettings>
 #include <QPrintDialog>
 #include <QPrintPreviewDialog>
-#include <QProgressDialog>
 #include <QStandardPaths>
 #include "main.h"
 #include "ui_mainwindow.h"
@@ -246,8 +245,15 @@
     settings.uiPos = tmpst.value("pos", QPoint(100, 100)).toPoint();
     settings.uiSize = tmpst.value("size", QSize(1000, 600)).toSize();
     settings.uiScale = tmpst.value("scale", 1.0f).toDouble();
-    settings.dbBackupURL = tmpst.value("dbBackupURL", "").toString();
-    settings.dbBackupSecret = tmpst.value("dbBackupSecret", "").toString();
+    settings.dbBackupURL = tmpst.value("dbBackupURL", QString()).toString();
+    settings.dbBackupSecret = tmpst.value("dbBackupSecret", QString()).toString();
+
+    // Check commandline arguments for configuring backup settings
+    if (argc >= 4 && strcmp(argv[1], "config") == 0)
+    {
+        settings.dbBackupURL = QString(argv[2]);
+        settings.dbBackupSecret = QString(argv[3]);
+    }
 
     //
     // Create logfile and data directory
@@ -319,7 +325,6 @@
 {
     // Setup UI
     ui->setupUi(this);
-    backupDialog = NULL;
 
     // Restore window size and position
     move(settings.uiPos);
@@ -431,6 +436,139 @@
     // Commit and close database
     QSqlDatabase::database().commit();
     QSqlDatabase::database().close();
+
+    // Back up the database
+    backupDatabase();
+}
+
+
+void SyntilistaMainWindow::backupDatabase()
+{
+    QString dbFilename = settings.dataPath + QDir::separator() + APP_SQLITE_FILE;
+    QString backupFilename = APP_SQLITE_FILE;
+    backupReply = NULL;
+    backupDialog = NULL;
+
+    if (settings.dbBackupURL == QString() || settings.dbBackupURL == "")
+    {
+        slLog("ERROR", QStringLiteral("Database backup URL not set in configuration."));
+        return;
+    }
+
+    if (settings.dbBackupSecret == QString() || settings.dbBackupSecret == "")
+    {
+        slLog("ERROR", QStringLiteral("Database backup secret key not set in configuration."));
+        return;
+    }
+
+    // Check for network access
+    QNetworkAccessManager *manager = new QNetworkAccessManager();
+    if (manager->networkAccessible() != QNetworkAccessManager::Accessible)
+    {
+        slLog("ERROR", QStringLiteral("Network not available, cannot backup the database."));
+        return;
+    }
+
+    // Attempt to open the database file 
+    QFile *file = new QFile(dbFilename);
+    if (!file->open(QIODevice::ReadOnly))
+    {
+        slLog("ERROR", QStringLiteral("Failed to open database file '%1' for backup.").arg(dbFilename));
+        return;
+    }
+
+    // Okay, we seem to be "go" ..
+    slLog("INFO",
+        QStringLiteral("Attempting database backup from '%1' to '%2'.").
+        arg(dbFilename).arg(settings.dbBackupURL));
+
+    // Create the HTTP POST request
+    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+
+    // The "secret" key as POST parameter
+    QHttpPart postPart;
+    postPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+        QVariant("form-data; name=\"secret\";"));
+    postPart.setBody(QByteArray(settings.dbBackupSecret.toUtf8()));
+
+    // Actual data as binary octet-stream
+    QHttpPart dataPart;
+    dataPart.setHeader(QNetworkRequest::ContentTypeHeader,
+        QVariant("binary/octet-stream"));
+
+    dataPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+        QVariant("form-data; name=\"file\"; filename=\""+ backupFilename +"\""));
+
+    dataPart.setBodyDevice(file);
+    file->setParent(multiPart); // we cannot delete the QFile object now, so delete it with the multiPart
+
+    multiPart->append(postPart);
+    multiPart->append(dataPart);
+
+    // Attempt to POST the whole thing
+    QUrl url(settings.dbBackupURL);
+    QNetworkRequest request(url);
+    backupReply = manager->post(request, multiPart);
+    multiPart->setParent(backupReply);
+
+    connect(
+        backupReply,
+        SIGNAL(finished()),
+        this,
+        SLOT(backupFinished()));
+
+    connect(
+        backupReply,
+        SIGNAL(uploadProgress(qint64, qint64)),
+        this,
+        SLOT(backupProgress(qint64, qint64)));
+
+    // Create progress dialog
+    backupDialog = new QProgressDialog(
+        tr("Varmuuskopioidaan tietokantaa ..."),
+        QString(),
+        0,
+        100,
+        this);
+
+    backupDialog->setAttribute(Qt::WA_DeleteOnClose);
+    backupDialog->setAutoClose(false);
+    backupDialog->setWindowModality(Qt::ApplicationModal);
+    backupDialog->exec();
+}
+
+
+void SyntilistaMainWindow::backupProgress(qint64 bytesSent, qint64 bytesTotal)
+{
+    if (bytesTotal > 0)
+    {
+        slLog("INFO",
+            QStringLiteral("Backup sent %1 / %2 bytes.").
+            arg(bytesSent).
+            arg(bytesTotal));
+
+        backupDialog->setValue((bytesSent * 100) / bytesTotal);
+    }
+}
+
+
+void SyntilistaMainWindow::backupError(QNetworkReply::NetworkError code)
+{
+    slLog("ERROR",
+        QStringLiteral("Backup failed with network error %1.\n").
+        arg(code)
+        );
+}
+
+
+void SyntilistaMainWindow::backupFinished()
+{
+    if (backupReply)
+    {
+        slLog("PAF", QString::fromUtf8(backupReply->readAll()));
+    }
+    slLog("INFO", "Backup finished.");
+    backupDialog->close();
 }