diff options
Diffstat (limited to 'installerbuilder/libinstaller')
40 files changed, 3862 insertions, 146 deletions
diff --git a/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.cpp b/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.cpp index 285f9bbbb..c679572fd 100644 --- a/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.cpp +++ b/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.cpp @@ -1,7 +1,10 @@ #include "lib7z_facade.h" +#include "errors.h" +#include "fileutils.h" + #ifndef Q_OS_WIN -#include "StdAfx.h" +# include "StdAfx.h" #endif #include "Common/MyInitGuid.h" @@ -36,9 +39,6 @@ #include "Windows/PropVariant.h" #include "Windows/PropVariantConversions.h" -#include <common/errors.h> -#include <common/fileutils.h> - #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QDir> diff --git a/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.h b/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.h index 855648aa2..58774df11 100644 --- a/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.h +++ b/installerbuilder/libinstaller/3rdparty/7zip/lib7z_facade.h @@ -1,21 +1,21 @@ #ifndef LIB7Z_FACADE_H #define LIB7Z_FACADE_H -#include <QCoreApplication> -#include <QDateTime> -#include <QFile> -#include <QString> -#include <QRunnable> -#include <QVector> -#include <QVariant> -#include <QPoint> - -#include <stdexcept> -#include <string> +#include "installer_global.h" #include "Common/MyWindows.h" -#include "../../installerbuilder/libinstaller/installer_global.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QDateTime> +#include <QtCore/QFile> +#include <QtCore/QPoint> +#include <QtCore/QRunnable> +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QVariant> + +#include <stdexcept> +#include <string> QT_BEGIN_NAMESPACE class QStringList; diff --git a/installerbuilder/libinstaller/adminauthorization_win.cpp b/installerbuilder/libinstaller/adminauthorization_win.cpp index 21a592b89..7cfd6b3dc 100644 --- a/installerbuilder/libinstaller/adminauthorization_win.cpp +++ b/installerbuilder/libinstaller/adminauthorization_win.cpp @@ -29,10 +29,9 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "adminauthorization.h" -#include <common/utils.h> +#include "utils.h" #include <QtCore/QDebug> #include <QtCore/QDir> diff --git a/installerbuilder/libinstaller/binaryformat.cpp b/installerbuilder/libinstaller/binaryformat.cpp new file mode 100644 index 000000000..133df3690 --- /dev/null +++ b/installerbuilder/libinstaller/binaryformat.cpp @@ -0,0 +1,1115 @@ +/************************************************************************** +** +** 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 "binaryformat.h" + +#include "errors.h" +#include "fileutils.h" +#include "lib7z_facade.h" +#include "utils.h" +#include "zipjob.h" + +#include <kdupdaterupdateoperationfactory.h> + +#include <QtCore/QResource> +#include <QtCore/QTemporaryFile> + +#include <errno.h> + +using namespace QInstaller; +using namespace QInstallerCreator; + +/* +TRANSLATOR QInstallerCreator::Archive +*/ + +/* +TRANSLATOR QInstallerCreator::Component +*/ + +static inline QByteArray &theBuffer(int size) +{ + static QByteArray b; + if (size > b.size()) + b.resize(size); + return b; +} + +void QInstaller::appendFileData(QIODevice *out, QIODevice *in) +{ + Q_ASSERT(!in->isSequential()); + const qint64 size = in->size(); + blockingCopy(in, out, size); +} + + +void QInstaller::retrieveFileData(QIODevice *out, QIODevice *in) +{ + qint64 size = QInstaller::retrieveInt64(in); + appendData(in, out, size); +/* QByteArray &b = theBuffer(size); + blockingRead(in, b.data(), size); + blockingWrite(out, b.constData(), size);*/ +} + +void QInstaller::appendInt64(QIODevice *out, qint64 n) +{ + blockingWrite(out, reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void QInstaller::appendInt64Range(QIODevice *out, const Range<qint64> &r) +{ + appendInt64(out, r.start()); + appendInt64(out, r.length()); +} + +qint64 QInstaller::retrieveInt64(QIODevice *in) +{ + qint64 n = 0; + blockingRead(in, reinterpret_cast<char*>(&n), sizeof(n)); + return n; +} + +Range<qint64> QInstaller::retrieveInt64Range(QIODevice *in) +{ + const quint64 start = retrieveInt64(in); + const quint64 length = retrieveInt64(in); + return Range<qint64>::fromStartAndLength(start, length); +} + +void QInstaller::appendData(QIODevice *out, QIODevice *in, qint64 size) +{ + while (size > 0) { + const qint64 nextSize = qMin(size, 16384LL); + QByteArray &b = theBuffer(nextSize); + blockingRead(in, b.data(), nextSize); + blockingWrite(out, b.constData(), nextSize); + size -= nextSize; + } +} + +void QInstaller::appendString(QIODevice *out, const QString &str) +{ + appendByteArray(out, str.toUtf8()); +} + +void QInstaller::appendByteArray(QIODevice *out, const QByteArray &ba) +{ + appendInt64(out, ba.size()); + blockingWrite(out, ba.constData(), ba.size()); +} + +void QInstaller::appendStringList(QIODevice *out, const QStringList &list) +{ + appendInt64(out, list.size()); + foreach (const QString &s, list) + appendString(out, s); +} + +void QInstaller::appendDictionary(QIODevice *out, const QHash<QString,QString> &dict) +{ + appendInt64(out, dict.size()); + foreach (const QString &key, dict.keys()) { + appendString(out, key); + appendString(out, dict.value(key)); + } +} + +qint64 QInstaller::appendCompressedData(QIODevice *out, QIODevice *in, qint64 size) +{ + QByteArray ba; + ba.resize(size); + blockingRead(in, ba.data(), size); + + QByteArray cba = qCompress(ba); + blockingWrite(out, cba, cba.size()); + return cba.size(); +} + +QString QInstaller::retrieveString(QIODevice *in) +{ + const QByteArray b = retrieveByteArray(in); + return QString::fromUtf8(b); +} + +QByteArray QInstaller::retrieveByteArray(QIODevice *in) +{ + QByteArray ba; + const qint64 n = retrieveInt64(in); + ba.resize(n); + blockingRead(in, ba.data(), n); + return ba; +} + +QStringList QInstaller::retrieveStringList(QIODevice *in) +{ + QStringList list; + for (qint64 i = retrieveInt64(in); --i >= 0;) + list << retrieveString(in); + return list; +} + +QHash<QString,QString> QInstaller::retrieveDictionary(QIODevice *in) +{ + QHash<QString,QString> dict; + for (qint64 i = retrieveInt64(in); --i >= 0;) { + QString key = retrieveString(in); + dict.insert(key, retrieveString(in)); + } + return dict; +} + +QByteArray QInstaller::retrieveData(QIODevice *in, qint64 size) +{ + QByteArray ba; + ba.resize(size); + blockingRead(in, ba.data(), size); + return ba; +} + +QByteArray QInstaller::retrieveCompressedData(QIODevice *in, qint64 size) +{ + QByteArray ba; + ba.resize(size); + blockingRead(in, ba.data(), size); + return qUncompress(ba); +} + +qint64 QInstaller::findMagicCookie(QFile *in, quint64 magicCookie) +{ + Q_ASSERT(in); + Q_ASSERT(in->isOpen()); + Q_ASSERT(in->isReadable()); + const qint64 oldPos = in->pos(); + const qint64 MAX_SEARCH = 1024 * 1024; // stop searching after one MB + qint64 searched = 0; + try { + while (searched < MAX_SEARCH) { + const qint64 pos = in->size() - searched - sizeof(qint64); + if (pos < 0) + throw Error(QObject::tr("Searched whole file, no marker found")); + if (!in->seek(pos)) { + throw Error(QObject::tr("Could not seek to %1 in file %2: %3").arg(QString::number(pos), + in->fileName(), in->errorString())); + } + const quint64 num = static_cast<quint64>(retrieveInt64(in)); + if (num == magicCookie) { + in->seek(oldPos); + return pos; + } + searched += 1; + } + throw Error(QObject::tr("No marker found, stopped after %1 bytes.").arg(QString::number(MAX_SEARCH))); + } catch (const Error& err) { + in->seek(oldPos); + throw err; + } catch (...) { + in->seek(oldPos); + throw Error(QObject::tr("No marker found, unknown exception caught.")); + } + return -1; // never reached +} + +/*! + Creates an archive providing the data in \a path. + \a path can be a path to a file or to a directory. If it's a file, it's considered to be + pre-zipped and gets delivered as it is. If it's a directory, it gets zipped by Archive. + */ +Archive::Archive(const QString &path) + : m_device(0), + m_isTempFile(false), + m_path(path), + m_name(QFileInfo(path).fileName().toUtf8()) +{ +} + +Archive::Archive(const QByteArray &identifier, const QByteArray &data) + : m_device(0), + m_isTempFile(true), + m_path(generateTemporaryFileName()), + m_name(identifier) +{ + QFile file(m_path); + file.open(QIODevice::WriteOnly); + file.write(data); +} + +/*! + Creates an archive identified by \a identifier providing a data \a segment within a \a device. + */ +Archive::Archive(const QByteArray &identifier, const QSharedPointer<QFile> &device, const Range<qint64> &segment) + : m_device(device), + m_segment(segment), + m_isTempFile(false), + m_name(identifier) +{ +} + +Archive::~Archive() +{ + if (isOpen()) + close(); + if (m_isTempFile) + QFile::remove(m_path); +} + +/*! + Copies the archives contents to the path \a name. + If the archive is a zipped directory, \a name is treated as a directory. The archive gets extracted there. + + If the archive is a plain file and \a name an existing directory, it gets created + with it's name. Otherwise it gets saved as \a name. + Note that if a file with the \a name already exists, copy() return false (i.e. Archive will not overwrite it). + */ +bool Archive::copy(const QString &name) +{ + const QFileInfo fileInfo(name); + if (isZippedDirectory()) { + if (fileInfo.exists() && !fileInfo.isDir()) + return false; + + errno = 0; + const QString absoluteFilePath = fileInfo.absoluteFilePath(); + if (!fileInfo.exists() && !QDir().mkpath(absoluteFilePath)) { + setErrorString(tr("Could not create %1: %2").arg(name, QString::fromLocal8Bit(strerror(errno)))); + return false; + } + + if (isOpen()) + close(); + open(QIODevice::ReadOnly); + + UnzipJob job; + job.setInputDevice(this); + job.setOutputPath(absoluteFilePath); + job.run(); + } else { + if (isOpen()) + close(); + open(QIODevice::ReadOnly); + + QFile target(fileInfo.isDir() ? QString::fromLatin1("%1/%2").arg(name) + .arg(QString::fromUtf8(m_name.data(), m_name.count())) : name); + if (target.exists()) + return false; + target.open(QIODevice::WriteOnly); + blockingCopy(this, &target, size()); + } + close(); + return true; +} + +/*! + \reimp + */ +bool Archive::seek(qint64 pos) +{ + if (m_inputFile.isOpen()) + return m_inputFile.seek(pos) && QIODevice::seek(pos); + return QIODevice::seek(pos); +} + +/*! + Returns true, if this archive was created by zipping a directory. + */ +bool Archive::isZippedDirectory() const +{ + if (m_device == 0) { + // easy, just check whether it's a dir + return QFileInfo(m_path).isDir(); + } + + // more complex, check the zip header magic + Archive* const arch = const_cast<Archive*> (this); + + const bool notOpened = !isOpen(); + if (notOpened) + arch->open(QIODevice::ReadOnly); + const qint64 p = pos(); + arch->seek(0); + + const QByteArray ba = arch->read(4); + const bool result = ba == QByteArray("\x50\x4b\x03\04"); + + arch->seek(p); + if (notOpened) + arch->close(); + return result; +} + +QByteArray Archive::name() const +{ + return m_name; +} + +void Archive::setName(const QByteArray &name) +{ + m_name = name; +} + +/*! + \reimpl + */ +void Archive::close() +{ + m_inputFile.close(); + if (QFileInfo(m_path).isDir()) + m_inputFile.remove(); + QIODevice::close(); +} + +/*! + \reimp + */ +bool Archive::open(OpenMode mode) +{ + if (isOpen()) + return false; + + const bool writeOnly = (mode & QIODevice::WriteOnly) != QIODevice::NotOpen; + const bool append = (mode & QIODevice::Append) != QIODevice::NotOpen; + + // no write support + if (writeOnly || append) + return false; + + if (m_device != 0) + return QIODevice::open(mode); + + const QFileInfo fi(m_path); + if (fi.isFile()) { + m_inputFile.setFileName(m_path); + if (!m_inputFile.open(mode)) { + setErrorString(tr("Could not open archive file %1 for reading.").arg(m_path)); + return false; + } + setOpenMode(mode); + return true; + } + + if (fi.isDir()) { + if (m_inputFile.fileName().isEmpty() || !m_inputFile.exists()) { + if (!createZippedFile()) + return false; + } + Q_ASSERT(!m_inputFile.fileName().isEmpty()); + if (!m_inputFile.open(mode)) + return false; + setOpenMode(mode); + return true; + } + + setErrorString(tr("Could not create archive from %1: Not a file.").arg(m_path)); + return false; +} + +bool Archive::createZippedFile() +{ + QTemporaryFile file; + file.setAutoRemove(false); + if (!file.open()) + return false; + + m_inputFile.setFileName(file.fileName()); + file.close(); + m_inputFile.open(QIODevice::ReadWrite); + try { + Lib7z::createArchive(&m_inputFile, QStringList() << m_path); + } catch(Lib7z::SevenZipException &e) { + m_inputFile.close(); + setErrorString(e.message()); + return false; + } + + if (!Lib7z::isSupportedArchive(&m_inputFile)) { + m_inputFile.close(); + setErrorString(tr("Error while packing directory at %1").arg(m_path)); + return false; + } + m_inputFile.close(); + return true; +} + +/*! + \reimp + */ +qint64 Archive::size() const +{ + // if we got a device, we just pass the length of the segment + if (m_device != 0) + return m_segment.length(); + + const QFileInfo fi(m_path); + // if we got a regular file, we pass the size of the file + if (fi.isFile()) + return fi.size(); + + if (fi.isDir()) { + if (m_inputFile.fileName().isEmpty() || !m_inputFile.exists()) { + if (!const_cast< Archive* >(this)->createZippedFile()) { + throw Error(QObject::tr("Cannot create zipped file for path %1: %2").arg(m_path, + errorString())); + } + } + Q_ASSERT(!m_inputFile.fileName().isEmpty()); + return m_inputFile.size(); + } + return 0; +} + +/*! + \reimp + */ +qint64 Archive::readData(char* data, qint64 maxSize) +{ + if (m_device == 0) + return m_inputFile.read(data, maxSize); + + const qint64 p = m_device->pos(); + m_device->seek(m_segment.start() + pos()); + const qint64 amountRead = m_device->read(data, qMin<quint64>(maxSize, m_segment.length() - pos())); + m_device->seek(p); + return amountRead; +} + +/*! + \reimp + */ +qint64 Archive::writeData(const char* data, qint64 maxSize) +{ + Q_UNUSED(data); + Q_UNUSED(maxSize); + // should never be called, as we're read only + return -1; +} + +QByteArray Component::name() const +{ + return m_name; +} + +void Component::setName(const QByteArray &ba) +{ + m_name = ba; +} + +Range<qint64> Component::binarySegment() const +{ + return m_binarySegment; +} + +void Component::setBinarySegment(const Range<qint64> &r) +{ + m_binarySegment = r; +} + +Component Component::readFromIndexEntry(const QSharedPointer<QFile> &in, qint64 offset) +{ + Component c; + c.m_name = retrieveByteArray(in.data()); + c.m_binarySegment = retrieveInt64Range(in.data()).moved(offset); + + c.readData(in, offset); + + return c; +} + +void Component::writeIndexEntry(QIODevice *out, qint64 positionOffset) const +{ + appendByteArray(out, m_name); + const Range<qint64> relative = m_binarySegment.moved(positionOffset); + appendInt64(out, binarySegment().start()); + appendInt64(out, binarySegment().length()); +} + +void Component::writeData(QIODevice *out, qint64 offset) const +{ + const qint64 dataBegin = out->pos() + offset; + + appendInt64(out, m_archives.count()); + + qint64 start = out->pos() + offset; + + // Why 16 + 16? This is 24, not 32??? + const int foo = 3 * sizeof(qint64); + // add 16 + 16 + number of name characters for each archive (the size of the table) + foreach (const QSharedPointer<Archive> &archive, m_archives) + start += foo + archive->name().count(); + + QList<qint64> starts; + foreach (const QSharedPointer<Archive> &archive, m_archives) { + appendByteArray(out, archive->name()); + starts.push_back(start); + appendInt64Range(out, Range<qint64>::fromStartAndLength(start, archive->size())); + start += archive->size(); + } + + foreach (const QSharedPointer<Archive> &archive, m_archives) { + if (!archive->open(QIODevice::ReadOnly)) { + throw Error(tr("Could not open archive %1: %2").arg(QLatin1String(archive->name()), + archive->errorString())); + } + + const qint64 expectedStart = starts.takeFirst(); + const qint64 actualStart = out->pos() + offset; + Q_UNUSED(expectedStart); + Q_UNUSED(actualStart); + Q_ASSERT(expectedStart == actualStart); + blockingCopy(archive.data(), out, archive->size()); + } + + m_binarySegment = Range<qint64>::fromStartAndEnd(dataBegin, out->pos() + offset); +} + +void Component::readData(const QSharedPointer<QFile> &in, qint64 offset) +{ + const qint64 pos = in->pos(); + + in->seek(m_binarySegment.start()); + const qint64 count = retrieveInt64(in.data()); + + QVector<QByteArray> names; + QVector<Range<qint64> > ranges; + for (int i = 0; i < count; ++i) { + names.push_back(retrieveByteArray(in.data())); + ranges.push_back(retrieveInt64Range(in.data()).moved(offset)); + } + + for (int i = 0; i < ranges.count(); ++i) + m_archives.append(QSharedPointer<Archive>(new Archive(names.at(i), in, ranges.at(i)))); + + in->seek(pos); +} + +QString Component::dataDirectory() const +{ + return m_dataDirectory; +} + +void Component::setDataDirectory(const QString &path) +{ + m_dataDirectory = path; +} + +bool Component::operator<(const Component& other) const +{ + if (m_name != other.name()) + return m_name < other.m_name; + return m_binarySegment < other.m_binarySegment; +} + +bool Component::operator==(const Component& other) const +{ + return m_name == other.m_name && m_binarySegment == other.m_binarySegment; +} + +/*! + Destroys this component. + */ +Component::~Component() +{ +} + +/*! + Appends \a archive to this component. The component takes ownership of \a archive. + */ +void Component::appendArchive(const QSharedPointer<Archive>& archive) +{ + Q_ASSERT(archive); + archive->setParent(0); + m_archives.push_back(archive); +} + +/*! + Returns the archives associated with this component. + */ +QVector<QSharedPointer<Archive> > Component::archives() const +{ + return m_archives; +} + +QSharedPointer<Archive> Component::archiveByName(const QByteArray &name) const +{ + foreach (const QSharedPointer<Archive>& i, m_archives) { + if (i->name() == name) + return i; + } + return QSharedPointer<Archive>(); +} + + +// -- ComponentIndex + +ComponentIndex::ComponentIndex() +{ +} + +ComponentIndex ComponentIndex::read(const QSharedPointer<QFile> &dev, qint64 offset) +{ + ComponentIndex result; + const qint64 size = retrieveInt64(dev.data()); + for (int i = 0; i < size; ++i) + result.insertComponent(Component::readFromIndexEntry(dev, offset)); + retrieveInt64(dev.data()); + return result; +} + +void ComponentIndex::writeIndex(QIODevice *out, qint64 offset) const +{ + // Q: why do we write the size twice? + // A: for us to be able to read it beginning from the end of the file as well + appendInt64(out, componentCount()); + foreach (const Component& i, components()) + i.writeIndexEntry(out, offset); + appendInt64(out, componentCount()); +} + +void ComponentIndex::writeComponentData(QIODevice *out, qint64 offset) const +{ + appendInt64(out, componentCount()); + + foreach (const Component &component, m_components) + component.writeData(out, offset); +} + +Component ComponentIndex::componentByName(const QByteArray &id) const +{ + return m_components.value(id); +} + +void ComponentIndex::insertComponent(const Component& c) +{ + m_components.insert(c.name(), c); +} + +void ComponentIndex::removeComponent(const QByteArray &name) +{ + m_components.remove(name); +} + +QVector<Component> ComponentIndex::components() const +{ + return m_components.values().toVector(); +} + +int ComponentIndex::componentCount() const +{ + return m_components.size(); +} + + +static QVector<QByteArray> sResourceVec; +/*! + \internal + Registers the resource found at \a segment within \a file into the Qt resource system. + */ +static const uchar* addResourceFromBinary(QFile* file, const Range<qint64> &segment) +{ + if (segment.length() <= 0) + return 0; + + if (!file->seek(segment.start())) { + throw Error(QObject::tr("Could not seek to in-binary resource. (offset: %1, length: %2)") + .arg(QString::number(segment.start()), QString::number(segment.length()))); + } + sResourceVec.append(retrieveData(file, segment.length())); + + if (!QResource::registerResource((const uchar*)(sResourceVec.last().constData()), + QLatin1String(":/metadata"))) { + throw Error(QObject::tr("Could not register in-binary resource.")); + } + return (const uchar*)(sResourceVec.last().constData()); +} + + +// -- BinaryContentPrivate + +BinaryContentPrivate::BinaryContentPrivate(const QString &path) + : m_magicMarker(0) + , m_dataBlockStart(0) + , m_appBinary(new QFile(path)) + , m_binaryDataFile(0) + , m_binaryFormatEngineHandler(m_componentIndex) +{ +} + +BinaryContentPrivate::BinaryContentPrivate(const BinaryContentPrivate &other) + : QSharedData(other) + , m_magicMarker(other.m_magicMarker) + , m_dataBlockStart(other.m_dataBlockStart) + , m_appBinary(other.m_appBinary) + , m_binaryDataFile(other.m_binaryDataFile) + , m_performedOperations(other.m_performedOperations) + , m_performedOperationsData(other.m_performedOperationsData) + , m_resourceMappings(other.m_resourceMappings) + , m_metadataResourceSegments(other.m_metadataResourceSegments) + , m_componentIndex(other.m_componentIndex) + , m_binaryFormatEngineHandler(other.m_binaryFormatEngineHandler) +{ +} + +BinaryContentPrivate::~BinaryContentPrivate() +{ + foreach (const uchar *rccData, m_resourceMappings) + QResource::unregisterResource(rccData); + sResourceVec.clear(); + m_resourceMappings.clear(); +} + + +// -- BinaryContent + +BinaryContent::BinaryContent(const QString &path) + : d(new BinaryContentPrivate(path)) +{ +} + +BinaryContent::~BinaryContent() +{ +} + +/*! + Reads binary content stored in the current application binary. Maps the embedded resources into memory + and instantiates performed operations if available. +*/ +BinaryContent BinaryContent::readAndRegisterFromApplicationFile() +{ + BinaryContent c = BinaryContent::readFromApplicationFile(); + c.registerEmbeddedQResources(); + c.registerPerformedOperations(); + return c; +} + +/*! + Reads binary content stored in the passed application binary. Maps the embedded resources into memory + and instantiates performed operations if available. +*/ +BinaryContent BinaryContent::readAndRegisterFromBinary(const QString &path) +{ + BinaryContent c = BinaryContent::readFromBinary(path); + c.registerEmbeddedQResources(); + c.registerPerformedOperations(); + return c; +} + +/*! + Reads binary content stored in the current application binary. +*/ +BinaryContent BinaryContent::readFromApplicationFile() +{ + return BinaryContent::readFromBinary(QCoreApplication::applicationFilePath());; +} + +/*! + * \class QInstaller::BinaryContent + * + * BinaryContent handles binary information embedded into executables. + * Qt resources as well as component information can be stored. + * + * Explanation of the binary blob at the end of the installer or separate data file: + * + * \verbatim + * Meta data segment 0 + * Meta data segment ... + * Meta data segment n + * ------------------------------------------------------ + * Component data segment 0 + * Component data segment .. + * Component data segment n + * ------------------------------------------------------ + * Component index segment + * ------------------------------------------------------ + * quint64 offset of component index segment + * quint64 length of component index segment + * ------------------------------------------------------ + * qint64 offset of meta data segment 0 + * qint64 length of meta data segment 0 + * qint64 offset of meta data segment .. + * qint64 length of meta data segment .. + * qint64 offset of meta data segment n + * qint64 length of meta data segment n + * ------------------------------------------------------ + * operations start offest + * operations end + * quint64 embedded resource count + * quint64 data block size + * quint64 Magic marker + * quint64 Magic cookie (0xc2 0x63 0x0a 0x1c 0x99 0xd6 0x68 0xf8) + * <eof> + * + * All offsets are addresses relative to the end of the file. + * + * Meta data segments are stored as Qt resources, which must be "mounted" + * via QResource::registerResource() + * + * Component index segment: + * quint64 number of index entries + * QString identifier of component 0 + * quint64 offset of component data segment 0 + * quint64 length of component data segment 0 + * QString identifier of component .. + * quint64 offset of component data segment .. + * quint64 length of component data segment .. + * QString identifier of component n + * quint64 offset of component data segment n + * quint64 length of component data segment n + * quint64 number of index entries + * + * Component data segment: + * quint64 number of archives in this component + * QString name of archive 0 + * quint64 offset of archive 0 + * quint64 length of archive 0 + * QString name of archive .. + * quint64 offset of archive .. + * quint64 length of archive .. + * QString name of archive n + * quint64 offset of archive n + * quint64 length of archive n + * Archive 0 + * Archive .. + * Archive n + * \endverbatim + */ + +BinaryContent BinaryContent::readFromBinary(const QString &path) +{ + BinaryContent c(path); + if (!c.d->m_appBinary->open(QIODevice::ReadOnly)) + throw Error(QObject::tr("Could not open binary %1: %2").arg(path, c.d->m_appBinary->errorString())); + + // check for supported binary, will throw if we can't find a marker + const BinaryLayout layout = readBinaryLayout(c.d->m_appBinary.data(), + findMagicCookie(c.d->m_appBinary.data(), QInstaller::MagicCookie)); + + bool retry = true; + if (layout.magicMarker != MagicInstallerMarker) { + QString binaryDataPath = path; + QFileInfo fi(path + QLatin1String("/../../..")); + if (QFileInfo(fi.absoluteFilePath()).isBundle()) + binaryDataPath = fi.absoluteFilePath(); + fi.setFile(binaryDataPath); + + c.d->m_binaryDataFile = QSharedPointer<QFile>(new QFile(fi.absolutePath() + QLatin1Char('/') + + fi.baseName() + QLatin1String(".dat"))); + if (c.d->m_binaryDataFile->exists() && c.d->m_binaryDataFile->open(QIODevice::ReadOnly)) { + // check for supported binary data file, will throw if we can't find a marker + try { + const qint64 cookiePos = findMagicCookie(c.d->m_binaryDataFile.data(), + QInstaller::MagicCookieDat); + const BinaryLayout binaryLayout = readBinaryLayout(c.d->m_binaryDataFile.data(), cookiePos); + readBinaryData(c, c.d->m_binaryDataFile, binaryLayout); + retry = false; + } catch (const Error &error) { + // this seems to be an unsupported dat file, try to read from original binary + c.d->m_binaryDataFile.clear(); + qDebug() << error.message(); + } + } else { + c.d->m_binaryDataFile.clear(); + } + } + + if (retry) + readBinaryData(c, c.d->m_appBinary, layout); + + return c; +} + +/* static */ +BinaryLayout BinaryContent::readBinaryLayout(QIODevice *const file, qint64 cookiePos) +{ + const qint64 indexSize = 5 * sizeof(qint64); + if (!file->seek(cookiePos - indexSize)) + throw Error(QObject::tr("Could not seek to binary layout section.")); + + BinaryLayout layout; + layout.operationsStart = retrieveInt64(file); + layout.operationsEnd = retrieveInt64(file); + layout.resourceCount = retrieveInt64(file); + layout.dataBlockSize = retrieveInt64(file); + layout.magicMarker = retrieveInt64(file); + layout.magicCookie = retrieveInt64(file); + layout.indexSize = indexSize + sizeof(qint64); + layout.endOfData = file->pos(); + + qDebug() << "Operations start:" << layout.operationsStart; + qDebug() << "Operations end:" << layout.operationsEnd; + qDebug() << "Resource count:" << layout.resourceCount; + qDebug() << "Data block size:" << layout.dataBlockSize; + qDebug() << "Magic marker:" << layout.magicMarker; + qDebug() << "Magic cookie:" << layout.magicCookie; + qDebug() << "Index size:" << layout.indexSize; + qDebug() << "End of data:" << layout.endOfData; + + const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64); + const qint64 dataBlockStart = layout.endOfData - layout.dataBlockSize; + for (int i = 0; i < layout.resourceCount; ++i) { + if (!file->seek(layout.endOfData - layout.indexSize - resourceOffsetAndLengtSize * (i + 1))) + throw Error(QObject::tr("Could not seek to metadata index.")); + + const qint64 metadataResourceOffset = retrieveInt64(file); + const qint64 metadataResourceLength = retrieveInt64(file); + layout.metadataResourceSegments.append(Range<qint64>::fromStartAndLength(metadataResourceOffset + + dataBlockStart, metadataResourceLength)); + } + + return layout; +} + +/* static */ +void BinaryContent::readBinaryData(BinaryContent &content, const QSharedPointer<QFile> &file, + const BinaryLayout &layout) +{ + content.d->m_magicMarker = layout.magicMarker; + content.d->m_metadataResourceSegments = layout.metadataResourceSegments; + + const qint64 dataBlockStart = layout.endOfData - layout.dataBlockSize; + const qint64 operationsStart = layout.operationsStart + dataBlockStart; + if (!file->seek(operationsStart)) + throw Error(QObject::tr("Could not seek to operation list.")); + + const qint64 operationsCount = retrieveInt64(file.data()); + qDebug() << "Number of operations:" << operationsCount; + + for (int i = 0; i < operationsCount; ++i) { + const QString name = retrieveString(file.data()); + const QString data = retrieveString(file.data()); + content.d->m_performedOperationsData.append(qMakePair(name, data)); + } + + // seek to the position of the component index + const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64); + const qint64 resourceSectionSize = resourceOffsetAndLengtSize * layout.resourceCount; + if (!file->seek(layout.endOfData - layout.indexSize - resourceSectionSize - resourceOffsetAndLengtSize)) + throw Error(QObject::tr("Could not seek to component index information.")); + + const qint64 compIndexStart = retrieveInt64(file.data()) + dataBlockStart; + if (!file->seek(compIndexStart)) + throw Error(QObject::tr("Could not seek to component index.")); + + content.d->m_componentIndex = QInstallerCreator::ComponentIndex::read(file, dataBlockStart); + content.d->m_binaryFormatEngineHandler.setComponentIndex(content.d->m_componentIndex); + + if (isVerbose()) { + const QVector<QInstallerCreator::Component> components = content.d->m_componentIndex.components(); + qDebug() << "Number of components loaded:" << components.count(); + foreach (const QInstallerCreator::Component &component, components) { + const QVector<QSharedPointer<Archive> > archives = component.archives(); + qDebug() << component.name().data() << "loaded..."; + QStringList archivesWithSize; + foreach (const QSharedPointer<Archive> &archive, archives) { + QString archiveWithSize(QLatin1String("%1 - %2 Bytes")); + archiveWithSize = archiveWithSize.arg(QString::fromLocal8Bit(archive->name()), + QString::number(archive->size())); + archivesWithSize.append(archiveWithSize); + } + if (!archivesWithSize.isEmpty()) { + qDebug() << " - " << archives.count() << "archives: " + << qPrintable(archivesWithSize.join(QLatin1String("; "))); + } + } + } +} + +/*! + Registers already performed operations. +*/ +int BinaryContent::registerPerformedOperations() +{ + if (d->m_performedOperations.count() > 0) + return d->m_performedOperations.count(); + + for (int i = 0; i < d->m_performedOperationsData.count(); ++ i) { + const QPair<QString, QString> opPair = d->m_performedOperationsData.at(i); + QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(opPair.first)); + Q_ASSERT_X(!op.isNull(), __FUNCTION__, QString::fromLatin1("Invalid operation name: %1.") + .arg(opPair.first).toLatin1()); + + if (!op->fromXml(opPair.second)) { + qWarning() << "Failed to load XML for operation:" << opPair.first; + continue; + } + d->m_performedOperations.append(op.take()); + } + return d->m_performedOperations.count(); +} + +/*! + Returns the operations performed during installation. Returns an empty list if no operations are + instantiated, performed or the binary is the installer application. +*/ +OperationList BinaryContent::performedOperations() const +{ + return d->m_performedOperations; +} + +/*! + Returns the magic marker found in the binary. Returns 0 if no marker has been found. +*/ +qint64 BinaryContent::magicMarker() const +{ + return d->m_magicMarker; +} + +/*! + Registers the Qt resources embedded in this binary. + */ +int BinaryContent::registerEmbeddedQResources() +{ + if (d->m_resourceMappings.count() > 0) + return d->m_resourceMappings.count(); + + const bool hasBinaryDataFile = !d->m_binaryDataFile.isNull(); + QFile *const data = hasBinaryDataFile ? d->m_binaryDataFile.data() : d->m_appBinary.data(); + if (!data->isOpen() && !data->open(QIODevice::ReadOnly)) { + throw Error(QObject::tr("Could not open binary %1: %2").arg(data->fileName(), + data->errorString())); + } + + foreach (const Range<qint64> &i, d->m_metadataResourceSegments) + d->m_resourceMappings.append(addResourceFromBinary(data, i)); + + d->m_appBinary.clear(); + if (hasBinaryDataFile) + d->m_binaryDataFile.clear(); + + return d->m_resourceMappings.count(); +} + +/*! + Returns the binary component index as read from the file. +*/ +QInstallerCreator::ComponentIndex BinaryContent::componentIndex() const +{ + return d->m_componentIndex; +} diff --git a/installerbuilder/libinstaller/binaryformat.h b/installerbuilder/libinstaller/binaryformat.h new file mode 100644 index 000000000..e2e1f6a0f --- /dev/null +++ b/installerbuilder/libinstaller/binaryformat.h @@ -0,0 +1,248 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef BINARYFORMAT_H +#define BINARYFORMAT_H + +#include "binaryformatenginehandler.h" +#include "range.h" +#include "qinstallerglobal.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QFile> +#include <QtCore/QHash> +#include <QtCore/QStack> +#include <QtCore/QVector> +#include <QtCore/QSharedPointer> + +namespace QInstaller { + static const qint64 MagicInstallerMarker = 0x12023233UL; + static const qint64 MagicUninstallerMarker = 0x12023234UL; + + static const qint64 MagicUpdaterMarker = 0x12023235UL; + static const qint64 MagicPackageManagerMarker = 0x12023236UL; + + // this cookie is put at the end of the file to determine whether we have data + static const quint64 MagicCookie = 0xc2630a1c99d668f8LL; + static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL; + + qint64 INSTALLER_EXPORT findMagicCookie(QFile *file, quint64 magicCookie = MagicCookie); + void INSTALLER_EXPORT appendFileData(QIODevice *out, QIODevice *in); + void INSTALLER_EXPORT appendInt64(QIODevice *out, qint64 n); + void INSTALLER_EXPORT appendInt64Range(QIODevice *out, const Range<qint64> &r); + void INSTALLER_EXPORT appendData(QIODevice *out, QIODevice *in, qint64 size); + void INSTALLER_EXPORT appendByteArray(QIODevice *out, const QByteArray &ba); + void INSTALLER_EXPORT appendString(QIODevice *out, const QString &str); + void INSTALLER_EXPORT appendStringList(QIODevice *out, const QStringList &list); + void INSTALLER_EXPORT appendDictionary(QIODevice *out, const QHash<QString,QString> &dict); + qint64 INSTALLER_EXPORT appendCompressedData(QIODevice *out, QIODevice *in, qint64 size); + + void INSTALLER_EXPORT retrieveFileData(QIODevice *out, QIODevice *in); + qint64 INSTALLER_EXPORT retrieveInt64(QIODevice *in); + Range<qint64> INSTALLER_EXPORT retrieveInt64Range(QIODevice *in); + QByteArray INSTALLER_EXPORT retrieveByteArray(QIODevice *in); + QString INSTALLER_EXPORT retrieveString(QIODevice *in); + QStringList INSTALLER_EXPORT retrieveStringList(QIODevice *in); + QHash<QString,QString> INSTALLER_EXPORT retrieveDictionary(QIODevice *in); + QByteArray INSTALLER_EXPORT retrieveData(QIODevice *in, qint64 size); + QByteArray INSTALLER_EXPORT retrieveCompressedData(QIODevice *in, qint64 size); +} + +namespace QInstallerCreator { +class Component; + +class INSTALLER_EXPORT Archive : public QIODevice +{ + Q_OBJECT +public: + explicit Archive(const QString &path); + Archive(const QByteArray &name, const QByteArray &data); + Archive(const QByteArray &name, const QSharedPointer<QFile> &device, const Range<qint64> &segment); + ~Archive(); + + bool open(OpenMode mode); + void close(); + + bool seek(qint64 pos); + qint64 size() const; + + bool createZippedFile(); + bool isZippedDirectory() const; + bool copy(const QString &name); + + QByteArray name() const; + void setName(const QByteArray &name); + +protected: + qint64 readData(char *data, qint64 maxSize); + qint64 writeData(const char *data, qint64 maxSize); + + Range< qint64 > binarySegment() const; + +private: + //used when when reading from the installer + QSharedPointer<QFile> m_device; + const Range<qint64> m_segment; + + //used when creating the installer, archive input file + QFile m_inputFile; + const bool m_isTempFile; + const QString m_path; + QByteArray m_name; +}; + +class INSTALLER_EXPORT Component +{ + Q_DECLARE_TR_FUNCTIONS(Component) + +public: + virtual ~Component(); + + static Component readFromIndexEntry(const QSharedPointer<QFile> &dev, qint64 offset); + void writeIndexEntry(QIODevice *dev, qint64 offset) const; + + void writeData(QIODevice *dev, qint64 positionOffset) const; + void readData(const QSharedPointer<QFile> &dev, qint64 offset); + + QByteArray name() const; + void setName(const QByteArray &ba); + + QString dataDirectory() const; + void setDataDirectory(const QString &path); + + Range<qint64> binarySegment() const; + void setBinarySegment(const Range<qint64> &r); + + void appendArchive(const QSharedPointer<Archive> &archive); + QSharedPointer<Archive> archiveByName(const QByteArray &name) const; + QVector< QSharedPointer<Archive> > archives() const; + + bool operator<(const Component &other) const; + bool operator==(const Component &other) const; + +private: + QByteArray m_name; + QVector<QSharedPointer<Archive> > m_archives; + mutable Range<qint64> m_binarySegment; + QString m_dataDirectory; +}; + + +class INSTALLER_EXPORT ComponentIndex +{ +public: + ComponentIndex(); + static ComponentIndex read(const QSharedPointer<QFile> &dev, qint64 offset); + void writeIndex(QIODevice *dev, qint64 offset) const; + void writeComponentData(QIODevice *dev, qint64 offset) const; + Component componentByName(const QByteArray &name) const; + void insertComponent(const Component &name); + void removeComponent(const QByteArray &name); + QVector<Component> components() const; + int componentCount() const; + +private: + QHash<QByteArray, Component> m_components; +}; +} + +namespace QInstaller { + +struct BinaryLayout +{ + QVector<Range<qint64> > metadataResourceSegments; + qint64 operationsStart; + qint64 operationsEnd; + qint64 resourceCount; + qint64 dataBlockSize; + qint64 magicMarker; + quint64 magicCookie; + qint64 indexSize; + qint64 endOfData; +}; + +class BinaryContentPrivate : public QSharedData +{ +public: + BinaryContentPrivate(const QString &path); + BinaryContentPrivate(const BinaryContentPrivate &other); + ~BinaryContentPrivate(); + + qint64 m_magicMarker; + qint64 m_dataBlockStart; + + QSharedPointer<QFile> m_appBinary; + QSharedPointer<QFile> m_binaryDataFile; + + QList<Operation *> m_performedOperations; + QList<QPair<QString, QString> > m_performedOperationsData; + + QVector<const uchar *> m_resourceMappings; + QVector<Range<qint64> > m_metadataResourceSegments; + + QInstallerCreator::ComponentIndex m_componentIndex; + QInstallerCreator::BinaryFormatEngineHandler m_binaryFormatEngineHandler; +}; + +class INSTALLER_EXPORT BinaryContent +{ + explicit BinaryContent(const QString &path); + +public: + virtual ~BinaryContent(); + + static BinaryContent readAndRegisterFromApplicationFile(); + static BinaryContent readAndRegisterFromBinary(const QString &path); + + static BinaryContent readFromApplicationFile(); + static BinaryContent readFromBinary(const QString &path); + + static BinaryLayout readBinaryLayout(QIODevice *const file, qint64 cookiePos); + + int registerPerformedOperations(); + OperationList performedOperations() const; + + qint64 magicMarker() const; + int registerEmbeddedQResources(); + QInstallerCreator::ComponentIndex componentIndex() const; + +private: + static void readBinaryData(BinaryContent &content, const QSharedPointer<QFile> &file, + const BinaryLayout &layout); + +private: + QSharedDataPointer<BinaryContentPrivate> d; +}; + +} + +#endif // BINARYFORMAT_H diff --git a/installerbuilder/libinstaller/binaryformatengine.cpp b/installerbuilder/libinstaller/binaryformatengine.cpp new file mode 100644 index 000000000..05affaf4b --- /dev/null +++ b/installerbuilder/libinstaller/binaryformatengine.cpp @@ -0,0 +1,297 @@ +/************************************************************************** +** +** 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 "binaryformatengine.h" + +using namespace QInstallerCreator; + +namespace { + +class StringListIterator : public QAbstractFileEngineIterator +{ +public: + StringListIterator( const QStringList &list, QDir::Filters filters, const QStringList &nameFilters) + : QAbstractFileEngineIterator(filters, nameFilters), + list(list), + index(-1) + { + } + + bool hasNext() const + { + return index < list.size() - 1; + } + + QString next() + { + if(!hasNext()) + return QString(); + ++index; + return currentFilePath(); + } + + QString currentFileName() const + { + return index < 0 ? QString() : list[index]; + } + +private: + const QStringList list; + int index; +}; + +} // anon namespace + +BinaryFormatEngine::BinaryFormatEngine(const ComponentIndex &index, const QString &fileName) + : m_index(index) + , m_hasComponent(false) + , m_hasArchive(false) + , m_archive(0) +{ + setArchive(fileName); +} + +BinaryFormatEngine::~BinaryFormatEngine() +{ +} + +void BinaryFormatEngine::setArchive(const QString &file) +{ + m_fileNamePath = file; + + static const QChar sep = QLatin1Char('/'); + static const QString prefix = QLatin1String("installer://"); + Q_ASSERT(file.toLower().startsWith(prefix)); + + // cut the prefix + QString path = file.mid(prefix.length()); + while (path.endsWith(sep)) + path.chop(1); + + QString arch; + const QString comp = path.section(sep, 0, 0); + m_hasComponent = !comp.isEmpty(); + m_hasArchive = path.contains(sep); + if (m_hasArchive) + arch = path.section(sep, 1, 1); + + m_component = m_index.componentByName(comp.toUtf8()); + m_archive = m_component.archiveByName(arch.toUtf8()); +} + +/** + * \reimp + */ +void BinaryFormatEngine::setFileName(const QString &file) +{ + setArchive(file); +} + +/** + * \reimp + */ +bool BinaryFormatEngine::close() +{ + if (m_archive == 0) + return false; + + const bool result = m_archive->isOpen(); + m_archive->close(); + return result; +} + +/** + * \reimp + */ +bool BinaryFormatEngine::open(QIODevice::OpenMode mode) +{ + return m_archive == 0 ? false : m_archive->open(mode); +} + +/** + * \reimp + */ +qint64 BinaryFormatEngine::pos() const +{ + return m_archive == 0 ? 0 : m_archive->pos(); +} + +/** + * \reimp + */ +qint64 BinaryFormatEngine::read(char *data, qint64 maxlen) +{ + return m_archive == 0 ? -1 : m_archive->read(data, maxlen); +} + +/** + * \reimp + */ +bool BinaryFormatEngine::seek(qint64 offset) +{ + return m_archive == 0 ? false : m_archive->seek(offset); +} + +/** + * \reimp + */ +QString BinaryFormatEngine::fileName(FileName file) const +{ + switch(file) { + case BaseName: + return m_fileNamePath.section(QChar::fromLatin1('/'), -1, -1, QString::SectionSkipEmpty); + case PathName: + case AbsolutePathName: + case CanonicalPathName: + return m_fileNamePath.section(QChar::fromLatin1('/'), 0, -2, QString::SectionSkipEmpty); + case DefaultName: + case AbsoluteName: + case CanonicalName: + return m_fileNamePath; + default: + return QString(); + } +} + +/** + * \reimp + */ +bool BinaryFormatEngine::copy(const QString &newName) +{ + if (QFile::exists(newName)) + return false; + + QFile target(newName); + if (!target.open(QIODevice::WriteOnly)) + return false; + + qint64 bytesLeft = size(); + if (!open(QIODevice::ReadOnly)) + return false; + + char data[4096]; + while(bytesLeft > 0) { + const qint64 len = qMin<qint64>(bytesLeft, 4096); + const qint64 bytesRead = read(data, len); + if (bytesRead != len) { + close(); + return false; + } + const qint64 bytesWritten = target.write(data, len); + if (bytesWritten != len) { + close(); + return false; + } + bytesLeft -= len; + } + close(); + + return true; +} + +/** + * \reimp + */ +QAbstractFileEngine::FileFlags BinaryFormatEngine::fileFlags(FileFlags type) const +{ + FileFlags result; + if ((type & FileType) && m_archive != 0) + result |= FileType; + if ((type & DirectoryType) && !m_hasArchive) + result |= DirectoryType; + if ((type & ExistsFlag) && m_hasArchive && m_archive != 0) + result |= ExistsFlag; + if ((type & ExistsFlag) && !m_hasArchive && !m_component.name().isEmpty()) + result |= ExistsFlag; + + return result; +} + +/** + * \reimp + */ +QAbstractFileEngineIterator *BinaryFormatEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +{ + const QStringList entries = entryList(filters, filterNames); + return new StringListIterator(entries, filters, filterNames); +} + +/** + * \reimp + */ +QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + if (m_hasArchive) + return QStringList(); + + QStringList result; + + if (m_hasComponent && (filters & QDir::Files)) { + const QVector< QSharedPointer<Archive> > archives = m_component.archives(); + foreach (const QSharedPointer<Archive> &i, archives) + result.push_back(QString::fromUtf8(i->name())); + } + else if (!m_hasComponent && (filters & QDir::Dirs)) { + const QVector<Component> components = m_index.components(); + foreach (const Component &i, components) + result.push_back(QString::fromUtf8(i.name())); + } + + if (filterNames.isEmpty()) + return result; + + QList<QRegExp> regexps; + foreach (const QString &i, filterNames) + regexps.push_back(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard)); + + QStringList entries; + foreach (const QString &i, result) { + bool matched = false; + foreach (const QRegExp ®, regexps) { + matched = reg.exactMatch(i); + if (matched) + break; + } + if (matched) + entries.push_back(i); + } + + return entries; +} + +/** + * \reimp + */ +qint64 BinaryFormatEngine::size() const +{ + return m_archive == 0 ? 0 : m_archive->size(); +} diff --git a/installerbuilder/libinstaller/binaryformatengine.h b/installerbuilder/libinstaller/binaryformatengine.h new file mode 100644 index 000000000..d5ac18a45 --- /dev/null +++ b/installerbuilder/libinstaller/binaryformatengine.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef BINARYFORMATENGINE_H +#define BINARYFORMATENGINE_H + +#include <QAbstractFileEngine> + +#include "binaryformat.h" + +namespace QInstallerCreator { + +class BinaryFormatEngine : public QAbstractFileEngine +{ +public: + BinaryFormatEngine(const ComponentIndex &index, const QString &fileName); + ~BinaryFormatEngine(); + + void setFileName(const QString &file); + + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames); + + bool copy(const QString &newName); + bool close(); + bool open(QIODevice::OpenMode mode); + qint64 pos() const; + qint64 read(char *data, qint64 maxlen); + bool seek(qint64 offset); + qint64 size() const; + + QString fileName(FileName file = DefaultName) const; + FileFlags fileFlags(FileFlags type = FileInfoAll) const; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; + +protected: + void setArchive(const QString &file); + +private: + const ComponentIndex m_index; + bool m_hasComponent; + bool m_hasArchive; + Component m_component; + QSharedPointer<Archive> m_archive; + QString m_fileNamePath; +}; + +} // namespace QInstallerCreator + +#endif diff --git a/installerbuilder/libinstaller/binaryformatenginehandler.cpp b/installerbuilder/libinstaller/binaryformatenginehandler.cpp new file mode 100644 index 000000000..2b1bbcad4 --- /dev/null +++ b/installerbuilder/libinstaller/binaryformatenginehandler.cpp @@ -0,0 +1,116 @@ +/************************************************************************** +** +** 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 "binaryformatenginehandler.h" +#include "binaryformatengine.h" +#include "binaryformat.h" + +#include <QDebug> +#include <QFile> +#include <QFSFileEngine> + +using namespace QInstallerCreator; + +static BinaryFormatEngineHandler *s_instance = 0; + + +class BinaryFormatEngineHandler::Private +{ +public: + Private(const ComponentIndex &i) + : index(i) + { + } + + ComponentIndex index; +}; + +BinaryFormatEngineHandler::BinaryFormatEngineHandler(const ComponentIndex &index) + : d(new Private(index)) +{ + s_instance = this; +} + +BinaryFormatEngineHandler::BinaryFormatEngineHandler(const BinaryFormatEngineHandler &other) + : QAbstractFileEngineHandler(), + d(new Private(other.d->index)) +{ + s_instance = this; +} + +BinaryFormatEngineHandler::~BinaryFormatEngineHandler() +{ + if (s_instance == this) + s_instance = 0; + delete d; +} + +void BinaryFormatEngineHandler::setComponentIndex(const ComponentIndex &index) +{ + d->index = index; +} + +QAbstractFileEngine *BinaryFormatEngineHandler::create(const QString &fileName) const +{ + return fileName.startsWith(QLatin1String("installer://"), Qt::CaseInsensitive ) ? new BinaryFormatEngine(d->index, fileName) : 0; +} + +BinaryFormatEngineHandler *BinaryFormatEngineHandler::instance() +{ + return s_instance; +} + +void BinaryFormatEngineHandler::registerArchive(const QString &pathName, const QString &archive) +{ + static const QChar sep = QChar::fromLatin1('/'); + static const QString prefix = QString::fromLatin1("installer://"); + Q_ASSERT(pathName.toLower().startsWith(prefix)); + + // cut the prefix + QString path = pathName.mid(prefix.length()); + while (path.endsWith(sep)) + path.chop(1); + + const QString comp = path.section(sep, 0, 0); + const QString archiveName = path.section(sep, 1, 1); + + Component c = d->index.componentByName(comp.toUtf8()); + if (c.name().isEmpty()) + c.setName(comp.toUtf8()); + + QList< QSharedPointer<Archive> > registered; + QSharedPointer<Archive> newArchive(new Archive(archive)); + newArchive->setName(archiveName.toUtf8()); + registered.push_back(newArchive); + c.appendArchive(newArchive); + d->index.insertComponent(c); +} diff --git a/installerbuilder/libinstaller/binaryformatenginehandler.h b/installerbuilder/libinstaller/binaryformatenginehandler.h new file mode 100644 index 000000000..82269eda9 --- /dev/null +++ b/installerbuilder/libinstaller/binaryformatenginehandler.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef BINARYFORMATENGINEHANDLER_H +#define BINARYFORMATENGINEHANDLER_H + +#include "installer_global.h" + +#include <QtCore/QAbstractFileEngineHandler> + + +namespace QInstallerCreator { + +class ComponentIndex; + +class INSTALLER_EXPORT BinaryFormatEngineHandler : public QAbstractFileEngineHandler +{ +public: + explicit BinaryFormatEngineHandler(const ComponentIndex &index); + BinaryFormatEngineHandler(const BinaryFormatEngineHandler &other); + ~BinaryFormatEngineHandler(); + QAbstractFileEngine *create(const QString &fileName) const; + + void setComponentIndex(const ComponentIndex &index); + + static BinaryFormatEngineHandler *instance(); + + void registerArchive(const QString &fileName, const QString &path); + +private: + class Private; + Private *const d; +}; + +} + +#endif diff --git a/installerbuilder/libinstaller/component.cpp b/installerbuilder/libinstaller/component.cpp index 39021fab0..4c57e981b 100644 --- a/installerbuilder/libinstaller/component.cpp +++ b/installerbuilder/libinstaller/component.cpp @@ -29,11 +29,10 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "component.h" -#include "common/errors.h" -#include "common/fileutils.h" +#include "errors.h" +#include "fileutils.h" #include "fsengineclient.h" #include "lib7z_facade.h" #include "packagemanagercore.h" diff --git a/installerbuilder/libinstaller/createdesktopentryoperation.cpp b/installerbuilder/libinstaller/createdesktopentryoperation.cpp index da2a253c2..feda777cc 100644 --- a/installerbuilder/libinstaller/createdesktopentryoperation.cpp +++ b/installerbuilder/libinstaller/createdesktopentryoperation.cpp @@ -29,18 +29,18 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "createdesktopentryoperation.h" -#include "common/errors.h" -#include "common/fileutils.h" - -#include <QDir> -#include <QFile> -#include <QFileInfo> -#include <QTextStream> -#include <QProcess> + +#include "errors.h" +#include "fileutils.h" + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QProcess> #if QT_VERSION >= 0x040600 -#include <QProcessEnvironment> +# include <QtCore/QProcessEnvironment> #endif using namespace QInstaller; diff --git a/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp b/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp index 5e4af2fb1..a0527f432 100644 --- a/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp +++ b/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp @@ -31,14 +31,15 @@ **************************************************************************/ #include "createlocalrepositoryoperation.h" -#include "common/binaryformat.h" -#include "common/errors.h" -#include "common/fileutils.h" +#include "binaryformat.h" +#include "errors.h" +#include "fileutils.h" #include "copydirectoryoperation.h" -#include "kdupdaterupdateoperations.h" #include "lib7z_facade.h" #include "packagemanagercore.h" +#include "kdupdaterupdateoperations.h" + #include <QtCore/QDir> #include <QtCore/QDirIterator> diff --git a/installerbuilder/libinstaller/createshortcutoperation.cpp b/installerbuilder/libinstaller/createshortcutoperation.cpp index 7d553792f..bd0608233 100644 --- a/installerbuilder/libinstaller/createshortcutoperation.cpp +++ b/installerbuilder/libinstaller/createshortcutoperation.cpp @@ -29,24 +29,24 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ +#include "createshortcutoperation.h" -#include <kdupdaterapplication.h> -#include <kdupdaterpackagesinfo.h> +#include "errors.h" +#include "fileutils.h" -#include "createshortcutoperation.h" -#include "common/errors.h" -#include "common/fileutils.h" +#include "kdupdaterapplication.h" +#include "kdupdaterpackagesinfo.h" -#include <QDir> -#include <QFileInfo> -#include <QTemporaryFile> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> #include <algorithm> #include <cerrno> #ifdef Q_WS_WIN -#include <windows.h> -#include <shlobj.h> +# include <windows.h> +# include <shlobj.h> #endif using namespace QInstaller; diff --git a/installerbuilder/libinstaller/downloadarchivesjob.cpp b/installerbuilder/libinstaller/downloadarchivesjob.cpp index 19417c3e6..f33108442 100644 --- a/installerbuilder/libinstaller/downloadarchivesjob.cpp +++ b/installerbuilder/libinstaller/downloadarchivesjob.cpp @@ -29,16 +29,15 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "downloadarchivesjob.h" -#include "common/binaryformatenginehandler.h" +#include "binaryformatenginehandler.h" #include "component.h" #include "messageboxhandler.h" #include "packagemanagercore.h" -#include <kdupdaterfiledownloader.h> -#include <kdupdaterfiledownloaderfactory.h> +#include "kdupdaterfiledownloader.h" +#include "kdupdaterfiledownloaderfactory.h" #include <QtCore/QFile> #include <QtCore/QTimerEvent> diff --git a/installerbuilder/libinstaller/errors.h b/installerbuilder/libinstaller/errors.h new file mode 100644 index 000000000..09ad52b14 --- /dev/null +++ b/installerbuilder/libinstaller/errors.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef ERRORS_H +#define ERRORS_H + +#include <QtCore/QDebug> +#include <QtCore/QString> + +#include <stdexcept> + +namespace QInstaller { + +class Error : public std::runtime_error +{ +public: + explicit Error(const QString &message) + : std::runtime_error(message.toStdString()) + , m_message (message) { qWarning() << "Error-Exception:" << message; } + virtual ~Error() throw() {} + + QString message() const { return m_message; } + +private: + QString m_message; +}; + +} + +#endif // ERRORS_H diff --git a/installerbuilder/libinstaller/extractarchiveoperation_p.h b/installerbuilder/libinstaller/extractarchiveoperation_p.h index bbb750630..25c38e61e 100644 --- a/installerbuilder/libinstaller/extractarchiveoperation_p.h +++ b/installerbuilder/libinstaller/extractarchiveoperation_p.h @@ -29,15 +29,14 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #ifndef EXTRACTARCHIVEOPERATION_P_H #define EXTRACTARCHIVEOPERATION_P_H #include "extractarchiveoperation.h" +#include "fileutils.h" #include "lib7z_facade.h" #include "packagemanagercore.h" -#include "common/fileutils.h" #include <QtCore/QDir> #include <QtCore/QFile> diff --git a/installerbuilder/libinstaller/fileutils.cpp b/installerbuilder/libinstaller/fileutils.cpp new file mode 100644 index 000000000..b07b5b08f --- /dev/null +++ b/installerbuilder/libinstaller/fileutils.cpp @@ -0,0 +1,507 @@ +/************************************************************************** +** +** 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 "fileutils.h" + +#include <errors.h> + +#include <QtCore/QDateTime> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QEventLoop> +#include <QtCore/QTemporaryFile> +#include <QtCore/QThread> +#include <QtCore/QUrl> + +#include <errno.h> + +using namespace QInstaller; + + +// -- TempDirDeleter + +TempDirDeleter::TempDirDeleter(const QString &path) +{ + m_paths.insert(path); +} + +TempDirDeleter::TempDirDeleter(const QStringList &paths) + : m_paths(paths.toSet()) +{ +} + +TempDirDeleter::~TempDirDeleter() +{ + releaseAndDeleteAll(); +} + +QStringList TempDirDeleter::paths() const +{ + return m_paths.toList(); +} + +void TempDirDeleter::add(const QString &path) +{ + m_paths.insert(path); +} + +void TempDirDeleter::add(const QStringList &paths) +{ + m_paths += paths.toSet(); +} + +void TempDirDeleter::releaseAll() +{ + m_paths.clear(); +} + +void TempDirDeleter::release(const QString &path) +{ + m_paths.remove(path); +} + +void TempDirDeleter::passAndReleaseAll(TempDirDeleter &tdd) +{ + tdd.m_paths = m_paths; + releaseAll(); +} + +void TempDirDeleter::passAndRelease(TempDirDeleter &tdd, const QString &path) +{ + tdd.add(path); + release(path); +} + +void TempDirDeleter::releaseAndDeleteAll() +{ + foreach (const QString &path, m_paths) + releaseAndDelete(path); +} + +void TempDirDeleter::releaseAndDelete(const QString &path) +{ + if (m_paths.contains(path)) { + try { + m_paths.remove(path); + removeDirectory(path); + } catch (const Error &e) { + qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message(); + } catch (...) { + qCritical() << Q_FUNC_INFO << "Unknown exception caught."; + } + } +} + + +// -- read, write operations + +bool QInstaller::isLocalUrl(const QUrl &url) +{ + return url.scheme().isEmpty() || url.scheme().toLower() == QLatin1String("file"); +} + +QString QInstaller::pathFromUrl(const QUrl &url) +{ + if (isLocalUrl(url)) + return url.toLocalFile(); + const QString str = url.toString(); + if (url.scheme() == QLatin1String("resource")) + return str.mid(QString::fromLatin1("resource").length()); + return str; +} + +void QInstaller::openForRead(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::ReadOnly)) + throw Error(QObject::tr("Cannot open file %1 for reading: %2").arg(name, dev->errorString())); +} + +void QInstaller::openForWrite(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::WriteOnly)) + throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg(name, dev->errorString())); +} + +void QInstaller::openForAppend(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::ReadWrite | QIODevice::Append)) + throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg(name, dev->errorString())); +} + +qint64 QInstaller::blockingWrite(QIODevice *out, const char *buffer, qint64 size) +{ + qint64 left = size; + while (left > 0) { + const qint64 n = out->write(buffer, left); + if (n < 0) { + throw Error(QObject::tr("Write failed after %1 bytes: %2").arg(QString::number(size-left), + out->errorString())); + } + left -= n; + } + return size; +} + +qint64 QInstaller::blockingWrite(QIODevice *out, const QByteArray &ba) +{ + return blockingWrite(out, ba.constData(), ba.size()); +} + +qint64 QInstaller::blockingRead(QIODevice *in, char *buffer, qint64 size) +{ + if (in->atEnd()) + return 0; + qint64 left = size; + while (left > 0) { + const qint64 n = in->read(buffer, left); + if (n < 0) { + throw Error(QObject::tr("Read failed after %1 bytes: %2").arg(QString::number(size-left), + in->errorString())); + } + left -= n; + buffer += n; + } + return size; +} + +void QInstaller::blockingCopy(QIODevice *in, QIODevice *out, qint64 size) +{ + static const qint64 blockSize = 4096; + QByteArray ba(blockSize, '\0'); + qint64 actual = qMin(blockSize, size); + while (actual > 0) { + blockingRead(in, ba.data(), actual); + blockingWrite(out, ba.constData(), actual); + size -= actual; + actual = qMin(blockSize, size); + } +} + +void QInstaller::removeFiles(const QString &path, bool ignoreErrors) +{ + const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden); + foreach (const QFileInfo &fi, entries) { + if (fi.isSymLink() || fi.isFile()) { + QFile f(fi.filePath()); + if (!f.remove() && !ignoreErrors) + throw Error(QObject::tr("Could not remove file %1: %2").arg(f.fileName(), f.errorString())); + } + } +} + +void QInstaller::removeDirectory(const QString &path, bool ignoreErrors) +{ + if (path.isEmpty()) // QDir("") points to the working directory! We never want to remove that one. + return; + + QStringList dirs; + QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks | QDir::Hidden, + QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + dirs.prepend(it.filePath()); + removeFiles(dirs.at(0), ignoreErrors); + } + + QDir d; + dirs.append(path); + removeFiles(path, ignoreErrors); + foreach (const QString &dir, dirs) { + errno = 0; + if (d.exists(path) && !d.rmdir(dir) && !ignoreErrors) + throw Error(QObject::tr("Could not remove folder %1: %2").arg(dir, QLatin1String(strerror(errno)))); + } +} + +/*! + \internal + */ +class RemoveDirectoryThread : public QThread +{ +public: + explicit RemoveDirectoryThread(const QString &path, bool ignoreErrors = false, QObject *parent = 0) + : QThread(parent), + p(path), + ignore(ignoreErrors) + { + } + + const QString &error() const + { + return err; + } + +protected: + /*! + \reimp + */ + void run() + { + try { + removeDirectory(p, ignore); + } catch (const Error &e) { + err = e.message(); + } + } + +private: + QString err; + const QString p; + const bool ignore; +}; + +void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors) +{ + RemoveDirectoryThread thread(path, ignoreErrors); + QEventLoop loop; + QObject::connect(&thread, SIGNAL(finished()), &loop, SLOT(quit())); + thread.start(); + loop.exec(); + if (!thread.error().isEmpty()) + throw Error(thread.error()); +} + +void QInstaller::removeSystemGeneratedFiles(const QString &path) +{ + if (path.isEmpty()) + return; +#if defined Q_WS_MAC + QFile::remove(path + QLatin1String("/.DS_Store")); +#elif defined Q_WS_WIN + QFile::remove(path + QLatin1String("/Thumbs.db")); +#endif +} + +void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir) +{ + qDebug() << "Copying" << sourceDir << "to" << targetDir; + Q_ASSERT(QFileInfo(sourceDir).isDir()); + Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); + if (!QDir().mkpath(targetDir)) + throw Error(QObject::tr("Could not create folder %1").arg(targetDir)); + + QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); + while (it.hasNext()) { + const QFileInfo i(it.next()); + if (i.isDir()) { + copyDirectoryContents(QDir(sourceDir).absoluteFilePath(i.fileName()), + QDir(targetDir).absoluteFilePath(i.fileName())); + } else { + QFile f(i.filePath()); + const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); + if (!f.copy(target)) { + throw Error(QObject::tr("Could not copy file from %1 to %2: %3").arg(f.fileName(), target, + f.errorString())); + } + } + } +} + +void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir) +{ + qDebug() << "Moving" << sourceDir << "to" << targetDir; + Q_ASSERT(QFileInfo(sourceDir).isDir()); + Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); + if (!QDir().mkpath(targetDir)) + throw Error(QObject::tr("Could not create folder %1").arg(targetDir)); + + QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); + while (it.hasNext()) { + const QFileInfo i(it.next()); + if (i.isDir()) { + moveDirectoryContents(QDir(sourceDir).absoluteFilePath(i.fileName()), + QDir(targetDir).absoluteFilePath(i.fileName())); + } else { + QFile f(i.filePath()); + const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); + if (!f.rename(target)) { + throw Error(QObject::tr("Could not move file from %1 to %2: %3").arg(f.fileName(), target, + f.errorString())); + } + } + } +} + +void QInstaller::mkdir(const QString &path) +{ + errno = 0; + if (!QDir().mkdir(QFileInfo(path).absoluteFilePath())) { + throw Error(QObject::tr("Could not create folder %1: %2").arg(path, + QString::fromLocal8Bit(strerror(errno)))); + } +} + +void QInstaller::mkpath(const QString &path) +{ + errno = 0; + if (!QDir().mkpath(QFileInfo(path).absoluteFilePath())) { + throw Error(QObject::tr("Could not create folder %1: %2").arg(path, + QString::fromLocal8Bit(strerror(errno)))); + } +} + +QString QInstaller::generateTemporaryFileName(const QString &templ) +{ + if (templ.isEmpty()) { + QTemporaryFile f; + if (!f.open()) + throw Error(QObject::tr("Could not open temporary file: %1").arg(f.errorString())); + return f.fileName(); + } + + static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); + QString suffix; + qsrand(qrand() * QDateTime::currentDateTime().toTime_t()); + for (int i = 0; i < 5; ++i) + suffix += characters[qrand() % characters.length()]; + + const QString tmp = QLatin1String("%1.tmp.%2.%3"); + int count = 1; + while (QFile::exists(tmp.arg(templ, suffix).arg(count))) + ++count; + + QFile f(tmp.arg(templ, suffix).arg(count)); + if (!f.open(QIODevice::WriteOnly)) + throw Error(QObject::tr("Could not open temporary file for template %1: %2").arg(templ, f.errorString())); + f.remove(); + return f.fileName(); +} + +QString QInstaller::createTemporaryDirectory(const QString &templ) +{ + const QString t = QDir::tempPath() + QLatin1String("/") + templ + QLatin1String("XXXXXX"); + QTemporaryFile f(t); + if (!f.open()) + throw Error(QObject::tr("Could not create temporary folder for template %1: %2").arg(t, f.errorString())); + const QString path = f.fileName() + QLatin1String("meta"); + qDebug() << "Creating meta data directory at" << path; + + QInstaller::mkpath(path); + return path; +} + +#ifdef Q_WS_WIN +#include <windows.h> + +#pragma pack(push) +#pragma pack(2) + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image (0 if >=8bpp) + BYTE bReserved; // Reserved + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // how many bytes in this resource? + DWORD dwImageOffset; // the ID +} ICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource type (1 for icons) + WORD idCount; // How many images? + ICONDIRENTRY idEntries[1]; // The entries for each image +} ICONDIR; + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image (0 if >=8bpp) + BYTE bReserved; // Reserved + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // how many bytes in this resource? + WORD nID; // the ID +} GRPICONDIRENTRY, *LPGRPICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource type (1 for icons) + WORD idCount; // How many images? + GRPICONDIRENTRY idEntries[1]; // The entries for each image +} GRPICONDIR, *LPGRPICONDIR; + + +#pragma pack(pop) + +void QInstaller::setApplicationIcon(const QString &application, const QString &icon) +{ + wchar_t* const path = new wchar_t[application.length() + 1]; + QDir::toNativeSeparators(application).toWCharArray(path); + path[application.length()] = 0; + + HANDLE updateRes = BeginUpdateResource(path, false); + delete[] path; + + QFile iconFile(icon); + if (!iconFile.open(QIODevice::ReadOnly)) + return; + + QByteArray temp = iconFile.readAll(); + + ICONDIR* ig = reinterpret_cast< ICONDIR* >(temp.data()); + + DWORD newSize = sizeof(GRPICONDIR) + sizeof(GRPICONDIRENTRY) * (ig->idCount - 1); + GRPICONDIR* newDir = reinterpret_cast< GRPICONDIR* >(new char[newSize]); + newDir->idReserved = ig->idReserved; + newDir->idType = ig->idType; + newDir->idCount = ig->idCount; + + for (int i = 0; i < ig->idCount; ++i) { + char* temp1 = temp.data() + ig->idEntries[i].dwImageOffset; + DWORD size1 = ig->idEntries[i].dwBytesInRes; + + newDir->idEntries[i].bWidth = ig->idEntries[i].bWidth; + newDir->idEntries[i].bHeight = ig->idEntries[i].bHeight; + newDir->idEntries[i].bColorCount = ig->idEntries[i].bColorCount; + newDir->idEntries[i].bReserved = ig->idEntries[i].bReserved; + newDir->idEntries[i].wPlanes = ig->idEntries[i].wPlanes; + newDir->idEntries[i].wBitCount = ig->idEntries[i].wBitCount; + newDir->idEntries[i].dwBytesInRes = ig->idEntries[i].dwBytesInRes; + newDir->idEntries[i].nID = i + 1; + + UpdateResource(updateRes, RT_ICON, MAKEINTRESOURCE(i + 1), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), temp1, size1); + } + + UpdateResource(updateRes, RT_GROUP_ICON, L"IDI_ICON1", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newDir + , newSize); + + delete [] newDir; + + EndUpdateResource(updateRes, false); +} + +#endif diff --git a/installerbuilder/libinstaller/fileutils.h b/installerbuilder/libinstaller/fileutils.h new file mode 100644 index 000000000..4e634a210 --- /dev/null +++ b/installerbuilder/libinstaller/fileutils.h @@ -0,0 +1,114 @@ +/************************************************************************** +** +** This file is part of Installer Framework** +** +** Copyright (c) 2010 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. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#ifndef QINSTALLER_FILEUTILS_H +#define QINSTALLER_FILEUTILS_H + +#include "installer_global.h" + +#include <QtCore/QSet> +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QByteArray; +class QIODevice; +class QUrl; +QT_END_NAMESPACE + +namespace QInstaller { +class INSTALLER_EXPORT TempDirDeleter +{ +public: + explicit TempDirDeleter(const QString &path); + explicit TempDirDeleter(const QStringList &paths = QStringList()); + ~TempDirDeleter(); + + QStringList paths() const; + + void add(const QString &path); + void add(const QStringList &paths); + + void releaseAll(); + void release(const QString &path); + void passAndReleaseAll(TempDirDeleter &tdd); + void passAndRelease(TempDirDeleter &tdd, const QString &path); + + void releaseAndDeleteAll(); + void releaseAndDelete(const QString &path); + +private: + Q_DISABLE_COPY(TempDirDeleter) + QSet<QString> m_paths; +}; + + void INSTALLER_EXPORT openForRead(QIODevice *dev, const QString &name); + void INSTALLER_EXPORT openForWrite(QIODevice *dev, const QString &name); + void INSTALLER_EXPORT openForAppend(QIODevice *dev, const QString &name); + + qint64 INSTALLER_EXPORT blockingRead(QIODevice *in, char *buffer, qint64 size); + void INSTALLER_EXPORT blockingCopy(QIODevice *in, QIODevice *out, qint64 size); + qint64 INSTALLER_EXPORT blockingWrite(QIODevice *out, const char *buffer, qint64 size); + qint64 INSTALLER_EXPORT blockingWrite(QIODevice *out, const QByteArray& ba); + + /*! + Removes the directory at \a path recursively. + @param path The directory to remove + @param ignoreErrors if @p true, errors will be silently ignored. Otherwise an exception will be thrown + if removing fails. + + @throws QInstaller::Error if the directory cannot be removed and ignoreErrors is @p false + */ + void INSTALLER_EXPORT removeFiles(const QString &path, bool ignoreErrors = false); + void INSTALLER_EXPORT removeDirectory(const QString &path, bool ignoreErrors = false); + void INSTALLER_EXPORT removeDirectoryThreaded(const QString &path, bool ignoreErrors = false); + void INSTALLER_EXPORT removeSystemGeneratedFiles(const QString &path); + + /*! + Creates a temporary directory + @throws QInstaller::Error if creating the temporary directory fails + */ + QString INSTALLER_EXPORT createTemporaryDirectory(const QString &templ=QString()); + + QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString()); + + void INSTALLER_EXPORT moveDirectoryContents(const QString &sourceDir, const QString &targetDir); + void INSTALLER_EXPORT copyDirectoryContents(const QString &sourceDir, const QString &targetDir); + + bool INSTALLER_EXPORT isLocalUrl(const QUrl &url); + QString INSTALLER_EXPORT pathFromUrl(const QUrl &url); + + void INSTALLER_EXPORT mkdir(const QString &path); + void INSTALLER_EXPORT mkpath(const QString &path); + +#ifdef Q_WS_WIN + /*! + Sets the .ico file at \a icon as application icon for \a application. + */ + void INSTALLER_EXPORT setApplicationIcon(const QString &application, const QString &icon); +#endif +} + +#endif // QINSTALLER_FILEUTILS_H diff --git a/installerbuilder/libinstaller/fsengineserver.cpp b/installerbuilder/libinstaller/fsengineserver.cpp index 06c63b283..19539334c 100644 --- a/installerbuilder/libinstaller/fsengineserver.cpp +++ b/installerbuilder/libinstaller/fsengineserver.cpp @@ -29,10 +29,9 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "fsengineserver.h" -#include <common/utils.h> +#include "utils.h" #include <QtCore/QCoreApplication> #include <QtCore/QFSFileEngine> diff --git a/installerbuilder/libinstaller/getrepositoriesmetainfojob.h b/installerbuilder/libinstaller/getrepositoriesmetainfojob.h index 968e0f129..e44032b48 100644 --- a/installerbuilder/libinstaller/getrepositoriesmetainfojob.h +++ b/installerbuilder/libinstaller/getrepositoriesmetainfojob.h @@ -29,22 +29,20 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #ifndef GETREPOSITORIESMETAINFOJOB_H #define GETREPOSITORIESMETAINFOJOB_H -#include <kdjob.h> +#include "fileutils.h" +#include "installer_global.h" +#include "repository.h" + +#include "kdjob.h" #include <QtCore/QList> #include <QtCore/QPointer> #include <QtCore/QString> #include <QtCore/QStringList> -#include <common/fileutils.h> -#include <common/repository.h> - -#include "installer_global.h" - namespace KDUpdater { class FileDownloader; } diff --git a/installerbuilder/libinstaller/getrepositorymetainfojob.cpp b/installerbuilder/libinstaller/getrepositorymetainfojob.cpp index 421db52b8..a72a861a0 100644 --- a/installerbuilder/libinstaller/getrepositorymetainfojob.cpp +++ b/installerbuilder/libinstaller/getrepositorymetainfojob.cpp @@ -29,18 +29,17 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "getrepositorymetainfojob.h" #include "constants.h" -#include "common/errors.h" +#include "errors.h" #include "lib7z_facade.h" #include "messageboxhandler.h" #include "packagemanagercore_p.h" #include "qinstallerglobal.h" -#include <kdupdaterfiledownloader.h> -#include <kdupdaterfiledownloaderfactory.h> +#include "kdupdaterfiledownloader.h" +#include "kdupdaterfiledownloaderfactory.h" #include <QtCore/QFile> #include <QtCore/QTimer> diff --git a/installerbuilder/libinstaller/getrepositorymetainfojob.h b/installerbuilder/libinstaller/getrepositorymetainfojob.h index c30de9828..aec94603d 100644 --- a/installerbuilder/libinstaller/getrepositorymetainfojob.h +++ b/installerbuilder/libinstaller/getrepositorymetainfojob.h @@ -29,22 +29,20 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #ifndef GETREPOSITORYMETAINFOJOB_H #define GETREPOSITORYMETAINFOJOB_H -#include <kdjob.h> +#include "fileutils.h" +#include "installer_global.h" +#include "repository.h" + +#include "kdjob.h" #include <QtCore/QPointer> #include <QtCore/QString> #include <QtCore/QStringList> #include <QtCore/QThreadPool> -#include <common/fileutils.h> -#include <common/repository.h> - -#include "installer_global.h" - namespace KDUpdater { class FileDownloader; } diff --git a/installerbuilder/libinstaller/init.cpp b/installerbuilder/libinstaller/init.cpp index f71facf5b..5a464684a 100644 --- a/installerbuilder/libinstaller/init.cpp +++ b/installerbuilder/libinstaller/init.cpp @@ -29,7 +29,6 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "init.h" #include "createshortcutoperation.h" @@ -69,15 +68,15 @@ #include "minimumprogressoperation.h" #ifdef Q_OS_MAC - #include "macreplaceinstallnamesoperation.h" +# include "macreplaceinstallnamesoperation.h" #endif // Q_OS_MAC -#include "common/utils.h" +#include "utils.h" -#include <kdupdaterupdateoperation.h> -#include <kdupdaterupdateoperationfactory.h> -#include <kdupdaterfiledownloader.h> -#include <kdupdaterfiledownloaderfactory.h> +#include "kdupdaterupdateoperation.h" +#include "kdupdaterupdateoperationfactory.h" +#include "kdupdaterfiledownloader.h" +#include "kdupdaterfiledownloaderfactory.h" #include <QtPlugin> #include <QNetworkProxyFactory> diff --git a/installerbuilder/libinstaller/installiconsoperation.cpp b/installerbuilder/libinstaller/installiconsoperation.cpp index 50c8454ba..c2fee33eb 100644 --- a/installerbuilder/libinstaller/installiconsoperation.cpp +++ b/installerbuilder/libinstaller/installiconsoperation.cpp @@ -29,19 +29,18 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "installiconsoperation.h" -#include "common/fileutils.h" +#include "fileutils.h" #include "packagemanagercore.h" #include <QtCore/QDir> #include <QtCore/QDirIterator> #if QT_VERSION >= 0x040600 -#include <QProcessEnvironment> +# include <QProcessEnvironment> #else -#include <QProcess> +# include <QProcess> #endif using namespace QInstaller; diff --git a/installerbuilder/libinstaller/libinstaller.pro b/installerbuilder/libinstaller/libinstaller.pro index 10f58d409..7a01e0a5f 100644 --- a/installerbuilder/libinstaller/libinstaller.pro +++ b/installerbuilder/libinstaller/libinstaller.pro @@ -1,11 +1,9 @@ TEMPLATE = lib TARGET = installer -DEPENDPATH += . \ - .. \ - ../common -INCLUDEPATH += . \ - .. +INCLUDEPATH += . .. +DEPENDPATH += . .. 3rdparty/kdtools + DESTDIR = $$OUT_PWD/../lib DLLDESTDIR = $$OUT_PWD/../bin @@ -29,16 +27,16 @@ contains(CONFIG, static): { include(3rdparty/7zip/7zip.pri) include(3rdparty/kdtools/kdtools.pri) -HEADERS += $$PWD/packagemanagercore.h \ - $$PWD/packagemanagercore_p.h \ - $$PWD/packagemanagergui.h \ - ../common/binaryformat.h \ - ../common/binaryformatengine.h \ - ../common/binaryformatenginehandler.h \ - ../common/repository.h \ - ../common/zipjob.h \ - ../common/utils.h \ - ../common/errors.h \ +HEADERS += packagemanagercore.h \ + packagemanagercore_p.h \ + packagemanagergui.h \ + binaryformat.h \ + binaryformatengine.h \ + binaryformatenginehandler.h \ + repository.h \ + zipjob.h \ + utils.h \ + errors.h \ component.h \ componentmodel.h \ qinstallerglobal.h \ @@ -101,16 +99,16 @@ HEADERS += $$PWD/packagemanagercore.h \ packagemanagerproxyfactory.h \ createlocalrepositoryoperation.h -SOURCES += $$PWD/packagemanagercore.cpp \ - $$PWD/packagemanagercore_p.cpp \ - $$PWD/packagemanagergui.cpp \ - ../common/binaryformat.cpp \ - ../common/binaryformatengine.cpp \ - ../common/binaryformatenginehandler.cpp \ - ../common/repository.cpp \ - ../common/zipjob.cpp \ - ../common/fileutils.cpp \ - ../common/utils.cpp \ +SOURCES += packagemanagercore.cpp \ + packagemanagercore_p.cpp \ + packagemanagergui.cpp \ + binaryformat.cpp \ + binaryformatengine.cpp \ + binaryformatenginehandler.cpp \ + repository.cpp \ + zipjob.cpp \ + fileutils.cpp \ + utils.cpp \ component.cpp \ componentmodel.cpp \ qtpatch.cpp \ diff --git a/installerbuilder/libinstaller/operationrunner.cpp b/installerbuilder/libinstaller/operationrunner.cpp index b9dd3cea0..26e7e6b02 100644 --- a/installerbuilder/libinstaller/operationrunner.cpp +++ b/installerbuilder/libinstaller/operationrunner.cpp @@ -29,18 +29,17 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "operationrunner.h" -#include "common/errors.h" -#include "common/binaryformat.h" -#include "common/utils.h" +#include "binaryformat.h" #include "component.h" +#include "errors.h" #include "init.h" #include "packagemanagercore.h" +#include "utils.h" -#include <kdupdaterupdateoperation.h> -#include <kdupdaterupdateoperationfactory.h> +#include "kdupdaterupdateoperation.h" +#include "kdupdaterupdateoperationfactory.h" #include <iostream> diff --git a/installerbuilder/libinstaller/packagemanagercore.cpp b/installerbuilder/libinstaller/packagemanagercore.cpp index b47708c7a..8b871c61e 100644 --- a/installerbuilder/libinstaller/packagemanagercore.cpp +++ b/installerbuilder/libinstaller/packagemanagercore.cpp @@ -29,15 +29,13 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "packagemanagercore.h" #include "adminauthorization.h" -#include "common/binaryformat.h" -#include "common/errors.h" -#include "common/utils.h" +#include "binaryformat.h" #include "component.h" #include "downloadarchivesjob.h" +#include "errors.h" #include "fsengineclient.h" #include "getrepositoriesmetainfojob.h" #include "messageboxhandler.h" @@ -48,6 +46,7 @@ #include "qprocesswrapper.h" #include "qsettingswrapper.h" #include "settings.h" +#include "utils.h" #include <QtCore/QTemporaryFile> @@ -56,11 +55,11 @@ #include <QtScript/QScriptEngine> #include <QtScript/QScriptContext> -#include <kdsysinfo.h> -#include <kdupdaterupdateoperationfactory.h> +#include "kdsysinfo.h" +#include "kdupdaterupdateoperationfactory.h" #ifdef Q_OS_WIN -#include "qt_windows.h" +# include "qt_windows.h" #endif using namespace QInstaller; diff --git a/installerbuilder/libinstaller/packagemanagercore.h b/installerbuilder/libinstaller/packagemanagercore.h index 13509b6b1..4e02b3ba3 100644 --- a/installerbuilder/libinstaller/packagemanagercore.h +++ b/installerbuilder/libinstaller/packagemanagercore.h @@ -29,11 +29,10 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #ifndef PACKAGEMANAGERCORE_H #define PACKAGEMANAGERCORE_H -#include "common/repository.h" +#include "repository.h" #include "qinstallerglobal.h" #include <QtCore/QHash> diff --git a/installerbuilder/libinstaller/packagemanagercore_p.cpp b/installerbuilder/libinstaller/packagemanagercore_p.cpp index abb1523a9..765db4e4e 100644 --- a/installerbuilder/libinstaller/packagemanagercore_p.cpp +++ b/installerbuilder/libinstaller/packagemanagercore_p.cpp @@ -29,14 +29,13 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "packagemanagercore_p.h" #include "adminauthorization.h" -#include "common/binaryformat.h" -#include "common/errors.h" -#include "common/fileutils.h" +#include "binaryformat.h" #include "component.h" +#include "errors.h" +#include "fileutils.h" #include "fsengineclient.h" #include "messageboxhandler.h" #include "packagemanagercore.h" @@ -44,12 +43,12 @@ #include "qprocesswrapper.h" #include "qsettingswrapper.h" -#include <kdsavefile.h> -#include <kdselfrestarter.h> +#include "kdsavefile.h" +#include "kdselfrestarter.h" #include "kdupdaterfiledownloaderfactory.h" -#include <kdupdaterupdatesourcesinfo.h> -#include <kdupdaterupdateoperationfactory.h> -#include <kdupdaterupdatefinder.h> +#include "kdupdaterupdatesourcesinfo.h" +#include "kdupdaterupdateoperationfactory.h" +#include "kdupdaterupdatefinder.h" #include <QtCore/QtConcurrentRun> #include <QtCore/QCoreApplication> diff --git a/installerbuilder/libinstaller/packagemanagergui.cpp b/installerbuilder/libinstaller/packagemanagergui.cpp index 3fb88bee8..a041c9838 100644 --- a/installerbuilder/libinstaller/packagemanagergui.cpp +++ b/installerbuilder/libinstaller/packagemanagergui.cpp @@ -29,23 +29,21 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "packagemanagergui.h" #include "component.h" #include "componentmodel.h" +#include "errors.h" +#include "fileutils.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "qinstallerglobal.h" #include "progresscoordinator.h" #include "performinstallationform.h" #include "settings.h" +#include "utils.h" -#include "common/errors.h" -#include "common/utils.h" -#include "common/fileutils.h" - -#include <kdsysinfo.h> +#include "kdsysinfo.h" #include <QtCore/QDir> #include <QtCore/QDynamicPropertyChangeEvent> diff --git a/installerbuilder/libinstaller/range.h b/installerbuilder/libinstaller/range.h new file mode 100644 index 000000000..f97aa0317 --- /dev/null +++ b/installerbuilder/libinstaller/range.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Installer Framework** +** +** Copyright (c) 2010 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. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#ifndef RANGE_H +#define RANGE_H + +#include <algorithm> + +template <typename T> +class Range { +public: + static Range<T> fromStartAndEnd( const T& start, const T& end ) { + Range<T> r; + r.m_start = start; + r.m_end = end; + return r; + } + + static Range<T> fromStartAndLength( const T& start, const T& length ) { + Range<T> r; + r.m_start = start; + r.m_end = start + length; + return r; + } + + Range() : m_start( 0 ), m_end( 0 ) {} + + T start() const { return m_start; } + + T end() const { return m_end; } + + void move( const T& by ) { + m_start += by; + m_end += by; + } + + Range<T> moved( const T& by ) const { + Range<T> b = *this; + b.move( by ); + return b; + } + + T length() const { return m_end - m_start; } + + Range<T> normalized() const { + Range<T> r2( *this ); + if ( r2.m_start > r2.m_end ) + std::swap( r2.m_start, r2.m_end ); + return r2; + } + + bool operator==( const Range<T>& other ) const { + return m_start == other.m_start && m_end && other.m_end; + } + bool operator<( const Range<T>& other ) const { + if ( m_start != other.m_start ) + return m_start < other.m_start; + return m_end < other.m_end; + } + +private: + T m_start; + T m_end; +}; + +#endif /* RANGE_H_ */ diff --git a/installerbuilder/libinstaller/repository.cpp b/installerbuilder/libinstaller/repository.cpp new file mode 100644 index 000000000..cc80a20d9 --- /dev/null +++ b/installerbuilder/libinstaller/repository.cpp @@ -0,0 +1,213 @@ +/************************************************************************** +** +** 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 "repository.h" + +namespace QInstaller { + +/* + Constructs an invalid Repository object. +*/ +Repository::Repository() + : m_default(false) + , m_enabled(false) +{ + registerMetaType(); +} + +/*! + Constructs a new repository by using all fields of the given repository \a other. +*/ +Repository::Repository(const Repository &other) + : m_url(other.m_url) + , m_default(other.m_default) + , m_enabled(other.m_enabled) + , m_username(other.m_username) + , m_password(other.m_password) +{ + registerMetaType(); +} + +/*! + Constructs a new repository by setting it's address to \a url and it's default state. +*/ +Repository::Repository(const QUrl &url, bool isDefault) + : m_url(url) + , m_default(isDefault) + , m_enabled(true) +{ + registerMetaType(); +} + +/*! + Returns true if the repository URL is valid; otherwise returns false. + + Note: The URL is simply run through a conformance test. It is not checked that the repository + actually exists. +*/ +bool Repository::isValid() const +{ + return m_url.isValid(); +} + +/*! + Returns true if the repository was set using the package manager configuration file; otherwise returns + false. +*/ +bool Repository::isDefault() const +{ + return m_default; +} + +/*! + Returns the URL of the repository. By default an invalid \sa QUrl is returned. +*/ +QUrl Repository::url() const +{ + return m_url; +} + +/*! + Sets the repository URL to the one specified at \a url. +*/ +void Repository::setUrl(const QUrl &url) +{ + m_url = url; +} + +/*! + Returns whether the repository is enabled and used during information retrieval. +*/ +bool Repository::isEnabled() const +{ + return m_enabled; +} + +/*! + Sets this repository to \n enabled state and thus to use this repository for information retrieval or not. +*/ +void Repository::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +/*! + Returns the user name used for authentication. +*/ +QString Repository::username() const +{ + return m_username; +} + +/*! + Sets the user name for authentication to be \a username. +*/ +void Repository::setUsername(const QString &username) +{ + m_username = username; +} + +/*! + Returns the password used for authentication. +*/ +QString Repository::password() const +{ + return m_password; +} + +/*! + Sets the password for authentication to be \a password. +*/ +void Repository::setPassword(const QString &password) +{ + m_password = password; +} + +/*! + Compares the values of this repository to \a other and returns true if they are equal (same server, + default state, enabled state as well as username and password). \sa operator!=() +*/ +bool Repository::operator==(const Repository &other) const +{ + return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled + && m_username == other.m_username && m_password == other.m_password; +} + +/*! + Returns true if the \a other repository is not equal to this repository; otherwise returns false. Two + repositories are considered equal if they contain the same elements. \sa operator==() +*/ +bool Repository::operator!=(const Repository &other) const +{ + return !(*this == other); +} + +/*! + Assigns the values of repository \a other to this repository. +*/ +const Repository &Repository::operator=(const Repository &other) +{ + if (this == &other) + return *this; + + m_url = other.m_url; + m_default = other.m_default; + m_enabled = other.m_enabled; + m_username = other.m_username; + m_password = other.m_password; + + return *this; +} + +void Repository::registerMetaType() +{ + qRegisterMetaType<Repository>("Repository"); + qRegisterMetaTypeStreamOperators<Repository>("Repository"); +} + +QDataStream &operator>>(QDataStream &istream, Repository &repository) +{ + QByteArray url, username, password; + istream >> url >> repository.m_default >> repository.m_enabled >> username >> password; + repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url))); + repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username))); + repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password))); + return istream; +} + +QDataStream &operator<<(QDataStream &ostream, const Repository &repository) +{ + return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled + << repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64(); +} + +} diff --git a/installerbuilder/libinstaller/repository.h b/installerbuilder/libinstaller/repository.h new file mode 100644 index 000000000..432a851c4 --- /dev/null +++ b/installerbuilder/libinstaller/repository.h @@ -0,0 +1,97 @@ +/************************************************************************** +** +** 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. +** +**************************************************************************/ + +#ifndef REPOSITORY_H +#define REPOSITORY_H + +#include "installer_global.h" + +#include <QtCore/QMetaType> +#include <QtCore/QUrl> + +namespace QInstaller { + +class INSTALLER_EXPORT Repository +{ +public: + explicit Repository(); + Repository(const Repository &other); + explicit Repository(const QUrl &url, bool isDefault); + + bool isValid() const; + bool isDefault() const; + + QUrl url() const; + void setUrl(const QUrl &url); + + bool isEnabled() const; + void setEnabled(bool enabled); + + QString username() const; + void setUsername(const QString &username); + + QString password() const; + void setPassword(const QString &password); + + bool operator==(const Repository &other) const; + bool operator!=(const Repository &other) const; + + uint qHash(const Repository &repository); + const Repository &operator=(const Repository &other); + + friend QDataStream &operator>>(QDataStream &istream, Repository &repository); + friend QDataStream &operator<<(QDataStream &ostream, const Repository &repository); + +private: + void registerMetaType(); + +private: + QUrl m_url; + bool m_default; + bool m_enabled; + QString m_username; + QString m_password; +}; + +inline uint qHash(const Repository &repository) +{ + return qHash(repository.url().toString()); +} + +QDataStream &operator>>(QDataStream &istream, Repository &repository); +QDataStream &operator<<(QDataStream &ostream, const Repository &repository); + +} // namespace QInstaller + +Q_DECLARE_METATYPE(QInstaller::Repository) + +#endif // REPOSITORY_H diff --git a/installerbuilder/libinstaller/settings.cpp b/installerbuilder/libinstaller/settings.cpp index 6507c0e7d..ace61761e 100644 --- a/installerbuilder/libinstaller/settings.cpp +++ b/installerbuilder/libinstaller/settings.cpp @@ -29,12 +29,11 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "settings.h" -#include "common/errors.h" -#include "common/repository.h" +#include "errors.h" #include "qinstallerglobal.h" +#include "repository.h" #include <QtCore/QFileInfo> #include <QtCore/QStringList> diff --git a/installerbuilder/libinstaller/updater.cpp b/installerbuilder/libinstaller/updater.cpp index 519c6d18f..ec0564a8f 100644 --- a/installerbuilder/libinstaller/updater.cpp +++ b/installerbuilder/libinstaller/updater.cpp @@ -29,16 +29,16 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "updater.h" -#include "common/binaryformat.h" -#include "common/utils.h" +#include "binaryformat.h" #include "component.h" #include "init.h" #include "packagemanagercore.h" +#include "utils.h" #include <QtCore/QDebug> + #include <QtXml/QDomDocument> #include <iostream> diff --git a/installerbuilder/libinstaller/updatesettings.cpp b/installerbuilder/libinstaller/updatesettings.cpp index 078db3adb..f06a1fa87 100644 --- a/installerbuilder/libinstaller/updatesettings.cpp +++ b/installerbuilder/libinstaller/updatesettings.cpp @@ -29,11 +29,10 @@ ** Nokia at qt-info@nokia.com. ** **************************************************************************/ - #include "updatesettings.h" -#include "common/errors.h" -#include "common/repository.h" +#include "errors.h" +#include "repository.h" #include "settings.h" #include <QtCore/QDateTime> diff --git a/installerbuilder/libinstaller/utils.cpp b/installerbuilder/libinstaller/utils.cpp new file mode 100644 index 000000000..4bd93430f --- /dev/null +++ b/installerbuilder/libinstaller/utils.cpp @@ -0,0 +1,341 @@ +/************************************************************************** +** +** 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 "utils.h" + +#include <QtCore/QDateTime> +#include <QtCore/QDir> +#include <QtCore/QProcessEnvironment> +#include <QtCore/QVector> + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +# include "qt_windows.h" +#endif + +#include <fstream> +#include <iostream> +#include <sstream> + +static bool verb = false; + +void QInstaller::setVerbose(bool v) +{ + verb = v; +} + +bool QInstaller::isVerbose() +{ + return verb; +} + +#ifdef Q_WS_WIN +void qWinMsgHandler(QtMsgType t, const char *str); + +class debugstream : public std::ostream +{ + class buf : public std::stringbuf + { + public: + buf() {} + + int sync() + { + std::string s = str(); + if (s[s.length() - 1] == '\n' ) + s[s.length() - 1] = '\0'; // remove \n + qWinMsgHandler(QtDebugMsg, s.c_str()); + std::cout << s << std::endl; + str(std::string()); + return 0; + } + }; +public: + debugstream() : std::ostream(&b) {} +private: + buf b; +}; +#endif + +std::ostream &QInstaller::stdverbose() +{ + static std::fstream null; +#ifdef Q_WS_WIN + static debugstream stream; +#else + static std::ostream& stream = std::cout; +#endif + if (verb) + return stream; + return null; +} + +std::ostream &QInstaller::operator<<(std::ostream &os, const QString &string) +{ + return os << qPrintable(string); +} + +//TODO from kdupdaterfiledownloader.cpp, use that one once merged +QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo) +{ + Q_ASSERT(device); + QCryptographicHash hash(algo); + QByteArray buffer; + buffer.resize(512 * 1024); + while (true) { + const qint64 numRead = device->read(buffer.data(), buffer.size()); + if (numRead <= 0) + return hash.result(); + hash.addData(buffer.constData(), numRead); + } + return QByteArray(); // never reached +} + + +QString QInstaller::replaceVariables(const QHash<QString, QString> &vars, const QString &str) +{ + QString res; + int pos = 0; + while (true) { + int pos1 = str.indexOf(QLatin1Char('@'), pos); + if (pos1 == -1) + break; + int pos2 = str.indexOf(QLatin1Char('@'), pos1 + 1); + if (pos2 == -1) + break; + res += str.mid(pos, pos1 - pos); + QString name = str.mid(pos1 + 1, pos2 - pos1 - 1); + res += vars.value(name); + pos = pos2 + 1; + } + res += str.mid(pos); + return res; +} + +QString QInstaller::replaceWindowsEnvironmentVariables(const QString &str) +{ + const QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString res; + int pos = 0; + while (true) { + int pos1 = str.indexOf(QLatin1Char( '%'), pos); + if (pos1 == -1) + break; + int pos2 = str.indexOf(QLatin1Char( '%'), pos1 + 1); + if (pos2 == -1) + break; + res += str.mid(pos, pos1 - pos); + QString name = str.mid(pos1 + 1, pos2 - pos1 - 1); + res += env.value(name); + pos = pos2 + 1; + } + res += str.mid(pos); + return res; +} + +QInstaller::VerboseWriter::VerboseWriter(QObject *parent) : QObject(parent) +{ + preFileBuffer.open(QIODevice::ReadWrite); + stream.setDevice(&preFileBuffer); +} + +QInstaller::VerboseWriter::~VerboseWriter() +{ + stream.flush(); + if (logFileName.isEmpty()) // binarycreator + return; + //if the installer installed nothing - there is no target directory - where the logfile can be saved + if (!QFileInfo(logFileName).absoluteDir().exists()) + return; + + QFile output(logFileName); + if (output.open(QIODevice::ReadWrite | QIODevice::Append)) { + QString logInfo; + logInfo += QLatin1String("*************************************"); + logInfo += QLatin1String("Invoked:") + QDateTime::currentDateTime().toString(); + output.write(logInfo.toLocal8Bit()); + output.write(preFileBuffer.data()); + output.close(); + } + stream.setDevice(0); +} + +void QInstaller::VerboseWriter::setOutputStream(const QString &fileName) +{ + logFileName = fileName; +} + + +Q_GLOBAL_STATIC(QInstaller::VerboseWriter, verboseWriter) + +QInstaller::VerboseWriter *QInstaller::VerboseWriter::instance() +{ + return verboseWriter(); +} + +QInstaller::VerboseWriter &QInstaller::verbose() +{ + return *verboseWriter(); +} + +#ifdef Q_OS_WIN +// taken from qcoreapplication_p.h +template<typename Char> +static QVector<Char*> qWinCmdLine(Char *cmdParam, int length, int &argc) +{ + QVector<Char*> argv(8); + Char *p = cmdParam; + Char *p_end = p + length; + + argc = 0; + + while (*p && p < p_end) { // parse cmd line arguments + while (QChar((short)(*p)).isSpace()) // skip white space + p++; + if (*p && p < p_end) { // arg starts + int quote; + Char *start, *r; + if (*p == Char('\"') || *p == Char('\'')) { // " or ' quote + quote = *p; + start = ++p; + } else { + quote = 0; + start = p; + } + r = start; + while (*p && p < p_end) { + if (quote) { + if (*p == quote) { + p++; + if (QChar((short)(*p)).isSpace()) + break; + quote = 0; + } + } + if (*p == '\\') { // escape char? + p++; + if (*p == Char('\"') || *p == Char('\'')) + ; // yes + else + p--; // treat \ literally + } else { + if (!quote && (*p == Char('\"') || *p == Char('\''))) { // " or ' quote + quote = *p++; + continue; + } else if (QChar((short)(*p)).isSpace() && !quote) + break; + } + if (*p) + *r++ = *p++; + } + if (*p && p < p_end) + p++; + *r = Char('\0'); + + if (argc >= (int)argv.size()-1) // expand array + argv.resize(argv.size()*2); + argv[argc++] = start; + } + } + argv[argc] = 0; + + return argv; +} + +QStringList QInstaller::parseCommandLineArgs(int argc, char **argv) +{ + Q_UNUSED(argc) + Q_UNUSED(argv) + + QStringList arguments; + QString cmdLine = QString::fromWCharArray(GetCommandLine()); + + QVector<wchar_t*> args = qWinCmdLine<wchar_t>((wchar_t *)cmdLine.utf16(), cmdLine.length(), argc); + for (int a = 0; a < argc; ++a) + arguments << QString::fromWCharArray(args[a]); + return arguments; +} +#else +QStringList QInstaller::parseCommandLineArgs(int argc, char **argv) +{ + QStringList arguments; + for (int a = 0; a < argc; ++a) + arguments << QString::fromLocal8Bit(argv[a]); + return arguments; +} +#endif + +#ifdef Q_OS_WIN +// taken from qprocess_win.cpp +static QString qt_create_commandline(const QString &program, const QStringList &arguments) +{ + QString args; + if (!program.isEmpty()) { + QString programName = program; + if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) + && programName.contains(QLatin1Char(' '))) { + programName = QLatin1Char('\"') + programName + QLatin1Char('\"'); + } + programName.replace(QLatin1Char('/'), QLatin1Char('\\')); + + // add the prgram as the first arg ... it works better + args = programName + QLatin1Char(' '); + } + + for (int i = 0; i < arguments.size(); ++i) { + QString tmp = arguments.at(i); + // in the case of \" already being in the string the \ must also be escaped + tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\"")); + // escape a single " because the arguments will be parsed + tmp.replace(QLatin1Char('\"'), QLatin1String("\\\"")); + if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + QString endQuote(QLatin1Char('\"')); + int i = tmp.length(); + while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) { + --i; + endQuote += QLatin1Char('\\'); + } + args += QLatin1String(" \"") + tmp.left(i) + endQuote; + } else { + args += QLatin1Char(' ') + tmp; + } + } + return args; +} + +QString QInstaller::createCommandline(const QString &program, const QStringList &arguments) +{ + return qt_create_commandline(program, arguments); +} +#endif diff --git a/installerbuilder/libinstaller/utils.h b/installerbuilder/libinstaller/utils.h new file mode 100644 index 000000000..cbf2e95e7 --- /dev/null +++ b/installerbuilder/libinstaller/utils.h @@ -0,0 +1,92 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef QINSTALLER_UTILS_H +#define QINSTALLER_UTILS_H + +#include "installer_global.h" + +#include <QtCore/QBuffer> +#include <QtCore/QCryptographicHash> +#include <QtCore/QHash> +#include <QtCore/QUrl> +#include <QtCore/QTextStream> + +#include <ostream> + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace QInstaller { + + QByteArray INSTALLER_EXPORT calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo); + + QString INSTALLER_EXPORT replaceVariables(const QHash<QString,QString> &vars, const QString &str); + QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str); + QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv); +#ifdef Q_OS_WIN + QString createCommandline(const QString &program, const QStringList &arguments); +#endif + + void INSTALLER_EXPORT setVerbose(bool v); + bool INSTALLER_EXPORT isVerbose(); + + INSTALLER_EXPORT std::ostream& stdverbose(); + INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string); + + class VerboseWriter; + INSTALLER_EXPORT VerboseWriter &verbose(); + + class INSTALLER_EXPORT VerboseWriter : public QObject + { + Q_OBJECT + public: + VerboseWriter(QObject *parent = 0); + ~VerboseWriter(); + + static VerboseWriter *instance(); + + inline VerboseWriter &operator<<(const char *t) { stdverbose() << t; stream << t; return *this; } + inline VerboseWriter &operator<<(std::ostream& (*f)(std::ostream &s)) { stdverbose() << *f; stream << "\n"; return *this; } + public slots: + void setOutputStream(const QString &fileName); + + private: + QTextStream stream; + QBuffer preFileBuffer; + QString logFileName; + }; + +} + +#endif // QINSTALLER_UTILS_H diff --git a/installerbuilder/libinstaller/zipjob.cpp b/installerbuilder/libinstaller/zipjob.cpp new file mode 100644 index 000000000..bcc617d31 --- /dev/null +++ b/installerbuilder/libinstaller/zipjob.cpp @@ -0,0 +1,206 @@ +/************************************************************************** +** +** 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 <zipjob.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QMetaType> +#include <QtCore/QStringList> + +#include <cassert> +#include <climits> + +class ZipJob::Private +{ +public: + Private() : outputDevice(0), process(0) {} + + QIODevice *outputDevice; + QDir workingDir; + QProcess *process; + QStringList filesToArchive; +}; + +Q_DECLARE_METATYPE(QProcess::ExitStatus) + +ZipJob::ZipJob() + : d(new Private()) +{ + qRegisterMetaType<QProcess::ExitStatus>(); +} + +ZipJob::~ZipJob() +{ + delete d; +} + +void ZipJob::run() +{ + assert(!d->process); + d->process = new QProcess; + d->process->setWorkingDirectory(d->workingDir.absolutePath()); + QStringList args; + args << QLatin1String( "-" ) << QLatin1String( "-r" ) << d->filesToArchive; + connect(d->process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); + connect(d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); + connect(d->process, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyReadStandardOutput())); + + d->process->start(QLatin1String("zip"), args); + if (!d->process->waitForStarted()) { + //TODO handle + } + + if (!d->process->waitForFinished(INT_MAX)) { + //TODO handle + } + + delete d->process; + d->process = 0; + // emit result +} + +void ZipJob::processError(QProcess::ProcessError) +{ + emit error(); +} + +void ZipJob::processFinished(int, QProcess::ExitStatus) +{ + emit finished(); +} + +void ZipJob::processReadyReadStandardOutput() +{ + const QByteArray buf = d->process->readAll(); + const qint64 toWrite = buf.size(); + qint64 written = 0; + while (written < toWrite) { + const qint64 num = d->outputDevice->write(buf.constData() + written, toWrite - written); + if (num < 0) { + //TODO: handle error + return; + } + written += num; + } +} + +void ZipJob::setOutputDevice(QIODevice *device) +{ + d->outputDevice = device; +} + +void ZipJob::setWorkingDirectory(const QDir &dir) +{ + d->workingDir = dir; +} + +void ZipJob::setFilesToArchive(const QStringList &files) +{ + d->filesToArchive = files; +} + +class UnzipJob::Private +{ +public: + Private() : inputDevice(0) {} + + QIODevice *inputDevice; + QString outputPath; + QStringList filesToExtract; +}; + +UnzipJob::UnzipJob() + : d(new Private()) +{ + qRegisterMetaType<QProcess::ExitStatus>(); +} + +UnzipJob::~UnzipJob() +{ + delete d; +} + +void UnzipJob::setInputDevice(QIODevice *device) +{ + d->inputDevice = device; +} + +void UnzipJob::setOutputPath(const QString &path) +{ + d->outputPath = path; +} + +void UnzipJob::processError(QProcess::ProcessError) +{ + emit error(); +} + +void UnzipJob::run() +{ + QProcess process; + // TODO: this won't work on Windows... grmpfl, but on Mac and Linux, at least... + QStringList args; + args << QLatin1String( "/dev/stdin" ); + if (!d->filesToExtract.isEmpty()) + args << QLatin1String("-x") << d->filesToExtract; + process.setWorkingDirectory(d->outputPath); + process.start(QLatin1String("unzip"), args); + connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); + connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus ))); + if (!process.waitForStarted()) { + // TODO handle + return; + } + + const int bufferSize = 4096; + QByteArray buffer; + while (d->inputDevice->bytesAvailable() > 0 || d->inputDevice->waitForReadyRead(INT_MAX)) { + buffer = d->inputDevice->read(bufferSize); + process.write(buffer); + process.waitForBytesWritten(INT_MAX); + } + process.closeWriteChannel(); + + if (!process.waitForFinished(INT_MAX)) { + // TODO handle + } +} + +void UnzipJob::processFinished(int, QProcess::ExitStatus) +{ + emit finished(); +} + +void UnzipJob::setFilesToExtract(const QStringList &files) +{ + d->filesToExtract = files; +} diff --git a/installerbuilder/libinstaller/zipjob.h b/installerbuilder/libinstaller/zipjob.h new file mode 100644 index 000000000..72ab40796 --- /dev/null +++ b/installerbuilder/libinstaller/zipjob.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2010-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. +** +**************************************************************************/ + +#ifndef ZIPJOB_H +#define ZIPJOB_H + +#include <QProcess> +#include <QRunnable> + +QT_BEGIN_NAMESPACE +class QDir; +class QIODevice; +class QStringList; +QT_END_NAMESPACE + +class ZipJob : public QObject, public QRunnable +{ + Q_OBJECT + +public: + ZipJob(); + ~ZipJob(); + + void setOutputDevice(QIODevice *device); + void setWorkingDirectory(const QDir &dir); + void setFilesToArchive(const QStringList &files); + + void run(); + +Q_SIGNALS: + void finished(); + void error(); + +private Q_SLOTS: + void processError(QProcess::ProcessError); + void processFinished(int, QProcess::ExitStatus); + void processReadyReadStandardOutput(); + +private: + class Private; + Private *const d; +}; + +class UnzipJob : public QObject, public QRunnable +{ + Q_OBJECT + +public: + UnzipJob(); + ~UnzipJob(); + + void setInputDevice(QIODevice *device); + void setOutputPath(const QString &path); + void setFilesToExtract(const QStringList &files); + + void run(); + +Q_SIGNALS: + void finished(); + void error(); + +private Q_SLOTS: + void processError(QProcess::ProcessError); + void processFinished(int, QProcess::ExitStatus); + +private: + class Private; + Private *const d; +}; + +#endif // ZIPJOB_H |