diff options
Diffstat (limited to 'src/sdk/installerbase_p.cpp')
-rw-r--r-- | src/sdk/installerbase_p.cpp | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/src/sdk/installerbase_p.cpp b/src/sdk/installerbase_p.cpp new file mode 100644 index 000000000..611e491bf --- /dev/null +++ b/src/sdk/installerbase_p.cpp @@ -0,0 +1,428 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "installerbase_p.h" + +#include <binaryformat.h> +#include <errors.h> +#include <fileutils.h> +#include <lib7z_facade.h> +#include <qprocesswrapper.h> +#include <utils.h> + +#include <kdsavefile.h> +#include <kdupdaterfiledownloader.h> +#include <kdupdaterfiledownloaderfactory.h> + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QTemporaryFile> +#include <QtCore/QUrl> + +#include <QtGui/QMessageBox> + +#include <fstream> +#include <iomanip> +#include <iostream> + +#ifdef Q_OS_WIN +# include <wincon.h> + +# ifndef ENABLE_INSERT_MODE +# define ENABLE_INSERT_MODE 0x0020 +# endif + +# ifndef ENABLE_QUICK_EDIT_MODE +# define ENABLE_QUICK_EDIT_MODE 0x0040 +# endif + +# ifndef ENABLE_EXTENDED_FLAGS +# define ENABLE_EXTENDED_FLAGS 0x0080 +# endif +#endif + +using namespace KDUpdater; +using namespace QInstaller; +using namespace QInstallerCreator; + + +// -- MyCoreApplication + +MyCoreApplication::MyCoreApplication(int &argc, char **argv) + : QCoreApplication(argc, argv) +{ +} + +// re-implemented from QCoreApplication so we can throw exceptions in scripts and slots +bool MyCoreApplication::notify(QObject *receiver, QEvent *event) +{ + try { + return QCoreApplication::notify(receiver, event); + } catch(std::exception &e) { + qFatal("Exception thrown: %s", e.what()); + } + return false; +} + + +// -- MyApplicationConsole + +class MyApplicationConsole +{ +public: + MyApplicationConsole() + { +#ifdef Q_OS_WIN + AllocConsole(); + + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle != INVALID_HANDLE_VALUE) { + COORD largestConsoleWindowSize = GetLargestConsoleWindowSize(handle); + largestConsoleWindowSize.X -= 3; + largestConsoleWindowSize.Y = 5000; + SetConsoleScreenBufferSize(handle, largestConsoleWindowSize); + } + + handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle != INVALID_HANDLE_VALUE) + SetConsoleMode(handle, ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS); + + m_oldCin = std::cin.rdbuf(); + m_newCin.open("CONIN$"); + std::cin.rdbuf(m_newCin.rdbuf()); + + m_oldCout = std::cout.rdbuf(); + m_newCout.open("CONOUT$"); + std::cout.rdbuf(m_newCout.rdbuf()); + + m_oldCerr = std::cerr.rdbuf(); + m_newCerr.open("CONOUT$"); + std::cerr.rdbuf(m_newCerr.rdbuf()); +#endif + } + ~MyApplicationConsole() + { +#ifdef Q_OS_WIN + system("PAUSE"); + + std::cin.rdbuf(m_oldCin); + std::cerr.rdbuf(m_oldCerr); + std::cout.rdbuf(m_oldCout); + + FreeConsole(); +#endif + } + +private: + std::ifstream m_newCin; + std::ofstream m_newCout; + std::ofstream m_newCerr; + + std::streambuf* m_oldCin; + std::streambuf* m_oldCout; + std::streambuf* m_oldCerr; +}; + + +// -- MyApplication + +MyApplication::MyApplication(int &argc, char **argv) + : QApplication(argc, argv) + , m_console(0) +{ +} + +MyApplication::~MyApplication() +{ + delete m_console; +} + +void MyApplication::setVerbose() +{ + if (!m_console) + m_console = new MyApplicationConsole; +} + +// re-implemented from QApplication so we can throw exceptions in scripts and slots +bool MyApplication::notify(QObject *receiver, QEvent *event) +{ + try { + return QApplication::notify(receiver, event); + } catch(std::exception &e) { + qFatal("Exception thrown: %s", e.what()); + } + return false; +} + + +// -- InstallerBase + +InstallerBase::InstallerBase(QObject *parent) + : QObject(parent) + , m_downloadFinished(false) +{ +} + +InstallerBase::~InstallerBase() +{ +} + +static bool supportedScheme(const QString &scheme) +{ + if (scheme == QLatin1String("http") || scheme == QLatin1String("ftp") || scheme == QLatin1String("file")) + return true; + return false; +} + +int InstallerBase::replaceMaintenanceToolBinary(QStringList arguments) +{ + QInstaller::setVerbose(arguments.contains(QLatin1String("--verbose")) + || arguments.contains(QLatin1String("-v"))); + + arguments.removeAll(QLatin1String("--verbose")); + arguments.removeAll(QLatin1String("-v")); + arguments.removeAll(QLatin1String("--update-installerbase")); + + QUrl url = arguments.value(1); + if (!supportedScheme(url.scheme()) && QFileInfo(url.toString()).exists()) + url = QLatin1String("file:///") + url.toString(); + m_downloader.reset(FileDownloaderFactory::instance().create(url.scheme(), 0)); + if (m_downloader.isNull()) { + qDebug() << QString::fromLatin1("Scheme not supported: %1 (%2)").arg(url.scheme(), url.toString()); + return EXIT_FAILURE; + } + m_downloader->setUrl(url); + m_downloader->setAutoRemoveDownloadedFile(true); + + QString target = QDir::tempPath() + QLatin1String("/") + QFileInfo(arguments.at(1)).fileName(); + if (supportedScheme(url.scheme())) + m_downloader->setDownloadedFileName(target); + + connect(m_downloader.data(), SIGNAL(downloadStarted()), this, SLOT(downloadStarted())); + connect(m_downloader.data(), SIGNAL(downloadCanceled()), this, SLOT(downloadFinished())); + connect(m_downloader.data(), SIGNAL(downloadCompleted()), this, SLOT(downloadFinished())); + connect(m_downloader.data(), SIGNAL(downloadAborted(QString)), this, SLOT(downloadAborted(QString))); + + m_downloader->download(); + + while (true) { + QCoreApplication::processEvents(); + if (m_downloadFinished) + break; + } + + if (!m_downloader->isDownloaded()) { + qDebug() << QString::fromLatin1("Could not download file %1: . Error: %2.").arg( + m_downloader->url().toString(), m_downloader->errorString()); + return EXIT_FAILURE; + } + + if (Lib7z::isSupportedArchive(target)) { + QFile archive(target); + if (archive.open(QIODevice::ReadOnly)) { + try { + Lib7z::extractArchive(&archive, QDir::tempPath()); + if (!archive.remove()) { + qDebug() << QString::fromLatin1("Could not delete file %1: %2.").arg( + target, archive.errorString()); + } + } catch (const Lib7z::SevenZipException& e) { + qDebug() << QString::fromLatin1("Error while extracting %1: %2.").arg(target, e.message()); + return EXIT_FAILURE; + } catch (...) { + qDebug() << QString::fromLatin1("Unknown exception caught while extracting %1.").arg(target); + return EXIT_FAILURE; + } + } else { + qDebug() << QString::fromLatin1("Could not open %1 for reading: %2.").arg( + target, archive.errorString()); + return EXIT_FAILURE; + } +#ifndef Q_OS_WIN + target = QDir::tempPath() + QLatin1String("/.tempSDKMaintenanceTool"); +#else + target = QDir::tempPath() + QLatin1String("/temp/SDKMaintenanceToolBase.exe"); +#endif + } + + try { + QFile installerBase(target); + QInstaller::openForRead(&installerBase, installerBase.fileName()); + writeMaintenanceBinary(arguments.value(0), &installerBase, installerBase.size()); + deferredRename(arguments.value(0) + QLatin1String(".new"), arguments.value(0)); + } catch (const QInstaller::Error &error) { + qDebug() << error.message(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* static*/ +void InstallerBase::showUsage() +{ + MyApplicationConsole c; + std::cout << "Usage: SDKMaintenanceTool [OPTIONS]" << std::endl << std::endl; + + std::cout << "User:"<<std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --help" << std::setw(40) + << "Show commandline usage" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --version" << std::setw(40) + << "Show current version" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --checkupdates" << std::setw(40) + << "Check for updates and return an XML file of the available updates" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --proxy" << std::setw(40) + << "Set system proxy on Win and Mac. This option has no effect on Linux." << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --verbose" << std::setw(40) + << "Show debug output on the console" << std::endl; + + std::cout << "\nDeveloper:"<< std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) + << " --runoperation [operationName] [arguments...]" << std::setw(40) + << "Perform an operation with a list of arguments" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) + << " --undooperation [operationName] [arguments...]" << std::setw(40) + << "Undo an operation with a list of arguments" <<std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) + << " --script [scriptName]" << std::setw(40) << "Execute a script" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --no-force-installations" + << std::setw(40) << "Enable deselection of forced components" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --addRepository [URI]" + << std::setw(40) << "Add a local or remote repo to the list of user defined repos." << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --addTempRepository [URI]" + << std::setw(40) << "Add a local or remote repo to the list of temporary available repos." + << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --setTempRepository [URI]" + << std::setw(40) << "Set a local or remote repo as tmp repo, it is the only one used during fetch." + << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " " << std::setw(40) << "Note: URI " + "must be prefixed with the protocol, i.e. file:/// , http:// or ftp://" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) << " --show-virtual-components" + << std::setw(40) << "Show virtual components in package manager and updater" << std::endl; + std::cout << std::setw(55) << std::setiosflags(std::ios::left) + << " --update-installerbase [path/to/new/installerbase]" << std::setw(40) + << "Patch a full installer with a new installer base" << std::endl; +} + +/* static*/ +void InstallerBase::showVersion(const QString &version) +{ + MyApplicationConsole c; + std::cout << qPrintable(version) << std::endl; +} + + +// -- private slots + +void InstallerBase::downloadStarted() +{ + m_downloadFinished = false; + qDebug() << QString::fromLatin1("Download started! Source: %1, Target: %2").arg( + m_downloader->url().toString(), m_downloader->downloadedFileName()); +} + +void InstallerBase::downloadFinished() +{ + m_downloadFinished = true; + qDebug() << QString::fromLatin1("Download finished! Source: %1, Target: %2").arg( + m_downloader->url().toString(), m_downloader->downloadedFileName()); +} + +void InstallerBase::downloadProgress(double progress) +{ + qDebug() << "Progress: " << progress; +} + +void InstallerBase::downloadAborted(const QString &error) +{ + m_downloadFinished = true; + qDebug() << QString::fromLatin1("Download aborted! Source: %1, Target: %2, Error: %3").arg( + m_downloader->url().toString(), m_downloader->downloadedFileName(), error); +} + + +// -- private + +void InstallerBase::deferredRename(const QString &oldName, const QString &newName) +{ +#ifdef Q_OS_WIN + QTemporaryFile vbScript(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX.vbs"))); + { + openForWrite(&vbScript, vbScript.fileName()); + vbScript.setAutoRemove(false); + + QTextStream batch(&vbScript); + batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; + batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n"; + batch << QString::fromLatin1("file = \"%1\"\n").arg(QDir::toNativeSeparators(newName)); + batch << QString::fromLatin1("backup = \"%1.bak\"\n").arg(QDir::toNativeSeparators(newName)); + batch << "on error resume next\n"; + + batch << "while fso.FileExists(file)\n"; + batch << " fso.MoveFile file, backup\n"; + batch << " WScript.Sleep(1000)\n"; + batch << "wend\n"; + batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(QDir::toNativeSeparators(oldName)); + batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; + } + + QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo") + << QDir::toNativeSeparators(vbScript.fileName())); +#else + QFile::rename(newName, newName + QLatin1String(".bak")); + QFile::rename(oldName, newName); +#endif +} + +void InstallerBase::writeMaintenanceBinary(const QString &target, QFile *const source, qint64 size) +{ + KDSaveFile out(target + QLatin1String(".new")); + QInstaller::openForWrite(&out, out.fileName()); // throws an exception in case of error + + if (!source->seek(0)) { + throw QInstaller::Error(QObject::tr("Failed to seek in file %1. Reason: %2.").arg(source->fileName(), + source->errorString())); + } + + QInstaller::appendData(&out, source, size); + QInstaller::appendInt64(&out, 0); // resource count + QInstaller::appendInt64(&out, 4 * sizeof(qint64)); // data block size + QInstaller::appendInt64(&out, QInstaller::MagicUninstallerMarker); + QInstaller::appendInt64(&out, QInstaller::MagicCookie); + + out.setPermissions(out.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther + | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser); + + if (!out.commit(KDSaveFile::OverwriteExistingFile)) { + throw QInstaller::Error(QString::fromLatin1("Could not write new maintenance-tool to %1. Reason: %2.") + .arg(out.fileName(), out.errorString())); + } +} |