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