aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/services
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2018-07-27 23:51:12 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2018-07-27 23:51:12 -0400
commitfeda8a7db8d1d7c5439aa8f8feef7cc0dd2b59a0 (patch)
tree1e50f5f666f419143f510d5ded00fe2006b7bd85 /SQLiteStudio3/coreSQLiteStudio/services
parentd9aa870e5d509cc7309ab82dd102a937ab58613a (diff)
New upstream version 3.2.1+dfsg1upstream/3.2.1+dfsg1
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/services')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/bugreporter.cpp202
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/bugreporter.h62
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/config.h14
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h3
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h1
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp4
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp311
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h20
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp13
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h2
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp8
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp28
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp70
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h23
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h6
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h34
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp1034
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/services/updatemanager.h102
18 files changed, 555 insertions, 1382 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.cpp b/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.cpp
deleted file mode 100644
index 54b0905..0000000
--- a/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-#include "bugreporter.h"
-#include "services/config.h"
-#include "services/notifymanager.h"
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QUrlQuery>
-
-BugReporter::BugReporter(QObject *parent) :
- QObject(parent)
-{
- networkManager = new QNetworkAccessManager(this);
- connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*)));
-}
-
-QUrl BugReporter::getReporterEmailHelpUrl() const
-{
- return QUrl(QString::fromLatin1(reporterEmailHelpUrl));
-}
-
-QUrl BugReporter::getReporterUserAndPasswordHelpUrl() const
-{
- return QUrl(QString::fromLatin1(reporterUserPassHelpUrl));
-}
-
-void BugReporter::validateBugReportCredentials(const QString& login, const QString& password)
-{
- if (credentialsValidationInProgress)
- {
- credentialsValidationInProgress->abort();
- credentialsValidationInProgress->deleteLater();
- }
-
- QUrlQuery query;
- query.addQueryItem("validateUser", login);
- query.addQueryItem("password", password);
-
- QUrl url = QUrl(QString::fromLatin1(bugReportServiceUrl) + "?" + query.query(QUrl::FullyEncoded));
- QNetworkRequest request(url);
- credentialsValidationInProgress = networkManager->get(request);
- replyToHandler[credentialsValidationInProgress] = [this](bool success, const QString& data)
- {
- if (success && data.trimmed() != "OK")
- {
- success = false;
- emit credentialsValidationResult(success, tr("Invalid login or password"));
- }
- else
- {
- emit credentialsValidationResult(success, success ? QString() : data);
- }
- };
-}
-
-void BugReporter::abortCredentialsValidation()
-{
- if (credentialsValidationInProgress)
- {
- credentialsValidationInProgress->abort();
- credentialsValidationInProgress->deleteLater();
- credentialsValidationInProgress = nullptr;
- }
-}
-
-void BugReporter::useBugReportCredentials(const QString& login, const QString& password)
-{
- CFG_CORE.Internal.BugReportUser.set(login);
- CFG_CORE.Internal.BugReportPassword.set(password);
-}
-
-void BugReporter::clearBugReportCredentials()
-{
- CFG_CORE.Internal.BugReportUser.set(QString());
- CFG_CORE.Internal.BugReportPassword.set(QString());
-}
-
-void BugReporter::reportBug(const QString& title, const QString& details, const QString& version, const QString& os, const QString& plugins, BugReporter::ResponseHandler responseHandler, const QString& urlSuffix)
-{
- static_qstring(contentsTpl, "%1\n\n<b>Plugins loaded:</b>\n%2\n\n<b>Version:</b>\n%3\n\n<b>Operating System:</b>\n%4");
- QString contents = contentsTpl.arg(escapeParam(details), plugins, version, os);
-
- QUrlQuery query;
- query.addQueryItem("brief", escapeParam(title));
- query.addQueryItem("contents", contents);
- query.addQueryItem("os", os);
- query.addQueryItem("version", version);
- query.addQueryItem("featureRequest", "0");
-
- QUrl url = QUrl(QString::fromLatin1(bugReportServiceUrl) + "?" + escapeUrl(query.query(QUrl::FullyEncoded) + urlSuffix));
- QNetworkRequest request(url);
- QNetworkReply* reply = networkManager->get(request);
- if (responseHandler)
- replyToHandler[reply] = responseHandler;
-
- replyToTypeAndTitle[reply] = QPair<bool,QString>(false, title);
-}
-
-void BugReporter::requestFeature(const QString& title, const QString& details, BugReporter::ResponseHandler responseHandler, const QString& urlSuffix)
-{
- QUrlQuery query;
- query.addQueryItem("brief", escapeParam(title));
- query.addQueryItem("contents", escapeParam(details));
- query.addQueryItem("featureRequest", "1");
-
- QUrl url = QUrl(QString::fromLatin1(bugReportServiceUrl) + "?" + escapeUrl(query.query(QUrl::FullyEncoded) + urlSuffix));
- QNetworkRequest request(url);
- QNetworkReply* reply = networkManager->get(request);
- if (responseHandler)
- replyToHandler[reply] = responseHandler;
-
- replyToTypeAndTitle[reply] = QPair<bool,QString>(true, title);
-}
-
-QString BugReporter::escapeParam(const QString &input)
-{
- return input.toHtmlEscaped();
-}
-
-QString BugReporter::escapeUrl(const QString &input)
-{
- // For some reason the ";" character is not encodedy by QUrlQuery when using FullEncoded. Pity. We have to do it manually.
- QString copy = input;
- return copy.replace(";", "%3B");
-}
-
-void BugReporter::finished(QNetworkReply* reply)
-{
- if (reply == credentialsValidationInProgress)
- credentialsValidationInProgress = nullptr;
-
- if (!replyToHandler.contains(reply))
- {
- reply->deleteLater();
- return;
- }
-
- bool success = (reply->error() == QNetworkReply::NoError);
- QString data;
- if (success)
- data = QString::fromLatin1(reply->readAll());
- else
- data = reply->errorString();
-
- replyToHandler[reply](success, data);
- replyToHandler.remove(reply);
-
- if (replyToTypeAndTitle.contains(reply))
- {
- if (success)
- CFG->addReportHistory(replyToTypeAndTitle[reply].first, replyToTypeAndTitle[reply].second, data);
-
- replyToTypeAndTitle.remove(reply);
- }
-
- reply->deleteLater();
-}
-
-void BugReporter::reportBug(const QString& email, const QString& title, const QString& details, const QString& version, const QString& os, const QString& plugins,
- ResponseHandler responseHandler)
-{
- QUrlQuery query;
- query.addQueryItem("byEmail", email);
- QString urlSuffix = "&" + query.query(QUrl::FullyEncoded);
-
- reportBug(title, details, version, os, plugins, responseHandler, urlSuffix);
-}
-
-void BugReporter::reportBug(const QString& title, const QString& details, const QString& version, const QString& os,
- const QString& plugins, ResponseHandler responseHandler)
-{
- QString user = CFG_CORE.Internal.BugReportUser.get();
- QString pass = CFG_CORE.Internal.BugReportPassword.get();
-
- QUrlQuery query;
- query.addQueryItem("byUser", user);
- query.addQueryItem("password", pass);
- QString urlSuffix = "&" + query.query(QUrl::FullyEncoded);
-
- reportBug(title, details, version, os, plugins, responseHandler, urlSuffix);
-}
-
-void BugReporter::requestFeature(const QString& email, const QString& title, const QString& details, ResponseHandler responseHandler)
-{
- QUrlQuery query;
- query.addQueryItem("byEmail", email);
- QString urlSuffix = "&" + query.query(QUrl::FullyEncoded);
-
- requestFeature(title, details, responseHandler, urlSuffix);
-}
-
-void BugReporter::requestFeature(const QString& title, const QString& details, ResponseHandler responseHandler)
-{
- QString user = CFG_CORE.Internal.BugReportUser.get();
- QString pass = CFG_CORE.Internal.BugReportPassword.get();
-
- QUrlQuery query;
- query.addQueryItem("byUser", user);
- query.addQueryItem("password", pass);
- QString urlSuffix = "&" + query.query(QUrl::FullyEncoded);
-
- requestFeature(title, details, responseHandler, urlSuffix);
-}
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.h b/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.h
deleted file mode 100644
index 3e8eb8d..0000000
--- a/SQLiteStudio3/coreSQLiteStudio/services/bugreporter.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef BUGREPORTER_H
-#define BUGREPORTER_H
-
-#include "common/global.h"
-#include "sqlitestudio.h"
-#include <QObject>
-#include <QHash>
-
-class QNetworkAccessManager;
-class QNetworkReply;
-
-class API_EXPORT BugReporter : public QObject
-{
- Q_OBJECT
-
- public:
- typedef std::function<void(bool success, const QString& data)> ResponseHandler;
-
- explicit BugReporter(QObject *parent = 0);
-
- QUrl getReporterEmailHelpUrl() const;
- QUrl getReporterUserAndPasswordHelpUrl() const;
- void validateBugReportCredentials(const QString& login, const QString& password);
- void abortCredentialsValidation();
- void useBugReportCredentials(const QString& login, const QString& password);
- void clearBugReportCredentials();
-
- private:
- void reportBug(const QString& title, const QString& details, const QString& version, const QString& os, const QString& plugins,
- ResponseHandler responseHandler, const QString& urlSuffix);
- void requestFeature(const QString& title, const QString& details, ResponseHandler responseHandler, const QString& urlSuffix);
-
- static QString escapeParam(const QString& input);
- static QString escapeUrl(const QString& input);
-
- QNetworkAccessManager* networkManager = nullptr;
- QHash<QNetworkReply*,ResponseHandler> replyToHandler;
- QHash<QNetworkReply*,QPair<bool,QString>> replyToTypeAndTitle;
- QNetworkReply* credentialsValidationInProgress = nullptr;
-
- static_char* bugReportServiceUrl = "http://sqlitestudio.pl/report_bug3.rvt";
- static_char* reporterEmailHelpUrl = "http://wiki.sqlitestudio.pl/index.php/User_Manual#Reporter_email_address";
- static_char* reporterUserPassHelpUrl = "http://wiki.sqlitestudio.pl/index.php/User_Manual#Reporter_user_and_password";
-
- signals:
- void credentialsValidationResult(bool success, const QString& errorMessage);
-
- private slots:
- void finished(QNetworkReply* reply);
-
- public slots:
- void reportBug(const QString& email, const QString& title, const QString& details, const QString& version, const QString& os, const QString& plugins,
- ResponseHandler responseHandler = nullptr);
- void reportBug(const QString& title, const QString& details, const QString& version, const QString& os, const QString& plugins,
- ResponseHandler responseHandler = nullptr);
- void requestFeature(const QString& email, const QString& title, const QString& details, ResponseHandler responseHandler = nullptr);
- void requestFeature(const QString& title, const QString& details, ResponseHandler responseHandler = nullptr);
-};
-
-#define BUGS SQLITESTUDIO->getBugReporter()
-
-#endif // BUGREPORTER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/config.h b/SQLiteStudio3/coreSQLiteStudio/services/config.h
index 1e4c410..202120a 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/config.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/config.h
@@ -20,6 +20,8 @@ CFG_CATEGORIES(Core,
CFG_CATEGORY(General,
CFG_ENTRY(int, SqlHistorySize, 10000)
CFG_ENTRY(int, DdlHistorySize, 1000)
+ CFG_ENTRY(int, BindParamsCacheSize, 1000)
+ CFG_ENTRY(int, PopulateHistorySize, 100)
CFG_ENTRY(QString, LoadedPlugins, "")
CFG_ENTRY(QVariantHash, ActiveCodeFormatter, QVariantHash())
CFG_ENTRY(bool, CheckUpdatesOnStartup, true)
@@ -31,6 +33,7 @@ CFG_CATEGORIES(Core,
CFG_CATEGORY(Internal,
CFG_ENTRY(QVariantList, Functions, QVariantList())
CFG_ENTRY(QVariantList, Collations, QVariantList())
+ CFG_ENTRY(QVariantList, Extensions, QVariantList())
CFG_ENTRY(QString, BugReportUser, QString())
CFG_ENTRY(QString, BugReportPassword, QString())
CFG_ENTRY(QString, BugReportRecentTitle, QString())
@@ -118,6 +121,7 @@ class API_EXPORT Config : public QObject
virtual bool isMassSaving() const = 0;
virtual void set(const QString& group, const QString& key, const QVariant& value) = 0;
virtual QVariant get(const QString& group, const QString& key) = 0;
+ virtual QVariant get(const QString& group, const QString& key, const QVariant& defaultValue) = 0;
virtual QHash<QString,QVariant> getAll() = 0;
virtual bool addDb(const QString& name, const QString& path, const QHash<QString, QVariant> &options) = 0;
@@ -144,6 +148,7 @@ class API_EXPORT Config : public QObject
virtual qint64 addSqlHistory(const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected) = 0;
virtual void updateSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected) = 0;
virtual void clearSqlHistory() = 0;
+ virtual void deleteSqlHistory(const QList<qint64>& ids) = 0;
virtual QAbstractItemModel* getSqlHistoryModel() = 0;
virtual void addCliHistory(const QString& text) = 0;
@@ -151,6 +156,15 @@ class API_EXPORT Config : public QObject
virtual void clearCliHistory() = 0;
virtual QStringList getCliHistory() const = 0;
+ virtual void addBindParamHistory(const QVector<QPair<QString, QVariant>>& params) = 0;
+ virtual void applyBindParamHistoryLimit() = 0;
+ virtual QVector<QPair<QString, QVariant>> getBindParamHistory(const QStringList& paramNames) const = 0;
+
+ virtual void addPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant>>& columnsPluginsConfig) = 0;
+ virtual void applyPopulateHistoryLimit() = 0;
+ virtual QHash<QString, QPair<QString, QVariant>> getPopulateHistory(const QString& database, const QString& table, int& rows) const = 0;
+ virtual QVariant getPopulateHistory(const QString& pluginName) const = 0;
+
virtual void addDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile) = 0;
virtual QList<DdlHistoryEntryPtr> getDdlHistoryFor(const QString& dbName, const QString& dbFile, const QDate& date) = 0;
virtual DdlHistoryModel* getDdlHistoryModel() = 0;
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
index 52746e4..2b5fa41 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/dbmanager.h
@@ -144,12 +144,13 @@ class API_EXPORT DbManager : public QObject
/**
* @brief Creates in-memory SQLite3 database.
+ * @param pureInit If true, avoids registering collations/functions/extensions in a database. Skips rich initialization and gives pure database connection.
* @return Created database.
*
* Created database can be used for any purpose. Note that DbManager doesn't own created
* database and it's up to the caller to delete the database when it's no longer needed.
*/
- virtual Db* createInMemDb() = 0;
+ virtual Db* createInMemDb(bool pureInit = false) = 0;
/**
* @brief Tells if given database is temporary.
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h
index 2581b4f..5fb4908 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/functionmanager.h
@@ -3,6 +3,7 @@
#include "coreSQLiteStudio_global.h"
#include "common/global.h"
+#include <QVariant>
#include <QList>
#include <QSharedPointer>
#include <QObject>
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp
index 5876021..7d24e47 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/collationmanagerimpl.cpp
@@ -27,7 +27,7 @@ QList<CollationManager::CollationPtr> CollationManagerImpl::getAllCollations() c
QList<CollationManager::CollationPtr> CollationManagerImpl::getCollationsForDatabase(const QString& dbName) const
{
QList<CollationPtr> results;
- foreach (const CollationPtr& coll, collations)
+ for (const CollationPtr& coll : collations)
{
if (coll->allDatabases || coll->databases.contains(dbName, Qt::CaseInsensitive))
results << coll;
@@ -120,6 +120,6 @@ void CollationManagerImpl::loadFromConfig()
void CollationManagerImpl::refreshCollationsByKey()
{
collationsByKey.clear();
- foreach (CollationPtr collation, collations)
+ for (CollationPtr collation : collations)
collationsByKey[collation->name] = collation;
}
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp
index cf8b115..860e828 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.cpp
@@ -4,6 +4,7 @@
#include "services/notifymanager.h"
#include "sqlitestudio.h"
#include "db/dbsqlite3.h"
+#include "common/utils.h"
#include <QtGlobal>
#include <QDebug>
#include <QList>
@@ -94,11 +95,7 @@ bool ConfigImpl::isMassSaving() const
void ConfigImpl::set(const QString &group, const QString &key, const QVariant &value)
{
- QByteArray bytes;
- QDataStream stream(&bytes, QIODevice::WriteOnly);
- stream << value;
-
- db->exec("INSERT OR REPLACE INTO settings VALUES (?, ?, ?)", {group, key, bytes});
+ db->exec("INSERT OR REPLACE INTO settings VALUES (?, ?, ?)", {group, key, serializeToBytes(value)});
}
QVariant ConfigImpl::get(const QString &group, const QString &key)
@@ -107,6 +104,15 @@ QVariant ConfigImpl::get(const QString &group, const QString &key)
return deserializeValue(results->getSingleCell());
}
+QVariant ConfigImpl::get(const QString &group, const QString &key, const QVariant &defaultValue)
+{
+ QVariant value = get(group, key);
+ if (!value.isValid() || value.isNull())
+ return defaultValue;
+
+ return value;
+}
+
QHash<QString,QVariant> ConfigImpl::getAll()
{
SqlQueryPtr results = db->exec("SELECT [group], [key], value FROM settings");
@@ -225,7 +231,7 @@ void ConfigImpl::storeGroups(const QList<DbGroupPtr>& groups)
db->begin();
db->exec("DELETE FROM groups");
- foreach (const DbGroupPtr& group, groups)
+ for (const DbGroupPtr& group : groups)
storeGroup(group);
db->commit();
@@ -241,7 +247,7 @@ void ConfigImpl::storeGroup(const ConfigImpl::DbGroupPtr &group, qint64 parentId
{group->name, group->order, parent, group->open, group->referencedDbName});
qint64 newParentId = results->getRegularInsertRowId();
- foreach (const DbGroupPtr& childGroup, group->childs)
+ for (const DbGroupPtr& childGroup : group->childs)
storeGroup(childGroup, newParentId);
}
@@ -304,6 +310,11 @@ void ConfigImpl::clearSqlHistory()
QtConcurrent::run(this, &ConfigImpl::asyncClearSqlHistory);
}
+void ConfigImpl::deleteSqlHistory(const QList<qint64>& ids)
+{
+ QtConcurrent::run(this, &ConfigImpl::asyncDeleteSqlHistory, ids);
+}
+
QAbstractItemModel* ConfigImpl::getSqlHistoryModel()
{
if (!sqlHistoryModel)
@@ -338,6 +349,122 @@ QStringList ConfigImpl::getCliHistory() const
return results->columnAsList<QString>("text");
}
+void ConfigImpl::addBindParamHistory(const QVector<QPair<QString, QVariant> >& params)
+{
+ QtConcurrent::run(this, &ConfigImpl::asyncAddBindParamHistory, params);
+}
+
+void ConfigImpl::applyBindParamHistoryLimit()
+{
+ QtConcurrent::run(this, &ConfigImpl::asyncApplyBindParamHistoryLimit);
+}
+
+QVector<QPair<QString, QVariant>> ConfigImpl::getBindParamHistory(const QStringList& paramNames) const
+{
+ static_qstring(directQuery, "SELECT id FROM bind_params WHERE pattern = ? ORDER BY id DESC");
+ static_qstring(paramsByIdQuery, "SELECT name, value FROM bind_param_values WHERE bind_params_id = ? ORDER BY position");
+ static_qstring(singleParamQuery, "SELECT value FROM bind_param_values WHERE %1 = ? ORDER BY id DESC LIMIT 1;");
+ static_qstring(singleParamName, "name");
+ static_qstring(singleParamPosition, "position");
+
+ QVector<QPair<QString, QVariant>> bindParams;
+ bindParams.reserve(paramNames.size());
+
+ SqlQueryPtr results = db->exec(directQuery, {paramNames.join(",")});
+ if (results->isError())
+ {
+ qWarning() << "Error while getting BindParams (1):" << db->getErrorText();
+ return bindParams;
+ }
+
+ // Got an exact match? Extract values and return.
+ QVariant exactMatch = results->getSingleCell();
+ if (!exactMatch.isNull())
+ {
+ results = db->exec(paramsByIdQuery, {exactMatch.toLongLong()});
+ if (results->isError())
+ {
+ qWarning() << "Error while getting BindParams (2):" << db->getErrorText();
+ }
+ else
+ {
+ for (const SqlResultsRowPtr& row : results->getAll())
+ bindParams << QPair<QString, QVariant>(row->value("name").toString(), row->value("value"));
+ }
+ return bindParams;
+ }
+
+ // No exact match. Will look for values one by one using param name and position.
+ int position = 0;
+ for (const QString& bindParam : paramNames)
+ {
+ if (bindParam == "?")
+ results = db->exec(singleParamQuery.arg(singleParamPosition), {position});
+ else
+ results = db->exec(singleParamQuery.arg(singleParamName), {bindParam});
+
+ bindParams << QPair<QString, QVariant>(bindParam, results->getSingleCell());
+ position++;
+ }
+ return bindParams;
+}
+
+void ConfigImpl::addPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant> >& columnsPluginsConfig)
+{
+ QtConcurrent::run(this, &ConfigImpl::asyncAddPopulateHistory, database, table, rows, columnsPluginsConfig);
+}
+
+void ConfigImpl::applyPopulateHistoryLimit()
+{
+ QtConcurrent::run(this, &ConfigImpl::asyncApplyPopulateHistoryLimit);
+}
+
+QHash<QString, QPair<QString, QVariant>> ConfigImpl::getPopulateHistory(const QString& database, const QString& table, int& rows) const
+{
+ static_qstring(initialQuery, "SELECT id, rows FROM populate_history WHERE [database] = ? AND [table] = ? ORDER BY id DESC LIMIT 1");
+ static_qstring(columnsQuery, "SELECT column_name, plugin_name, plugin_config FROM populate_column_history WHERE populate_history_id = ?");
+
+ QHash<QString, QPair<QString, QVariant>> historyEntry;
+ SqlQueryPtr results = db->exec(initialQuery, {database, table});
+ if (results->isError())
+ {
+ qWarning() << "Error while getting Populating history entry (1):" << db->getErrorText();
+ return historyEntry;
+ }
+
+ if (!results->hasNext())
+ return historyEntry;
+
+ SqlResultsRowPtr row = results->next();
+ qint64 historyEntryId = row->value("id").toLongLong();
+ rows = row->value("rows").toInt();
+
+ results = db->exec(columnsQuery, {historyEntryId});
+ QVariant value;
+ while (results->hasNext())
+ {
+ row = results->next();
+ value = deserializeValue(row->value("plugin_config"));
+ historyEntry[row->value("column_name").toString()] = QPair<QString, QVariant>(row->value("plugin_name").toString(), value);
+ }
+
+ return historyEntry;
+}
+
+QVariant ConfigImpl::getPopulateHistory(const QString& pluginName) const
+{
+ static_qstring(columnsQuery, "SELECT plugin_config FROM populate_column_history WHERE plugin_name = ? ORDER BY id DESC LiMIT 1");
+
+ SqlQueryPtr results = db->exec(columnsQuery, {pluginName});
+ if (results->isError())
+ {
+ qWarning() << "Error while getting Populating history entry (2):" << db->getErrorText();
+ return QVariant();
+ }
+
+ return deserializeValue(results->getSingleCell());
+}
+
void ConfigImpl::addDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile)
{
QtConcurrent::run(this, &ConfigImpl::asyncAddDdlHistory, queries, dbName, dbFile);
@@ -503,9 +630,9 @@ QString ConfigImpl::getPortableConfigPath()
if (!file.isDir() || !file.isReadable() || !file.isWritable())
continue;
- foreach (file, dir.entryInfoList())
+ for (const QFileInfo& entryFile : dir.entryInfoList())
{
- if (!file.isReadable() || !file.isWritable())
+ if (!entryFile.isReadable() || !entryFile.isWritable())
continue;
}
@@ -522,8 +649,7 @@ void ConfigImpl::initTables()
if (!tables.contains("version"))
{
- QString table;
- foreach (table, tables)
+ for (const QString& table : tables)
db->exec("DROP TABLE "+table);
tables.clear();
@@ -554,6 +680,34 @@ void ConfigImpl::initTables()
if (!tables.contains("reports_history"))
db->exec("CREATE TABLE reports_history (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, feature_request BOOLEAN, title TEXT, url TEXT)");
+
+ if (!tables.contains("bind_params"))
+ {
+ db->exec("CREATE TABLE bind_params (id INTEGER PRIMARY KEY AUTOINCREMENT, pattern TEXT NOT NULL)");
+ db->exec("CREATE INDEX bind_params_patt_idx ON bind_params (pattern);");
+ }
+
+ if (!tables.contains("bind_param_values"))
+ {
+ db->exec("CREATE TABLE bind_param_values (id INTEGER PRIMARY KEY AUTOINCREMENT, bind_params_id INTEGER REFERENCES bind_params (id) "
+ "ON DELETE CASCADE ON UPDATE CASCADE NOT NULL, position INTEGER NOT NULL, name TEXT NOT NULL, value)");
+ db->exec("CREATE INDEX bind_param_values_fk_idx ON bind_param_values (bind_params_id);");
+ }
+
+ if (!tables.contains("populate_history"))
+ {
+ db->exec("CREATE TABLE populate_history (id INTEGER PRIMARY KEY AUTOINCREMENT, [database] TEXT NOT NULL, [table] TEXT NOT NULL, rows INTEGER NOT NULL)");
+ }
+
+ if (!tables.contains("populate_column_history"))
+ {
+ db->exec("CREATE TABLE populate_column_history (id INTEGER PRIMARY KEY AUTOINCREMENT, populate_history_id INTEGER REFERENCES populate_history (id) "
+ "ON DELETE CASCADE ON UPDATE CASCADE NOT NULL, column_name TEXT NOT NULL, plugin_name TEXT NOT NULL, plugin_config BLOB)");
+ db->exec("CREATE INDEX populate_plugin_history_idx ON populate_column_history (plugin_name)");
+ }
+
+ if (!tables.contains("reports_history"))
+ db->exec("CREATE TABLE reports_history (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, feature_request BOOLEAN, title TEXT, url TEXT)");
}
void ConfigImpl::initDbFile()
@@ -646,19 +800,13 @@ bool ConfigImpl::tryInitDbFile(const QPair<QString, bool> &dbPath)
return true;
}
-QVariant ConfigImpl::deserializeValue(const QVariant &value)
+QVariant ConfigImpl::deserializeValue(const QVariant &value) const
{
if (!value.isValid())
return QVariant();
QByteArray bytes = value.toByteArray();
- if (bytes.isNull())
- return QVariant();
-
- QVariant deserializedValue;
- QDataStream stream(bytes);
- stream >> deserializedValue;
- return deserializedValue;
+ return deserializeFromBytes(bytes);
}
void ConfigImpl::asyncAddSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected)
@@ -709,6 +857,23 @@ void ConfigImpl::asyncClearSqlHistory()
emit sqlHistoryRefreshNeeded();
}
+void ConfigImpl::asyncDeleteSqlHistory(const QList<qint64>& ids)
+{
+ if (!db->begin()) {
+ NOTIFY_MANAGER->warn(tr("Could not start database transaction for deleting SQL history, therefore it's not deleted."));
+ return;
+ }
+ for (const qint64& id : ids)
+ db->exec("DELETE FROM sqleditor_history WHERE id = ?", id);
+
+ if (!db->commit()) {
+ NOTIFY_MANAGER->warn(tr("Could not commit database transaction for deleting SQL history, therefore it's not deleted."));
+ db->rollback();
+ return;
+ }
+ emit sqlHistoryRefreshNeeded();
+}
+
void ConfigImpl::asyncAddCliHistory(const QString& text)
{
static_qstring(insertQuery, "INSERT INTO cli_history (text) VALUES (?)");
@@ -722,7 +887,7 @@ void ConfigImpl::asyncAddCliHistory(const QString& text)
void ConfigImpl::asyncApplyCliHistoryLimit()
{
- static_qstring(limitQuery, "DELETE FROM cli_history WHERE id >= (SELECT id FROM cli_history ORDER BY id LIMIT 1 OFFSET %1)");
+ static_qstring(limitQuery, "DELETE FROM cli_history WHERE id <= (SELECT id FROM cli_history ORDER BY id DESC LIMIT 1 OFFSET %1)");
SqlQueryPtr results = db->exec(limitQuery.arg(CFG_CORE.Console.HistorySize.get()));
if (results->isError())
@@ -738,6 +903,105 @@ void ConfigImpl::asyncClearCliHistory()
qWarning() << "Error while clearing CLI history:" << db->getErrorText();
}
+void ConfigImpl::asyncAddBindParamHistory(const QVector<QPair<QString, QVariant> >& params)
+{
+ static_qstring(insertParamsQuery, "INSERT INTO bind_params (pattern) VALUES (?)");
+ static_qstring(insertValuesQuery, "INSERT INTO bind_param_values (bind_params_id, position, name, value) VALUES (?, ?, ?, ?)");
+
+ if (!db->begin())
+ {
+ qWarning() << "Failed to store BindParam cache, because could not begin SQL transaction. Details:" << db->getErrorText();
+ return;
+ }
+
+ QStringList paramNames;
+ for (const QPair<QString, QVariant>& paramPair : params)
+ paramNames << paramPair.first;
+
+ SqlQueryPtr results = db->exec(insertParamsQuery, {paramNames.join(",")});
+ RowId rowId = results->getInsertRowId();
+ qint64 bindParamsId = rowId["ROWID"].toLongLong();
+
+ int position = 0;
+ for (const QPair<QString, QVariant>& paramPair : params)
+ {
+ results = db->exec(insertValuesQuery, {bindParamsId, position++, paramPair.first, paramPair.second});
+ if (results->isError())
+ {
+ qWarning() << "Failed to store BindParam cache, due to SQL error:" << db->getErrorText();
+ db->rollback();
+ return;
+ }
+ }
+
+ if (!db->commit())
+ {
+ qWarning() << "Failed to store BindParam cache, because could not commit SQL transaction. Details:" << db->getErrorText();
+ db->rollback();
+ }
+
+ asyncApplyBindParamHistoryLimit();
+}
+
+void ConfigImpl::asyncApplyBindParamHistoryLimit()
+{
+ static_qstring(findBindParamIdQuery, "SELECT bind_params_id FROM bind_param_values ORDER BY id DESC LIMIT 1 OFFSET %1");
+ static_qstring(limitBindParamsQuery, "DELETE FROM bind_params WHERE id <= ?"); // will cascade with FK to bind_param_values
+
+ SqlQueryPtr results = db->exec(findBindParamIdQuery.arg(CFG_CORE.General.BindParamsCacheSize.get()));
+ if (results->isError())
+ qWarning() << "Error while limiting BindParam history (step 1):" << db->getErrorText();
+
+ qint64 bindParamId = results->getSingleCell().toLongLong();
+ results = db->exec(limitBindParamsQuery, {bindParamId});
+ if (results->isError())
+ qWarning() << "Error while limiting BindParam history (step 2):" << db->getErrorText();
+}
+
+void ConfigImpl::asyncAddPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant>>& columnsPluginsConfig)
+{
+ static_qstring(insertQuery, "INSERT INTO populate_history ([database], [table], rows) VALUES (?, ?, ?)");
+ static_qstring(insertColumnQuery, "INSERT INTO populate_column_history (populate_history_id, column_name, plugin_name, plugin_config) VALUES (?, ?, ?, ?)");
+
+ if (!db->begin())
+ {
+ qWarning() << "Failed to store Populating history entry, because could not begin SQL transaction. Details:" << db->getErrorText();
+ return;
+ }
+
+ SqlQueryPtr results = db->exec(insertQuery, {database, table, rows});
+ RowId rowId = results->getInsertRowId();
+ qint64 populateHistoryId = rowId["ROWID"].toLongLong();
+
+ for (QHash<QString, QPair<QString, QVariant>>::const_iterator colIt = columnsPluginsConfig.begin(); colIt != columnsPluginsConfig.end(); colIt++)
+ {
+ results = db->exec(insertColumnQuery, {populateHistoryId, colIt.key(), colIt.value().first, serializeToBytes(colIt.value().second)});
+ if (results->isError())
+ {
+ qWarning() << "Failed to store Populating history entry, due to SQL error:" << db->getErrorText();
+ db->rollback();
+ return;
+ }
+ }
+
+ if (!db->commit())
+ {
+ qWarning() << "Failed to store Populating history entry, because could not commit SQL transaction. Details:" << db->getErrorText();
+ db->rollback();
+ }
+
+ asyncApplyPopulateHistoryLimit();
+}
+
+void ConfigImpl::asyncApplyPopulateHistoryLimit()
+{
+ static_qstring(limitQuery, "DELETE FROM populate_history WHERE id <= (SELECT id FROM populate_history ORDER BY id DESC LIMIT 1 OFFSET %1)");
+
+ SqlQueryPtr results = db->exec(limitQuery.arg(CFG_CORE.General.PopulateHistorySize.get()));
+ if (results->isError())
+ qWarning() << "Error while limiting Populating history:" << db->getErrorText();
+}
+
void ConfigImpl::asyncAddDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile)
{
static_qstring(insert, "INSERT INTO ddl_history (dbname, file, timestamp, queries) VALUES (?, ?, ?, ?)");
@@ -799,7 +1063,12 @@ void ConfigImpl::mergeMasterConfig()
if (masterConfigFile.isEmpty())
return;
- qInfo() << "Updating settings from master configuration file: " << masterConfigFile;
+#if QT_VERSION >= 0x050500
+ qInfo()
+#else
+ qDebug()
+#endif
+ << "Updating settings from master configuration file: " << masterConfigFile;
Db* masterDb = new DbSqlite3("SQLiteStudio master settings", masterConfigFile, {{DB_PURE_INIT, true}});
if (!masterDb->open())
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h
index 08bcec7..561aab4 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/configimpl.h
@@ -29,6 +29,7 @@ class API_EXPORT ConfigImpl : public Config
bool isMassSaving() const;
void set(const QString& group, const QString& key, const QVariant& value);
QVariant get(const QString& group, const QString& key);
+ QVariant get(const QString& group, const QString& key, const QVariant& defaultValue);
QHash<QString,QVariant> getAll();
bool addDb(const QString& name, const QString& path, const QHash<QString, QVariant> &options);
@@ -56,6 +57,7 @@ class API_EXPORT ConfigImpl : public Config
qint64 addSqlHistory(const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected);
void updateSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected);
void clearSqlHistory();
+ void deleteSqlHistory(const QList<qint64>& ids);
QAbstractItemModel* getSqlHistoryModel();
void addCliHistory(const QString& text);
@@ -63,6 +65,15 @@ class API_EXPORT ConfigImpl : public Config
void clearCliHistory();
QStringList getCliHistory() const;
+ void addBindParamHistory(const QVector<QPair<QString, QVariant>>& params);
+ void applyBindParamHistoryLimit();
+ QVector<QPair<QString, QVariant>> getBindParamHistory(const QStringList& paramNames) const;
+
+ void addPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant>>& columnsPluginsConfig);
+ void applyPopulateHistoryLimit();
+ QHash<QString, QPair<QString, QVariant>> getPopulateHistory(const QString& database, const QString& table, int& rows) const;
+ QVariant getPopulateHistory(const QString& pluginName) const;
+
void addDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile);
QList<DdlHistoryEntryPtr> getDdlHistoryFor(const QString& dbName, const QString& dbFile, const QDate& date);
DdlHistoryModel* getDdlHistoryModel();
@@ -94,16 +105,23 @@ class API_EXPORT ConfigImpl : public Config
void initTables();
void initDbFile();
bool tryInitDbFile(const QPair<QString, bool>& dbPath);
- QVariant deserializeValue(const QVariant& value);
+ QVariant deserializeValue(const QVariant& value) const;
void asyncAddSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected);
void asyncUpdateSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected);
void asyncClearSqlHistory();
+ void asyncDeleteSqlHistory(const QList<qint64> &ids);
void asyncAddCliHistory(const QString& text);
void asyncApplyCliHistoryLimit();
void asyncClearCliHistory();
+ void asyncAddBindParamHistory(const QVector<QPair<QString, QVariant>>& params);
+ void asyncApplyBindParamHistoryLimit();
+
+ void asyncAddPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant>>& columnsPluginsConfig);
+ void asyncApplyPopulateHistoryLimit();
+
void asyncAddDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile);
void asyncClearDdlHistory();
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
index 74f482f..217c2b7 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.cpp
@@ -23,7 +23,8 @@ DbManagerImpl::DbManagerImpl(QObject *parent) :
DbManagerImpl::~DbManagerImpl()
{
- foreach (Db* db, dbList)
+// qDebug() << "DbManagerImpl::~DbManagerImpl()";
+ for (Db* db : dbList)
{
disconnect(db, SIGNAL(disconnected()), this, SLOT(dbDisconnectedSlot()));
disconnect(db, SIGNAL(aboutToDisconnect(bool&)), this, SLOT(dbAboutToDisconnect(bool&)));
@@ -261,12 +262,16 @@ Db* DbManagerImpl::getByPath(const QString &path)
return pathToDb.value(pathDir.absolutePath());
}
-Db* DbManagerImpl::createInMemDb()
+Db* DbManagerImpl::createInMemDb(bool pureInit)
{
if (!inMemDbCreatorPlugin)
return nullptr;
- return inMemDbCreatorPlugin->getInstance("", ":memory:", {});
+ QHash<QString, QVariant> opts;
+ if (pureInit)
+ opts[DB_PURE_INIT] = true;
+
+ return inMemDbCreatorPlugin->getInstance("", ":memory:", opts);
}
bool DbManagerImpl::isTemporary(Db* db)
@@ -341,7 +346,7 @@ void DbManagerImpl::loadInitialDbList()
{
QUrl url;
InvalidDb* db = nullptr;
- foreach (const Config::CfgDbPtr& cfgDb, CFG->dbList())
+ for (const Config::CfgDbPtr& cfgDb : CFG->dbList())
{
db = new InvalidDb(cfgDb->name, cfgDb->path, cfgDb->options);
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
index 2e3630a..5f99f86 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/dbmanagerimpl.h
@@ -42,7 +42,7 @@ class API_EXPORT DbManagerImpl : public DbManager
QStringList getDbNames();
Db* getByName(const QString& name, Qt::CaseSensitivity cs = Qt::CaseInsensitive);
Db* getByPath(const QString& path);
- Db* createInMemDb();
+ Db* createInMemDb(bool pureInit = false);
bool isTemporary(Db* db);
QString quickAddDb(const QString &path, const QHash<QString, QVariant> &options);
DbPlugin* getPluginForDbFile(const QString& filePath);
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp
index 826b34b..2fcc689 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/functionmanagerimpl.cpp
@@ -40,7 +40,7 @@ QList<FunctionManager::ScriptFunction*> FunctionManagerImpl::getAllScriptFunctio
QList<FunctionManager::ScriptFunction*> FunctionManagerImpl::getScriptFunctionsForDatabase(const QString& dbName) const
{
QList<ScriptFunction*> results;
- foreach (ScriptFunction* func, functions)
+ for (ScriptFunction* func : functions)
{
if (func->allDatabases || func->databases.contains(dbName, Qt::CaseInsensitive))
results << func;
@@ -280,10 +280,10 @@ void FunctionManagerImpl::initNativeFunctions()
void FunctionManagerImpl::refreshFunctionsByKey()
{
functionsByKey.clear();
- foreach (ScriptFunction* func, functions)
+ for (ScriptFunction* func : functions)
functionsByKey[Key(func)] = func;
- foreach (NativeFunction* func, nativeFunctions)
+ for (NativeFunction* func : nativeFunctions)
nativeFunctionsByKey[Key(func)] = func;
}
@@ -291,7 +291,7 @@ void FunctionManagerImpl::storeInConfig()
{
QVariantList list;
QHash<QString,QVariant> fnHash;
- foreach (ScriptFunction* func, functions)
+ for (ScriptFunction* func : functions)
{
fnHash["name"] = func->name;
fnHash["lang"] = func->lang;
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp
index 9fe21de..c67156c 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/pluginmanagerimpl.cpp
@@ -50,7 +50,7 @@ void PluginManagerImpl::deinit()
emit aboutToQuit();
// Plugin containers and their plugins
- foreach (PluginContainer* container, pluginContainer.values())
+ for (PluginContainer* container : pluginContainer.values())
{
if (container->builtIn)
{
@@ -61,13 +61,13 @@ void PluginManagerImpl::deinit()
unload(container->name);
}
- foreach (PluginContainer* container, pluginContainer.values())
+ for (PluginContainer* container : pluginContainer.values())
delete container;
pluginContainer.clear();
// Types
- foreach (PluginType* type, registeredPluginTypes)
+ for (PluginType* type : registeredPluginTypes)
delete type;
registeredPluginTypes.clear();
@@ -113,10 +113,10 @@ void PluginManagerImpl::scanPlugins()
nameFilters << "*.so" << "*.dll" << "*.dylib";
QPluginLoader* loader = nullptr;
- foreach (QString pluginDirPath, pluginDirs)
+ for (QString pluginDirPath : pluginDirs)
{
QDir pluginDir(pluginDirPath);
- foreach (QString fileName, pluginDir.entryList(nameFilters, QDir::Files))
+ for (QString fileName : pluginDir.entryList(nameFilters, QDir::Files))
{
fileName = pluginDir.absoluteFilePath(fileName);
loader = new QPluginLoader(fileName);
@@ -158,7 +158,7 @@ bool PluginManagerImpl::initPlugin(QPluginLoader* loader, const QString& fileNam
QJsonObject pluginMetaData = loader->metaData();
QString pluginTypeName = pluginMetaData.value("MetaData").toObject().value("type").toString();
PluginType* pluginType = nullptr;
- foreach (PluginType* type, registeredPluginTypes)
+ for (PluginType* type : registeredPluginTypes)
{
if (type->getName() == pluginTypeName)
{
@@ -169,7 +169,7 @@ bool PluginManagerImpl::initPlugin(QPluginLoader* loader, const QString& fileNam
if (!pluginType)
{
- qWarning() << "Could not load plugin" + fileName + "because its type was not recognized:" << pluginTypeName;
+ qWarning() << "Could not load plugin" << fileName << "because its type was not recognized:" << pluginTypeName;
return false;
}
@@ -305,7 +305,7 @@ bool PluginManagerImpl::initPlugin(Plugin* plugin)
{
QString pluginName = plugin->getName();
PluginType* pluginType = nullptr;
- foreach (PluginType* type, registeredPluginTypes)
+ for (PluginType* type : registeredPluginTypes)
{
if (type->test(plugin))
{
@@ -316,7 +316,7 @@ bool PluginManagerImpl::initPlugin(Plugin* plugin)
if (!pluginType)
{
- qWarning() << "Could not load built-in plugin" + pluginName + "because its type was not recognized.";
+ qWarning() << "Could not load built-in plugin" << pluginName << "because its type was not recognized.";
return false;
}
@@ -363,7 +363,7 @@ QStringList PluginManagerImpl::getAllPluginNames(PluginType* type) const
if (!pluginCategories.contains(type))
return names;
- foreach (PluginContainer* container, pluginCategories[type])
+ for (PluginContainer* container : pluginCategories[type])
names << container->name;
return names;
@@ -685,7 +685,7 @@ QList<Plugin*> PluginManagerImpl::getLoadedPlugins(PluginType* type) const
if (!pluginCategories.contains(type))
return list;
- foreach (PluginContainer* container, pluginCategories[type])
+ for (PluginContainer* container : pluginCategories[type])
{
if (container->loaded)
list << container->plugin;
@@ -769,7 +769,7 @@ bool PluginManagerImpl::arePluginsInitiallyLoaded() const
QList<Plugin*> PluginManagerImpl::getLoadedPlugins() const
{
QList<Plugin*> plugins;
- foreach (PluginContainer* container, pluginContainer.values())
+ for (PluginContainer* container : pluginContainer.values())
{
if (container->loaded)
plugins << container->plugin;
@@ -780,7 +780,7 @@ QList<Plugin*> PluginManagerImpl::getLoadedPlugins() const
QStringList PluginManagerImpl::getLoadedPluginNames() const
{
QStringList names;
- foreach (PluginContainer* container, pluginContainer.values())
+ for (PluginContainer* container : pluginContainer.values())
{
if (container->loaded)
names << container->name;
@@ -792,7 +792,7 @@ QList<PluginManager::PluginDetails> PluginManagerImpl::getAllPluginDetails() con
{
QList<PluginManager::PluginDetails> results;
PluginManager::PluginDetails details;
- foreach (PluginContainer* container, pluginContainer.values())
+ for (PluginContainer* container : pluginContainer.values())
{
details.name = container->name;
details.title = container->title;
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp
new file mode 100644
index 0000000..63dbaf6
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.cpp
@@ -0,0 +1,70 @@
+#include "sqliteextensionmanagerimpl.h"
+#include "services/notifymanager.h"
+#include "services/dbmanager.h"
+
+SqliteExtensionManagerImpl::SqliteExtensionManagerImpl()
+{
+ init();
+}
+
+void SqliteExtensionManagerImpl::setExtensions(const QList<SqliteExtensionManager::ExtensionPtr>& newExtensions)
+{
+ extensions = newExtensions;
+ storeInConfig();
+ emit extensionListChanged();
+}
+
+QList<SqliteExtensionManager::ExtensionPtr> SqliteExtensionManagerImpl::getAllExtensions() const
+{
+ return extensions;
+}
+
+QList<SqliteExtensionManager::ExtensionPtr> SqliteExtensionManagerImpl::getExtensionForDatabase(const QString& dbName) const
+{
+ QList<ExtensionPtr> results;
+ for (const ExtensionPtr& ext : extensions)
+ {
+ if (ext->allDatabases || ext->databases.contains(dbName, Qt::CaseInsensitive))
+ results << ext;
+ }
+ return results;
+}
+
+void SqliteExtensionManagerImpl::init()
+{
+ loadFromConfig();
+}
+
+void SqliteExtensionManagerImpl::storeInConfig()
+{
+ QVariantList list;
+ QHash<QString,QVariant> extHash;
+ for (ExtensionPtr ext : extensions)
+ {
+ extHash["filePath"] = ext->filePath;
+ extHash["initFunc"] = ext->initFunc;
+ extHash["allDatabases"] = ext->allDatabases;
+ extHash["databases"] =common(DBLIST->getDbNames(), ext->databases);
+ list << extHash;
+ }
+ CFG_CORE.Internal.Extensions.set(list);
+}
+
+void SqliteExtensionManagerImpl::loadFromConfig()
+{
+ extensions.clear();
+
+ QVariantList list = CFG_CORE.Internal.Extensions.get();
+ QHash<QString,QVariant> extHash;
+ ExtensionPtr ext;
+ for (const QVariant& var : list)
+ {
+ extHash = var.toHash();
+ ext = ExtensionPtr::create();
+ ext->filePath = extHash["filePath"].toString();
+ ext->initFunc = extHash["initFunc"].toString();
+ ext->databases = extHash["databases"].toStringList();
+ ext->allDatabases = extHash["allDatabases"].toBool();
+ extensions << ext;
+ }
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h
new file mode 100644
index 0000000..6fd7f46
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/services/impl/sqliteextensionmanagerimpl.h
@@ -0,0 +1,23 @@
+#ifndef SQLITEEXTENSIONMANAGERIMPL_H
+#define SQLITEEXTENSIONMANAGERIMPL_H
+
+#include "services/sqliteextensionmanager.h"
+
+class SqliteExtensionManagerImpl : public SqliteExtensionManager
+{
+ public:
+ SqliteExtensionManagerImpl();
+
+ void setExtensions(const QList<ExtensionPtr>& newExtensions);
+ QList<ExtensionPtr> getAllExtensions() const;
+ QList<ExtensionPtr> getExtensionForDatabase(const QString& dbName) const;
+
+ private:
+ void init();
+ void storeInConfig();
+ void loadFromConfig();
+
+ QList<ExtensionPtr> extensions;
+};
+
+#endif // SQLITEEXTENSIONMANAGERIMPL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h
index 4f822bc..f771c2c 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/pluginmanager.h
@@ -380,7 +380,7 @@ class API_EXPORT PluginManager : public QObject
template <class T>
PluginType* getPluginType() const
{
- foreach (PluginType* type, getPluginTypes())
+ for (PluginType* type : getPluginTypes())
{
if (!dynamic_cast<DefinedPluginType<T>*>(type))
continue;
@@ -406,7 +406,7 @@ class API_EXPORT PluginManager : public QObject
if (!type)
return typedPlugins;
- foreach (Plugin* plugin, getLoadedPlugins(type))
+ for (Plugin* plugin : getLoadedPlugins(type))
typedPlugins << dynamic_cast<T*>(plugin);
return typedPlugins;
@@ -427,7 +427,7 @@ class API_EXPORT PluginManager : public QObject
if (!type)
return names;
- foreach (Plugin* plugin, getLoadedPlugins(type))
+ for (Plugin* plugin : getLoadedPlugins(type))
names << plugin->getName();
return names;
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h b/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h
new file mode 100644
index 0000000..a135f3b
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/services/sqliteextensionmanager.h
@@ -0,0 +1,34 @@
+#ifndef SQLITEEXTENSIONMANAGER_H
+#define SQLITEEXTENSIONMANAGER_H
+
+#include "coreSQLiteStudio_global.h"
+#include "sqlitestudio.h"
+#include <QSharedPointer>
+#include <QObject>
+
+class API_EXPORT SqliteExtensionManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ struct API_EXPORT Extension
+ {
+ QString filePath;
+ QString initFunc;
+ QStringList databases;
+ bool allDatabases = true;
+ };
+
+ typedef QSharedPointer<Extension> ExtensionPtr;
+
+ virtual void setExtensions(const QList<ExtensionPtr>& newExtensions) = 0;
+ virtual QList<ExtensionPtr> getAllExtensions() const = 0;
+ virtual QList<ExtensionPtr> getExtensionForDatabase(const QString& dbName) const = 0;
+
+ signals:
+ void extensionListChanged();
+};
+
+#define SQLITE_EXTENSIONS SQLITESTUDIO->getSqliteExtensionManager()
+
+#endif // SQLITEEXTENSIONMANAGER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp b/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp
index 3663a1b..87df73b 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp
@@ -1,461 +1,100 @@
#ifdef PORTABLE_CONFIG
#include "updatemanager.h"
-#include "services/pluginmanager.h"
#include "services/notifymanager.h"
#include "common/unused.h"
-#include <QTemporaryDir>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QUrl>
-#include <QUrlQuery>
#include <QDebug>
+#include <QRegularExpression>
#include <QCoreApplication>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <QProcess>
-#include <QThread>
-#include <QtConcurrent/QtConcurrent>
-
-#ifdef Q_OS_WIN32
-#include "JlCompress.h"
-#include <windows.h>
-#include <shellapi.h>
-#endif
-
-// Note on creating update packages:
-// Packages for Linux and MacOSX should be an archive of _contents_ of SQLiteStudio directory,
-// while for Windows it should be an archive of SQLiteStudio directory itself.
-
-QString UpdateManager::staticErrorMessage;
-UpdateManager::RetryFunction UpdateManager::retryFunction = nullptr;
+#include <QFileInfo>
+#include <QtConcurrent/QtConcurrentRun>
UpdateManager::UpdateManager(QObject *parent) :
QObject(parent)
{
- networkManager = new QNetworkAccessManager(this);
- connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*)));
- connect(this, SIGNAL(updatingError(QString)), NOTIFY_MANAGER, SLOT(error(QString)));
-}
-
-UpdateManager::~UpdateManager()
-{
- cleanup();
-}
+ qRegisterMetaType<QList<UpdateManager::UpdateEntry>>();
-void UpdateManager::checkForUpdates(bool force)
-{
- getUpdatesMetadata(updatesCheckReply, force);
-}
-
-void UpdateManager::update()
-{
- if (updatesGetUrlsReply || updatesInProgress)
- return;
-
- getUpdatesMetadata(updatesGetUrlsReply);
-}
+ connect(this, SIGNAL(updatingError(QString)), NOTIFY_MANAGER, SLOT(error(QString)));
-QString UpdateManager::getPlatformForUpdate() const
-{
-#if defined(Q_OS_LINUX)
- if (QSysInfo::WordSize == 64)
- return "linux64";
- else
- return "linux32";
-#elif defined(Q_OS_WIN)
- return "win32";
+ QString updateBinary =
+#if defined(Q_OS_WIN)
+ "UpdateSQLiteStudio.exe";
+#elif defined(Q_OS_LINUX)
+ "UpdateSQLiteStudio";
#elif defined(Q_OS_OSX)
- return "macosx";
+ "../../UpdateSQLiteStudio.app/Contents/MacOS/UpdateSQLiteStudio";
#else
- return QString();
+ "";
#endif
-}
-
-QString UpdateManager::getCurrentVersions() const
-{
- QJsonArray versionsArray;
-
- QJsonObject arrayEntry;
- arrayEntry["component"] = "SQLiteStudio";
- arrayEntry["version"] = SQLITESTUDIO->getVersionString();
- versionsArray.append(arrayEntry);
-
- for (const PluginManager::PluginDetails& details : PLUGINS->getAllPluginDetails())
- {
- if (details.builtIn)
- continue;
- arrayEntry["component"] = details.name;
- arrayEntry["version"] = details.versionString;
- versionsArray.append(arrayEntry);
+ if (!updateBinary.isEmpty()) {
+ updateBinaryAbsolutePath = QFileInfo(QCoreApplication::applicationDirPath() + "/" + updateBinary).absoluteFilePath();
}
-
- QJsonObject topObj;
- topObj["versions"] = versionsArray;
-
- QJsonDocument doc(topObj);
- return QString::fromLatin1(doc.toJson(QJsonDocument::Compact));
-}
-
-bool UpdateManager::isPlatformEligibleForUpdate() const
-{
- return !getPlatformForUpdate().isNull() && getDistributionType() != DistributionType::OS_MANAGED;
-}
-
-#if defined(Q_OS_WIN32)
-bool UpdateManager::executePreFinalStepWin(const QString &tempDir, const QString &backupDir, const QString &appDir, bool reqAdmin)
-{
- bool res;
- if (reqAdmin)
- res = executeFinalStepAsRootWin(tempDir, backupDir, appDir);
- else
- res = executeFinalStep(tempDir, backupDir, appDir);
-
- if (res)
- {
- QFileInfo path(qApp->applicationFilePath());
- QProcess::startDetached(appDir + "/" + path.fileName(), {WIN_POST_FINAL_UPDATE_OPTION_NAME, tempDir});
- }
- return res;
}
-#endif
-void UpdateManager::handleAvailableUpdatesReply(QNetworkReply* reply)
-{
- if (reply->error() != QNetworkReply::NoError)
- {
- updatingFailed(tr("An error occurred while checking for updates: %1.").arg(reply->errorString()));
- reply->deleteLater();
- return;
- }
-
- QJsonParseError err;
- QByteArray data = reply->readAll();
- reply->deleteLater();
-
- QJsonDocument doc = QJsonDocument::fromJson(data, &err);
- if (err.error != QJsonParseError::NoError)
- {
- qWarning() << "Invalid response from update service:" << err.errorString() << "\n" << "The data was:" << QString::fromLatin1(data);
- notifyWarn(tr("Could not check available updates, because server responded with invalid message format. It is safe to ignore this warning."));
- return;
- }
-
- QList<UpdateEntry> updates = readMetadata(doc);
- if (updates.size() > 0)
- emit updatesAvailable(updates);
- else
- emit noUpdatesAvailable();
-}
-
-void UpdateManager::getUpdatesMetadata(QNetworkReply*& replyStoragePointer, bool force)
+UpdateManager::~UpdateManager()
{
-#ifdef PORTABLE_CONFIG
- if ((!CFG_CORE.General.CheckUpdatesOnStartup.get() && !force) || !isPlatformEligibleForUpdate() || replyStoragePointer)
- return;
-
- QUrlQuery query;
- query.addQueryItem("platform", getPlatformForUpdate());
- query.addQueryItem("data", getCurrentVersions());
- QUrl url(QString::fromLatin1(updateServiceUrl) + "?" + query.query(QUrl::FullyEncoded));
- QNetworkRequest request(url);
- replyStoragePointer = networkManager->get(request);
-#endif
}
-void UpdateManager::handleUpdatesMetadata(QNetworkReply* reply)
+void UpdateManager::checkForUpdates()
{
- if (reply->error() != QNetworkReply::NoError)
- {
- updatingFailed(tr("An error occurred while reading updates metadata: %1.").arg(reply->errorString()));
- reply->deleteLater();
+ if (!CFG_CORE.General.CheckUpdatesOnStartup.get())
return;
- }
- QJsonParseError err;
- QByteArray data = reply->readAll();
- reply->deleteLater();
-
- QJsonDocument doc = QJsonDocument::fromJson(data, &err);
- if (err.error != QJsonParseError::NoError)
- {
- qWarning() << "Invalid response from update service for getting metadata:" << err.errorString() << "\n" << "The data was:" << QString::fromLatin1(data);
- notifyWarn(tr("Could not download updates, because server responded with invalid message format. "
- "You can try again later or download and install updates manually. See <a href=\"%1\">User Manual</a> for details.").arg(manualUpdatesHelpUrl));
+ if (updateBinaryAbsolutePath.isEmpty()) {
+ qDebug() << "Updater binary not defined. Skipping updates checking.";
return;
}
- tempDir = new QTemporaryDir();
- if (!tempDir->isValid()) {
- notifyWarn(tr("Could not create temporary directory for downloading the update. Updating aborted."));
+ if (!QFileInfo(updateBinaryAbsolutePath).exists()) {
+ QString errorDetails = tr("Updates installer executable is missing.");
+ emit updatingError(tr("Unable to check for updates (%1)").arg(errorDetails.trimmed()));
+ qWarning() << "Error while checking for updates: " << errorDetails;
return;
}
- updatesInProgress = true;
- updatesToDownload = readMetadata(doc);
- totalDownloadsCount = updatesToDownload.size();
- totalPercent = 0;
-
- if (totalDownloadsCount == 0)
- {
- updatingFailed(tr("There was no updates to download. Updating aborted."));
- return;
- }
-
- downloadUpdates();
+ QtConcurrent::run(this, &UpdateManager::checkForUpdatesAsync);
}
-QList<UpdateManager::UpdateEntry> UpdateManager::readMetadata(const QJsonDocument& doc)
+void UpdateManager::checkForUpdatesAsync()
{
- QList<UpdateEntry> updates;
- UpdateEntry entry;
- QJsonObject obj = doc.object();
- QJsonArray versionsArray = obj["newVersions"].toArray();
- QJsonObject entryObj;
- for (const QJsonValue& value : versionsArray)
- {
- entryObj = value.toObject();
- entry.compontent = entryObj["component"].toString();
- entry.version = entryObj["version"].toString();
- entry.url = entryObj["url"].toString();
- updates << entry;
- }
-
- return updates;
-}
-
-void UpdateManager::downloadUpdates()
-{
- if (updatesToDownload.size() == 0)
+ QProcess proc;
+ proc.start(updateBinaryAbsolutePath, {"--checkupdates"});
+ if (!waitForProcess(proc))
{
- QtConcurrent::run(this, &UpdateManager::installUpdates);
- return;
- }
+ QString errorDetails = QString::fromLocal8Bit(proc.readAllStandardError());
- UpdateEntry entry = updatesToDownload.takeFirst();
- currentJobTitle = tr("Downloading: %1").arg(entry.compontent);
- emit updatingProgress(currentJobTitle, 0, totalPercent);
+ if (errorDetails.toLower().contains("no updates")) {
+ emit noUpdatesAvailable();
+ return;
+ }
- QStringList parts = entry.url.split("/");
- if (parts.size() < 1)
- {
- updatingFailed(tr("Could not determinate file name from update URL: %1. Updating aborted.").arg(entry.url));
- return;
- }
+ if (errorDetails.isEmpty())
+ errorDetails = tr("details are unknown");
- QString path = tempDir->path() + QLatin1Char('/') + parts.last();
- currentDownloadFile = new QFile(path);
- if (!currentDownloadFile->open(QIODevice::WriteOnly))
- {
- updatingFailed(tr("Failed to open file '%1' for writting: %2. Updating aborted.").arg(path, currentDownloadFile->errorString()));
+ emit updatingError(tr("Unable to check for updates (%1)").arg(errorDetails.trimmed()));
+ qWarning() << "Error while checking for updates: " << errorDetails;
return;
}
- updatesToInstall[entry.compontent] = path;
-
- QNetworkRequest request(QUrl(entry.url));
- updatesGetReply = networkManager->get(request);
- connect(updatesGetReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
- connect(updatesGetReply, SIGNAL(readyRead()), this, SLOT(readDownload()));
+ processCheckResults(proc.readAllStandardOutput());
}
-void UpdateManager::updatingFailed(const QString& errMsg)
-{
- cleanup();
- updatesInProgress = false;
- emit updatingError(errMsg);
-}
-
-void UpdateManager::installUpdates()
+void UpdateManager::update()
{
- currentJobTitle = tr("Installing updates.");
- totalPercent = (totalDownloadsCount - updatesToDownload.size()) * 100 / (totalDownloadsCount + 1);
- emit updatingProgress(currentJobTitle, 0, totalPercent);
-
- requireAdmin = doRequireAdminPrivileges();
-
- QTemporaryDir installTempDir;
- QString appDirName = QDir(getAppDirPath()).dirName();
- QString targetDir = installTempDir.path() + QLatin1Char('/') + appDirName;
- if (!copyRecursively(getAppDirPath(), targetDir))
+ bool success = QProcess::startDetached(updateBinaryAbsolutePath, {"--updater"});
+ if (!success)
{
- updatingFailed(tr("Could not copy current application directory into %1 directory.").arg(installTempDir.path()));
+ emit updatingError(tr("Unable to run updater application (%1). Please report this.").arg(updateBinaryAbsolutePath));
return;
}
- emit updatingProgress(currentJobTitle, 40, totalPercent);
-
- int i = 0;
- int updatesCnt = updatesToInstall.size();
- for (const QString& component : updatesToInstall.keys())
- {
- if (!installComponent(component, targetDir))
- {
- cleanup();
- updatesInProgress = false;
- return;
- }
- i++;
- emit updatingProgress(currentJobTitle, (30 + (50 / updatesCnt * i)), totalPercent);
- }
-
- if (!executeFinalStep(targetDir))
- {
- cleanup();
- updatesInProgress = false;
- return;
- }
-
- currentJobTitle = QString();
- totalPercent = 100;
- emit updatingProgress(currentJobTitle, 100, totalPercent);
- cleanup();
- updatesInProgress = false;
-#ifdef Q_OS_WIN32
- installTempDir.setAutoRemove(false);
-#endif
-
- SQLITESTUDIO->setImmediateQuit(true);
qApp->exit(0);
}
-bool UpdateManager::executeFinalStep(const QString& tempDir, const QString& backupDir, const QString& appDir)
-{
- bool isWin = false;
-#ifdef Q_OS_WIN32
- isWin = true;
-
- // Windows needs to wait for previus process to exit
- QThread::sleep(3);
-
- QDir dir(backupDir);
- QString dirName = dir.dirName();
- dir.cdUp();
- if (!dir.mkdir(dirName))
- {
- staticUpdatingFailed(tr("Could not create directory %1.").arg(backupDir));
- return false;
- }
-#endif
- while (!moveDir(appDir, backupDir, isWin))
- {
- if (!retryFunction)
- {
- staticUpdatingFailed(tr("Could not rename directory %1 to %2.\nDetails: %3").arg(appDir, backupDir, staticErrorMessage));
- return false;
- }
-
- if (!retryFunction(tr("Cannot not rename directory %1 to %2.\nDetails: %3").arg(appDir, backupDir, staticErrorMessage)))
- return false;
- }
-
- if (!moveDir(tempDir, appDir, isWin))
- {
- if (!moveDir(backupDir, appDir, isWin))
- {
- staticUpdatingFailed(tr("Could not move directory %1 to %2 and also failed to restore original directory, "
- "so the original SQLiteStudio directory is now located at: %3").arg(tempDir, appDir, backupDir));
- }
- else
- {
- staticUpdatingFailed(tr("Could not rename directory %1 to %2. Rolled back to the original SQLiteStudio version.").arg(tempDir, appDir));
- }
- deleteDir(backupDir);
- return false;
- }
-
- deleteDir(backupDir);
- return true;
-}
-
-bool UpdateManager::handleUpdateOptions(const QStringList& argList, int& returnCode)
-{
- if (argList.size() == 5 && argList[1] == UPDATE_OPTION_NAME)
- {
- bool result = UpdateManager::executeFinalStep(argList[2], argList[3], argList[4]);
- if (result)
- returnCode = 0;
- else
- returnCode = 1;
-
- return true;
- }
-
-#ifdef Q_OS_WIN32
- if (argList.size() == 6 && argList[1] == WIN_PRE_FINAL_UPDATE_OPTION_NAME)
- {
- bool result = UpdateManager::executePreFinalStepWin(argList[2], argList[3], argList[4], (bool)argList[5].toInt());
- if (result)
- returnCode = 0;
- else
- returnCode = -1;
-
- return true;
- }
-
- if (argList.size() == 3 && argList[1] == WIN_POST_FINAL_UPDATE_OPTION_NAME)
- {
- QThread::sleep(1); // to make sure that the previous process has quit
- returnCode = 0;
- UpdateManager::executePostFinalStepWin(argList[2]);
- return true;
- }
-#endif
-
- return false;
-}
-
-QString UpdateManager::getStaticErrorMessage()
-{
- return staticErrorMessage;
-}
-
-bool UpdateManager::executeFinalStep(const QString& tempDir)
-{
- QString appDir = getAppDirPath();
-
- // Find inexisting dir name next to app dir
- QDir backupDir(getBackupDir(appDir));
-
-#if defined(Q_OS_WIN32)
- return runAnotherInstanceForUpdate(tempDir, backupDir.absolutePath(), qApp->applicationDirPath(), requireAdmin);
-#else
- bool res;
- if (requireAdmin)
- res = executeFinalStepAsRoot(tempDir, backupDir.absolutePath(), appDir);
- else
- res = executeFinalStep(tempDir, backupDir.absolutePath(), appDir);
-
- if (res)
- QProcess::startDetached(qApp->applicationFilePath(), QStringList());
-
- return res;
-#endif
-}
-
-bool UpdateManager::installComponent(const QString& component, const QString& tempDir)
-{
- if (!unpackToDir(updatesToInstall[component], tempDir))
- {
- updatingFailed(tr("Could not unpack component %1 into %2 directory.").arg(component, tempDir));
- return false;
- }
-
- // In future here we might also delete/change some files, according to some update script.
- return true;
-}
-
-void UpdateManager::cleanup()
+bool UpdateManager::isPlatformEligibleForUpdate() const
{
- safe_delete(currentDownloadFile);
- safe_delete(tempDir);
- updatesToDownload.clear();
- updatesToInstall.clear();
- requireAdmin = false;
+ return getDistributionType() != DistributionType::OS_MANAGED;
}
bool UpdateManager::waitForProcess(QProcess& proc)
@@ -481,582 +120,33 @@ bool UpdateManager::waitForProcess(QProcess& proc)
return true;
}
-QString UpdateManager::readError(QProcess& proc, bool reverseOrder)
-{
- QString err = QString::fromLocal8Bit(reverseOrder ? proc.readAllStandardOutput() : proc.readAllStandardError());
- if (err.isEmpty())
- err = QString::fromLocal8Bit(reverseOrder ? proc.readAllStandardError() : proc.readAllStandardOutput());
-
- QString errStr = proc.errorString();
- if (!errStr.isEmpty())
- err += "\n" + errStr;
-
- return err;
-}
-
-void UpdateManager::staticUpdatingFailed(const QString& errMsg)
+void UpdateManager::processCheckResults(const QByteArray &results)
{
-#if defined(Q_OS_WIN32)
- staticErrorMessage = errMsg;
-#else
- UPDATES->handleStaticFail(errMsg);
-#endif
- qCritical() << errMsg;
-}
-
-bool UpdateManager::executeFinalStepAsRoot(const QString& tempDir, const QString& backupDir, const QString& appDir)
-{
-#if defined(Q_OS_LINUX)
- return executeFinalStepAsRootLinux(tempDir, backupDir, appDir);
-#elif defined(Q_OS_WIN32)
- return executeFinalStepAsRootWin(tempDir, backupDir, appDir);
-#elif defined(Q_OS_MACX)
- return executeFinalStepAsRootMac(tempDir, backupDir, appDir);
-#else
- qCritical() << "Unknown update platform in UpdateManager::executeFinalStepAsRoot() for package" << packagePath;
- return false;
-#endif
-}
-
-#if defined(Q_OS_LINUX)
-bool UpdateManager::executeFinalStepAsRootLinux(const QString& tempDir, const QString& backupDir, const QString& appDir)
-{
- QStringList args = {qApp->applicationFilePath(), UPDATE_OPTION_NAME, tempDir, backupDir, appDir};
-
- QProcess proc;
- LinuxPermElevator elevator = findPermElevatorForLinux();
- switch (elevator)
- {
- case LinuxPermElevator::KDESU:
- proc.setProgram("kdesu");
- args.prepend("-t");
- proc.setArguments(args);
- break;
- case LinuxPermElevator::GKSU:
- proc.setProgram("gksu"); // TODO test gksu updates
- proc.setArguments(args);
- break;
- case LinuxPermElevator::PKEXEC:
- {
- // We call CLI for doing final step, because pkexec runs cmd completly in root env, so there's no X server.
- args[0] += "cli";
-
- QStringList newArgs;
- for (const QString& arg : args)
- newArgs << wrapCmdLineArgument(arg);
-
- QString cmd = "cd " + wrapCmdLineArgument(qApp->applicationDirPath()) +"; " + newArgs.join(" ");
-
- proc.setProgram("pkexec");
- proc.setArguments({"sh", "-c", cmd});
- }
- break;
- case LinuxPermElevator::NONE:
- updatingFailed(tr("Could not find permissions elevator application to run update as a root. Looked for: %1").arg("kdesu, gksu, pkexec"));
- return false;
- }
-
- proc.start();
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Could not execute final updating steps as root: %1").arg(readError(proc, (elevator == LinuxPermElevator::KDESU))));
- return false;
- }
-
- return true;
-}
-#endif
-
-#ifdef Q_OS_MACX
-bool UpdateManager::executeFinalStepAsRootMac(const QString& tempDir, const QString& backupDir, const QString& appDir)
-{
- // Prepare script for updater
- // osascript -e "do shell script \"stufftorunasroot\" with administrator privileges"
- QStringList args = {wrapCmdLineArgument(qApp->applicationFilePath() + "cli"),
- UPDATE_OPTION_NAME,
- wrapCmdLineArgument(tempDir),
- wrapCmdLineArgument(backupDir),
- wrapCmdLineArgument(appDir)};
- QProcess proc;
-
- QString innerCmd = wrapCmdLineArgument(args.join(" "));
-
- static_qstring(scriptTpl, "do shell script %1 with administrator privileges");
- QString scriptCmd = scriptTpl.arg(innerCmd);
-
- // Prepare updater temporary directory
- QTemporaryDir updaterDir;
- if (!updaterDir.isValid())
- {
- updatingFailed(tr("Could not execute final updating steps as admin: %1").arg(tr("Cannot create temporary directory for updater.")));
- return false;
- }
-
- // Create updater script
- QString scriptPath = updaterDir.path() + "/UpdateSQLiteStudio.scpt";
- QFile updaterScript(scriptPath);
- if (!updaterScript.open(QIODevice::WriteOnly))
- {
- updatingFailed(tr("Could not execute final updating steps as admin: %1").arg(tr("Cannot create updater script file.")));
- return false;
- }
- updaterScript.write(scriptCmd.toLocal8Bit());
- updaterScript.close();
-
- // Compile script to updater application
- QString updaterApp = updaterDir.path() + "/UpdateSQLiteStudio.app";
- proc.setProgram("osacompile");
- proc.setArguments({"-o", updaterApp, scriptPath});
- proc.start();
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Could not execute final updating steps as admin: %1").arg(readError(proc)));
- return false;
- }
-
- // Execute updater
- proc.setProgram(updaterApp + "/Contents/MacOS/applet");
- proc.setArguments({});
- proc.start();
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Could not execute final updating steps as admin: %1").arg(readError(proc)));
- return false;
- }
-
- // Validating update
- // The updater script will not return error if the user canceled the password prompt.
- // We need to check if the update was actually made and return true only then.
- if (QDir(tempDir).exists())
- {
- // Temp dir still exists, so it was not moved by root process
- updatingFailed(tr("Updating canceled."));
- return false;
- }
-
- return true;
-}
-#endif
-
-#ifdef Q_OS_WIN32
-bool UpdateManager::executeFinalStepAsRootWin(const QString& tempDir, const QString& backupDir, const QString& appDir)
-{
- QString updateBin = qApp->applicationDirPath() + "/" + WIN_UPDATER_BINARY;
-
- QString installFilePath = tempDir + "/" + WIN_INSTALL_FILE;
- QFile installFile(installFilePath);
- installFile.open(QIODevice::WriteOnly);
- QString nl("\n");
- installFile.write(UPDATE_OPTION_NAME);
- installFile.write(nl.toLocal8Bit());
- installFile.write(backupDir.toLocal8Bit());
- installFile.write(nl.toLocal8Bit());
- installFile.write(appDir.toLocal8Bit());
- installFile.write(nl.toLocal8Bit());
- installFile.close();
-
- int res = (int)::ShellExecuteA(0, "runas", updateBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
- if (res < 32)
- {
- staticUpdatingFailed(tr("Could not execute final updating steps as administrator."));
- return false;
- }
-
- // Since I suck as a developer and I cannot implement a simple synchronous app call under Windows
- // (QProcess does it somehow, but I'm too lazy to look it up and probably the solution wouldn't be compatible
- // with our "privileges elevation" trick above... so after all I think we're stuck with this solution for now),
- // I do the workaround here, which makes this process wait for the other process to create the "done"
- // file when it's done, so this process knows when the other has ended. This way we can proceed with this
- // process and we will delete some directories later on, which were required by that other process.
- if (!waitForFileToDisappear(installFilePath, 10))
- {
- staticUpdatingFailed(tr("Could not execute final updating steps as administrator. Updater startup timed out."));
- return false;
- }
-
- if (!waitForFileToAppear(appDir + QLatin1Char('/') + WIN_UPDATE_DONE_FILE, 30))
- {
- staticUpdatingFailed(tr("Could not execute final updating steps as administrator. Updater operation timed out."));
- return false;
- }
-
- return true;
-}
-#endif
-
-#if defined(Q_OS_WIN32)
-bool UpdateManager::executePostFinalStepWin(const QString &tempDir)
-{
- QString doneFile = qApp->applicationDirPath() + QLatin1Char('/') + WIN_UPDATE_DONE_FILE;
- QFile::remove(doneFile);
-
- QDir dir(tempDir);
- dir.cdUp();
- if (!deleteDir(dir.absolutePath()))
- staticUpdatingFailed(tr("Could not clean up temporary directory %1. You can delete it manually at any time.").arg(dir.absolutePath()));
-
- QProcess::startDetached(qApp->applicationFilePath(), QStringList());
- return true;
-}
-
-bool UpdateManager::waitForFileToDisappear(const QString &filePath, int seconds)
-{
- QFile file(filePath);
- while (file.exists() && seconds > 0)
- {
- QThread::sleep(1);
- seconds--;
- }
-
- return !file.exists();
-}
-
-bool UpdateManager::waitForFileToAppear(const QString &filePath, int seconds)
-{
- QFile file(filePath);
- while (!file.exists() && seconds > 0)
- {
- QThread::sleep(1);
- seconds--;
- }
-
- return file.exists();
-}
-
-bool UpdateManager::runAnotherInstanceForUpdate(const QString &tempDir, const QString &backupDir, const QString &appDir, bool reqAdmin)
-{
- bool res = QProcess::startDetached(tempDir + "/SQLiteStudio.exe", {WIN_PRE_FINAL_UPDATE_OPTION_NAME, tempDir, backupDir, appDir,
- QString::number((int)reqAdmin)});
- if (!res)
- {
- updatingFailed(tr("Could not run new version for continuing update."));
- return false;
- }
-
- return true;
-}
-#endif
-
-UpdateManager::LinuxPermElevator UpdateManager::findPermElevatorForLinux()
-{
-#if defined(Q_OS_LINUX)
- QProcess proc;
- proc.setProgram("which");
-
- if (!SQLITESTUDIO->getEnv("DISPLAY").isEmpty())
- {
- proc.setArguments({"kdesu"});
- proc.start();
- if (waitForProcess(proc))
- return LinuxPermElevator::KDESU;
-
- proc.setArguments({"gksu"});
- proc.start();
- if (waitForProcess(proc))
- return LinuxPermElevator::GKSU;
- }
-
- proc.setArguments({"pkexec"});
- proc.start();
- if (waitForProcess(proc))
- return LinuxPermElevator::PKEXEC;
-#endif
-
- return LinuxPermElevator::NONE;
-}
-
-QString UpdateManager::wrapCmdLineArgument(const QString& arg)
-{
- return "\"" + escapeCmdLineArgument(arg) + "\"";
-}
-
-QString UpdateManager::escapeCmdLineArgument(const QString& arg)
-{
- if (!arg.contains("\\") && !arg.contains("\""))
- return arg;
-
- QString str = arg;
- return str.replace("\\", "\\\\").replace("\"", "\\\"");
-}
-
-QString UpdateManager::getBackupDir(const QString &appDir)
-{
- static_qstring(bakDirTpl, "%1.old%2");
- QDir backupDir(bakDirTpl.arg(appDir, ""));
- int cnt = 1;
- while (backupDir.exists())
- backupDir = QDir(bakDirTpl.arg(appDir, QString::number(cnt)));
-
- return backupDir.absolutePath();
-}
-
-bool UpdateManager::unpackToDir(const QString& packagePath, const QString& outputDir)
-{
-#if defined(Q_OS_LINUX)
- return unpackToDirLinux(packagePath, outputDir);
-#elif defined(Q_OS_WIN32)
- return unpackToDirWin(packagePath, outputDir);
-#elif defined(Q_OS_MACX)
- return unpackToDirMac(packagePath, outputDir);
-#else
- qCritical() << "Unknown update platform in UpdateManager::unpackToDir() for package" << packagePath;
- return false;
-#endif
-}
-
-#if defined(Q_OS_LINUX)
-bool UpdateManager::unpackToDirLinux(const QString &packagePath, const QString &outputDir)
-{
- QProcess proc;
- proc.setWorkingDirectory(outputDir);
- proc.setStandardOutputFile(QProcess::nullDevice());
- proc.setStandardErrorFile(QProcess::nullDevice());
-
- if (!packagePath.endsWith("tar.gz"))
- {
- updatingFailed(tr("Package not in tar.gz format, cannot install: %1").arg(packagePath));
- return false;
- }
-
- proc.start("mv", {packagePath, outputDir});
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Package %1 cannot be installed, because cannot move it to directory: %2").arg(packagePath, outputDir));
- return false;
- }
-
- QString fileName = packagePath.split("/").last();
- QString newPath = outputDir + "/" + fileName;
- proc.start("tar", {"-xzf", newPath});
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Package %1 cannot be installed, because cannot unpack it: %2").arg(packagePath, readError(proc)));
- return false;
- }
-
- QProcess::execute("rm", {"-f", newPath});
- return true;
-}
-#endif
-
-#if defined(Q_OS_MACX)
-bool UpdateManager::unpackToDirMac(const QString &packagePath, const QString &outputDir)
-{
- QProcess proc;
- proc.setWorkingDirectory(outputDir);
- proc.setStandardOutputFile(QProcess::nullDevice());
- proc.setStandardErrorFile(QProcess::nullDevice());
-
- if (!packagePath.endsWith("zip"))
- {
- updatingFailed(tr("Package not in zip format, cannot install: %1").arg(packagePath));
- return false;
- }
-
- proc.start("unzip", {"-o", "-d", outputDir, packagePath});
- if (!waitForProcess(proc))
- {
- updatingFailed(tr("Package %1 cannot be installed, because cannot unzip it to directory %2: %3")
- .arg(packagePath, outputDir, readError(proc)));
- return false;
- }
-
- return true;
-}
-#endif
-
-#if defined(Q_OS_WIN32)
-bool UpdateManager::unpackToDirWin(const QString& packagePath, const QString& outputDir)
-{
- if (JlCompress::extractDir(packagePath, outputDir + "/..").isEmpty())
- {
- updatingFailed(tr("Package %1 cannot be installed, because cannot unzip it to directory: %2").arg(packagePath, outputDir));
- return false;
- }
-
- return true;
-}
-#endif
-
-void UpdateManager::handleStaticFail(const QString& errMsg)
-{
- emit updatingFailed(errMsg);
-}
-
-QString UpdateManager::getAppDirPath() const
-{
- static QString appDir;
- if (appDir.isNull())
- {
- appDir = qApp->applicationDirPath();
-#ifdef Q_OS_MACX
- QDir tmpAppDir(appDir);
- tmpAppDir.cdUp();
- tmpAppDir.cdUp();
- appDir = tmpAppDir.absolutePath();
-#endif
- }
- return appDir;
-}
-
-bool UpdateManager::moveDir(const QString& src, const QString& dst, bool contentsOnly)
-{
- // If we're doing a rename in the very same parent directory then we don't want
- // the 'move between partitions' to be involved, cause any failure to rename
- // is due to permissions or file lock.
- QFileInfo srcFi(src);
- QFileInfo dstFi(dst);
- bool sameParentDir = (srcFi.dir() == dstFi.dir());
-
- QDir dir;
- if (contentsOnly)
- {
- QString localSrc;
- QString localDst;
- QDir srcDir(src);
- for (const QFileInfo& entry : srcDir.entryInfoList(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot|QDir::Hidden|QDir::System))
- {
- localSrc = entry.absoluteFilePath();
- localDst = dst + "/" + entry.fileName();
- if (!dir.rename(localSrc, localDst) && (sameParentDir || !renameBetweenPartitions(localSrc, localDst)))
- {
- staticUpdatingFailed(tr("Could not rename directory %1 to %2.").arg(localSrc, localDst));
- return false;
- }
- }
- }
- else
- {
- if (!dir.rename(src, dst) && (sameParentDir || !renameBetweenPartitions(src, dst)))
- {
- staticUpdatingFailed(tr("Could not rename directory %1 to %2.").arg(src, dst));
- return false;
- }
- }
-
- return true;
-}
-
-bool UpdateManager::deleteDir(const QString& path)
-{
- QDir dir(path);
- if (!dir.removeRecursively())
- {
- staticUpdatingFailed(tr("Could not delete directory %1.").arg(path));
- return false;
- }
-
- return true;
-}
-
-bool UpdateManager::execCmd(const QString& cmd, const QStringList& args, QString* errorMsg)
-{
- QProcess proc;
- proc.start(cmd, args);
- QString cmdString = QString("%1 \"%2\"").arg(cmd, args.join("\\\" \\\""));
-
- if (!waitForProcess(proc))
- {
- if (errorMsg)
- *errorMsg = tr("Error executing update command: %1\nError message: %2").arg(cmdString).arg(readError(proc));
-
- return false;
- }
-
- return true;
-}
-
-void UpdateManager::setRetryFunction(const RetryFunction &value)
-{
- retryFunction = value;
-}
-
-bool UpdateManager::doRequireAdminPrivileges()
-{
- QString appDirPath = getAppDirPath();
- QDir appDir(appDirPath);
- bool isWritable = isWritableRecursively(appDir.absolutePath());
-
- appDir.cdUp();
- QFileInfo fi(appDir.absolutePath());
- isWritable &= fi.isWritable();
-
- if (isWritable)
- {
- QDir backupDir(getBackupDir(appDirPath));
- QString backupDirName = backupDir.dirName();
- backupDir.cdUp();
- if (backupDir.mkdir(backupDirName))
- backupDir.rmdir(backupDirName);
- else
- isWritable = false;
- }
-
- return !isWritable;
-}
-
-void UpdateManager::finished(QNetworkReply* reply)
-{
- if (reply == updatesCheckReply)
- {
- updatesCheckReply = nullptr;
- handleAvailableUpdatesReply(reply);
- return;
- }
-
- if (reply == updatesGetUrlsReply)
- {
- updatesGetUrlsReply = nullptr;
- handleUpdatesMetadata(reply);
+ if (results.trimmed().isEmpty()) {
+ emit noUpdatesAvailable();
return;
}
- if (reply == updatesGetReply)
- {
- handleDownloadReply(reply);
- if (reply == updatesGetReply) // if no new download is requested
- updatesGetReply = nullptr;
+ QRegularExpression re(R"(\<update\s+([^\>]+)\>)");
+ QRegularExpression versionRe(R"(version\=\"([\d\.]+)\")");
+ QRegularExpression nameRe(R"(name\=\"([^\"]+)\")");
- return;
- }
-}
-
-void UpdateManager::handleDownloadReply(QNetworkReply* reply)
-{
- if (reply->error() != QNetworkReply::NoError)
+ QRegularExpressionMatchIterator reIter = re.globalMatch(results);
+ QString updateNode;
+ UpdateEntry theUpdate;
+ QList<UpdateEntry> updates;
+ while (reIter.hasNext())
{
- updatingFailed(tr("An error occurred while downloading updates: %1. Updating aborted.").arg(reply->errorString()));
- reply->deleteLater();
- return;
+ updateNode = reIter.next().captured(1);
+ theUpdate.version = versionRe.match(updateNode).captured(1);
+ theUpdate.compontent = nameRe.match(updateNode).captured(1);
+ updates << theUpdate;
}
- totalPercent = (totalDownloadsCount - updatesToDownload.size()) * 100 / (totalDownloadsCount + 1);
-
- readDownload();
- currentDownloadFile->close();
-
- safe_delete(currentDownloadFile);
-
- reply->deleteLater();
- downloadUpdates();
-}
-
-void UpdateManager::downloadProgress(qint64 bytesReceived, qint64 totalBytes)
-{
- int perc;
- if (totalBytes < 0)
- perc = -1;
- else if (totalBytes == 0)
- perc = 100;
+ if (updates.isEmpty())
+ emit noUpdatesAvailable();
else
- perc = bytesReceived * 100 / totalBytes;
-
- emit updatingProgress(currentJobTitle, perc, totalPercent);
-}
-
-void UpdateManager::readDownload()
-{
- currentDownloadFile->write(updatesGetReply->readAll());
+ emit updatesAvailable(updates);
}
#endif // PORTABLE_CONFIG
diff --git a/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.h b/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.h
index bb33487..50f4b6b 100644
--- a/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.h
+++ b/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.h
@@ -18,122 +18,34 @@ class API_EXPORT UpdateManager : public QObject
{
Q_OBJECT
public:
- typedef std::function<bool(const QString& msg)> RetryFunction;
-
struct UpdateEntry
{
QString compontent;
QString version;
- QString url;
};
explicit UpdateManager(QObject *parent = 0);
~UpdateManager();
- void checkForUpdates(bool force = false);
+ void checkForUpdates();
void update();
bool isPlatformEligibleForUpdate() const;
- static bool executeFinalStep(const QString& tempDir, const QString& backupDir, const QString& appDir);
- static bool handleUpdateOptions(const QStringList& argList, int& returnCode);
- static QString getStaticErrorMessage();
-
- static void setRetryFunction(const RetryFunction &value);
-
- static_char* UPDATE_OPTION_NAME = "--update-final-step";
- static_char* WIN_INSTALL_FILE = "install.dat";
- static_char* WIN_UPDATE_DONE_FILE = "UpdateFinished.lck";
private:
- enum class LinuxPermElevator
- {
- KDESU,
- GKSU,
- PKEXEC,
- NONE
- };
+ QString updateBinaryAbsolutePath;
- QString getPlatformForUpdate() const;
- QString getCurrentVersions() const;
- void handleAvailableUpdatesReply(QNetworkReply* reply);
- void handleDownloadReply(QNetworkReply* reply);
- void getUpdatesMetadata(QNetworkReply*& replyStoragePointer, bool force = false);
- void handleUpdatesMetadata(QNetworkReply* reply);
- QList<UpdateEntry> readMetadata(const QJsonDocument& doc);
- void downloadUpdates();
- void updatingFailed(const QString& errMsg);
- void installUpdates();
- bool installComponent(const QString& component, const QString& tempDir);
- bool executeFinalStep(const QString& tempDir);
- bool executeFinalStepAsRoot(const QString& tempDir, const QString& backupDir, const QString& appDir);
-#if defined(Q_OS_LINUX)
- bool executeFinalStepAsRootLinux(const QString& tempDir, const QString& backupDir, const QString& appDir);
- bool unpackToDirLinux(const QString& packagePath, const QString& outputDir);
-#elif defined(Q_OS_MACX)
- bool unpackToDirMac(const QString& packagePath, const QString& outputDir);
- bool executeFinalStepAsRootMac(const QString& tempDir, const QString& backupDir, const QString& appDir);
-#elif defined(Q_OS_WIN32)
- bool runAnotherInstanceForUpdate(const QString& tempDir, const QString& backupDir, const QString& appDir, bool reqAdmin);
- bool unpackToDirWin(const QString& packagePath, const QString& outputDir);
-#endif
- bool doRequireAdminPrivileges();
- bool unpackToDir(const QString& packagePath, const QString& outputDir);
- void handleStaticFail(const QString& errMsg);
- QString getAppDirPath() const;
- void cleanup();
-
- static bool moveDir(const QString& src, const QString& dst, bool contentsOnly = false);
- static bool deleteDir(const QString& path);
- static bool execCmd(const QString& cmd, const QStringList& args, QString* errorMsg = nullptr);
- static bool waitForProcess(QProcess& proc);
- static QString readError(QProcess& proc, bool reverseOrder = false);
- static void staticUpdatingFailed(const QString& errMsg);
- static LinuxPermElevator findPermElevatorForLinux();
- static QString wrapCmdLineArgument(const QString& arg);
- static QString escapeCmdLineArgument(const QString& arg);
- static QString getBackupDir(const QString& appDir);
-#if defined(Q_OS_WIN32)
- static bool executePreFinalStepWin(const QString& tempDir, const QString& backupDir, const QString& appDir, bool reqAdmin);
- static bool executeFinalStepAsRootWin(const QString& tempDir, const QString& backupDir, const QString& appDir);
- static bool executePostFinalStepWin(const QString& tempDir);
- static bool waitForFileToDisappear(const QString& filePath, int seconds);
- static bool waitForFileToAppear(const QString& filePath, int seconds);
-#endif
-
- QNetworkAccessManager* networkManager = nullptr;
- QNetworkReply* updatesCheckReply = nullptr;
- QNetworkReply* updatesGetUrlsReply = nullptr;
- QNetworkReply* updatesGetReply = nullptr;
- bool updatesInProgress = false;
- QList<UpdateEntry> updatesToDownload;
- QHash<QString,QString> updatesToInstall;
- QTemporaryDir* tempDir = nullptr;
- QFile* currentDownloadFile = nullptr;
- int totalPercent = 0;
- int totalDownloadsCount = 0;
- QString currentJobTitle;
- bool requireAdmin = false;
- static RetryFunction retryFunction;
-
- static QString staticErrorMessage;
- static_char* WIN_PRE_FINAL_UPDATE_OPTION_NAME = "--update-pre-final-step";
- static_char* WIN_POST_FINAL_UPDATE_OPTION_NAME = "--update-post-final-step";
- static_char* WIN_UPDATER_BINARY = "UpdateSQLiteStudio.exe";
- static_char* updateServiceUrl = "http://sqlitestudio.pl/updates3.rvt";
- static_char* manualUpdatesHelpUrl = "http://wiki.sqlitestudio.pl/index.php/User_Manual#Manual";
-
- private slots:
- void finished(QNetworkReply* reply);
- void downloadProgress(qint64 bytesReceived, qint64 totalBytes);
- void readDownload();
+ void checkForUpdatesAsync();
+ bool waitForProcess(QProcess& proc);
+ void processCheckResults(const QByteArray& results);
signals:
void updatesAvailable(const QList<UpdateManager::UpdateEntry>& updates);
void noUpdatesAvailable();
- void updatingProgress(const QString& jobTitle, int jobPercent, int totalPercent);
- void updatingFinished();
void updatingError(const QString& errorMessage);
};
+Q_DECLARE_METATYPE(QList<UpdateManager::UpdateEntry>)
+
#define UPDATES SQLITESTUDIO->getUpdateManager()
#endif // PORTABLE_CONFIG