aboutsummaryrefslogtreecommitdiff
path: root/launcher/UpdateController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/UpdateController.cpp')
-rw-r--r--launcher/UpdateController.cpp443
1 files changed, 0 insertions, 443 deletions
diff --git a/launcher/UpdateController.cpp b/launcher/UpdateController.cpp
deleted file mode 100644
index 9ff44854..00000000
--- a/launcher/UpdateController.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-#include <QFile>
-#include <QMessageBox>
-#include <FileSystem.h>
-#include <updater/GoUpdate.h>
-#include "UpdateController.h"
-#include <QApplication>
-#include <thread>
-#include <chrono>
-#include <LocalPeer.h>
-
-#include "BuildConfig.h"
-
-
-// from <sys/stat.h>
-#ifndef S_IRUSR
-#define __S_IREAD 0400 /* Read by owner. */
-#define __S_IWRITE 0200 /* Write by owner. */
-#define __S_IEXEC 0100 /* Execute by owner. */
-#define S_IRUSR __S_IREAD /* Read by owner. */
-#define S_IWUSR __S_IWRITE /* Write by owner. */
-#define S_IXUSR __S_IEXEC /* Execute by owner. */
-
-#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
-#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
-#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
-
-#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
-#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
-#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
-#endif
-static QFile::Permissions unixModeToPermissions(const int mode)
-{
- QFile::Permissions perms;
-
- if (mode & S_IRUSR)
- {
- perms |= QFile::ReadUser;
- }
- if (mode & S_IWUSR)
- {
- perms |= QFile::WriteUser;
- }
- if (mode & S_IXUSR)
- {
- perms |= QFile::ExeUser;
- }
-
- if (mode & S_IRGRP)
- {
- perms |= QFile::ReadGroup;
- }
- if (mode & S_IWGRP)
- {
- perms |= QFile::WriteGroup;
- }
- if (mode & S_IXGRP)
- {
- perms |= QFile::ExeGroup;
- }
-
- if (mode & S_IROTH)
- {
- perms |= QFile::ReadOther;
- }
- if (mode & S_IWOTH)
- {
- perms |= QFile::WriteOther;
- }
- if (mode & S_IXOTH)
- {
- perms |= QFile::ExeOther;
- }
- return perms;
-}
-
-static const QLatin1String liveCheckFile("live.check");
-
-UpdateController::UpdateController(QWidget * parent, const QString& root, const QString updateFilesDir, GoUpdate::OperationList operations)
-{
- m_parent = parent;
- m_root = root;
- m_updateFilesDir = updateFilesDir;
- m_operations = operations;
-}
-
-
-void UpdateController::installUpdates()
-{
- qint64 pid = -1;
- QStringList args;
- bool started = false;
-
- qDebug() << "Installing updates.";
-#ifdef Q_OS_WIN
- QString finishCmd = QApplication::applicationFilePath();
-#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined (Q_OS_OPENBSD)
- QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME);
-#elif defined Q_OS_MAC
- QString finishCmd = QApplication::applicationFilePath();
-#else
-#error Unsupported operating system.
-#endif
-
- QString backupPath = FS::PathCombine(m_root, "update", "backup");
- QDir origin(m_root);
-
- // clean up the backup folder. it should be empty before we start
- if(!FS::deletePath(backupPath))
- {
- qWarning() << "couldn't remove previous backup folder" << backupPath;
- }
- // and it should exist.
- if(!FS::ensureFolderPathExists(backupPath))
- {
- qWarning() << "couldn't create folder" << backupPath;
- return;
- }
-
- bool useXPHack = false;
- QString exePath;
- QString exeOrigin;
- QString exeBackup;
-
- // perform the update operations
- for(auto op: m_operations)
- {
- switch(op.type)
- {
- // replace = move original out to backup, if it exists, move the new file in its place
- case GoUpdate::Operation::OP_REPLACE:
- {
-#ifdef Q_OS_WIN32
- QString windowsExeName = BuildConfig.LAUNCHER_NAME + ".exe";
- // hack for people renaming the .exe because ... reasons :)
- if(op.destination == windowsExeName)
- {
- op.destination = QFileInfo(QApplication::applicationFilePath()).fileName();
- }
-#endif
- QFileInfo destination (FS::PathCombine(m_root, op.destination));
- if(destination.exists())
- {
- QString backupName = op.destination;
- backupName.replace('/', '_');
- QString backupFilePath = FS::PathCombine(backupPath, backupName);
- if(!QFile::rename(destination.absoluteFilePath(), backupFilePath))
- {
- qWarning() << "Couldn't move:" << destination.absoluteFilePath() << "to" << backupFilePath;
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- BackupEntry be;
- be.original = destination.absoluteFilePath();
- be.backup = backupFilePath;
- be.update = op.source;
- m_replace_backups.append(be);
- }
- // make sure the folder we are putting this into exists
- if(!FS::ensureFilePathExists(destination.absoluteFilePath()))
- {
- qWarning() << "REPLACE: Couldn't create folder:" << destination.absoluteFilePath();
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- // now move the new file in
- if(!QFile::rename(op.source, destination.absoluteFilePath()))
- {
- qWarning() << "REPLACE: Couldn't move:" << op.source << "to" << destination.absoluteFilePath();
- m_failedOperationType = Replace;
- m_failedFile = op.destination;
- fail();
- return;
- }
- QFile::setPermissions(destination.absoluteFilePath(), unixModeToPermissions(op.destinationMode));
- }
- break;
- // delete = move original to backup
- case GoUpdate::Operation::OP_DELETE:
- {
- QString destFilePath = FS::PathCombine(m_root, op.destination);
- if(QFile::exists(destFilePath))
- {
- QString backupName = op.destination;
- backupName.replace('/', '_');
- QString trashFilePath = FS::PathCombine(backupPath, backupName);
-
- if(!QFile::rename(destFilePath, trashFilePath))
- {
- qWarning() << "DELETE: Couldn't move:" << op.destination << "to" << trashFilePath;
- m_failedFile = op.destination;
- m_failedOperationType = Delete;
- fail();
- return;
- }
- BackupEntry be;
- be.original = destFilePath;
- be.backup = trashFilePath;
- m_delete_backups.append(be);
- }
- }
- break;
- }
- }
-
- // try to start the new binary
- args = qApp->arguments();
- args.removeFirst();
-
- // on old Windows, do insane things... no error checking here, this is just to have something.
- if(useXPHack)
- {
- QString script;
- auto nativePath = QDir::toNativeSeparators(exePath);
- auto nativeOriginPath = QDir::toNativeSeparators(exeOrigin);
- auto nativeBackupPath = QDir::toNativeSeparators(exeBackup);
-
- // so we write this vbscript thing...
- QTextStream out(&script);
- out << "WScript.Sleep 1000\n";
- out << "Set fso=CreateObject(\"Scripting.FileSystemObject\")\n";
- out << "Set shell=CreateObject(\"WScript.Shell\")\n";
- out << "fso.MoveFile \"" << nativePath << "\", \"" << nativeBackupPath << "\"\n";
- out << "fso.MoveFile \"" << nativeOriginPath << "\", \"" << nativePath << "\"\n";
- out << "shell.Run \"" << nativePath << "\"\n";
-
- QString scriptPath = FS::PathCombine(m_root, "update", "update.vbs");
-
- // we save it
- QFile scriptFile(scriptPath);
- scriptFile.open(QIODevice::WriteOnly);
- scriptFile.write(script.toLocal8Bit().replace("\n", "\r\n"));
- scriptFile.close();
-
- // we run it
- started = QProcess::startDetached("wscript", {scriptPath}, m_root);
-
- // and we quit. conscious thought.
- qApp->quit();
- return;
- }
- bool doLiveCheck = true;
- bool startFailed = false;
-
- // remove live check file, if any
- if(QFile::exists(liveCheckFile))
- {
- if(!QFile::remove(liveCheckFile))
- {
- qWarning() << "Couldn't remove the" << liveCheckFile << "file! We will proceed without :(";
- doLiveCheck = false;
- }
- }
-
- if(doLiveCheck)
- {
- if(!args.contains("--alive"))
- {
- args.append("--alive");
- }
- }
-
- // FIXME: reparse args and construct a safe variant from scratch. This is a workaround for GH-1874:
- QStringList realargs;
- int skip = 0;
- for(auto & arg: args)
- {
- if(skip)
- {
- skip--;
- continue;
- }
- if(arg == "-l")
- {
- skip = 1;
- continue;
- }
- realargs.append(arg);
- }
-
- // start the updated application
- started = QProcess::startDetached(finishCmd, realargs, QDir::currentPath(), &pid);
- // much dumber check - just find out if the call
- if(!started || pid == -1)
- {
- qWarning() << "Couldn't start new process properly!";
- startFailed = true;
- }
- if(!startFailed && doLiveCheck)
- {
- int attempts = 0;
- while(attempts < 10)
- {
- attempts++;
- QString key;
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- if(!QFile::exists(liveCheckFile))
- {
- qWarning() << "Couldn't find the" << liveCheckFile << "file!";
- startFailed = true;
- continue;
- }
- try
- {
- key = QString::fromUtf8(FS::read(liveCheckFile));
- auto id = ApplicationId::fromRawString(key);
- LocalPeer peer(nullptr, id);
- if(peer.isClient())
- {
- startFailed = false;
- qDebug() << "Found process started with key " << key;
- break;
- }
- else
- {
- startFailed = true;
- qDebug() << "Process started with key " << key << "apparently died or is not reponding...";
- break;
- }
- }
- catch (const Exception &e)
- {
- qWarning() << "Couldn't read the" << liveCheckFile << "file!";
- startFailed = true;
- continue;
- }
- }
- }
- if(startFailed)
- {
- m_failedOperationType = Start;
- fail();
- return;
- }
- else
- {
- origin.rmdir(m_updateFilesDir);
- qApp->quit();
- return;
- }
-}
-
-void UpdateController::fail()
-{
- qWarning() << "Update failed!";
-
- QString msg;
- bool doRollback = false;
- QString failTitle = QObject::tr("Update failed!");
- QString rollFailTitle = QObject::tr("Rollback failed!");
- switch (m_failedOperationType)
- {
- case Replace:
- {
- msg = QObject::tr(
- "Couldn't replace file %1. Changes will be reverted.\n"
- "See the %2 log file for details."
- ).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME);
- doRollback = true;
- QMessageBox::critical(m_parent, failTitle, msg);
- break;
- }
- case Delete:
- {
- msg = QObject::tr(
- "Couldn't remove file %1. Changes will be reverted.\n"
- "See the %2 log file for details."
- ).arg(m_failedFile, BuildConfig.LAUNCHER_DISPLAYNAME);
- doRollback = true;
- QMessageBox::critical(m_parent, failTitle, msg);
- break;
- }
- case Start:
- {
- msg = QObject::tr("The new version didn't start or is too old and doesn't respond to startup checks.\n"
- "\n"
- "Roll back to previous version?");
- auto result = QMessageBox::critical(
- m_parent,
- failTitle,
- msg,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes
- );
- doRollback = (result == QMessageBox::Yes);
- break;
- }
- case Nothing:
- default:
- return;
- }
- if(doRollback)
- {
- auto rollbackOK = rollback();
- if(!rollbackOK)
- {
- msg = QObject::tr("The rollback failed too.\n"
- "You will have to repair %1 manually.\n"
- "Please let us know why and how this happened.").arg(BuildConfig.LAUNCHER_DISPLAYNAME);
- QMessageBox::critical(m_parent, rollFailTitle, msg);
- qApp->quit();
- }
- }
- else
- {
- qApp->quit();
- }
-}
-
-bool UpdateController::rollback()
-{
- bool revertOK = true;
- // if the above failed, roll back changes
- for(auto backup:m_replace_backups)
- {
- qWarning() << "restoring" << backup.original << "from" << backup.backup;
- if(!QFile::rename(backup.original, backup.update))
- {
- revertOK = false;
- qWarning() << "moving new" << backup.original << "back to" << backup.update << "failed!";
- continue;
- }
-
- if(!QFile::rename(backup.backup, backup.original))
- {
- revertOK = false;
- qWarning() << "restoring" << backup.original << "failed!";
- }
- }
- for(auto backup:m_delete_backups)
- {
- qWarning() << "restoring" << backup.original << "from" << backup.backup;
- if(!QFile::rename(backup.backup, backup.original))
- {
- revertOK = false;
- qWarning() << "restoring" << backup.original << "failed!";
- }
- }
- return revertOK;
-}