mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-23 13:11:12 -05:00
Update checking feature (#2648)
* Check on startup (toggleable setting) and manually * Option to check for pre-releases (eg, 2.4.0-beta1) * Only included if WITH_XC_NETWORKING is enabled
This commit is contained in:
parent
5c9b062f13
commit
779b529da2
@ -61,9 +61,9 @@ if(WITH_XC_ALL)
|
||||
set(WITH_XC_YUBIKEY ON)
|
||||
set(WITH_XC_SSHAGENT ON)
|
||||
set(WITH_XC_KEESHARE ON)
|
||||
if(APPLE)
|
||||
set(WITH_XC_TOUCHID ON)
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(WITH_XC_TOUCHID ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_XC_KEESHARE_SECURE)
|
||||
|
@ -102,9 +102,7 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct
|
||||
-DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group syncronization extension (default: OFF)
|
||||
-DWITH_XC_TOUCHID=[ON|OFF] (macOS Only) Enable/Disable Touch ID unlock (default:OFF)
|
||||
-DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (default: OFF)
|
||||
|
||||
-DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare secure containers, requires libquazip5 (default: OFF)
|
||||
|
||||
-DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON)
|
||||
-DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF)
|
||||
-DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF)
|
||||
|
@ -257,6 +257,10 @@ else()
|
||||
list(APPEND keepassx_SOURCES keys/drivers/YubiKey.h keys/drivers/YubiKeyStub.cpp)
|
||||
endif()
|
||||
|
||||
if(WITH_XC_NETWORKING)
|
||||
list(APPEND keepassx_SOURCES updatecheck/UpdateChecker.cpp gui/UpdateCheckDialog.cpp)
|
||||
endif()
|
||||
|
||||
if(WITH_XC_TOUCHID)
|
||||
list(APPEND keepassx_SOURCES touchid/TouchID.mm)
|
||||
endif()
|
||||
|
@ -92,6 +92,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
|
||||
// clang-format on
|
||||
|
||||
#ifndef WITH_XC_NETWORKING
|
||||
m_generalUi->checkForUpdatesOnStartupCheckBox->setVisible(false);
|
||||
m_secUi->privacy->setVisible(false);
|
||||
#endif
|
||||
|
||||
@ -174,6 +175,8 @@ void ApplicationSettingsWidget::loadSettings()
|
||||
m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get("GUI/MinimizeToTray").toBool());
|
||||
m_generalUi->minimizeOnCloseCheckBox->setChecked(config()->get("GUI/MinimizeOnClose").toBool());
|
||||
m_generalUi->systrayMinimizeOnStartup->setChecked(config()->get("GUI/MinimizeOnStartup").toBool());
|
||||
m_generalUi->checkForUpdatesOnStartupCheckBox->setChecked(config()->get("GUI/CheckForUpdates").toBool());
|
||||
m_generalUi->checkForUpdatesIncludeBetasCheckBox->setChecked(config()->get("GUI/CheckForUpdatesIncludeBetas").toBool());
|
||||
m_generalUi->autoTypeAskCheckBox->setChecked(config()->get("security/autotypeask").toBool());
|
||||
|
||||
if (autoType()->isAvailable()) {
|
||||
@ -256,6 +259,8 @@ void ApplicationSettingsWidget::saveSettings()
|
||||
config()->set("GUI/MinimizeToTray", m_generalUi->systrayMinimizeToTrayCheckBox->isChecked());
|
||||
config()->set("GUI/MinimizeOnClose", m_generalUi->minimizeOnCloseCheckBox->isChecked());
|
||||
config()->set("GUI/MinimizeOnStartup", m_generalUi->systrayMinimizeOnStartup->isChecked());
|
||||
config()->set("GUI/CheckForUpdates", m_generalUi->checkForUpdatesOnStartupCheckBox->isChecked());
|
||||
config()->set("GUI/CheckForUpdatesIncludeBetas", m_generalUi->checkForUpdatesIncludeBetasCheckBox->isChecked());
|
||||
|
||||
config()->set("security/autotypeask", m_generalUi->autoTypeAskCheckBox->isChecked());
|
||||
|
||||
|
@ -83,6 +83,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkForUpdatesOnStartupCheckBox">
|
||||
<property name="text">
|
||||
<string>Check for updates at application startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -179,6 +186,13 @@
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkForUpdatesIncludeBetasCheckBox">
|
||||
<property name="text">
|
||||
<string>Include pre-releases when checking for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toolbarHideCheckBox">
|
||||
<property name="text">
|
||||
|
@ -41,6 +41,12 @@
|
||||
#include "keys/FileKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
#include "updatecheck/UpdateChecker.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/UpdateCheckDialog.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
#include "sshagent/AgentSettingsPage.h"
|
||||
#include "sshagent/SSHAgent.h"
|
||||
@ -282,6 +288,7 @@ MainWindow::MainWindow()
|
||||
m_ui->actionPasswordGenerator->setIcon(filePath()->icon("actions", "password-generator"));
|
||||
|
||||
m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about"));
|
||||
m_ui->actionCheckForUpdates->setIcon(filePath()->icon("actions", "system-software-update"));
|
||||
|
||||
m_actionMultiplexer.connect(
|
||||
SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
|
||||
@ -364,6 +371,15 @@ MainWindow::MainWindow()
|
||||
#ifdef Q_OS_MACOS
|
||||
setUnifiedTitleAndToolBarOnMac(true);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
connect(m_ui->actionCheckForUpdates, SIGNAL(triggered()), SLOT(showUpdateCheckDialog()));
|
||||
connect(UpdateChecker::instance(), SIGNAL(updateCheckFinished(bool, QString, bool)), SLOT(hasUpdateAvailable(bool, QString, bool)));
|
||||
QTimer::singleShot(3000, this, SLOT(showUpdateCheckStartup()));
|
||||
#else
|
||||
m_ui->actionCheckForUpdates->setVisible(false);
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
connect(m_ui->tabWidget,
|
||||
SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
|
||||
@ -661,6 +677,48 @@ void MainWindow::showAboutDialog()
|
||||
aboutDialog->open();
|
||||
}
|
||||
|
||||
void MainWindow::showUpdateCheckStartup()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
if (!config()->get("UpdateCheckMessageShown", false).toBool()) {
|
||||
auto result = MessageBox::question(this,
|
||||
tr("Check for updates on startup?"),
|
||||
tr("Would you like KeePassXC to check for updates on startup?") + "\n\n" +
|
||||
tr("You can always check for updates manually from the application menu."),
|
||||
MessageBox::Yes | MessageBox::No,
|
||||
MessageBox::Yes);
|
||||
|
||||
config()->set("GUI/CheckForUpdates", (result == MessageBox::Yes));
|
||||
config()->set("UpdateCheckMessageShown", true);
|
||||
}
|
||||
|
||||
if (config()->get("GUI/CheckForUpdates", false).toBool()) {
|
||||
updateCheck()->checkForUpdates(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested)
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
if (hasUpdate && !isManuallyRequested) {
|
||||
auto* updateCheckDialog = new UpdateCheckDialog(this);
|
||||
updateCheckDialog->showUpdateCheckResponse(hasUpdate, version);
|
||||
updateCheckDialog->show();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::showUpdateCheckDialog()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
updateCheck()->checkForUpdates(true);
|
||||
auto* updateCheckDialog = new UpdateCheckDialog(this);
|
||||
updateCheckDialog->show();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::openDonateUrl()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://keepassxc.org/donate"));
|
||||
|
@ -83,6 +83,9 @@ private slots:
|
||||
void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::Mode::None);
|
||||
void updateWindowTitle();
|
||||
void showAboutDialog();
|
||||
void showUpdateCheckStartup();
|
||||
void showUpdateCheckDialog();
|
||||
void hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested);
|
||||
void openDonateUrl();
|
||||
void openBugReportUrl();
|
||||
void switchToDatabases();
|
||||
|
@ -223,6 +223,7 @@
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="actionCheckForUpdates"/>
|
||||
<addaction name="actionDonate"/>
|
||||
<addaction name="actionBugReport"/>
|
||||
</widget>
|
||||
@ -348,6 +349,14 @@
|
||||
<enum>QAction::AboutRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCheckForUpdates">
|
||||
<property name="text">
|
||||
<string>Check for Updates...</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::ApplicationSpecificRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseOpen">
|
||||
<property name="text">
|
||||
<string>&Open database...</string>
|
||||
|
68
src/gui/UpdateCheckDialog.cpp
Normal file
68
src/gui/UpdateCheckDialog.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "UpdateCheckDialog.h"
|
||||
#include "ui_UpdateCheckDialog.h"
|
||||
#include "updatecheck/UpdateChecker.h"
|
||||
#include "core/FilePath.h"
|
||||
|
||||
UpdateCheckDialog::UpdateCheckDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_ui(new Ui::UpdateCheckDialog())
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
setWindowFlags(Qt::Window);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
m_ui->iconLabel->setPixmap(filePath()->applicationIcon().pixmap(48));
|
||||
|
||||
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||
connect(UpdateChecker::instance(), SIGNAL(updateCheckFinished(bool, QString, bool)), SLOT(showUpdateCheckResponse(bool, QString)));
|
||||
}
|
||||
|
||||
void UpdateCheckDialog::showUpdateCheckResponse(bool status, const QString& version) {
|
||||
m_ui->progressBar->setVisible(false);
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close"));
|
||||
|
||||
if (version == QString("error")) {
|
||||
setWindowTitle(tr("Update Error!"));
|
||||
|
||||
m_ui->statusLabel->setText(
|
||||
"<strong>" + tr("Update Error!") + "</strong><br><br>" +
|
||||
tr("An error occurred in retrieving update information.") + "<br>" +
|
||||
tr("Please try again later."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
setWindowTitle(tr("Software Update"));
|
||||
m_ui->statusLabel->setText(
|
||||
"<strong>" + tr("A new version of KeePassXC is available!") + "</strong><br><br>" +
|
||||
tr("KeePassXC %1 is now available — you have %2.").arg(version, KEEPASSXC_VERSION) + "<br><br>" +
|
||||
"<a href='https://keepassxc.org/download/'>" +
|
||||
tr("Download it at keepassxc.org") +
|
||||
"</a>");
|
||||
} else {
|
||||
setWindowTitle(tr("You're up-to-date!"));
|
||||
m_ui->statusLabel->setText(tr(
|
||||
"KeePassXC %1 is currently the newest version available").arg(KEEPASSXC_VERSION));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCheckDialog::~UpdateCheckDialog()
|
||||
{
|
||||
}
|
50
src/gui/UpdateCheckDialog.h
Normal file
50
src/gui/UpdateCheckDialog.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_UPDATECHECKDIALOG_H
|
||||
#define KEEPASSXC_UPDATECHECKDIALOG_H
|
||||
|
||||
#include <QUrl>
|
||||
#include <QDialog>
|
||||
#include <QScopedPointer>
|
||||
#include "gui/MessageBox.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Global.h"
|
||||
#include "updatecheck/UpdateChecker.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class UpdateCheckDialog;
|
||||
}
|
||||
|
||||
class UpdateCheckDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UpdateCheckDialog(QWidget* parent = nullptr);
|
||||
~UpdateCheckDialog() override;
|
||||
|
||||
public slots:
|
||||
void showUpdateCheckResponse(bool status, const QString& version);
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::UpdateCheckDialog> m_ui;
|
||||
};
|
||||
|
||||
|
||||
#endif //KEEPASSXC_UPDATECHECKDIALOG_H
|
175
src/gui/UpdateCheckDialog.ui
Normal file
175
src/gui/UpdateCheckDialog.ui
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UpdateCheckDialog</class>
|
||||
<widget class="QDialog" name="UpdateCheckDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>375</width>
|
||||
<height>136</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Checking for updates</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Checking for updates...</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>UpdateCheckDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>UpdateCheckDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
148
src/updatecheck/UpdateChecker.cpp
Normal file
148
src/updatecheck/UpdateChecker.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "UpdateChecker.h"
|
||||
#include "core/Config.h"
|
||||
#include "config-keepassx.h"
|
||||
#include <QJsonObject>
|
||||
#include <QtNetwork>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
UpdateChecker* UpdateChecker::m_instance(nullptr);
|
||||
|
||||
UpdateChecker::UpdateChecker(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_netMgr(new QNetworkAccessManager(this))
|
||||
, m_reply(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
UpdateChecker::~UpdateChecker()
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateChecker::checkForUpdates(bool manuallyRequested)
|
||||
{
|
||||
m_isManuallyRequested = manuallyRequested;
|
||||
m_bytesReceived.clear();
|
||||
|
||||
QString apiUrlStr = QString("https://api.github.com/repos/keepassxreboot/keepassxc/releases");
|
||||
|
||||
if (!config()->get("GUI/CheckForUpdatesIncludeBetas", false).toBool()) {
|
||||
apiUrlStr += "/latest";
|
||||
}
|
||||
|
||||
QUrl apiUrl = QUrl(apiUrlStr);
|
||||
|
||||
QNetworkRequest request(apiUrl);
|
||||
request.setRawHeader("Accept", "application/json");
|
||||
|
||||
m_reply = m_netMgr->get(request);
|
||||
|
||||
connect(m_reply, &QNetworkReply::finished, this, &UpdateChecker::fetchFinished);
|
||||
connect(m_reply, &QIODevice::readyRead, this, &UpdateChecker::fetchReadyRead);
|
||||
}
|
||||
|
||||
void UpdateChecker::fetchReadyRead()
|
||||
{
|
||||
m_bytesReceived += m_reply->readAll();
|
||||
}
|
||||
|
||||
void UpdateChecker::fetchFinished()
|
||||
{
|
||||
bool error = (m_reply->error() != QNetworkReply::NoError);
|
||||
bool hasNewVersion = false;
|
||||
QUrl url = m_reply->url();
|
||||
QString version = "";
|
||||
|
||||
m_reply->deleteLater();
|
||||
m_reply = nullptr;
|
||||
|
||||
if (!error) {
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(m_bytesReceived);
|
||||
QJsonObject jsonObject = jsonResponse.object();
|
||||
|
||||
if (config()->get("GUI/CheckForUpdatesIncludeBetas", false).toBool()) {
|
||||
QJsonArray jsonArray = jsonResponse.array();
|
||||
jsonObject = jsonArray.at(0).toObject();
|
||||
}
|
||||
|
||||
if (!jsonObject.value("tag_name").isUndefined()) {
|
||||
version = jsonObject.value("tag_name").toString();
|
||||
hasNewVersion = compareVersions(version, QString(KEEPASSXC_VERSION));
|
||||
}
|
||||
} else {
|
||||
version = "error";
|
||||
}
|
||||
|
||||
emit updateCheckFinished(hasNewVersion, version, m_isManuallyRequested);
|
||||
}
|
||||
|
||||
bool UpdateChecker::compareVersions(const QString& remoteVersion, const QString& localVersion)
|
||||
{
|
||||
if (localVersion == remoteVersion) {
|
||||
return false; // Currently using updated version
|
||||
}
|
||||
|
||||
QRegularExpression verRegex("^(\\d+(\\.\\d+){0,2})(-\\w+)?$", QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
QRegularExpressionMatch lmatch = verRegex.match(localVersion);
|
||||
QRegularExpressionMatch rmatch = verRegex.match(remoteVersion);
|
||||
|
||||
if (!lmatch.captured(1).isNull() && !rmatch.captured(1).isNull()) {
|
||||
if (lmatch.captured(1) == rmatch.captured(1) && !lmatch.captured(3).isNull()) {
|
||||
// Same version, but installed version has snapshot/beta suffix and should be updated to stable
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList lparts = lmatch.captured(1).split(".");
|
||||
QStringList rparts = rmatch.captured(1).split(".");
|
||||
|
||||
if (lparts.length() < 3)
|
||||
lparts << "0";
|
||||
|
||||
if (rparts.length() < 3)
|
||||
rparts << "0";
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int l = lparts[i].toInt();
|
||||
int r = rparts[i].toInt();
|
||||
|
||||
if (l == r)
|
||||
continue;
|
||||
|
||||
if (l > r) {
|
||||
return false; // Installed version is newer than release
|
||||
} else {
|
||||
return true; // Installed version is outdated
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Installed version is the same
|
||||
}
|
||||
|
||||
return false; // Invalid version string
|
||||
}
|
||||
|
||||
UpdateChecker* UpdateChecker::instance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new UpdateChecker();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
60
src/updatecheck/UpdateChecker.h
Normal file
60
src/updatecheck/UpdateChecker.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_UPDATECHECK_H
|
||||
#define KEEPASSXC_UPDATECHECK_H
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
class UpdateChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UpdateChecker(QObject* parent = nullptr);
|
||||
~UpdateChecker() override;
|
||||
|
||||
void checkForUpdates(bool manuallyRequested);
|
||||
static bool compareVersions(const QString& remoteVersion, const QString& localVersion);
|
||||
static UpdateChecker* instance();
|
||||
|
||||
signals:
|
||||
void updateCheckFinished(bool hasNewVersion, QString version, bool isManuallyRequested);
|
||||
|
||||
private slots:
|
||||
void fetchFinished();
|
||||
void fetchReadyRead();
|
||||
|
||||
private:
|
||||
QNetworkAccessManager* m_netMgr;
|
||||
QNetworkReply* m_reply;
|
||||
QByteArray m_bytesReceived;
|
||||
bool m_isManuallyRequested;
|
||||
|
||||
static UpdateChecker* m_instance;
|
||||
|
||||
Q_DISABLE_COPY(UpdateChecker)
|
||||
};
|
||||
|
||||
inline UpdateChecker* updateCheck()
|
||||
{
|
||||
return UpdateChecker::instance();
|
||||
}
|
||||
|
||||
#endif //KEEPASSXC_UPDATECHECK_H
|
@ -162,6 +162,11 @@ add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp
|
||||
add_unit_test(NAME testwildcardmatcher SOURCES TestWildcardMatcher.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
if(WITH_XC_NETWORKING)
|
||||
add_unit_test(NAME testupdatecheck SOURCES TestUpdateCheck.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(WITH_XC_AUTOTYPE)
|
||||
add_unit_test(NAME testautotype SOURCES TestAutoType.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
41
tests/TestUpdateCheck.cpp
Normal file
41
tests/TestUpdateCheck.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestUpdateCheck.h"
|
||||
#include "TestGlobal.h"
|
||||
#include "updatecheck/UpdateChecker.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestUpdateCheck)
|
||||
|
||||
void TestUpdateCheck::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestUpdateCheck::testCompareVersion()
|
||||
{
|
||||
// Remote Version , Installed Version
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.4.0"), QString("2.3.4")), true);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.3.0"), QString("2.4.0")), false);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.3.0"), QString("2.3.0")), false);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.3.0"), QString("2.3.0-beta1")), true);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.3.0-beta2"), QString("2.3.0-beta1")), true);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("2.3.4"), QString("2.4.0-snapshot")), false);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString("invalid"), QString("2.4.0")), false);
|
||||
QCOMPARE(UpdateChecker::compareVersions(QString(""), QString("2.4.0")), false);
|
||||
}
|
32
tests/TestUpdateCheck.h
Normal file
32
tests/TestUpdateCheck.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTUPDATECHECK_H
|
||||
#define KEEPASSX_TESTUPDATECHECK_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestUpdateCheck : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testCompareVersion();
|
||||
};
|
||||
|
||||
#endif // #define KEEPASSX_TESTUPDATECHECK_H
|
@ -86,6 +86,8 @@ void TestGui::initTestCase()
|
||||
config()->set("GUI/ShowTrayIcon", true);
|
||||
// Disable advanced settings mode (activate within individual tests to test advanced settings)
|
||||
config()->set("GUI/AdvancedSettings", false);
|
||||
// Disable the update check first time alert
|
||||
config()->set("UpdateCheckMessageShown", true);
|
||||
|
||||
m_mainWindow.reset(new MainWindow());
|
||||
Bootstrap::restoreMainWindowState(*m_mainWindow);
|
||||
|
Loading…
Reference in New Issue
Block a user