1 //=============================================================================
2 // MuseScore
3 // Linux Music Score Editor
4 //
5 // Copyright (C) 2002-2010 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19
20 #include "updatechecker.h"
21 #include "musescore.h"
22 #include "libmscore/mscore.h"
23 #include "preferences.h"
24 #include "resourceManager.h"
25 #include "extension.h"
26 #include "libmscore/utils.h"
27
28 namespace Ms {
29
30 #if !defined(Q_OS_MAC) && (!defined(Q_OS_WIN) || defined(MSCORE_UNSTABLE))
31 //---------------------------------------------------------
32 // default period
33 //---------------------------------------------------------
34
defaultPeriod()35 static int defaultPeriod()
36 {
37 int result = 24;
38 if(qApp->applicationName() == "MuseScore2"){ //avoid nightly cymbals
39 if (MuseScore::unstable())
40 result = 24;
41 else
42 result = 24; // yes, it's again the same but let's keep the logic for now
43 }
44 return result;
45 }
46 #endif
47
UpdateChecker(QObject * parent)48 UpdateChecker::UpdateChecker(QObject* parent)
49 : UpdateCheckerBase(parent)
50 {
51 manager = new QNetworkAccessManager(this);
52 connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*)));
53 }
54
onRequestFinished(QNetworkReply * reply)55 void UpdateChecker::onRequestFinished(QNetworkReply* reply)
56 {
57 if (reply->error() != QNetworkReply::NoError) {
58 qDebug("Error while checking update [%s]", reply->errorString().toLatin1().constData());
59 return;
60 }
61
62 QSettings s;
63 s.beginGroup("Update");
64 s.setValue("lastUpdateDate", QDateTime::currentDateTime());
65 s.endGroup();
66
67 QByteArray data = reply->readAll();
68 QXmlStreamReader reader(data);
69 QString version;
70 QString upgradeRevision;
71 QString downloadUrl;
72 QString infoUrl;
73 QString description;
74
75 while (!reader.atEnd() && !reader.hasError()) {
76 QXmlStreamReader::TokenType token = reader.readNext();
77 if(token == QXmlStreamReader::StartDocument) {
78 continue;
79 }
80 if(token == QXmlStreamReader::StartElement) {
81 if(reader.name() == "version")
82 version = parseText(reader);
83 else if (reader.name() == "revision")
84 upgradeRevision = parseText(reader);
85 else if (reader.name() == "downloadUrl") {
86 downloadUrl = parseText(reader);
87 #if defined(FOR_WINSTORE)
88 downloadUrl = QString("%1?package=appx&version=%2").arg(downloadUrl).arg(_currentVersion);
89 #endif
90 }
91 else if (reader.name() == "infoUrl")
92 infoUrl = parseText(reader);
93 else if (reader.name() == "description")
94 description = parseText(reader);
95 }
96 }
97
98 if (reader.error() != QXmlStreamReader::NoError)
99 qDebug() << reader.error() << reader.errorString();
100
101 QString message = tr("An update for MuseScore is available: %1MuseScore %2 r.%3%4")
102 .arg("<a href=\"" + downloadUrl + "\">")
103 .arg(version)
104 .arg(upgradeRevision)
105 .arg("</a>");
106 // qDebug("revision %s", revision.toLatin1().constData());
107 if (!version.isEmpty() && version > _currentVersion ) {
108 QMessageBox msgBox;
109 msgBox.setWindowTitle(tr("Update Available"));
110 msgBox.setText(message);
111 msgBox.setTextFormat(Qt::RichText);
112 msgBox.exec();
113 }
114 else if (manual) {
115 QMessageBox msgBox;
116 msgBox.setWindowTitle(tr("No Update Available"));
117 msgBox.setText(tr("No Update Available"));
118 msgBox.setTextFormat(Qt::RichText);
119 msgBox.exec();
120 }
121 }
122
parseText(QXmlStreamReader & reader)123 QString UpdateChecker::parseText(QXmlStreamReader& reader)
124 {
125 QString result;
126 reader.readNext();
127 if (reader.tokenType() == QXmlStreamReader::Characters)
128 result = reader.text().toString();
129 return result;
130 }
131
getUpdatePrefValue()132 bool UpdateChecker::getUpdatePrefValue()
133 {
134 return preferences.getBool(PREF_UI_APP_STARTUP_CHECKUPDATE);
135 }
136
getUpdatePrefString()137 QString UpdateChecker::getUpdatePrefString()
138 {
139 return "lastUpdateDate";
140 }
141
check(QString currentVersion,bool m)142 void UpdateChecker::check(QString currentVersion, bool m)
143 {
144 manual = m;
145 #if defined(Q_OS_WIN)
146 os = "win";
147 #endif
148 #if defined(Q_OS_MAC)
149 os = "mac";
150 #endif
151 bool check = true;
152 if (MuseScore::unstable()) {
153 release = "nightly";
154 QString buildNumber = QString("%1").arg(BUILD_NUMBER);
155 if (!buildNumber.isEmpty())
156 _currentVersion = QString("%1.%2").arg(currentVersion).arg(BUILD_NUMBER);
157 else {
158 _currentVersion = currentVersion;
159 check = false;
160 }
161 }
162 else {
163 release = "stable";
164 _currentVersion = currentVersion;
165 }
166 if (MScore::debugMode)
167 qDebug("release type: %s", release.toLatin1().constData());
168 if (!os.isEmpty() && !release.isEmpty() && check)
169 manager->get(QNetworkRequest(QUrl("http://update.musescore.org/update_" + os +"_" + release +".xml")));
170 }
171
UpdateCheckerBase(QObject * parent)172 UpdateCheckerBase::UpdateCheckerBase(QObject* parent)
173 : QObject(parent)
174 {
175
176 }
177
178 //---------------------------------------------------------
179 // default hasToCheck
180 //---------------------------------------------------------
181
hasToCheck()182 bool UpdateCheckerBase::hasToCheck()
183 {
184 if(!getUpdatePrefValue())
185 return false;
186 //disable embedded updating for both stable/unstable Mac builds and stable Win builds
187 #if !defined(Q_OS_MAC) && (!defined(Q_OS_WIN) || defined(MSCORE_UNSTABLE))
188 QSettings s;
189 s.beginGroup("Update");
190 QDateTime now = QDateTime::currentDateTime();
191 QDateTime lastUpdate = s.value(getUpdatePrefString(), now).value<QDateTime>();
192
193 if (MScore::debugMode) {
194 qDebug(QString("preferences." + getUpdatePrefString() + ": %d").toStdString().c_str() , getUpdatePrefValue());
195 qDebug(QString("last update for " + getUpdatePrefString() + ": %s").toStdString().c_str(), qPrintable(lastUpdate.toString("dd.MM.yyyy hh:mm:ss.zzz")));
196 }
197 s.endGroup();
198 return now == lastUpdate || now > lastUpdate.addSecs(3600 * defaultPeriod()) ;
199 #else
200 return true;
201 #endif
202 }
203
ExtensionsUpdateChecker(QObject * parent)204 ExtensionsUpdateChecker::ExtensionsUpdateChecker(QObject* parent)
205 : UpdateCheckerBase(parent)
206 {
207
208 }
209
check()210 void ExtensionsUpdateChecker::check()
211 {
212 DownloadUtils *js = new DownloadUtils();
213 js->setTarget(ResourceManager::baseAddr() + "extensions/details.json");
214 js->download();
215 QByteArray json = js->returnData();
216
217 // parse the json file
218 QJsonParseError err;
219 QJsonDocument result = QJsonDocument::fromJson(json, &err);
220 if (err.error != QJsonParseError::NoError || !result.isObject()) {
221 qDebug("An error occurred during parsing");
222 return;
223 }
224
225 QStringList extensions = result.object().keys();
226 for (QString key : extensions) {
227 if (!result.object().value(key).isObject())
228 continue;
229 QJsonObject value = result.object().value(key).toObject();
230 QString version = value.value("version").toString();
231
232 // get the installed version of the extension if any
233 if (Extension::isInstalled(key)) {
234 QString installedVersion = Extension::getLatestVersion(key);
235 if (compareVersion(installedVersion, version)) {
236 QMessageBox msgBox;
237 msgBox.setWindowTitle(tr("Extension Updates Available"));
238 msgBox.setText(tr("One or more installed extensions have updates available in Help > Resource Manager…"));
239 msgBox.setTextFormat(Qt::RichText);
240 msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
241 msgBox.setDefaultButton(QMessageBox::Ok);
242 msgBox.setEscapeButton(QMessageBox::Cancel);
243 int ret = msgBox.exec();
244 switch (ret) {
245 case QMessageBox::Ok: {
246 ResourceManager r(static_cast<QWidget*>(this->parent()));
247 r.selectExtensionsTab();
248 r.exec();
249 break;
250 }
251 case QMessageBox::Cancel: {
252 break;
253 }
254 default:
255 qWarning() << "undefined action in ExtensionsUpdateChecker::check" << ret;
256 }
257 QSettings s;
258 s.beginGroup("Update");
259 s.setValue(getUpdatePrefString(), QDateTime::currentDateTime());
260 s.endGroup();
261 break;
262 }
263 }
264 }
265 }
266
getUpdatePrefValue()267 bool ExtensionsUpdateChecker::getUpdatePrefValue()
268 {
269 return preferences.getBool(PREF_UI_APP_STARTUP_CHECK_EXTENSIONS_UPDATE);
270 }
271
getUpdatePrefString()272 QString ExtensionsUpdateChecker::getUpdatePrefString()
273 {
274 return "lastExtensionsUpdateDate";
275 }
276
277 }
278
279