1 /************************************************************************
2 **
3 ** Copyright (C) 2015-2020 Kevin B. Hendricks, Stratford Ontario Canada
4 ** Copyright (C) 2009-2011 Strahinja Markovic <strahinja.markovic@gmail.com>
5 **
6 ** This file is part of Sigil.
7 **
8 ** Sigil is free software: you can redistribute it and/or modify
9 ** it under the terms of the GNU General Public License as published by
10 ** the Free Software Foundation, either version 3 of the License, or
11 ** (at your option) any later version.
12 **
13 ** Sigil is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with Sigil. If not, see <http://www.gnu.org/licenses/>.
20 **
21 *************************************************************************/
22
23 #include "EmbedPython/EmbeddedPython.h"
24
25 #include <QtCore/QDateTime>
26 #include <QtCore/QDir>
27 #include <QtGui/QDesktopServices>
28 #include <QtWidgets/QMessageBox>
29 #include <QRegularExpression>
30 #include <QRegularExpressionMatch>
31 #include <QUrl>
32 #include <QDebug>
33
34 #include "Misc/SettingsStore.h"
35 #include "Misc/UpdateChecker.h"
36 #include "sigil_constants.h"
37
38 static const QString DOWNLOAD_PAGE_LOCATION = "http://sigil-ebook.com/get";
39 static const QString UPDATE_XML_LOCATION = "https://raw.githubusercontent.com/Sigil-Ebook/Sigil/master/version.xml";
40 static const QString LAST_ONLINE_VERSION_KEY = "last_online_version";
41 static const QString LAST_CHECK_TIME_KEY = "last_check_time";
42 static const QString SETTINGS_GROUP = "updatechecker";
43
44 // Delta is six hours
45 static const int SECONDS_BETWEEN_CHECKS = 60 * 60 * 6 ;
46
47
UpdateChecker(QObject * parent)48 UpdateChecker::UpdateChecker(QObject *parent)
49 :
50 QObject(parent)
51 {
52 }
53
CheckForUpdate()54 void UpdateChecker::CheckForUpdate()
55 {
56 SettingsStore settings;
57 settings.beginGroup(SETTINGS_GROUP);
58
59 // The default time is one always longer than the check interval
60 QDateTime default_time = QDateTime::currentDateTime().addSecs(- SECONDS_BETWEEN_CHECKS - 1);
61 QDateTime last_check_time = settings.value(LAST_CHECK_TIME_KEY, default_time).toDateTime();
62 QString last_online_version = settings.value(LAST_ONLINE_VERSION_KEY, QString()).toString();
63
64 // We want to check for a new version
65 // no sooner than every six hours
66 if (last_check_time.secsTo(QDateTime::currentDateTime()) > SECONDS_BETWEEN_CHECKS) {
67 settings.setValue(LAST_CHECK_TIME_KEY, QDateTime::currentDateTime());
68
69 int rv = 0;
70 QString error_traceback;
71 QList<QVariant> args;
72 args.append(QVariant(UPDATE_XML_LOCATION));
73
74 EmbeddedPython * epython = EmbeddedPython::instance();
75 QVariant res = epython->runInPython( QString("updatechecker"),
76 QString("check_for_updates"),
77 args,
78 &rv,
79 error_traceback);
80 if (rv != 0) {
81 qDebug() << QString("error in updatechecker check_for_updates: ") + QString::number(rv) + error_traceback;
82 return;
83 }
84
85 QString current_online_version = res.toString();
86
87 if (current_online_version.isEmpty()) {
88 return;
89 }
90
91 bool is_newer = IsOnlineVersionNewer(SIGIL_VERSION, current_online_version);
92 // The message box is displayed only if the online version is newer
93 // and only if the user hasn't been informed about this release before
94 if (is_newer && (current_online_version != last_online_version)) {
95 QMessageBox::StandardButton button_clicked;
96 button_clicked = QMessageBox::question(
97 0,
98 QObject::tr("Sigil"),
99 QObject::tr("<p>A newer version of Sigil is available, version <b>%1</b>.<br/>"
100 "<p>Would you like to go to the download page?</p>")
101 .arg(current_online_version),
102 QMessageBox::Yes | QMessageBox::No,
103 QMessageBox::Yes);
104
105 if (button_clicked == QMessageBox::Yes) {
106 QDesktopServices::openUrl(QUrl(DOWNLOAD_PAGE_LOCATION));
107 }
108 }
109
110 // Store the current online version as the last one checked
111 settings.setValue(LAST_ONLINE_VERSION_KEY, current_online_version);
112 settings.endGroup();
113 }
114 }
115
116
IsOnlineVersionNewer(const QString & current_version_string,const QString & online_version_string)117 bool UpdateChecker::IsOnlineVersionNewer(const QString ¤t_version_string,
118 const QString &online_version_string)
119 {
120 if (current_version_string.isEmpty() || online_version_string.isEmpty()) {
121 return false;
122 }
123
124 QRegularExpression current_version_numbers(VERSION_NUMBERS);
125 QRegularExpressionMatch current_version_numbers_match = current_version_numbers.match(current_version_string);
126 if (!current_version_numbers_match.hasMatch()) {
127 return false;
128 }
129
130 QRegularExpression online_version_numbers(VERSION_NUMBERS);
131 QRegularExpressionMatch online_version_numbers_match = online_version_numbers.match(online_version_string);
132 if (!online_version_numbers_match.hasMatch()) {
133 return false;
134 }
135
136 // This code assumes three digits per field,
137 // which should be way more than enough.
138 int current_version = current_version_numbers_match.captured(1).toInt() * 1000000 +
139 current_version_numbers_match.captured(2).toInt() * 1000 +
140 current_version_numbers_match.captured(3).toInt();
141 int online_version = online_version_numbers_match.captured(1).toInt() * 1000000 +
142 online_version_numbers_match.captured(2).toInt() * 1000 +
143 online_version_numbers_match.captured(3).toInt();
144 return online_version > current_version;
145 }
146