qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
updatecheck.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "src/net/updatecheck.h"
21 
22 #include <QNetworkAccessManager>
23 #include <QDebug>
24 #include <QJsonDocument>
25 #include <QJsonObject>
26 #include <QNetworkReply>
27 #include <QObject>
28 #include <QRegularExpression>
29 #include <QTimer>
30 #include <cassert>
31 
32 namespace {
33 const QString versionUrl{QStringLiteral("https://api.github.com/repos/qTox/qTox/releases/latest")};
34 const QString versionRegexString{QStringLiteral("v([0-9]+)\\.([0-9]+)\\.([0-9]+)")};
35 
36 struct Version {
37  int major;
38  int minor;
39  int patch;
40 };
41 
42 Version tagToVersion(QString tagName)
43 {
44  // capture tag name to avoid showing update available on dev builds which include hash as part of describe
45  QRegularExpression versionFormat(versionRegexString);
46  auto matches = versionFormat.match(tagName);
47  assert(matches.lastCapturedIndex() == 3);
48 
49  bool ok;
50  auto major = matches.captured(1).toInt(&ok);
51  assert(ok);
52  auto minor = matches.captured(2).toInt(&ok);
53  assert(ok);
54  auto patch = matches.captured(3).toInt(&ok);
55  assert(ok);
56 
57  return {major, minor, patch};
58 }
59 
60 bool isUpdateAvailable(Version current, Version available)
61 {
62  // A user may have a version greater than our latest release in the time between a tag being pushed and the release
63  // being published. Don't notify about an update in that case.
64 
65  if (current.major < available.major) {
66  return true;
67  }
68  if (current.major > available.major) {
69  return false;
70  }
71 
72  if (current.minor < available.minor) {
73  return true;
74  }
75  if (current.minor > available.minor) {
76  return false;
77  }
78 
79  if (current.patch < available.patch) {
80  return true;
81  }
82  if (current.patch > available.patch) {
83  return false;
84  }
85 
86  return false;
87 }
88 
89 bool isCurrentVersionStable()
90 {
91  QRegularExpression versionRegex(versionRegexString);
92  auto currentVer = versionRegex.match(GIT_DESCRIBE_EXACT);
93  if (currentVer.hasMatch()){
94  return true;
95  } else {
96  return false;
97  }
98 }
99 
100 } // namespace
101 
103  : settings(settings)
104 {
105  qInfo() << "qTox is running version:" << GIT_DESCRIBE;
106  updateTimer.start(1000 * 60 * 60 * 24 /* 1 day */);
107  connect(&updateTimer, &QTimer::timeout, this, &UpdateCheck::checkForUpdate);
108  connect(&manager, &QNetworkAccessManager::finished, this, &UpdateCheck::handleResponse);
109 }
110 
112 {
113  if (!settings.getCheckUpdates()) {
114  // still run the timer to check periodically incase setting changes
115  return;
116  }
117 
118  if (isCurrentVersionStable() == false) {
119  qWarning() << "Currently running an untested/unstable version of qTox";
120  emit versionIsUnstable();
121  return;
122  }
123 
124  manager.setProxy(settings.getProxy());
125  QNetworkRequest request{versionUrl};
126  manager.get(request);
127 }
128 
129 void UpdateCheck::handleResponse(QNetworkReply* reply)
130 {
131  assert(reply != nullptr);
132  if (reply == nullptr) {
133  qWarning() << "Update check returned null reply, ignoring";
134  return;
135  }
136 
137  if (reply->error() != QNetworkReply::NoError) {
138  qWarning() << "Failed to check for update:" << reply->error();
139  emit updateCheckFailed();
140  reply->deleteLater();
141  return;
142  }
143  QByteArray result = reply->readAll();
144  QJsonDocument doc = QJsonDocument::fromJson(result);
145  QJsonObject jObject = doc.object();
146  QVariantMap mainMap = jObject.toVariantMap();
147  QString latestVersion = mainMap["tag_name"].toString();
148  if (latestVersion.isEmpty()) {
149  qWarning() << "No tag name found in response:";
150  emit updateCheckFailed();
151  reply->deleteLater();
152  return;
153  }
154 
155  auto currentVer = tagToVersion(GIT_DESCRIBE);
156  auto availableVer = tagToVersion(latestVersion);
157 
158  if (isUpdateAvailable(currentVer, availableVer)) {
159  qInfo() << "Update available to version" << latestVersion;
160  QUrl link{mainMap["html_url"].toString()};
161  emit updateAvailable(latestVersion, link);
162  } else {
163  qInfo() << "qTox is up to date";
164  emit upToDate();
165  }
166 
167  reply->deleteLater();
168 }
Settings
Definition: settings.h:51
Settings::getCheckUpdates
bool getCheckUpdates() const
Definition: settings.cpp:1546
settings.h
UpdateCheck::versionIsUnstable
void versionIsUnstable()
UpdateCheck::UpdateCheck
UpdateCheck(const Settings &settings)
Definition: updatecheck.cpp:102
UpdateCheck::updateTimer
QTimer updateTimer
Definition: updatecheck.h:51
UpdateCheck::handleResponse
void handleResponse(QNetworkReply *reply)
Definition: updatecheck.cpp:129
UpdateCheck::updateAvailable
void updateAvailable(QString latestVersion, QUrl link)
UpdateCheck::manager
QNetworkAccessManager manager
Definition: updatecheck.h:50
Settings::getProxy
QNetworkProxy getProxy() const override
Definition: settings.cpp:1145
updatecheck.h
UpdateCheck::checkForUpdate
void checkForUpdate()
Definition: updatecheck.cpp:111
updateIndex::available
@ available
UpdateCheck::settings
const Settings & settings
Definition: updatecheck.h:52
UpdateCheck::updateCheckFailed
void updateCheckFailed()
UpdateCheck::upToDate
void upToDate()