diff options
Diffstat (limited to 'logic/updater')
-rw-r--r-- | logic/updater/DownloadUpdateTask.cpp | 68 | ||||
-rw-r--r-- | logic/updater/DownloadUpdateTask.h | 10 | ||||
-rw-r--r-- | logic/updater/NotificationChecker.cpp | 121 | ||||
-rw-r--r-- | logic/updater/NotificationChecker.h | 54 | ||||
-rw-r--r-- | logic/updater/UpdateChecker.cpp | 12 | ||||
-rw-r--r-- | logic/updater/UpdateChecker.h | 2 |
6 files changed, 215 insertions, 52 deletions
diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 029286dd..83679f19 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -60,13 +60,14 @@ void DownloadUpdateTask::processChannels() QList<UpdateChecker::ChannelListEntry> channels = checker->getChannelList(); QString channelId = MMC->version().channel; + m_cRepoUrl.clear(); // Search through the channel list for a channel with the correct ID. for (auto channel : channels) { if (channel.id == channelId) { QLOG_INFO() << "Found matching channel."; - m_cRepoUrl = fixPathForTests(channel.url); + m_cRepoUrl = channel.url; break; } } @@ -229,12 +230,12 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis if (type == "http") { file.sources.append( - FileSource("http", fixPathForTests(sourceObj.value("Url").toString()))); + FileSource("http", sourceObj.value("Url").toString())); } else if (type == "httpc") { file.sources.append( - FileSource("httpc", fixPathForTests(sourceObj.value("Url").toString()), + FileSource("httpc", sourceObj.value("Url").toString(), sourceObj.value("CompressionType").toString())); } else @@ -290,12 +291,11 @@ DownloadUpdateTask::processFileLists(NetJob *job, // delete anything in the current one version's list that isn't in the new version's list. for (VersionFileEntry entry : currentVersion) { - QFileInfo toDelete(entry.path); + QFileInfo toDelete(PathCombine(MMC->root(), entry.path)); if (!toDelete.exists()) { QLOG_ERROR() << "Expected file " << toDelete.absoluteFilePath() << " doesn't exist!"; - QLOG_ERROR() << "CWD: " << QDir::currentPath(); } bool keep = false; @@ -314,7 +314,6 @@ DownloadUpdateTask::processFileLists(NetJob *job, // If the loop reaches the end and we didn't find a match, delete the file. if (!keep) { - QFileInfo toDelete(entry.path); if (toDelete.exists()) ops.append(UpdateOperation::DeleteOp(entry.path)); } @@ -326,8 +325,9 @@ DownloadUpdateTask::processFileLists(NetJob *job, // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a // way to do this in the background. QString fileMD5; - QFile entryFile(entry.path); - QFileInfo entryInfo(entry.path); + QString realEntryPath = PathCombine(MMC->root(), entry.path); + QFile entryFile(realEntryPath); + QFileInfo entryInfo(realEntryPath); bool needs_upgrade = false; if (!entryFile.exists()) @@ -339,49 +339,52 @@ DownloadUpdateTask::processFileLists(NetJob *job, bool pass = true; if (!entryInfo.isReadable()) { - QLOG_ERROR() << "File " << entry.path << " is not readable."; + QLOG_ERROR() << "File " << realEntryPath << " is not readable."; pass = false; } if (!entryInfo.isWritable()) { - QLOG_ERROR() << "File " << entry.path << " is not writable."; + QLOG_ERROR() << "File " << realEntryPath << " is not writable."; pass = false; } if (!entryFile.open(QFile::ReadOnly)) { - QLOG_ERROR() << "File " << entry.path << " cannot be opened for reading."; + QLOG_ERROR() << "File " << realEntryPath << " cannot be opened for reading."; pass = false; } if (!pass) { - QLOG_ERROR() << "CWD: " << QDir::currentPath(); + QLOG_ERROR() << "ROOT: " << MMC->root(); ops.clear(); return false; } } - QCryptographicHash hash(QCryptographicHash::Md5); - auto foo = entryFile.readAll(); - - hash.addData(foo); - fileMD5 = hash.result().toHex(); - if ((fileMD5 != entry.md5)) + if(!needs_upgrade) { - QLOG_DEBUG() << "MD5Sum does not match!"; - QLOG_DEBUG() << "Expected:'" << entry.md5 << "'"; - QLOG_DEBUG() << "Got: '" << fileMD5 << "'"; - needs_upgrade = true; + QCryptographicHash hash(QCryptographicHash::Md5); + auto foo = entryFile.readAll(); + + hash.addData(foo); + fileMD5 = hash.result().toHex(); + if ((fileMD5 != entry.md5)) + { + QLOG_DEBUG() << "MD5Sum does not match!"; + QLOG_DEBUG() << "Expected:'" << entry.md5 << "'"; + QLOG_DEBUG() << "Got: '" << fileMD5 << "'"; + needs_upgrade = true; + } } // skip file. it doesn't need an upgrade. if (!needs_upgrade) { - QLOG_DEBUG() << "File" << entry.path << " does not need updating."; + QLOG_DEBUG() << "File" << realEntryPath << " does not need updating."; continue; } // yep. this file actually needs an upgrade. PROCEED. - QLOG_DEBUG() << "Found file" << entry.path << " that needs updating."; + QLOG_DEBUG() << "Found file" << realEntryPath << " that needs updating."; // if it's the updater we want to treat it separately bool isUpdater = entry.path.endsWith("updater") || entry.path.endsWith("updater.exe"); @@ -402,12 +405,17 @@ DownloadUpdateTask::processFileLists(NetJob *job, if (isUpdater) { +#ifdef MultiMC_UPDATER_FORCE_LOCAL + QLOG_DEBUG() << "Skipping updater download and using local version."; +#else auto cache_entry = MMC->metacache()->resolveEntry("root", entry.path); QLOG_DEBUG() << "Updater will be in " << cache_entry->getFullPath(); // force check. cache_entry->stale = true; + auto download = CacheDownload::make(QUrl(source.url), cache_entry); job->addNetAction(download); +#endif } else { @@ -497,24 +505,12 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin return true; } -QString DownloadUpdateTask::fixPathForTests(const QString &path) -{ - if (path.startsWith("$PWD")) - { - QString foo = path; - foo.replace("$PWD", qApp->applicationDirPath()); - return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); - } - return path; -} - bool DownloadUpdateTask::fixPathForOSX(QString &path) { if (path.startsWith("MultiMC.app/")) { // remove the prefix and add a new, more appropriate one. path.remove(0, 12); - path = QString("../../") + path; return true; } else diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index b1d14846..518bc235 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -196,21 +196,13 @@ protected: /*! * Filters paths - * Path of the format $PWD/path, it is converted to a file:///$PWD/ URL - */ - static QString fixPathForTests(const QString &path); - - /*! - * Filters paths * This fixes destination paths for OSX. * The updater runs in MultiMC.app/Contents/MacOs by default * The destination paths are such as this: MultiMC.app/blah/blah * - * Therefore we chop off the 'MultiMC.app' prefix and prepend ../.. + * Therefore we chop off the 'MultiMC.app' prefix * * Returns false if the path couldn't be fixed (is invalid) - * - * Has no effect on systems that aren't OSX */ static bool fixPathForOSX(QString &path); diff --git a/logic/updater/NotificationChecker.cpp b/logic/updater/NotificationChecker.cpp new file mode 100644 index 00000000..b2d67632 --- /dev/null +++ b/logic/updater/NotificationChecker.cpp @@ -0,0 +1,121 @@ +#include "NotificationChecker.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "MultiMC.h" +#include "MultiMCVersion.h" +#include "logic/net/CacheDownload.h" + +NotificationChecker::NotificationChecker(QObject *parent) + : QObject(parent), m_notificationsUrl(QUrl(NOTIFICATION_URL)) +{ + // this will call checkForNotifications once the event loop is running + QMetaObject::invokeMethod(this, "checkForNotifications", Qt::QueuedConnection); +} + +QUrl NotificationChecker::notificationsUrl() const +{ + return m_notificationsUrl; +} +void NotificationChecker::setNotificationsUrl(const QUrl ¬ificationsUrl) +{ + m_notificationsUrl = notificationsUrl; +} + +QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const +{ + return m_entries; +} + +void NotificationChecker::checkForNotifications() +{ + if (!m_notificationsUrl.isValid()) + { + QLOG_ERROR() << "Failed to check for notifications. No notifications URL set." + << "If you'd like to use MultiMC's notification system, please pass the " + "URL to CMake at compile time."; + return; + } + if (m_checkJob) + { + return; + } + m_checkJob.reset(new NetJob("Checking for notifications")); + auto entry = MMC->metacache()->resolveEntry("root", "notifications.json"); + entry->stale = true; + m_checkJob->addNetAction(m_download = CacheDownload::make(m_notificationsUrl, entry)); + connect(m_download.get(), &CacheDownload::succeeded, this, + &NotificationChecker::downloadSucceeded); + m_checkJob->start(); +} + +void NotificationChecker::downloadSucceeded(int) +{ + m_entries.clear(); + + QFile file(m_download->m_output_file.fileName()); + if (file.open(QFile::ReadOnly)) + { + QJsonArray root = QJsonDocument::fromJson(file.readAll()).array(); + for (auto it = root.begin(); it != root.end(); ++it) + { + QJsonObject obj = (*it).toObject(); + NotificationEntry entry; + entry.id = obj.value("id").toDouble(); + entry.message = obj.value("message").toString(); + entry.channel = obj.value("channel").toString(); + entry.platform = obj.value("platform").toString(); + entry.from = obj.value("from").toString(); + entry.to = obj.value("to").toString(); + const QString type = obj.value("type").toString("critical"); + if (type == "critical") + { + entry.type = NotificationEntry::Critical; + } + else if (type == "warning") + { + entry.type = NotificationEntry::Warning; + } + else if (type == "information") + { + entry.type = NotificationEntry::Information; + } + m_entries.append(entry); + } + } + + m_checkJob.reset(); + + emit notificationCheckFinished(); +} + +bool NotificationChecker::NotificationEntry::applies() const +{ + MultiMCVersion version = MMC->version(); + bool channelApplies = channel.isEmpty() || channel == version.channel; + bool platformApplies = platform.isEmpty() || platform == version.platform; + bool fromApplies = + from.isEmpty() || from == FULL_VERSION_STR || !versionLessThan(FULL_VERSION_STR, from); + bool toApplies = + to.isEmpty() || to == FULL_VERSION_STR || !versionLessThan(to, FULL_VERSION_STR); + return channelApplies && platformApplies && fromApplies && toApplies; +} + +bool NotificationChecker::NotificationEntry::versionLessThan(const QString &v1, + const QString &v2) +{ + QStringList l1 = v1.split('.'); + QStringList l2 = v2.split('.'); + while (!l1.isEmpty() && !l2.isEmpty()) + { + int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt(); + int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt(); + if (one != two) + { + return one < two; + } + } + return false; +} diff --git a/logic/updater/NotificationChecker.h b/logic/updater/NotificationChecker.h new file mode 100644 index 00000000..915ee54d --- /dev/null +++ b/logic/updater/NotificationChecker.h @@ -0,0 +1,54 @@ +#pragma once + +#include <QObject> + +#include "logic/net/NetJob.h" +#include "logic/net/CacheDownload.h" + +class NotificationChecker : public QObject +{ + Q_OBJECT + +public: + explicit NotificationChecker(QObject *parent = 0); + + QUrl notificationsUrl() const; + void setNotificationsUrl(const QUrl ¬ificationsUrl); + + struct NotificationEntry + { + int id; + QString message; + enum + { + Critical, + Warning, + Information + } type; + QString channel; + QString platform; + QString from; + QString to; + bool applies() const; + static bool versionLessThan(const QString &v1, const QString &v2); + }; + + QList<NotificationEntry> notificationEntries() const; + +public +slots: + void checkForNotifications(); + +private +slots: + void downloadSucceeded(int); + +signals: + void notificationCheckFinished(); + +private: + QList<NotificationEntry> m_entries; + QUrl m_notificationsUrl; + NetJobPtr m_checkJob; + CacheDownloadPtr m_download; +}; diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp index d0795c0d..8a280dd1 100644 --- a/logic/updater/UpdateChecker.cpp +++ b/logic/updater/UpdateChecker.cpp @@ -17,13 +17,14 @@ #include "MultiMC.h" -#include "config.h" #include "logger/QsLog.h" #include <QJsonObject> #include <QJsonArray> #include <QJsonValue> +#include <settingsobject.h> + #define API_VERSION 0 #define CHANLIST_FORMAT 0 @@ -70,9 +71,8 @@ void UpdateChecker::checkForUpdate(bool notifyNoUpdate) m_updateChecking = true; - // Get the URL for the channel we're using. - // TODO: Allow user to select channels. For now, we'll just use the current channel. - QString updateChannel = m_currentChannel; + // Get the channel we're checking. + QString updateChannel = MMC->settings()->get("UpdateChannel").toString(); // Find the desired channel within the channel list and get its repo URL. If if cannot be // found, error. @@ -142,8 +142,6 @@ void UpdateChecker::updateCheckFinished(bool notifyNoUpdate) if (newestVersion.value("Id").toVariant().toInt() < version.value("Id").toVariant().toInt()) { - QLOG_DEBUG() << "Found newer version with ID" - << version.value("Id").toVariant().toInt(); newestVersion = version; } } @@ -153,6 +151,7 @@ void UpdateChecker::updateCheckFinished(bool notifyNoUpdate) int newBuildNumber = newestVersion.value("Id").toVariant().toInt(); if (newBuildNumber != MMC->version().build) { + QLOG_DEBUG() << "Found newer version with ID" << newBuildNumber; // Update! emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), newBuildNumber); @@ -262,3 +261,4 @@ void UpdateChecker::chanListDownloadFailed() QLOG_ERROR() << "Failed to download channel list."; emit channelListLoaded(); } + diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index a47e8903..7840cedc 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -54,7 +54,7 @@ public: QList<ChannelListEntry> getChannelList() const; /*! - * Returns true if the channel list is empty. + * Returns false if the channel list is empty. */ bool hasChannels() const; |