From e90f1a27569ac6b9e9782646c9de92fc9534b1d2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 5 Dec 2013 20:32:12 -0600 Subject: Implement update installer --- logic/net/FileDownload.cpp | 9 ++++ logic/updater/DownloadUpdateTask.cpp | 79 ++++++++++++++++++++++++++++++++++-- logic/updater/DownloadUpdateTask.h | 17 +++++--- 3 files changed, 95 insertions(+), 10 deletions(-) (limited to 'logic') diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp index 239af351..38f0b9c2 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/FileDownload.cpp @@ -63,6 +63,15 @@ void FileDownload::start() request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); + // Go ahead and try to open the file. + // If we don't do this, empty files won't be created, which breaks the updater. + // Plus, this way, we don't end up starting a download for a file we can't open. + if (!m_output_file.open(QIODevice::WriteOnly)) + { + emit failed(index_within_job); + return; + } + auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index fc4d321f..7c5eb84a 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -24,6 +24,8 @@ #include #include +#include + DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent) : Task(parent) @@ -197,10 +199,11 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile VersionFileEntry file{ fileObj.value("Path").toString(), - fileObj.value("Executable").toBool(false), + 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) @@ -274,7 +277,7 @@ void DownloadUpdateTask::processFileLists() 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(), entry.path.replace("/", "_")); + 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. FileDownloadPtr download = FileDownload::make(source.url, dlPath); @@ -283,7 +286,7 @@ void DownloadUpdateTask::processFileLists() netJob->addNetAction(download); // Now add a copy operation to our operations list to install the file. - m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path)); + m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } } @@ -300,7 +303,75 @@ void DownloadUpdateTask::processFileLists() m_filesNetJob.reset(netJob); netJob->start(); - // TODO: Write update operations to a file for the update installer. + writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); +} + +void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +{ + // Build the base structure of the XML document. + QDomDocument doc; + + QDomElement root = doc.createElement("update"); + root.setAttribute("version", "3"); + doc.appendChild(root); + + QDomElement installFiles = doc.createElement("install"); + root.appendChild(installFiles); + + QDomElement removeFiles = doc.createElement("uninstall"); + root.appendChild(removeFiles); + + // Write the operation list to the XML document. + for (UpdateOperation op : opsList) + { + QDomElement file = doc.createElement("file"); + + 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_DELETE: + { + // Delete the file. + file.appendChild(doc.createTextNode(op.file)); + removeFiles.appendChild(file); + QLOG_DEBUG() << "Will remove file" << op.file; + } + break; + + default: + QLOG_WARN() << "Can't write update operation of type" << op.type << "to file. Not implemented."; + continue; + } + } + + // Write the XML document to the file. + QFile outFile(scriptFile); + + if (outFile.open(QIODevice::WriteOnly)) + { + outFile.write(doc.toByteArray()); + } + else + { + emitFailed(tr("Failed to write update script file.")); + } } void DownloadUpdateTask::fileDownloadFinished() diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 6fb745ab..dc30ca15 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -57,7 +57,7 @@ protected: struct VersionFileEntry { QString path; - bool isExecutable; + int mode; FileSourceList sources; QString md5; }; @@ -70,9 +70,9 @@ protected: */ struct UpdateOperation { - static UpdateOperation CopyOp(QString fsource, QString fdest) { return UpdateOperation{OP_COPY, fsource, fdest, 644}; } - static UpdateOperation MoveOp(QString fsource, QString fdest) { return UpdateOperation{OP_MOVE, fsource, fdest, 644}; } - static UpdateOperation DeleteOp(QString file) { return UpdateOperation{OP_DELETE, file, "", 644}; } + static UpdateOperation CopyOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_COPY, fsource, fdest, fmode}; } + static UpdateOperation MoveOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_MOVE, fsource, fdest, fmode}; } + static UpdateOperation DeleteOp(QString file) { return UpdateOperation{OP_DELETE, file, "", 0644}; } static UpdateOperation ChmodOp(QString file, int fmode) { return UpdateOperation{OP_CHMOD, file, "", fmode}; } //! Specifies the type of operation that this is. @@ -84,8 +84,8 @@ protected: OP_CHMOD, } type; - //! The source file. If this is a DELETE or CHMOD operation, this is the file that will be modified. - QString source; + //! The file to operate on. If this is a DELETE or CHMOD operation, this is the file that will be modified. + QString file; //! The destination file. If this is a DELETE or CHMOD operation, this field will be ignored. QString dest; @@ -145,6 +145,11 @@ protected: */ virtual void processFileLists(); + /*! + * Takes the operations list and writes an install script for the updater to the update files directory. + */ + virtual void writeInstallScript(UpdateOperationList& opsList, QString scriptFile); + VersionFileList m_downloadList; UpdateOperationList m_operationList; -- cgit v1.2.3 From 6ac94ddcb6f64ffae3948bed778bccc33a92f0fd Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 6 Dec 2013 12:59:58 -0600 Subject: Finish implementing update installation. Also add the option to update on exit. --- logic/updater/DownloadUpdateTask.cpp | 5 +++++ logic/updater/DownloadUpdateTask.h | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 7c5eb84a..ef975c93 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -391,3 +391,8 @@ void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 tota 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 dc30ca15..f5b23d12 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -28,6 +28,11 @@ class DownloadUpdateTask : public Task public: explicit DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent=0); + + /*! + * Gets the directory that contains the update files. + */ + QString updateFilesDir(); protected: // TODO: We should probably put these data structures into a separate header... -- cgit v1.2.3