FlightGear next
UpdateChecker.cxx
Go to the documentation of this file.
1// Written by James Turner, started October 2020
2//
3// Copyright (C) 2020 James Turner <james@flightgear.org>
4//
5// This program is free software; you can redistribute it and/or
6// modify it under the terms of the GNU General Public License as
7// published by the Free Software Foundation; either version 2 of the
8// License, or (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful, but
11// WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13// General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19#include "config.h"
20
21#include "UpdateChecker.hxx"
22
23#include <QSettings>
24#include <QDate>
25#include <QDebug>
26
27#include <simgear/io/HTTPMemoryRequest.hxx>
28#include <simgear/props/props_io.hxx>
29#include <simgear/structure/exception.hxx>
30
32#include <Main/globals.hxx>
33
35
36namespace {
37
38class UpdateXMLRequest : public simgear::HTTP::MemoryRequest
39{
40public:
41 UpdateXMLRequest(UpdateChecker* u, const std::string& uri) :
42 simgear::HTTP::MemoryRequest(uri),
43 _owner(u)
44 {
45 // in case it's useful, send our version as an additional header
46 // this would allow a server side script to return different content
47 // based on our version in the future
48 requestHeader("FlightGear-Version") = FLIGHTGEAR_VERSION;
49 }
50private:
51 void onFail() override
52 {
53 didFail();
54 }
55
56 void onDone() override
57 {
58 int response = responseCode();
59 if (response == 200) {
60 // send response to the main thread for processing
61 QMetaObject::invokeMethod(_owner, "receivedUpdateXML", Qt::QueuedConnection,
62 Q_ARG(QByteArray, QByteArray::fromStdString(responseBody())));
63
64 } else {
65 didFail();
66 }
67 }
68
69 void didFail()
70 {
71 // reset check time to tomorrow
72 QSettings settings;
73 const QDate n = QDate::currentDate().addDays(1);
74 settings.setValue("next-update-check", n);
75 }
76private:
77 UpdateChecker* _owner;
78};
79
80} // of namespace
81
82UpdateChecker::UpdateChecker(QObject *parent) : QObject(parent)
83{
84 QSettings settings;
85 QDate nextCheck = settings.value("next-update-check").toDate();
86 if (!nextCheck.isValid()) {
87 // check tomorrow, so we don't nag immediately after installaion
88 const QDate n = QDate::currentDate().addDays(1);
89 settings.setValue("next-update-check", n);
90
91 return;
92 }
93
94 if (nextCheck <= QDate::currentDate()) {
95 // start a check
96 auto http = globals->get_subsystem<FGHTTPClient>();
97 if (!http) {
98 return;
99 }
100
101 const string_list versionParts = simgear::strutils::split(FLIGHTGEAR_VERSION, ".");
102 _majorMinorVersion = versionParts[0] + "." + versionParts[1];
103
104 // definitiely want to ensure HTTPS for this.
105 std::string uri = "https://download.flightgear.org/builds/" + _majorMinorVersion + "/updates.xml";
106 m_request = new UpdateXMLRequest(this, uri);
107 http->makeRequest(m_request);
108 } else {
109 // nothing to do
110 }
111}
112
114{
115 if (m_request) {
116 auto http = globals->get_subsystem<FGHTTPClient>();
117 http->client()->cancelRequest(m_request);
118 m_request.clear();
119 }
120}
121
123{
124 QSettings settings;
125 if (m_status == PointUpdate) {
126 settings.setValue("ignored-point-release", _currentUpdateVersion);
127 } else if (m_status == MajorUpdate) {
128 settings.setValue("ignored-major-release", _currentUpdateVersion);
129 } else {
130 return;
131 }
132
133 m_status = NoUpdate;
134 m_updateUri.clear();
135 _currentUpdateVersion.clear();
136 emit statusChanged(m_status);
137}
138
139void UpdateChecker::receivedUpdateXML(QByteArray body)
140{
141 SGPropertyNode_ptr props(new SGPropertyNode);
142 const auto s = body.toStdString();
143
144 QSettings settings;
146
147 try {
148 const char* buffer = s.c_str();
149 readProperties(buffer, s.size(), props, true);
150
151 const QDate n = QDate::currentDate().addDays(7);
152 settings.setValue("next-update-check", n);
153
154 const std::string newMajorVersion = props->getStringValue("current-major-release");
155 if (simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, newMajorVersion) < 0) {
156 // we have a newer version!
157 _currentUpdateVersion = QString::fromStdString(newMajorVersion);
158 if (settings.value("ignored-major-release") == _currentUpdateVersion) {
159 // ignore it
160 } else {
161 m_status = MajorUpdate;
162
163 const std::string newVersionUri = props->getStringValue("upgrade-uri");
164 m_updateUri = QUrl(QString::fromStdString(newVersionUri));
165 emit statusChanged(m_status);
166
167 nc->postNotification("flightgear-update-major", QUrl{"qrc:///qml/NewVersionNotification.qml"});
168
169 return; // don't consider minor updates
170 }
171 }
172
173 // check current version
174 const std::string newPointVersion = props->getStringValue("current-point-release");
175 if (simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, newPointVersion) < 0) {
176 // we have a newer version!
177 _currentUpdateVersion = QString::fromStdString(newPointVersion);
178 if (settings.value("ignored-point-release") == _currentUpdateVersion) {
179 // ignore it
180 } else {
181 m_status = PointUpdate;
182 const std::string newVersionUri = props->getStringValue("download-uri");
183 m_updateUri = QUrl(QString::fromStdString(newVersionUri));
184 emit statusChanged(m_status);
185
186 nc->postNotification("flightgear-update-point", QUrl{"qrc:///qml/NewVersionNotification.qml"});
187 }
188 }
189 } catch (const sg_exception &e) {
190 SG_LOG(SG_IO, SG_WARN, "parsing update XML failed: " << e.getFormattedMessage());
191 }
192}
simgear::HTTP::Client * client()
static LauncherNotificationsController * instance()
UpdateChecker(QObject *parent=nullptr)
void statusChanged(Status status)
FGGlobals * globals
Definition globals.cxx:142
std::vector< std::string > string_list
Definition globals.hxx:36