From a02e62f17f7b51c489e209ab6937ad717fbcfb07 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 14 Dec 2013 16:02:51 +0100 Subject: Tests for parsing of channel lists in UpdateChecker --- logic/updater/UpdateChecker.cpp | 2 +- logic/updater/UpdateChecker.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'logic/updater') diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp index 5ff1898e..af56288c 100644 --- a/logic/updater/UpdateChecker.cpp +++ b/logic/updater/UpdateChecker.cpp @@ -44,7 +44,7 @@ QList UpdateChecker::getChannelList() const bool UpdateChecker::hasChannels() const { - return m_channels.isEmpty(); + return !m_channels.isEmpty(); } void UpdateChecker::checkForUpdate() diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index 59fb8e47..131f49a2 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -27,6 +27,9 @@ public: UpdateChecker(); void checkForUpdate(); + void setCurrentChannel(const QString &channel) { m_currentChannel = channel; } + void setChannelListUrl(const QString &url) { m_channelListUrl = url; } + /*! * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake). * If this isn't called before checkForUpdate(), it will automatically be called. -- cgit From f273334212274b1f1c7da376ef186314de8c4428 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 14 Dec 2013 19:19:14 +0100 Subject: More tests for the UpdateChecker class. It should be done for now. --- logic/updater/UpdateChecker.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'logic/updater') diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index 131f49a2..5b7efc05 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -73,6 +73,8 @@ private slots: void chanListDownloadFailed(); private: + friend class UpdateCheckerTest; + NetJobPtr indexJob; NetJobPtr chanListJob; -- cgit From 3e8bcc1cf6f3400fff9aa361ddc109bafe16d646 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 15 Dec 2013 12:18:42 +0100 Subject: Unit tests for the DownloadUpdateTask class --- logic/updater/DownloadUpdateTask.cpp | 112 +++++++++++++++++++---------------- logic/updater/DownloadUpdateTask.h | 22 ++++++- 2 files changed, 80 insertions(+), 54 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index d9aab826..ed5bfd02 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -45,55 +45,54 @@ void DownloadUpdateTask::executeTask() findCurrentVersionInfo(); } -void DownloadUpdateTask::findCurrentVersionInfo() +void DownloadUpdateTask::processChannels() { - setStatus(tr("Finding information about the current version.")); - auto checker = MMC->updateChecker(); - // This runs after we've tried loading the channel list. - // If the channel list doesn't need to be loaded, this will be called immediately. - // If the channel list does need to be loaded, this will be called when it's done. - auto processFunc = [this, &checker] () -> void + // Now, check the channel list again. + if (!checker->hasChannels()) { - // Now, check the channel list again. - if (checker->hasChannels()) - { - // We still couldn't load the channel list. Give up. Call loadVersionInfo and return. - QLOG_INFO() << "Reloading the channel list didn't work. Giving up."; - loadVersionInfo(); - return; - } + // We still couldn't load the channel list. Give up. Call loadVersionInfo and return. + QLOG_INFO() << "Reloading the channel list didn't work. Giving up."; + loadVersionInfo(); + return; + } - QList channels = checker->getChannelList(); - QString channelId = MMC->version().channel; + QList channels = checker->getChannelList(); + QString channelId = MMC->version().channel; - // Search through the channel list for a channel with the correct ID. - for (auto channel : channels) + // Search through the channel list for a channel with the correct ID. + for (auto channel : channels) + { + if (channel.id == channelId) { - if (channel.id == channelId) - { - QLOG_INFO() << "Found matching channel."; - m_cRepoUrl = channel.url; - break; - } + QLOG_INFO() << "Found matching channel."; + m_cRepoUrl = preparePath(channel.url); + break; } + } - // Now that we've done that, load version info. - loadVersionInfo(); - }; + // Now that we've done that, load version info. + loadVersionInfo(); +} + +void DownloadUpdateTask::findCurrentVersionInfo() +{ + setStatus(tr("Finding information about the current version.")); + + auto checker = MMC->updateChecker(); - if (checker->hasChannels()) + if (!checker->hasChannels()) { // Load the channel list and wait for it to finish loading. QLOG_INFO() << "No channel list entries found. Will try reloading it."; - QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, processFunc); + QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, &DownloadUpdateTask::processChannels); checker->updateChanList(); } else { - processFunc(); + processChannels(); } } @@ -152,12 +151,24 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() { setStatus(tr("Reading file lists.")); - parseVersionInfo(NEW_VERSION, &m_nVersionFileList); + setStatus(tr("Reading file list for new version.")); + QLOG_DEBUG() << "Reading file list for new version."; + QString error; + if (!parseVersionInfo(std::dynamic_pointer_cast( + m_vinfoNetJob->first())->m_data, &m_nVersionFileList, &error)) + { + emitFailed(error); + return; + } // If there is a second entry in the network job's list, load it as the current version's info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { - parseVersionInfo(CURRENT_VERSION, &m_cVersionFileList); + setStatus(tr("Reading file list for current version.")); + QLOG_DEBUG() << "Reading file list for current version."; + QString error; + parseVersionInfo(std::dynamic_pointer_cast( + m_vinfoNetJob->operator[](1))->m_data, &m_cVersionFileList, &error); } // We don't need this any more. @@ -167,26 +178,15 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() processFileLists(); } -void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list) +bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error) { - if (vfile == CURRENT_VERSION) setStatus(tr("Reading file list for current version.")); - else if (vfile == NEW_VERSION) setStatus(tr("Reading file list for new version.")); - - QLOG_DEBUG() << "Reading file list for" << (vfile == NEW_VERSION ? "new" : "current") << "version."; - - QByteArray data; - { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast( - vfile == NEW_VERSION ? m_vinfoNetJob->first() : m_vinfoNetJob->operator[](1)); - data = dl->m_data; - } - QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { - QLOG_ERROR() << "Failed to parse version info JSON:" << jsonError.errorString() << "at" << jsonError.offset; - return; + *error = QString("Failed to parse version info JSON: %1 at %2").arg(jsonError.errorString()).arg(jsonError.offset); + QLOG_ERROR() << error; + return false; } QJsonObject json = jsonDoc.object(); @@ -213,11 +213,11 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile QString type = sourceObj.value("SourceType").toString(); if (type == "http") { - file.sources.append(FileSource("http", sourceObj.value("Url").toString())); + file.sources.append(FileSource("http", preparePath(sourceObj.value("Url").toString()))); } else if (type == "httpc") { - file.sources.append(FileSource("httpc", sourceObj.value("Url").toString(), sourceObj.value("CompressionType").toString())); + file.sources.append(FileSource("httpc", preparePath(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); } else { @@ -229,6 +229,8 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile list->append(file); } + + return true; } void DownloadUpdateTask::processFileLists() @@ -312,7 +314,7 @@ void DownloadUpdateTask::processFileLists() writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } -void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) { // Build the base structure of the XML document. QDomDocument doc; @@ -377,7 +379,15 @@ void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin else { emitFailed(tr("Failed to write update script file.")); + return false; } + + return true; +} + +QString DownloadUpdateTask::preparePath(const QString &path) +{ + return QString(path).replace("$PWD", qApp->applicationDirPath()); } void DownloadUpdateTask::fileDownloadFinished() diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index f5b23d12..1d1fc7bf 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -34,7 +34,8 @@ public: */ QString updateFilesDir(); -protected: +public: + // TODO: We should probably put these data structures into a separate header... /*! @@ -59,6 +60,7 @@ protected: /*! * Structure that describes an entry in a GoUpdate version's `Files` list. */ + struct VersionFileEntry { QString path; @@ -69,6 +71,8 @@ protected: typedef QList VersionFileList; +protected: + friend class DownloadUpdateTaskTest; /*! * Structure that describes an operation to perform when installing updates. @@ -119,6 +123,13 @@ protected: */ virtual void findCurrentVersionInfo(); + /*! + * This runs after we've tried loading the channel list. + * If the channel list doesn't need to be loaded, this will be called immediately. + * If the channel list does need to be loaded, this will be called when it's done. + */ + void processChannels(); + /*! * Downloads the version info files from the repository. * The files for both the current build, and the build that we're updating to need to be downloaded. @@ -142,7 +153,7 @@ protected: /*! * Loads the file list from the given version info JSON object into the given list. */ - virtual void parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list); + virtual bool parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error); /*! * Takes a list of file entries for the current version's files and the new version's files @@ -153,7 +164,7 @@ protected: /*! * Takes the operations list and writes an install script for the updater to the update files directory. */ - virtual void writeInstallScript(UpdateOperationList& opsList, QString scriptFile); + virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile); VersionFileList m_downloadList; UpdateOperationList m_operationList; @@ -181,6 +192,11 @@ protected: */ QTemporaryDir m_updateFilesDir; + /*! + * Substitutes $PWD for the application directory + */ + static QString preparePath(const QString &path); + protected slots: void vinfoDownloadFinished(); void vinfoDownloadFailed(); -- cgit From 7f884a18a85eca8c1a395ab0e9d421f17a98f142 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 15 Dec 2013 18:50:56 +0100 Subject: Finish unit tests for the DownloadUpdateTask class --- logic/updater/DownloadUpdateTask.cpp | 60 ++++++++++++++++++++---------------- logic/updater/DownloadUpdateTask.h | 18 ++++++----- 2 files changed, 44 insertions(+), 34 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index ed5bfd02..d72cfcf6 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -234,15 +234,36 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis } void DownloadUpdateTask::processFileLists() +{ + // Create a network job for downloading files. + NetJob* netJob = new NetJob("Update Files"); + + processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList); + + // Add listeners to wait for the downloads to finish. + QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); + QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); + QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); + + // Now start the download. + setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size()))); + QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path(); + m_filesNetJob.reset(netJob); + netJob->start(); + + writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); +} + +void DownloadUpdateTask::processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) { setStatus(tr("Processing file lists. Figuring out how to install the update.")); // First, if we've loaded the current version's file list, we need to iterate through it and // delete anything in the current one version's list that isn't in the new version's list. - for (VersionFileEntry entry : m_cVersionFileList) + for (VersionFileEntry entry : currentVersion) { bool keep = false; - for (VersionFileEntry newEntry : m_nVersionFileList) + for (VersionFileEntry newEntry : newVersion) { if (newEntry.path == entry.path) { @@ -253,14 +274,11 @@ void DownloadUpdateTask::processFileLists() } // If the loop reaches the end and we didn't find a match, delete the file. if(!keep) - m_operationList.append(UpdateOperation::DeleteOp(entry.path)); + ops.append(UpdateOperation::DeleteOp(entry.path)); } - // Create a network job for downloading files. - NetJob* netJob = new NetJob("Update Files"); - // Next, check each file in MultiMC's folder and see if we need to update them. - for (VersionFileEntry entry : m_nVersionFileList) + for (VersionFileEntry entry : newVersion) { // 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; @@ -287,31 +305,21 @@ void DownloadUpdateTask::processFileLists() // Download it to updatedir/- where filepath is the file's path with slashes replaced by underscores. QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - // We need to download the file to the updatefiles folder and add a task to copy it to its install path. - auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; - download->m_expected_md5 = entry.md5; - netJob->addNetAction(download); + if (job) + { + // We need to download the file to the updatefiles folder and add a task to copy it to its install path. + auto download = MD5EtagDownload::make(source.url, dlPath); + download->m_check_md5 = true; + download->m_expected_md5 = entry.md5; + job->addNetAction(download); + } // Now add a copy operation to our operations list to install the file. - m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } } } - - // Add listeners to wait for the downloads to finish. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); - QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); - QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); - - // Now start the download. - setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size()))); - QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path(); - m_filesNetJob.reset(netJob); - netJob->start(); - - writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 1d1fc7bf..8530be77 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -54,13 +54,11 @@ public: QString url; QString compressionType; }; - typedef QList FileSourceList; /*! * Structure that describes an entry in a GoUpdate version's `Files` list. */ - struct VersionFileEntry { QString path; @@ -68,12 +66,8 @@ public: FileSourceList sources; QString md5; }; - typedef QList VersionFileList; -protected: - friend class DownloadUpdateTaskTest; - /*! * Structure that describes an operation to perform when installing updates. */ @@ -104,9 +98,12 @@ protected: // Yeah yeah, polymorphism blah blah inheritance, blah blah object oriented. I'm lazy, OK? }; - typedef QList UpdateOperationList; +protected: + friend class DownloadUpdateTaskTest; + + /*! * Used for arguments to parseVersionInfo and friends to specify which version info file to parse. */ @@ -159,6 +156,12 @@ protected: * Takes a list of file entries for the current version's files and the new version's files * and populates the downloadList and operationList with information about how to download and install the update. */ + virtual void processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); + + /*! + * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes + * the NetJob to fetch all needed files + */ virtual void processFileLists(); /*! @@ -166,7 +169,6 @@ protected: */ virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile); - VersionFileList m_downloadList; UpdateOperationList m_operationList; VersionFileList m_nVersionFileList; -- cgit From d6c71488b34a2854461feee3296c11568542ecbe Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 17 Dec 2013 02:09:58 +0100 Subject: Some test madness --- logic/updater/DownloadUpdateTask.cpp | 279 +++++++++++++++++++++++------------ logic/updater/DownloadUpdateTask.h | 5 +- 2 files changed, 189 insertions(+), 95 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index d72cfcf6..cc06104a 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -26,9 +26,8 @@ #include - -DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent) : - Task(parent) +DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject *parent) + : Task(parent) { m_cVersionId = MMC->version().build; @@ -87,7 +86,8 @@ void DownloadUpdateTask::findCurrentVersionInfo() // Load the channel list and wait for it to finish loading. QLOG_INFO() << "No channel list entries found. Will try reloading it."; - QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, &DownloadUpdateTask::processChannels); + QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, + &DownloadUpdateTask::processChannels); checker->updateChanList(); } else @@ -101,11 +101,12 @@ void DownloadUpdateTask::loadVersionInfo() setStatus(tr("Loading version information.")); // Create the net job for loading version info. - NetJob* netJob = new NetJob("Version Info"); - + NetJob *netJob = new NetJob("Version Info"); + // Find the index URL. QUrl newIndexUrl = QUrl(m_nRepoUrl).resolved(QString::number(m_nVersionId) + ".json"); - + QLOG_DEBUG() << m_nRepoUrl << " turns into " << newIndexUrl; + // Add a net action to download the version info for the version we're updating to. netJob->addNetAction(ByteArrayDownload::make(newIndexUrl)); @@ -114,10 +115,12 @@ void DownloadUpdateTask::loadVersionInfo() { QUrl cIndexUrl = QUrl(m_cRepoUrl).resolved(QString::number(m_cVersionId) + ".json"); netJob->addNetAction(ByteArrayDownload::make(cIndexUrl)); + QLOG_DEBUG() << m_cRepoUrl << " turns into " << cIndexUrl; } // Connect slots so we know when it's done. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::vinfoDownloadFinished); + QObject::connect(netJob, &NetJob::succeeded, this, + &DownloadUpdateTask::vinfoDownloadFinished); QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::vinfoDownloadFailed); // Store the NetJob in a class member. We don't want to lose it! @@ -135,7 +138,8 @@ void DownloadUpdateTask::vinfoDownloadFinished() void DownloadUpdateTask::vinfoDownloadFailed() { - // Something failed. We really need the second download (current version info), so parse downloads anyways as long as the first one succeeded. + // Something failed. We really need the second download (current version info), so parse + // downloads anyways as long as the first one succeeded. if (m_vinfoNetJob->first()->m_status != Job_Failed) { parseDownloadedVersionInfo(); @@ -154,43 +158,51 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() setStatus(tr("Reading file list for new version.")); QLOG_DEBUG() << "Reading file list for new version."; QString error; - if (!parseVersionInfo(std::dynamic_pointer_cast( - m_vinfoNetJob->first())->m_data, &m_nVersionFileList, &error)) + if (!parseVersionInfo( + std::dynamic_pointer_cast(m_vinfoNetJob->first())->m_data, + &m_nVersionFileList, &error)) { emitFailed(error); return; } - // If there is a second entry in the network job's list, load it as the current version's info. + // If there is a second entry in the network job's list, load it as the current version's + // info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { setStatus(tr("Reading file list for current version.")); QLOG_DEBUG() << "Reading file list for current version."; QString error; - parseVersionInfo(std::dynamic_pointer_cast( - m_vinfoNetJob->operator[](1))->m_data, &m_cVersionFileList, &error); + parseVersionInfo( + std::dynamic_pointer_cast(m_vinfoNetJob->operator[](1))->m_data, + &m_cVersionFileList, &error); } // We don't need this any more. m_vinfoNetJob.reset(); - // Now that we're done loading version info, we can move on to the next step. Process file lists and download files. + // Now that we're done loading version info, we can move on to the next step. Process file + // lists and download files. processFileLists(); } -bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error) +bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList *list, + QString *error) { QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { - *error = QString("Failed to parse version info JSON: %1 at %2").arg(jsonError.errorString()).arg(jsonError.offset); + *error = QString("Failed to parse version info JSON: %1 at %2") + .arg(jsonError.errorString()) + .arg(jsonError.offset); QLOG_ERROR() << error; return false; } QJsonObject json = jsonDoc.object(); + QLOG_DEBUG() << data; QLOG_DEBUG() << "Loading version info from JSON."; QJsonArray filesArray = json.value("Files").toArray(); for (QJsonValue fileValue : filesArray) @@ -198,13 +210,10 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis QJsonObject fileObj = fileValue.toObject(); VersionFileEntry file{ - fileObj.value("Path").toString(), - fileObj.value("Perms").toVariant().toInt(), - FileSourceList(), - fileObj.value("MD5").toString(), - }; + fileObj.value("Path").toString(), fileObj.value("Perms").toVariant().toInt(), + FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; - + QJsonArray sourceArray = fileObj.value("Sources").toArray(); for (QJsonValue val : sourceArray) { @@ -213,11 +222,14 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis QString type = sourceObj.value("SourceType").toString(); if (type == "http") { - file.sources.append(FileSource("http", preparePath(sourceObj.value("Url").toString()))); + file.sources.append( + FileSource("http", preparePath(sourceObj.value("Url").toString()))); } else if (type == "httpc") { - file.sources.append(FileSource("httpc", preparePath(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); + file.sources.append(FileSource("httpc", + preparePath(sourceObj.value("Url").toString()), + sourceObj.value("CompressionType").toString())); } else { @@ -236,13 +248,19 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis void DownloadUpdateTask::processFileLists() { // Create a network job for downloading files. - NetJob* netJob = new NetJob("Update Files"); + NetJob *netJob = new NetJob("Update Files"); - processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList); + if (!processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList)) + { + emitFailed(tr("Failed to process update lists...")); + return; + } // Add listeners to wait for the downloads to finish. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); - QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); + QObject::connect(netJob, &NetJob::succeeded, this, + &DownloadUpdateTask::fileDownloadFinished); + QObject::connect(netJob, &NetJob::progress, this, + &DownloadUpdateTask::fileDownloadProgressChanged); QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); // Now start the download. @@ -254,75 +272,144 @@ void DownloadUpdateTask::processFileLists() writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } -void DownloadUpdateTask::processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) +bool +DownloadUpdateTask::processFileLists(NetJob *job, + const DownloadUpdateTask::VersionFileList ¤tVersion, + const DownloadUpdateTask::VersionFileList &newVersion, + DownloadUpdateTask::UpdateOperationList &ops) { setStatus(tr("Processing file lists. Figuring out how to install the update.")); - // First, if we've loaded the current version's file list, we need to iterate through it and + // First, if we've loaded the current version's file list, we need to iterate through it and // 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); + if (!toDelete.exists()) + { + QLOG_ERROR() << "Expected file " << toDelete.absoluteFilePath() + << " doesn't exist!"; + QLOG_ERROR() << "CWD: " << QDir::currentPath(); + } bool keep = false; + + // for (VersionFileEntry newEntry : newVersion) { if (newEntry.path == entry.path) { - QLOG_DEBUG() << "Not deleting" << entry.path << "because it is still present in the new version."; + QLOG_DEBUG() << "Not deleting" << entry.path + << "because it is still present in the new version."; keep = true; break; } } + // If the loop reaches the end and we didn't find a match, delete the file. - if(!keep) - ops.append(UpdateOperation::DeleteOp(entry.path)); + if (!keep) + { + QFileInfo toDelete(entry.path); + if (toDelete.exists()) + ops.append(UpdateOperation::DeleteOp(entry.path)); + } } // Next, check each file in MultiMC's folder and see if we need to update them. for (VersionFileEntry entry : newVersion) { - // 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. + // 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); - if (entryFile.open(QFile::ReadOnly)) + QFileInfo entryInfo(entry.path); + + bool needs_upgrade = false; + if (!entryFile.exists()) + { + needs_upgrade = true; + } + else { - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(entryFile.readAll()); - fileMD5 = hash.result().toHex(); + bool pass = true; + if (!entryInfo.isReadable()) + { + QLOG_ERROR() << "File " << entry.path << " is not readable."; + pass = false; + } + if (!entryInfo.isWritable()) + { + QLOG_ERROR() << "File " << entry.path << " is not writable."; + pass = false; + } + if (!entryFile.open(QFile::ReadOnly)) + { + QLOG_ERROR() << "File " << entry.path << " cannot be opened for reading."; + pass = false; + } + if (!pass) + { + QLOG_ERROR() << "CWD: " << QDir::currentPath(); + ops.clear(); + return false; + } } - if (!entryFile.exists() || fileMD5.isEmpty() || fileMD5 != entry.md5) + 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() << "Found file" << entry.path << "that needs updating."; + QLOG_DEBUG() << "File" << entry.path << " does not need updating."; + continue; + } + + // yep. this file actually needs an upgrade. PROCEED. + QLOG_DEBUG() << "Found file" << entry.path << " that needs updating."; - // Go through the sources list and find one to use. - // TODO: Make a NetAction that takes a source list and tries each of them until one works. For now, we'll just use the first http one. - for (FileSource source : entry.sources) + // Go through the sources list and find one to use. + // TODO: Make a NetAction that takes a source list and tries each of them until one + // works. For now, we'll just use the first http one. + for (FileSource source : entry.sources) + { + if (source.type == "http") { - if (source.type == "http") + QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url; + + // Download it to updatedir/- where filepath is the file's + // path with slashes replaced by underscores. + QString dlPath = + PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); + + if (job) { - QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url; - - // Download it to updatedir/- where filepath is the file's path with slashes replaced by underscores. - QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - - if (job) - { - // We need to download the file to the updatefiles folder and add a task to copy it to its install path. - auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; - download->m_expected_md5 = entry.md5; - job->addNetAction(download); - } - - // Now add a copy operation to our operations list to install the file. - ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + // We need to download the file to the updatefiles folder and add a task + // to copy it to its install path. + auto download = MD5EtagDownload::make(source.url, dlPath); + download->m_check_md5 = true; + download->m_expected_md5 = entry.md5; + job->addNetAction(download); } + + // Now add a copy operation to our operations list to install the file. + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } } + return true; } -bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QString scriptFile) { // Build the base structure of the XML document. QDomDocument doc; @@ -342,38 +429,43 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin { QDomElement file = doc.createElement("file"); + QString native_file = QDir::toNativeSeparators(op.file); + QString native_dest = QDir::toNativeSeparators(op.dest); + switch (op.type) { - case UpdateOperation::OP_COPY: - { - // Install the file. - QDomElement name = doc.createElement("source"); - QDomElement path = doc.createElement("dest"); - QDomElement mode = doc.createElement("mode"); - name.appendChild(doc.createTextNode(op.file)); - path.appendChild(doc.createTextNode(op.dest)); - // We need to add a 0 at the beginning here, because Qt doesn't convert to octal correctly. - mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); - file.appendChild(name); - file.appendChild(path); - file.appendChild(mode); - installFiles.appendChild(file); - QLOG_DEBUG() << "Will install file" << op.file; - } - break; + case UpdateOperation::OP_COPY: + { + // Install the file. + QDomElement name = doc.createElement("source"); + QDomElement path = doc.createElement("dest"); + QDomElement mode = doc.createElement("mode"); + name.appendChild(doc.createTextNode(native_file)); + path.appendChild(doc.createTextNode(native_dest)); + // We need to add a 0 at the beginning here, because Qt doesn't convert to octal + // correctly. + mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); + file.appendChild(name); + file.appendChild(path); + file.appendChild(mode); + installFiles.appendChild(file); + QLOG_DEBUG() << "Will install file" << native_file; + } + break; - case UpdateOperation::OP_DELETE: - { - // Delete the file. - file.appendChild(doc.createTextNode(op.file)); - removeFiles.appendChild(file); - QLOG_DEBUG() << "Will remove file" << op.file; - } - break; + case UpdateOperation::OP_DELETE: + { + // Delete the file. + file.appendChild(doc.createTextNode(native_file)); + removeFiles.appendChild(file); + QLOG_DEBUG() << "Will remove file" << native_file; + } + break; - default: - QLOG_WARN() << "Can't write update operation of type" << op.type << "to file. Not implemented."; - continue; + default: + QLOG_WARN() << "Can't write update operation of type" << op.type + << "to file. Not implemented."; + continue; } } @@ -395,7 +487,9 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin QString DownloadUpdateTask::preparePath(const QString &path) { - return QString(path).replace("$PWD", qApp->applicationDirPath()); + QString foo = path; + foo.replace("$PWD", qApp->applicationDirPath()); + return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); } void DownloadUpdateTask::fileDownloadFinished() @@ -412,11 +506,10 @@ void DownloadUpdateTask::fileDownloadFailed() void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 total) { - setProgress((int)(((float)current / (float)total)*100)); + setProgress((int)(((float)current / (float)total) * 100)); } QString DownloadUpdateTask::updateFilesDir() { return m_updateFilesDir.path(); } - diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 8530be77..1fc14049 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -156,7 +156,7 @@ protected: * Takes a list of file entries for the current version's files and the new version's files * and populates the downloadList and operationList with information about how to download and install the update. */ - virtual void processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); + virtual bool processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); /*! * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes @@ -195,7 +195,8 @@ protected: QTemporaryDir m_updateFilesDir; /*! - * Substitutes $PWD for the application directory + * Filters paths + * Path of the format $PWD/path, it is converted to a file:///$PWD/ URL */ static QString preparePath(const QString &path); -- cgit From 01dbebdfc81abab00048f48e68a4e04d391bc50e Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 21 Dec 2013 14:25:16 +0100 Subject: Fix issues with the updater * Bad URLs used for downloading update files * MD5ETagDownload resetting the expected ETag after failure to the failed file MD5 checksum * Delete MD5ETagDownload downloaded files if the download fails. --- logic/updater/DownloadUpdateTask.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index cc06104a..b017afeb 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -396,7 +396,6 @@ DownloadUpdateTask::processFileLists(NetJob *job, // We need to download the file to the updatefiles folder and add a task // to copy it to its install path. auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; download->m_expected_md5 = entry.md5; job->addNetAction(download); } @@ -487,9 +486,13 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin QString DownloadUpdateTask::preparePath(const QString &path) { - QString foo = path; - foo.replace("$PWD", qApp->applicationDirPath()); - return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); + if(path.startsWith("$PWD")) + { + QString foo = path; + foo.replace("$PWD", qApp->applicationDirPath()); + return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); + } + return path; } void DownloadUpdateTask::fileDownloadFinished() -- cgit From 00822fa0f9d3cd93e460c992aac77693ac00a741 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 23 Dec 2013 10:34:43 +0100 Subject: Treat the updater separately --- logic/updater/DownloadUpdateTask.cpp | 31 +++++++++++++++++++++++++++++-- logic/updater/DownloadUpdateTask.h | 2 ++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index b017afeb..6157608f 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -377,6 +377,9 @@ DownloadUpdateTask::processFileLists(NetJob *job, // yep. this file actually needs an upgrade. PROCEED. QLOG_DEBUG() << "Found file" << entry.path << " 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"); + // Go through the sources list and find one to use. // TODO: Make a NetAction that takes a source list and tries each of them until one // works. For now, we'll just use the first http one. @@ -398,10 +401,19 @@ DownloadUpdateTask::processFileLists(NetJob *job, auto download = MD5EtagDownload::make(source.url, dlPath); download->m_expected_md5 = entry.md5; job->addNetAction(download); + + if (isUpdater) + { + download->setProperty("finalPath", entry.path); + connect(download.get(), &MD5EtagDownload::succeeded, this, &DownloadUpdateTask::directDeployFile); + } } - // Now add a copy operation to our operations list to install the file. - ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + if (!isUpdater) + { + // Now add a copy operation to our operations list to install the file. + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + } } } } @@ -512,6 +524,21 @@ void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 tota setProgress((int)(((float)current / (float)total) * 100)); } +void DownloadUpdateTask::directDeployFile(const int index) +{ + Md5EtagDownloadPtr download = std::dynamic_pointer_cast(m_filesNetJob->operator[](index)); + const QString finalPath = download->property("finalPath").toString(); + QLOG_INFO() << "Replacing" << finalPath << "with" << download->m_output_file.fileName(); + if (QFile::remove(finalPath)) + { + if (download->m_output_file.copy(finalPath)) + { + return; + } + } + emitFailed("Couldn't copy updater files"); +} + QString DownloadUpdateTask::updateFilesDir() { return m_updateFilesDir.path(); diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 1fc14049..79d73af3 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -207,5 +207,7 @@ protected slots: void fileDownloadFinished(); void fileDownloadFailed(); void fileDownloadProgressChanged(qint64 current, qint64 total); + + void directDeployFile(const int index); }; -- cgit From 027aafc3c1fc5e78c91ee439cd38562387f7ed9f Mon Sep 17 00:00:00 2001 From: Sky Date: Mon, 23 Dec 2013 15:46:01 +0000 Subject: Tidy status messages a bit --- logic/updater/DownloadUpdateTask.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index b017afeb..cffac75f 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -77,7 +77,7 @@ void DownloadUpdateTask::processChannels() void DownloadUpdateTask::findCurrentVersionInfo() { - setStatus(tr("Finding information about the current version.")); + setStatus(tr("Finding information about the current version...")); auto checker = MMC->updateChecker(); @@ -98,7 +98,7 @@ void DownloadUpdateTask::findCurrentVersionInfo() void DownloadUpdateTask::loadVersionInfo() { - setStatus(tr("Loading version information.")); + setStatus(tr("Loading version information...")); // Create the net job for loading version info. NetJob *netJob = new NetJob("Version Info"); @@ -153,10 +153,8 @@ void DownloadUpdateTask::vinfoDownloadFailed() void DownloadUpdateTask::parseDownloadedVersionInfo() { - setStatus(tr("Reading file lists.")); - - setStatus(tr("Reading file list for new version.")); - QLOG_DEBUG() << "Reading file list for new version."; + setStatus(tr("Reading file list for new version...")); + QLOG_DEBUG() << "Reading file list for new version..."; QString error; if (!parseVersionInfo( std::dynamic_pointer_cast(m_vinfoNetJob->first())->m_data, @@ -170,8 +168,8 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() // info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { - setStatus(tr("Reading file list for current version.")); - QLOG_DEBUG() << "Reading file list for current version."; + setStatus(tr("Reading file list for current version...")); + QLOG_DEBUG() << "Reading file list for current version..."; QString error; parseVersionInfo( std::dynamic_pointer_cast(m_vinfoNetJob->operator[](1))->m_data, @@ -278,7 +276,7 @@ DownloadUpdateTask::processFileLists(NetJob *job, const DownloadUpdateTask::VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) { - setStatus(tr("Processing file lists. Figuring out how to install the update.")); + setStatus(tr("Processing file lists - figuring out how to install the update...")); // First, if we've loaded the current version's file list, we need to iterate through it and // delete anything in the current one version's list that isn't in the new version's list. -- cgit From b1ec7841e04f2a60f54895e1646bc33486fd9fbf Mon Sep 17 00:00:00 2001 From: robotbrainify Date: Tue, 24 Dec 2013 16:00:07 -0500 Subject: Get the updater to display a no update found message. --- logic/updater/UpdateChecker.cpp | 71 +++++++++++++++++++++++++---------------- logic/updater/UpdateChecker.h | 6 ++-- 2 files changed, 48 insertions(+), 29 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp index af56288c..d0795c0d 100644 --- a/logic/updater/UpdateChecker.cpp +++ b/logic/updater/UpdateChecker.cpp @@ -47,14 +47,16 @@ bool UpdateChecker::hasChannels() const return !m_channels.isEmpty(); } -void UpdateChecker::checkForUpdate() +void UpdateChecker::checkForUpdate(bool notifyNoUpdate) { QLOG_DEBUG() << "Checking for updates."; - // If the channel list hasn't loaded yet, load it and defer checking for updates until later. + // If the channel list hasn't loaded yet, load it and defer checking for updates until + // later. if (!m_chanListLoaded) { - QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring update check."; + QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring " + "update check."; m_checkUpdateWaiting = true; updateChanList(); return; @@ -72,7 +74,8 @@ void UpdateChecker::checkForUpdate() // TODO: Allow user to select channels. For now, we'll just use the current channel. QString updateChannel = m_currentChannel; - // Find the desired channel within the channel list and get its repo URL. If if cannot be found, error. + // Find the desired channel within the channel list and get its repo URL. If if cannot be + // found, error. m_repoUrl = ""; for (ChannelListEntry entry : m_channels) { @@ -91,20 +94,22 @@ void UpdateChecker::checkForUpdate() auto job = new NetJob("GoUpdate Repository Index"); job->addNetAction(ByteArrayDownload::make(indexUrl)); - connect(job, SIGNAL(succeeded()), SLOT(updateCheckFinished())); + connect(job, &NetJob::succeeded, [this, notifyNoUpdate]() + { updateCheckFinished(notifyNoUpdate); }); connect(job, SIGNAL(failed()), SLOT(updateCheckFailed())); indexJob.reset(job); job->start(); } -void UpdateChecker::updateCheckFinished() +void UpdateChecker::updateCheckFinished(bool notifyNoUpdate) { QLOG_DEBUG() << "Finished downloading repo index. Checking for new versions."; QJsonParseError jsonError; QByteArray data; { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast(indexJob->first()); + ByteArrayDownloadPtr dl = + std::dynamic_pointer_cast(indexJob->first()); data = dl->m_data; indexJob.reset(); } @@ -112,7 +117,8 @@ void UpdateChecker::updateCheckFinished() QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject()) { - QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" << jsonError.errorString() << "at offset" << jsonError.offset; + QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" + << jsonError.errorString() << "at offset" << jsonError.offset; return; } @@ -122,7 +128,8 @@ void UpdateChecker::updateCheckFinished() int apiVersion = object.value("ApiVersion").toVariant().toInt(&success); if (apiVersion != API_VERSION || !success) { - QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" << API_VERSION << "server has" << apiVersion; + QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" + << API_VERSION << "server has" << apiVersion; return; } @@ -132,19 +139,27 @@ void UpdateChecker::updateCheckFinished() for (QJsonValue versionVal : versions) { QJsonObject version = versionVal.toObject(); - if (newestVersion.value("Id").toVariant().toInt() < version.value("Id").toVariant().toInt()) + if (newestVersion.value("Id").toVariant().toInt() < + version.value("Id").toVariant().toInt()) { - QLOG_DEBUG() << "Found newer version with ID" << version.value("Id").toVariant().toInt(); + QLOG_DEBUG() << "Found newer version with ID" + << version.value("Id").toVariant().toInt(); newestVersion = version; } } - // We've got the version with the greatest ID number. Now compare it to our current build number and update if they're different. + // We've got the version with the greatest ID number. Now compare it to our current build + // number and update if they're different. int newBuildNumber = newestVersion.value("Id").toVariant().toInt(); if (newBuildNumber != MMC->version().build) { // Update! - emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), newBuildNumber); + emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), + newBuildNumber); + } + else if (notifyNoUpdate) + { + emit noUpdateFound(); } m_updateChecking = false; @@ -163,12 +178,13 @@ void UpdateChecker::updateChanList() if (m_channelListUrl.isEmpty()) { QLOG_ERROR() << "Failed to update channel list. No channel list URL set." - << "If you'd like to use MultiMC's update system, please pass the channel list URL to CMake at compile time."; + << "If you'd like to use MultiMC's update system, please pass the channel " + "list URL to CMake at compile time."; return; } m_chanListLoading = true; - NetJob* job = new NetJob("Update System Channel List"); + NetJob *job = new NetJob("Update System Channel List"); job->addNetAction(ByteArrayDownload::make(QUrl(m_channelListUrl))); QObject::connect(job, &NetJob::succeeded, this, &UpdateChecker::chanListDownloadFinished); QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed); @@ -180,7 +196,8 @@ void UpdateChecker::chanListDownloadFinished() { QByteArray data; { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast(chanListJob->first()); + ByteArrayDownloadPtr dl = + std::dynamic_pointer_cast(chanListJob->first()); data = dl->m_data; chanListJob.reset(); } @@ -190,17 +207,20 @@ void UpdateChecker::chanListDownloadFinished() if (jsonError.error != QJsonParseError::NoError) { // TODO: Report errors to the user. - QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset; + QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" + << jsonError.offset; return; } - + QJsonObject object = jsonDoc.object(); bool success = false; int formatVersion = object.value("format_version").toVariant().toInt(&success); if (formatVersion != CHANLIST_FORMAT || !success) { - QLOG_ERROR() << "Failed to check for updates. Channel list format version mismatch. We're using" << CHANLIST_FORMAT << "server has" << formatVersion; + QLOG_ERROR() + << "Failed to check for updates. Channel list format version mismatch. We're using" + << CHANLIST_FORMAT << "server has" << formatVersion; return; } @@ -210,12 +230,10 @@ void UpdateChecker::chanListDownloadFinished() for (QJsonValue chanVal : channelArray) { QJsonObject channelObj = chanVal.toObject(); - ChannelListEntry entry{ - channelObj.value("id").toVariant().toString(), - channelObj.value("name").toVariant().toString(), - channelObj.value("description").toVariant().toString(), - channelObj.value("url").toVariant().toString() - }; + ChannelListEntry entry{channelObj.value("id").toVariant().toString(), + channelObj.value("name").toVariant().toString(), + channelObj.value("description").toVariant().toString(), + channelObj.value("url").toVariant().toString()}; if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty()) { QLOG_ERROR() << "Channel list entry with empty ID, name, or URL. Skipping."; @@ -233,7 +251,7 @@ void UpdateChecker::chanListDownloadFinished() // If we're waiting to check for updates, do that now. if (m_checkUpdateWaiting) - checkForUpdate(); + checkForUpdate(false); emit channelListLoaded(); } @@ -244,4 +262,3 @@ 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 5b7efc05..a47e8903 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -25,7 +25,7 @@ class UpdateChecker : public QObject public: UpdateChecker(); - void checkForUpdate(); + void checkForUpdate(bool notifyNoUpdate); void setCurrentChannel(const QString &channel) { m_currentChannel = channel; } void setChannelListUrl(const QString &url) { m_channelListUrl = url; } @@ -65,8 +65,10 @@ signals: //! Signal emitted when the channel list finishes loading or fails to load. void channelListLoaded(); + void noUpdateFound(); + private slots: - void updateCheckFinished(); + void updateCheckFinished(bool notifyNoUpdate); void updateCheckFailed(); void chanListDownloadFinished(); -- cgit From 8edd0100e852cda9f44abd12cc06931542a2004a Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 25 Dec 2013 02:46:06 +0100 Subject: Fix more updater derps. * Updater requires unix style paths on input. * No update notification was getting cloned with every check --- logic/updater/DownloadUpdateTask.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 5f05b0ba..057ca436 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -438,9 +438,6 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin { QDomElement file = doc.createElement("file"); - QString native_file = QDir::toNativeSeparators(op.file); - QString native_dest = QDir::toNativeSeparators(op.dest); - switch (op.type) { case UpdateOperation::OP_COPY: @@ -449,8 +446,8 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin QDomElement name = doc.createElement("source"); QDomElement path = doc.createElement("dest"); QDomElement mode = doc.createElement("mode"); - name.appendChild(doc.createTextNode(native_file)); - path.appendChild(doc.createTextNode(native_dest)); + name.appendChild(doc.createTextNode(op.file)); + path.appendChild(doc.createTextNode(op.dest)); // We need to add a 0 at the beginning here, because Qt doesn't convert to octal // correctly. mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); @@ -458,16 +455,16 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin file.appendChild(path); file.appendChild(mode); installFiles.appendChild(file); - QLOG_DEBUG() << "Will install file" << native_file; + QLOG_DEBUG() << "Will install file " << op.file << " to " << op.dest; } break; case UpdateOperation::OP_DELETE: { // Delete the file. - file.appendChild(doc.createTextNode(native_file)); + file.appendChild(doc.createTextNode(op.file)); removeFiles.appendChild(file); - QLOG_DEBUG() << "Will remove file" << native_file; + QLOG_DEBUG() << "Will remove file" << op.file; } break; -- cgit From 7652b3d64a63c587f520633364412345083210d4 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 28 Dec 2013 02:03:53 +0100 Subject: Various updater fixes Updater tests for path utils The updater now doesn't use splitpath on Windows (fixes problems with Windows XP) Fix up paths for the OSX updater - should now install the updates into the right place Fix translations install path - translation isntall and deploy should be fixed --- logic/updater/DownloadUpdateTask.cpp | 36 +++++++++++++++++++++++++++++++----- logic/updater/DownloadUpdateTask.h | 16 +++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 057ca436..0b09ad2a 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -66,7 +66,7 @@ void DownloadUpdateTask::processChannels() if (channel.id == channelId) { QLOG_INFO() << "Found matching channel."; - m_cRepoUrl = preparePath(channel.url); + m_cRepoUrl = fixPathForTests(channel.url); break; } } @@ -207,8 +207,17 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis { QJsonObject fileObj = fileValue.toObject(); + QString file_path = fileObj.value("Path").toString(); +#ifdef Q_OS_MAC + // On OSX, the paths for the updater need to be fixed. + // basically, anything that isn't in the .app folder is ignored. + // everything else is changed so the code that processes the files actually finds + // them and puts the replacements in the right spots. + if(!fixPathForOSX(file_path)) + continue; +#endif VersionFileEntry file{ - fileObj.value("Path").toString(), fileObj.value("Perms").toVariant().toInt(), + file_path , fileObj.value("Perms").toVariant().toInt(), FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; @@ -221,12 +230,12 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis if (type == "http") { file.sources.append( - FileSource("http", preparePath(sourceObj.value("Url").toString()))); + FileSource("http", fixPathForTests(sourceObj.value("Url").toString()))); } else if (type == "httpc") { file.sources.append(FileSource("httpc", - preparePath(sourceObj.value("Url").toString()), + fixPathForTests(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); } else @@ -491,7 +500,7 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin return true; } -QString DownloadUpdateTask::preparePath(const QString &path) +QString DownloadUpdateTask::fixPathForTests(const QString &path) { if(path.startsWith("$PWD")) { @@ -502,6 +511,23 @@ QString DownloadUpdateTask::preparePath(const QString &path) 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 + { + QLOG_ERROR() << "Update path not within .app: " << path; + return false; + } +} + + void DownloadUpdateTask::fileDownloadFinished() { emitSucceeded(); diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 79d73af3..d82b044f 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -198,7 +198,21 @@ protected: * Filters paths * Path of the format $PWD/path, it is converted to a file:///$PWD/ URL */ - static QString preparePath(const QString &path); + 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 ../.. + * + * 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); protected slots: void vinfoDownloadFinished(); -- cgit From c816a26647ca0537709f0d15cdd550feea4de109 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 28 Dec 2013 22:32:45 +0100 Subject: Set permissions for the updater binary after updating it. --- logic/updater/DownloadUpdateTask.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 0b09ad2a..9282c4d8 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -412,6 +412,7 @@ DownloadUpdateTask::processFileLists(NetJob *job, if (isUpdater) { download->setProperty("finalPath", entry.path); + download->setProperty("finalPerms", entry.mode); connect(download.get(), &MD5EtagDownload::succeeded, this, &DownloadUpdateTask::directDeployFile); } } @@ -549,11 +550,16 @@ void DownloadUpdateTask::directDeployFile(const int index) { Md5EtagDownloadPtr download = std::dynamic_pointer_cast(m_filesNetJob->operator[](index)); const QString finalPath = download->property("finalPath").toString(); + bool ok = true; + int finalMode = download->property("finalPerms").toInt(&ok); + if(!ok) + finalMode = 0755; QLOG_INFO() << "Replacing" << finalPath << "with" << download->m_output_file.fileName(); if (QFile::remove(finalPath)) { if (download->m_output_file.copy(finalPath)) { + QFile::setPermissions(finalPath, (QFileDevice::Permission) finalMode); return; } } -- cgit From 997be947c9baa1499f708594d7a954d772ea99b7 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 29 Dec 2013 01:13:57 +0100 Subject: Maybe break updater even more? --- logic/updater/DownloadUpdateTask.cpp | 64 +++++++++++------------------------- logic/updater/DownloadUpdateTask.h | 2 -- 2 files changed, 20 insertions(+), 46 deletions(-) (limited to 'logic/updater') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 9282c4d8..6e0a92f0 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -213,12 +213,11 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis // basically, anything that isn't in the .app folder is ignored. // everything else is changed so the code that processes the files actually finds // them and puts the replacements in the right spots. - if(!fixPathForOSX(file_path)) + if (!fixPathForOSX(file_path)) continue; #endif - VersionFileEntry file{ - file_path , fileObj.value("Perms").toVariant().toInt(), - FileSourceList(), fileObj.value("MD5").toString(), }; + VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(), + FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; QJsonArray sourceArray = fileObj.value("Sources").toArray(); @@ -234,9 +233,9 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis } else if (type == "httpc") { - file.sources.append(FileSource("httpc", - fixPathForTests(sourceObj.value("Url").toString()), - sourceObj.value("CompressionType").toString())); + file.sources.append( + FileSource("httpc", fixPathForTests(sourceObj.value("Url").toString()), + sourceObj.value("CompressionType").toString())); } else { @@ -401,25 +400,23 @@ DownloadUpdateTask::processFileLists(NetJob *job, QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - if (job) + if (isUpdater) + { + auto cache_entry = MMC->metacache()->resolveEntry(