1 /*
2    Drawpile - a collaborative drawing program.
3 
4    Copyright (C) 2019 Calle Laakkonen
5 
6    Drawpile is free 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    Drawpile 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 Drawpile.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef DP_CLIENT_NEWVERSION_CHECK_H
21 #define DP_CLIENT_NEWVERSION_CHECK_H
22 
23 #include <QObject>
24 #include <QUrl>
25 #include <QVector>
26 
27 class QXmlStreamReader;
28 
29 /**
30  * A tool for checking if a new version of the software is available.
31  *
32  * This class fetches and parses an AppStream appdata file and returns a
33  * list of versions newer than the current one.
34  *
35  */
36 class NewVersionCheck : public QObject {
37 	Q_OBJECT
38 public:
39 	//! Discovered version
40 	struct Version {
41 		//! Version string
42 		QString version;
43 
44 		//! URL of the release announcement page
45 		QString announcementUrl;
46 
47 		//! What's new in this version (in a simple HTML subset format)
48 		QString description;
49 
50 		//! Direct download link
51 		QString downloadUrl;
52 
53 		//! Hash of the download
54 		QString downloadChecksum;
55 
56 		//! Type of the checksum (e.g. sha256)
57 		QString downloadChecksumType;
58 
59 		//! Size of the download in bytes
60 		int downloadSize = 0;
61 	};
62 
63 	//! Construct a version checker with the current version info
64 	explicit NewVersionCheck(QObject *parent=nullptr);
65 
66 	//! Construct a version checker with the given version
67 	NewVersionCheck(int server, int major, int minor, QObject *parent=nullptr);
68 
69 	/**
70 	 * Is a new version check needed?
71 	 *
72 	 * A version check should be performed if:
73 	 *
74 	 *  - the user hasn't opted out (QSettings: versioncheck/enabled, default=true)
75 	 *  - it has been at least one day since the previous check
76 	 *  - if the last version checked failed, it has been at least one week
77 	 */
78 	static bool needCheck();
79 
80 	/**
81 	 * @brief Is a new series (e.g. 2.0 --> 2.1) out? (cached)
82 	 *
83 	 * Check the cached latest release if a newer incompatible version
84 	 * is out.
85 	 */
86 	static bool isThereANewSeries();
87 
88 	/**
89 	 * Show beta releases?
90 	 *
91 	 * By default, only stable releases are shown.
92 	 * This must be called before queryVersion or parseAppDataFile.
93 	 */
setShowBetas(bool show)94 	void setShowBetas(bool show) { m_showBetas = show; }
95 
96 	/**
97 	 * Explicitly set this system's platform
98 	 *
99 	 * This is used to select which artifact to include in the Version structure.
100 	 * If not set explicitly, the platform is selected by the build type:
101 	 *
102 	 *  - win64 if built for 64 bit windows
103 	 *  - win32 if built for 32 bit windows
104 	 *  - macos if built of macOS
105 	 *  - blank for everything else
106 	 *
107 	 * If a blank platform is set, the download fields will not be populated.
108 	 */
setPlatform(const QString & platform)109 	void setPlatform(const QString &platform) { m_platform = platform; }
110 
111 	/**
112 	 * Make a HTTP request and check the version file
113 	 *
114 	 * Emits versionChecked when done.
115 	 * The "newVersionAvailable" parameter will be true if a new version has
116 	 * been released since the last time queryVersions was called.
117 	 *
118 	 * @param url the URL to query (if null, the default built-in URL is used)
119 	 */
120 	void queryVersions(QUrl url=QUrl());
121 
122 	/**
123 	 * Parse an AppData file and get the list of releases.
124 	 *
125 	 * Typically, you should not need to call this directly. Call
126 	 * queryVersions() instead to fetch and parse the version list.
127 	 *
128 	 * @return true if the file was parsed successfully
129 	 */
130 	bool parseAppDataFile(QXmlStreamReader &reader);
131 
132 	/**
133 	 * Get all available versions newer than this one.
134 	 *
135 	 * The list of new versions is populated by parseAppDataFile,
136 	 * which (or queryVersions, rather) should be called before this.
137 	 * The list is returned in the order it appears in the appdata file.
138 	 *
139 	 * If nothing newer is available, an empty vector is returned.
140 	 */
getNewer()141 	QVector<Version> getNewer() const { return m_newer; }
142 
143 signals:
144 	/**
145 	 * @brief Version check complete
146 	 *
147 	 * The errorMessage parameter will be non-empty if an error occurred.
148 	 */
149 	void versionChecked(bool isNewVersionAvailable, const QString &errorMessage);
150 
151 private:
152 	bool parseDesktopElement(QXmlStreamReader &reader);
153 	bool parseReleasesElement(QXmlStreamReader &reader);
154 
155 	void queryFail(const QString &errorMessage);
156 	void querySuccess();
157 
158 	int m_server, m_major, m_minor;
159 	QVector<Version> m_newer;
160 	bool m_showBetas;
161 	QString m_platform;
162 };
163 
164 #endif
165 
166