1 /*!
2 * \copyright Copyright (c) 2016-2021 Governikus GmbH & Co. KG, Germany
3 */
4
5 #include "AppUpdateData.h"
6
7 #include "AppSettings.h"
8 #include "VersionNumber.h"
9
10 #include <QDir>
11 #include <QFile>
12 #include <QJsonArray>
13 #include <QJsonParseError>
14 #include <QLoggingCategory>
15 #include <QOperatingSystemVersion>
16
17 using namespace governikus;
18
Q_DECLARE_LOGGING_CATEGORY(appupdate)19 Q_DECLARE_LOGGING_CATEGORY(appupdate)
20
21 AppUpdateData::AppUpdateData(const GlobalStatus& pParsingResult)
22 : mMinOsVersion()
23 , mDate()
24 , mVersion()
25 , mUrl()
26 , mSize(-1)
27 , mChecksumUrl()
28 , mNotesUrl()
29 , mNotes()
30 , mChecksumAlgorithm(QCryptographicHash::Sha256)
31 , mChecksum()
32 , mChecksumValid(false)
33 , mUpdatePackagePath()
34 , mParsingResult(pParsingResult)
35 {
36 }
37
38
AppUpdateData(const QByteArray & pData)39 AppUpdateData::AppUpdateData(const QByteArray& pData)
40 : AppUpdateData(GlobalStatus::Code::No_Error)
41 {
42 QJsonParseError jsonError;
43
44 const auto& json = QJsonDocument::fromJson(pData, &jsonError);
45 if (jsonError.error != QJsonParseError::NoError)
46 {
47 qCWarning(appupdate) << "Cannot parse json data:" << jsonError.errorString();
48 mParsingResult = GlobalStatus::Code::Downloader_Data_Corrupted;
49 return;
50 }
51
52 const auto& obj = json.object();
53 const QJsonValue& items = obj[QLatin1String("items")];
54
55 if (!items.isArray())
56 {
57 qCWarning(appupdate) << "Field 'items' cannot be parsed";
58 mParsingResult = GlobalStatus::Code::Downloader_Data_Corrupted;
59 return;
60 }
61
62 const auto& itemArray = items.toArray();
63 const auto& end = itemArray.constEnd();
64 for (auto iter = itemArray.constBegin(); iter != end; ++iter)
65 {
66 if (iter->isObject())
67 {
68 auto jsonObject = iter->toObject();
69 if (checkPlatformObject(jsonObject))
70 {
71 mMinOsVersion = QVersionNumber::fromString(jsonObject[QLatin1String("minimum_platform")].toString());
72 mVersion = jsonObject[QLatin1String("version")].toString();
73 mUrl = QUrl(jsonObject[QLatin1String("url")].toString());
74 mNotesUrl = QUrl(jsonObject[QLatin1String("notes")].toString());
75 mDate = QDateTime::fromString(jsonObject[QLatin1String("date")].toString(), Qt::ISODate);
76 mChecksumUrl = QUrl(jsonObject[QLatin1String("checksum")].toString());
77 mSize = qMax(-1, jsonObject[QLatin1String("size")].toInt(-1));
78 return;
79 }
80 }
81 else
82 {
83 qCWarning(appupdate) << "Object of field 'items' cannot be parsed";
84 mParsingResult = GlobalStatus::Code::Downloader_Data_Corrupted;
85 return;
86 }
87 }
88
89 mParsingResult = GlobalStatus::Code::Downloader_Missing_Platform;
90 qCWarning(appupdate) << "No matching platform found in update json";
91 }
92
93
isValid() const94 bool AppUpdateData::isValid() const
95 {
96 // Valid means = required data!
97 return !mVersion.isEmpty() &&
98 mUrl.isValid() &&
99 !mUrl.fileName().isEmpty() &&
100 mChecksumUrl.isValid() &&
101 !mChecksumUrl.fileName().isEmpty() &&
102 mNotesUrl.isValid();
103 }
104
105
getParsingResult() const106 const GlobalStatus& AppUpdateData::getParsingResult() const
107 {
108 return mParsingResult;
109 }
110
111
isCompatible() const112 bool AppUpdateData::isCompatible() const
113 {
114 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
115 const auto osv = QOperatingSystemVersion::current();
116 const auto currentVersion = QVersionNumber(osv.majorVersion(), osv.minorVersion(), osv.microVersion());
117 return currentVersion >= mMinOsVersion;
118
119 #else
120 return true;
121
122 #endif
123 }
124
125
getDate() const126 const QDateTime& AppUpdateData::getDate() const
127 {
128 return mDate;
129 }
130
131
getVersion() const132 const QString& AppUpdateData::getVersion() const
133 {
134 return mVersion;
135 }
136
137
getUrl() const138 const QUrl& AppUpdateData::getUrl() const
139 {
140 return mUrl;
141 }
142
143
getSize() const144 int AppUpdateData::getSize() const
145 {
146 return mSize;
147 }
148
149
getChecksumUrl() const150 const QUrl& AppUpdateData::getChecksumUrl() const
151 {
152 return mChecksumUrl;
153 }
154
155
getNotesUrl() const156 const QUrl& AppUpdateData::getNotesUrl() const
157 {
158 return mNotesUrl;
159 }
160
161
setNotes(const QString & pNotes)162 void AppUpdateData::setNotes(const QString& pNotes)
163 {
164 mNotes = pNotes;
165 }
166
167
getNotes() const168 const QString& AppUpdateData::getNotes() const
169 {
170 return mNotes;
171 }
172
173
setChecksum(const QByteArray & pChecksum,QCryptographicHash::Algorithm pAlgorithm)174 void AppUpdateData::setChecksum(const QByteArray& pChecksum, QCryptographicHash::Algorithm pAlgorithm)
175 {
176 if (mChecksum == pChecksum && mChecksumAlgorithm == pAlgorithm)
177 {
178 return;
179 }
180
181 mChecksumAlgorithm = pAlgorithm;
182
183 if (pChecksum.isEmpty())
184 {
185 mChecksum.clear();
186 mChecksumValid = false;
187 return;
188 }
189
190 mChecksum = pChecksum.split(' ').first();
191 verifyChecksum();
192 }
193
194
getChecksum() const195 const QByteArray& AppUpdateData::getChecksum() const
196 {
197 return mChecksum;
198 }
199
200
verifyChecksum()201 void AppUpdateData::verifyChecksum()
202 {
203 if (mUpdatePackagePath.isEmpty()
204 || mChecksum.isEmpty()
205 || !QFile::exists(mUpdatePackagePath))
206 {
207 mChecksumValid = false;
208 return;
209 }
210
211 qCDebug(appupdate) << "Verify checksum with algorithm:" << mChecksumAlgorithm;
212
213 QFile file(mUpdatePackagePath);
214 file.open(QFile::ReadOnly);
215 QCryptographicHash hasher(mChecksumAlgorithm);
216 hasher.addData(&file);
217 file.close();
218
219 mChecksumValid = hasher.result().toHex() == mChecksum;
220 }
221
222
isChecksumValid() const223 bool AppUpdateData::isChecksumValid() const
224 {
225 return mChecksumValid;
226 }
227
228
setUpdatePackagePath(const QString & pFile)229 void AppUpdateData::setUpdatePackagePath(const QString& pFile)
230 {
231 mUpdatePackagePath = pFile;
232 verifyChecksum();
233 }
234
235
getUpdatePackagePath() const236 QString AppUpdateData::getUpdatePackagePath() const
237 {
238 return QDir::toNativeSeparators(mUpdatePackagePath);
239 }
240
241
checkPlatformObject(const QJsonObject & pJson)242 bool AppUpdateData::checkPlatformObject(const QJsonObject& pJson)
243 {
244 const auto& platform = pJson[QLatin1String("platform")].toString();
245
246 if (!isPlatform(platform))
247 {
248 qCDebug(appupdate) << "Unused platform:" << platform;
249 return false;
250 }
251
252 qCDebug(appupdate) << "Found platform:" << platform;
253 return true;
254 }
255
256
isPlatform(const QString & pPlatform)257 bool AppUpdateData::isPlatform(const QString& pPlatform)
258 {
259 #ifdef Q_OS_WIN
260 if (pPlatform == QLatin1String("win"))
261 {
262 return true;
263 }
264 #endif
265
266 #ifdef Q_OS_MACOS
267 if (pPlatform == QLatin1String("mac"))
268 {
269 return true;
270 }
271 #endif
272
273 #if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
274 if (pPlatform == QLatin1String("src"))
275 {
276 return true;
277 }
278 #endif
279
280 return false;
281 }
282