forked from qt-creator/qt-creator
		
	
		
			
				
	
	
		
			1873 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1873 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/***************************************************************************
 | 
						|
**
 | 
						|
** This file is part of Qt Creator
 | 
						|
**
 | 
						|
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
 | 
						|
**
 | 
						|
** Contact:  Qt Software Information (qt-info@nokia.com)
 | 
						|
**
 | 
						|
**
 | 
						|
** Non-Open Source Usage
 | 
						|
**
 | 
						|
** Licensees may use this file in accordance with the Qt Beta Version
 | 
						|
** License Agreement, Agreement version 2.2 provided with the Software or,
 | 
						|
** alternatively, in accordance with the terms contained in a written
 | 
						|
** agreement between you and Nokia.
 | 
						|
**
 | 
						|
** GNU General Public License Usage
 | 
						|
**
 | 
						|
** Alternatively, this file may be used under the terms of the GNU General
 | 
						|
** Public License versions 2.0 or 3.0 as published by the Free Software
 | 
						|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
 | 
						|
** of this file.  Please review the following information to ensure GNU
 | 
						|
** General Public Licensing requirements will be met:
 | 
						|
**
 | 
						|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
 | 
						|
** http://www.gnu.org/copyleft/gpl.html.
 | 
						|
**
 | 
						|
** In addition, as a special exception, Nokia gives you certain additional
 | 
						|
** rights. These rights are described in the Nokia Qt GPL Exception
 | 
						|
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
 | 
						|
**
 | 
						|
***************************************************************************/
 | 
						|
 | 
						|
#include "qinstaller.h"
 | 
						|
 | 
						|
#include <QtCore/QBuffer>
 | 
						|
#include <QtCore/QCoreApplication>
 | 
						|
#include <QtCore/QDateTime>
 | 
						|
#include <QtCore/QDebug>
 | 
						|
#include <QtCore/QDir>
 | 
						|
#include <QtCore/QDirIterator>
 | 
						|
#include <QtCore/QFile>
 | 
						|
#include <QtCore/QHash>
 | 
						|
#include <QtCore/QProcess>
 | 
						|
#include <QtCore/QSettings>
 | 
						|
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
#include "qt_windows.h"
 | 
						|
#include <shlobj.h>
 | 
						|
#endif
 | 
						|
 | 
						|
QT_BEGIN_NAMESPACE
 | 
						|
 | 
						|
/*
 | 
						|
 FIXME: Documentation
 | 
						|
 | 
						|
NAME = "Name";
 | 
						|
DISPLAY_NAME = "DisplayName";
 | 
						|
DESCRIPTION = "Description";
 | 
						|
TASK_COUNT = "TaskCount";
 | 
						|
SIZE = "ComponentSize";
 | 
						|
OUTPUT_FILE = "OutputFile";
 | 
						|
WANTED_STATE = "WantedState";
 | 
						|
SUGGESTED_STATE = "SuggestedState";
 | 
						|
PRODUCT_NAME = "ProductName";
 | 
						|
GUI_START_PAGE = "GuiStartPage";
 | 
						|
RUN_PROGRAM = "RunProgram";
 | 
						|
COMMENTS = "Comments";
 | 
						|
CONTACT = "Contact";
 | 
						|
DISPLAY_VERSION = "DisplayVersion";
 | 
						|
ESTIMATED_SIZE = "EstimatedSize";
 | 
						|
HELP_LINK = "HelpLink";
 | 
						|
INSTALL_DATE = "InstallDate";
 | 
						|
INSTALL_LOCATION = "InstallLocation";
 | 
						|
NO_MODIFY = "NoModify";
 | 
						|
NO_REPAIR = "NoRepair";
 | 
						|
PUBLISHER = "Publisher";
 | 
						|
UNINSTALL_STRING = "UninstallString";
 | 
						|
URL_INFO_ABOUT = "UrlInfoAbout";
 | 
						|
LOGO_PIXMAP
 | 
						|
WATERMARK_PIXMAP
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
//static qint64 magicInstallerMarker   = (0xdea0d345UL << 32) + 0x12023233UL;
 | 
						|
//static qint64 magicUninstallerMarker = (0xdea0d345UL << 32) + 0x12023234UL;
 | 
						|
static const qint64 magicInstallerMarker      =  0x12023233UL;
 | 
						|
static const qint64 magicUninstallerMarker    =  0x12023234UL;
 | 
						|
static const qint64 magicTempUninstallerMarker =  0x12023235UL;
 | 
						|
 | 
						|
// Installer Layout:
 | 
						|
//
 | 
						|
//   0000:                            <binary: installer code>
 | 
						|
//     ...
 | 
						|
//   $comptask[0]:                    <int:  $ctc[0]: 1st component task count>
 | 
						|
//     $comptask[0][0]:               <task: first component task data>
 | 
						|
//     ...
 | 
						|
//     $comptask[0][$ctc0-1]:         <task: first component task data>
 | 
						|
//    ...
 | 
						|
//   $comptask[$ncomp-1]:             <int : $cnn: last component task data>
 | 
						|
//     $comptask[$ncomp-1][0]:        <task: last component task data>
 | 
						|
//     ...
 | 
						|
//     $comtaskp[$ncomp-1][$ctc-1]:   <task: last component task data>
 | 
						|
//   $compvars[0]:                    <dict:  $cn0: first component variables>
 | 
						|
//     ...
 | 
						|
//   $compvars[$ncomp-1]:             <dict:  $cnn: last component var data>
 | 
						|
//   $comptaskoffsets:                <int:  $comptask[0]: offset>
 | 
						|
//                                    <int:  $comptask[1]: offset>
 | 
						|
//     ...
 | 
						|
//   $compvarsoffsets:                <int:  $compvars[0]: offset>
 | 
						|
//                                    <int:  $compvars[1]: offset>
 | 
						|
//     ...
 | 
						|
//   $installervars:                  <dict: installer variable data>
 | 
						|
//     ...
 | 
						|
//   end - 7:                         <int:  $comptask[0]: start of comp.tasks>
 | 
						|
//   end - 6:                         <int:  $compvars[0]: start of .vars>
 | 
						|
//   end - 5:                         <int:  $ncomp: number of components>
 | 
						|
//   end - 4:                         <int:  $comptaskoffsets>
 | 
						|
//   end - 3:                         <int:  $compvarsoffsets>
 | 
						|
//   end - 2:                         <int:  $installervars: offset installer vars>
 | 
						|
//   end - 1:                         <int:  magic marker>
 | 
						|
//
 | 
						|
// The stuff after the binary is not present in the "Creator" incarnation
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Misc helpers
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
#define ifVerbose(s) if (!installer()->isVerbose()) {} else { qDebug() << s; }
 | 
						|
 | 
						|
 | 
						|
QDebug &operator<<(QDebug &os, QInstallerTask *task)
 | 
						|
{
 | 
						|
    task->dump(os);
 | 
						|
    return os;
 | 
						|
}
 | 
						|
 | 
						|
class Dictionary : public QHash<QString, QString>
 | 
						|
{
 | 
						|
public:
 | 
						|
    typedef QHash<QString, QString> BaseType;
 | 
						|
 | 
						|
    void setValue(const QString &key, const QString &val)
 | 
						|
    {
 | 
						|
        insert(key, val);
 | 
						|
    }
 | 
						|
 | 
						|
    void removeTemporaryKeys()
 | 
						|
    {
 | 
						|
        foreach (const QString &key, keys())
 | 
						|
            if (key.startsWith('@'))
 | 
						|
                remove(key);
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
class Error : public QInstallerError
 | 
						|
{
 | 
						|
public:
 | 
						|
    Error(const QString &m)
 | 
						|
        : QInstallerError(m) {}
 | 
						|
    // convenience
 | 
						|
    Error(const char *m, int n)
 | 
						|
        : QInstallerError(QString(QLatin1String(m)).arg(n)) {}
 | 
						|
    Error(const char *m, const QString & n)
 | 
						|
        : QInstallerError(QString(QLatin1String(m)).arg(n)) {}
 | 
						|
private:
 | 
						|
private:
 | 
						|
    QString m_message;
 | 
						|
};
 | 
						|
 | 
						|
} // anonymous namespace
 | 
						|
 | 
						|
static void openForWrite(QFile &file)
 | 
						|
{
 | 
						|
    if (!file.open(QIODevice::WriteOnly))
 | 
						|
        throw Error("Cannot open file %1 for writing", file.fileName());
 | 
						|
}
 | 
						|
 | 
						|
static void openForRead(QFile &file)
 | 
						|
{
 | 
						|
    if (!file.open(QIODevice::ReadOnly))
 | 
						|
        throw Error("Cannot open file %1 for reading", file.fileName());
 | 
						|
}
 | 
						|
 | 
						|
static void rawWrite(QIODevice *out, const char *buffer, qint64 size)
 | 
						|
{
 | 
						|
    while (size > 0) {
 | 
						|
        qint64 n = out->write(buffer, size);
 | 
						|
        if (n == -1)
 | 
						|
            throw Error("RAW WRITE FAILED: %1", size);
 | 
						|
        size -= n;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void rawRead(QIODevice *in, char *buffer, qint64 size)
 | 
						|
{
 | 
						|
    while (size > 0) {
 | 
						|
        qint64 n = in->read(buffer, size);
 | 
						|
        size -= n;
 | 
						|
        buffer += n;
 | 
						|
        if (size != 0)
 | 
						|
            qDebug() << "COULD ONLY READ " << n << "OF" << size + n << "BYTES";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static inline QByteArray &theBuffer(int size)
 | 
						|
{
 | 
						|
    static QByteArray b;
 | 
						|
    if (size > b.size())
 | 
						|
        b.reserve(size * 3 / 2);
 | 
						|
    return b;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#if 0
 | 
						|
// Faster or not?
 | 
						|
static void appendFileData(QIODevice *out, const QString &fileName)
 | 
						|
{
 | 
						|
    QFile file(fileName);
 | 
						|
    openForRead(file);
 | 
						|
    qint64 size = file.size();
 | 
						|
    QInstaller::appendInt(out, size);
 | 
						|
    if (size == 0)
 | 
						|
        return;
 | 
						|
    uchar *data = file.map(0, size);
 | 
						|
    if (!data)
 | 
						|
        throw Error(QInstaller::tr("Cannot map file %1").arg(file.fileName()));
 | 
						|
    rawWrite(out, (const char *)data, size);
 | 
						|
    if (!file.unmap(data))
 | 
						|
        throw Error(QInstaller::tr("Cannot unmap file %1").arg(file.fileName()));
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static void appendFileData(QIODevice *out, QIODevice *in)
 | 
						|
{
 | 
						|
    Q_ASSERT(!in->isSequential());
 | 
						|
    qint64 size = in->size();
 | 
						|
    QByteArray &b = theBuffer(size);
 | 
						|
    rawRead(in, b.data(), size);
 | 
						|
    QInstaller::appendInt(out, size);
 | 
						|
    rawWrite(out, b.constData(), size);
 | 
						|
}
 | 
						|
 | 
						|
static void retrieveFileData(QIODevice *out, QIODevice *in)
 | 
						|
{
 | 
						|
    qint64 size = QInstaller::retrieveInt(in);
 | 
						|
    QByteArray &b = theBuffer(size);
 | 
						|
    rawRead(in, b.data(), size);
 | 
						|
    rawWrite(out, b.constData(), size);
 | 
						|
}
 | 
						|
 | 
						|
static void appendData(QIODevice *out, QIODevice *in, qint64 size)
 | 
						|
{
 | 
						|
    QByteArray &b = theBuffer(size);
 | 
						|
    rawRead(in, b.data(), size);
 | 
						|
    rawWrite(out, b.constData(), size);
 | 
						|
}
 | 
						|
 | 
						|
static void appendInt(QIODevice *out, qint64 n)
 | 
						|
{
 | 
						|
    rawWrite(out, (char*)&n, sizeof(n));
 | 
						|
}
 | 
						|
 | 
						|
static void appendString(QIODevice *out, const QString &str)
 | 
						|
{
 | 
						|
    int n = str.size();
 | 
						|
    appendInt(out, n);
 | 
						|
    rawWrite(out, (char*)str.utf16(), n * sizeof(QChar));
 | 
						|
}
 | 
						|
 | 
						|
static void appendByteArray(QIODevice *out, const QByteArray &ba)
 | 
						|
{
 | 
						|
    appendInt(out, ba.size());
 | 
						|
    rawWrite(out, ba.constData(), ba.size());
 | 
						|
}
 | 
						|
 | 
						|
static void appendDictionary(QIODevice *out, const Dictionary &dict)
 | 
						|
{
 | 
						|
    appendInt(out, dict.size());
 | 
						|
    foreach (const QString &key, dict.keys()) {
 | 
						|
        appendString(out, key);
 | 
						|
        appendString(out, dict.value(key));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static qint64 retrieveInt(QIODevice *in)
 | 
						|
{
 | 
						|
    qint64 n = 0;
 | 
						|
    in->read((char*)&n, sizeof(n));
 | 
						|
    return n;
 | 
						|
}
 | 
						|
 | 
						|
static QString retrieveString(QIODevice *in)
 | 
						|
{
 | 
						|
    static QByteArray b;
 | 
						|
    qint64 n = retrieveInt(in);
 | 
						|
    if (n * int(sizeof(QChar)) > b.size())
 | 
						|
        b.reserve(n * sizeof(QChar) * 3 / 2);
 | 
						|
    in->read(b.data(), n * sizeof(QChar));
 | 
						|
    QString str((QChar *)b.data(), n);
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
static QByteArray retrieveByteArray(QIODevice *in)
 | 
						|
{
 | 
						|
    QByteArray ba;
 | 
						|
    qint64 n = retrieveInt(in);
 | 
						|
    ba.resize(n);
 | 
						|
    rawRead(in, ba.data(), n);
 | 
						|
    return ba;
 | 
						|
}
 | 
						|
 | 
						|
static Dictionary retrieveDictionary(QIODevice *in)
 | 
						|
{
 | 
						|
    Dictionary dict;
 | 
						|
    for (qint64 i = retrieveInt(in); --i >= 0; ) {
 | 
						|
        QString key = retrieveString(in);
 | 
						|
        dict.insert(key, retrieveString(in));
 | 
						|
    }
 | 
						|
    return dict;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerComponent::Private
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
class QInstallerComponent::Private
 | 
						|
{
 | 
						|
public:
 | 
						|
    QInstaller *m_installer;
 | 
						|
    Dictionary m_vars;
 | 
						|
    QList<QInstallerTask *> m_tasks;
 | 
						|
 | 
						|
    // filled before intaller runs
 | 
						|
    qint64 m_offsetInInstaller;
 | 
						|
};
 | 
						|
 | 
						|
#if 0
 | 
						|
 | 
						|
// this is dead slow as QDirIterator::Private::advance needlessly
 | 
						|
// creates tons of QFileInfo objects
 | 
						|
 | 
						|
static void listDir
 | 
						|
    (const QString &sourcePath0, const QString &targetPath0,
 | 
						|
    QList<QInstallerTask *> *copyTasks,
 | 
						|
    QList<QInstallerTask *> *linkTasks,
 | 
						|
    int sourcePathLength)
 | 
						|
{
 | 
						|
    Q_UNUSED(sourcePathLength);
 | 
						|
    QDirIterator it(sourcePath0, QDir::Files, QDirIterator::Subdirectories);
 | 
						|
    const int pos = sourcePath0.size();
 | 
						|
    while (it.hasNext()) {
 | 
						|
        QString sourcePath = it.next();
 | 
						|
        QFileInfo sourceInfo = it.fileInfo();
 | 
						|
        if (sourceInfo.isSymLink()) {
 | 
						|
            QFileInfo absSourceInfo(sourceInfo.absoluteFilePath());
 | 
						|
            //QString linkTarget = sourceInfo.symLinkTarget();
 | 
						|
            QString absLinkTarget = absSourceInfo.symLinkTarget();
 | 
						|
            //QString relPath = sourceInfo.dir().relativeFilePath(linkTarget);
 | 
						|
            QString absRelPath = absSourceInfo.dir().relativeFilePath(absLinkTarget);
 | 
						|
            if (0) {
 | 
						|
                ifVerbose("\n\nCREATING LINK: "
 | 
						|
                    << "\nSOURCE : " << sourceInfo.filePath()
 | 
						|
                    << "\nSOURCE ABS: " << absSourceInfo.filePath()
 | 
						|
                    //<< "\nLINK : " << linkTarget
 | 
						|
                    << "\nLINK ABS: " << absLinkTarget
 | 
						|
                    //<< "\nREL : " << relPath
 | 
						|
                    << "\nREL ABS: " << absRelPath);
 | 
						|
            }
 | 
						|
            QString targetPath = targetPath0;
 | 
						|
            targetPath += sourcePath.midRef(pos);
 | 
						|
            //qDebug() << "LINK " << absRelPath << targetPath << targetPath0;
 | 
						|
            QInstallerLinkFileTask *task = new QInstallerLinkFileTask(m_installer);
 | 
						|
            task->setLinkTargetPath(absRelPath);
 | 
						|
            task->setTargetPath(targetPath);
 | 
						|
            task->setPermissions(sourceInfo.permissions());
 | 
						|
            linkTasks->append(task);
 | 
						|
        } else {
 | 
						|
            QInstallerCopyFileTask *task = new QInstallerCopyFileTask(m_installer);
 | 
						|
            QString targetPath = targetPath0;
 | 
						|
            targetPath += sourcePath.midRef(pos);
 | 
						|
            //qDebug() << "COPY " << sourcePath << targetPath << targetPath0;
 | 
						|
            task->setSourcePath(sourcePath);
 | 
						|
            task->setTargetPath(targetPath);
 | 
						|
            task->setPermissions(sourceInfo.permissions());
 | 
						|
            copyTasks.append(task);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
#else
 | 
						|
 | 
						|
static void listDir
 | 
						|
    (const QString &sourcePath, const QString &targetPath0,
 | 
						|
    QList<QInstallerTask *> *copyTasks,
 | 
						|
    QList<QInstallerTask *> *linkTasks,
 | 
						|
    QInstaller *installer,
 | 
						|
    int sourcePathLength = -1)
 | 
						|
 | 
						|
{
 | 
						|
    if (sourcePathLength == -1)
 | 
						|
        sourcePathLength = sourcePath.size();
 | 
						|
    QFileInfo sourceInfo(sourcePath);
 | 
						|
    if (sourceInfo.isDir()) {
 | 
						|
        QDir dir(sourcePath);
 | 
						|
        dir.setSorting(QDir::Unsorted);
 | 
						|
        foreach (const QFileInfo &fi, dir.entryInfoList()) {
 | 
						|
            QString sourceFile = fi.fileName();
 | 
						|
            if (sourceFile == QLatin1String(".")
 | 
						|
                    || sourceFile == QLatin1String(".."))
 | 
						|
                continue;
 | 
						|
            listDir(sourcePath + '/' + sourceFile, targetPath0,
 | 
						|
                copyTasks, linkTasks, installer, sourcePathLength);
 | 
						|
        }
 | 
						|
    } else if (sourceInfo.isSymLink()) {
 | 
						|
        QFileInfo absSourceInfo(sourceInfo.absoluteFilePath());
 | 
						|
        QString absLinkTarget = absSourceInfo.symLinkTarget();
 | 
						|
        QString absRelPath = absSourceInfo.dir().relativeFilePath(absLinkTarget);
 | 
						|
        QString targetPath = targetPath0;
 | 
						|
        targetPath += sourcePath.midRef(sourcePathLength);
 | 
						|
        //qDebug() << "LINK " << absRelPath << targetPath << targetPath0;
 | 
						|
        QInstallerLinkFileTask *task = new QInstallerLinkFileTask(installer);
 | 
						|
        task->setLinkTargetPath(absRelPath);
 | 
						|
        task->setTargetPath(targetPath);
 | 
						|
        task->setPermissions(sourceInfo.permissions());
 | 
						|
        linkTasks->append(task);
 | 
						|
    } else {
 | 
						|
        QInstallerCopyFileTask *task = new QInstallerCopyFileTask(installer);
 | 
						|
        QString targetPath = targetPath0;
 | 
						|
        targetPath += sourcePath.midRef(sourcePathLength);
 | 
						|
        //qDebug() << "COPY " << sourcePath << targetPath << targetPath0;
 | 
						|
        task->setSourcePath(sourcePath);
 | 
						|
        task->setTargetPath(targetPath);
 | 
						|
        task->setPermissions(sourceInfo.permissions());
 | 
						|
        copyTasks->append(task);
 | 
						|
    }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerComponent
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstallerComponent::QInstallerComponent(QInstaller *installer)
 | 
						|
  : d(new QInstallerComponent::Private)
 | 
						|
{
 | 
						|
    d->m_installer = installer;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
QInstallerComponent::~QInstallerComponent()
 | 
						|
{
 | 
						|
    qDeleteAll(d->m_tasks);
 | 
						|
    d->m_tasks.clear();
 | 
						|
    delete d;
 | 
						|
}
 | 
						|
 | 
						|
QString QInstallerComponent::value(const QString &key,
 | 
						|
    const QString &defaultValue) const
 | 
						|
{
 | 
						|
    return d->m_vars.value(key, defaultValue);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerComponent::setValue(const QString &key, const QString &value)
 | 
						|
{
 | 
						|
    d->m_vars[key] = value;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerComponent::appendDirectoryTasks
 | 
						|
    (const QString &sourcePath0, const QString &targetPath)
 | 
						|
{
 | 
						|
    QList<QInstallerTask *> copyTasks;
 | 
						|
    QList<QInstallerTask *> linkTasks;
 | 
						|
    QString sourcePath = d->m_installer->replaceVariables(sourcePath0);
 | 
						|
    listDir(sourcePath, targetPath, ©Tasks, &linkTasks, d->m_installer);
 | 
						|
    d->m_tasks += copyTasks;
 | 
						|
    d->m_tasks += linkTasks;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerComponent::appendSettingsTask
 | 
						|
    (const QString &key, const QString &value)
 | 
						|
{
 | 
						|
    QInstallerWriteSettingsTask *task =
 | 
						|
        new QInstallerWriteSettingsTask(d->m_installer);
 | 
						|
    task->setKey(key);
 | 
						|
    task->setValue(value);
 | 
						|
    appendTask(task);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerComponent::appendTask(QInstallerTask *task)
 | 
						|
{
 | 
						|
    d->m_tasks.append(task);
 | 
						|
}
 | 
						|
 | 
						|
int QInstallerComponent::taskCount() const
 | 
						|
{
 | 
						|
    return d->m_tasks.size();
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *QInstallerComponent::task(int i) const
 | 
						|
{
 | 
						|
    return d->m_tasks.at(i);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstaller::Private
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstallerTask *createCopyFileTask(QInstaller *installer)
 | 
						|
{
 | 
						|
    return new QInstallerCopyFileTask(installer);
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *createLinkFileTask(QInstaller *installer)
 | 
						|
{
 | 
						|
    return new QInstallerLinkFileTask(installer);
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *createWriteSettingsTask(QInstaller *installer)
 | 
						|
{
 | 
						|
    return new QInstallerWriteSettingsTask(installer);
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *createPatchFileTask(QInstaller *installer)
 | 
						|
{
 | 
						|
    return new QInstallerPatchFileTask(installer);
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *createMenuShortcutTask(QInstaller *installer)
 | 
						|
{
 | 
						|
    return new QInstallerMenuShortcutTask(installer);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class QInstaller::Private : public QObject
 | 
						|
{
 | 
						|
    Q_OBJECT;
 | 
						|
 | 
						|
public:
 | 
						|
    explicit Private(QInstaller *);
 | 
						|
    ~Private();
 | 
						|
 | 
						|
    void initialize();
 | 
						|
 | 
						|
    QInstallerTask *createTaskFromCode(int code);
 | 
						|
    void undo(const QList<QInstallerTask *> &tasks);
 | 
						|
    void writeUninstaller(const QList<QInstallerTask *> &tasks);
 | 
						|
    bool statusCanceledOrFailed() const;
 | 
						|
 | 
						|
    void writeInstaller(QIODevice *out);
 | 
						|
    void writeInstaller();
 | 
						|
    void appendCode(QIODevice *out);
 | 
						|
    void runInstaller();
 | 
						|
    void runUninstaller();
 | 
						|
    void deleteUninstaller();
 | 
						|
    QString uninstallerName() const;
 | 
						|
    QString replaceVariables(const QString &str) const;
 | 
						|
    QByteArray replaceVariables(const QByteArray &str) const;
 | 
						|
    QString registerPath() const;
 | 
						|
    void registerInstaller();
 | 
						|
    void unregisterInstaller();
 | 
						|
    QString installerBinaryPath() const;
 | 
						|
    bool isCreator() const;
 | 
						|
    bool isInstaller() const;
 | 
						|
    bool isUninstaller() const;
 | 
						|
    bool isTempUninstaller() const;
 | 
						|
    QInstaller *installer() const { return q; }
 | 
						|
    bool restartTempUninstaller(const QStringList &args);
 | 
						|
    void setInstallationProgress(qint64 progress); // relative to m_totalProgress
 | 
						|
 | 
						|
signals:
 | 
						|
    void installationStarted();
 | 
						|
    void installationFinished();
 | 
						|
    void uninstallationStarted();
 | 
						|
    void uninstallationFinished();
 | 
						|
 | 
						|
public:
 | 
						|
    QInstaller *q;
 | 
						|
 | 
						|
    Dictionary m_vars;
 | 
						|
    QInstaller::InstallerStatus m_status;
 | 
						|
    bool m_verbose;
 | 
						|
 | 
						|
    qint64 m_codeSize;
 | 
						|
    qint64 m_tasksStart;
 | 
						|
    qint64 m_variablesStart;
 | 
						|
    qint64 m_componentCount;
 | 
						|
    qint64 m_tasksOffsetsStart;
 | 
						|
    qint64 m_variablesOffsetsStart;
 | 
						|
    qint64 m_variableDataStart;
 | 
						|
    qint64 m_magicMarker;
 | 
						|
    
 | 
						|
    int m_installationProgress;
 | 
						|
    int m_totalProgress;
 | 
						|
    QString m_installationProgressText;
 | 
						|
 | 
						|
    // Owned. Indexed by component name
 | 
						|
    QList<QInstallerComponent *> m_components;
 | 
						|
    QList<QInstaller::TaskCreator> m_taskCreators;
 | 
						|
};
 | 
						|
 | 
						|
QInstaller::Private::Private(QInstaller *q_)
 | 
						|
  : q(q_), m_status(QInstaller::InstallerUnfinished), m_verbose(false)
 | 
						|
{
 | 
						|
    connect(this, SIGNAL(installationStarted()),
 | 
						|
        q, SIGNAL(installationStarted()));
 | 
						|
    connect(this, SIGNAL(installationFinished()),
 | 
						|
        q, SIGNAL(installationFinished()));
 | 
						|
    connect(this, SIGNAL(uninstallationStarted()),
 | 
						|
        q, SIGNAL(uninstallationStarted()));
 | 
						|
    connect(this, SIGNAL(uninstallationFinished()),
 | 
						|
        q, SIGNAL(uninstallationFinished()));
 | 
						|
}
 | 
						|
 | 
						|
QInstaller::Private::~Private()
 | 
						|
{
 | 
						|
    qDeleteAll(m_components);
 | 
						|
    m_components.clear();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void QInstaller::Private::initialize()
 | 
						|
{
 | 
						|
    m_installationProgress = 0;
 | 
						|
    m_totalProgress = 100;
 | 
						|
 | 
						|
    m_vars["ProductName"] = "Unknown Product";
 | 
						|
    m_vars["LogoPixmap"] = ":/resources/logo.png";
 | 
						|
    m_vars["WatermarkPixmap"] = ":/resources/watermark.png";
 | 
						|
 | 
						|
    QFile in(installerBinaryPath());
 | 
						|
    openForRead(in);
 | 
						|
    m_codeSize = in.size();
 | 
						|
 | 
						|
    // this reads bogus values for 'creators', but it does not harm
 | 
						|
    in.seek(in.size() - 7 * sizeof(qint64));
 | 
						|
    m_tasksStart = retrieveInt(&in);
 | 
						|
    m_variablesStart = retrieveInt(&in);
 | 
						|
    m_componentCount = retrieveInt(&in);
 | 
						|
    m_tasksOffsetsStart = retrieveInt(&in);
 | 
						|
    m_variablesOffsetsStart = retrieveInt(&in);
 | 
						|
    m_variableDataStart = retrieveInt(&in);
 | 
						|
    m_magicMarker = retrieveInt(&in);
 | 
						|
 | 
						|
    if (isCreator()) {
 | 
						|
        // valgrind complains otherwise
 | 
						|
        m_tasksStart = 0;
 | 
						|
        m_variablesStart = 0;
 | 
						|
        m_componentCount = 0;
 | 
						|
        m_tasksOffsetsStart = 0;
 | 
						|
        m_variablesOffsetsStart = 0;
 | 
						|
        m_variableDataStart = 0;
 | 
						|
        m_magicMarker = 0;
 | 
						|
    } else {
 | 
						|
        // fix code size
 | 
						|
        m_codeSize = m_tasksStart;
 | 
						|
 | 
						|
        // merge stored variables
 | 
						|
        in.seek(m_variablesStart);
 | 
						|
 | 
						|
        for (int i = 0; i != m_componentCount; ++i) {
 | 
						|
            QInstallerComponent *component = new QInstallerComponent(q);
 | 
						|
            component->d->m_vars = retrieveDictionary(&in);
 | 
						|
            qDebug() << "DICT  " << i << component->d->m_vars;
 | 
						|
            m_components.append(component);
 | 
						|
        }
 | 
						|
 | 
						|
        // read installer variables
 | 
						|
        Dictionary dict = retrieveDictionary(&in);
 | 
						|
        if (m_verbose)
 | 
						|
            qDebug() << "READ VARIABLES FROM INSTALLER:" << dict;
 | 
						|
        foreach (const QString &key, dict.keys()) {
 | 
						|
            if (!m_vars.contains(key))
 | 
						|
                m_vars.insert(key, dict.value(key));
 | 
						|
        }
 | 
						|
        if (m_verbose)
 | 
						|
            qDebug() << "MERGED VARIABLES:" << m_vars;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::setInstallationProgress(qint64 progress)
 | 
						|
{
 | 
						|
    // from 0 to m_totalProgress
 | 
						|
    int percent = progress * 100 / m_totalProgress;
 | 
						|
    if (percent == m_installationProgress)
 | 
						|
        return;
 | 
						|
    //qDebug() << "setting progress to " << progress
 | 
						|
    //    << " of " << m_totalProgress << " " << percent << "%";
 | 
						|
    m_installationProgress = percent;
 | 
						|
    qApp->processEvents();
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::Private::installerBinaryPath() const
 | 
						|
{
 | 
						|
    return qApp->arguments().at(0);
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::isCreator() const
 | 
						|
{
 | 
						|
    return !isInstaller() && !isUninstaller() && !isTempUninstaller();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::isInstaller() const
 | 
						|
{
 | 
						|
    return m_magicMarker == magicInstallerMarker;
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::isUninstaller() const
 | 
						|
{
 | 
						|
    return m_magicMarker == magicUninstallerMarker;
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::isTempUninstaller() const
 | 
						|
{
 | 
						|
    return m_magicMarker == magicTempUninstallerMarker;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::writeInstaller()
 | 
						|
{
 | 
						|
    QString fileName = m_vars["OutputFile"];
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    if (!fileName.endsWith(QLatin1String(".exe")))
 | 
						|
        fileName += QLatin1String(".exe");
 | 
						|
#endif
 | 
						|
    QFile out;
 | 
						|
    out.setFileName(fileName);
 | 
						|
    openForWrite(out);
 | 
						|
    writeInstaller(&out);
 | 
						|
    out.setPermissions(out.permissions() | QFile::WriteUser
 | 
						|
        | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::writeInstaller(QIODevice *out)
 | 
						|
{
 | 
						|
    appendCode(out);
 | 
						|
 | 
						|
    QList<qint64> tasksOffsets;
 | 
						|
    QList<qint64> variablesOffsets;
 | 
						|
 | 
						|
    // write component task data
 | 
						|
    foreach (QInstallerComponent *component, m_components) {
 | 
						|
        qint64 componentStart = out->size();
 | 
						|
        tasksOffsets.append(out->size()); // record start of tasks
 | 
						|
        // pack the component as a whole
 | 
						|
        QBuffer buffer;
 | 
						|
        buffer.open(QIODevice::WriteOnly);
 | 
						|
        foreach (QInstallerTask *task, component->d->m_tasks) {
 | 
						|
            appendInt(&buffer, q->indexOfTaskType(task->creator()));
 | 
						|
            task->writeToInstaller(&buffer);
 | 
						|
        }
 | 
						|
        buffer.close();
 | 
						|
        QByteArray compressed = qCompress(buffer.buffer());
 | 
						|
        int uncompressedSize = buffer.buffer().size();
 | 
						|
        int compressedSize = compressed.size();
 | 
						|
        appendByteArray(out, compressed);
 | 
						|
        qDebug() << "COMPRESS: " << uncompressedSize << compressedSize;
 | 
						|
        component->setValue("TaskCount", QString::number(component->d->m_tasks.size()));
 | 
						|
        component->setValue("ComponentStart", QString::number(componentStart));
 | 
						|
        component->setValue("CompressedSize", QString::number(compressedSize));
 | 
						|
        component->setValue("UncompressedSize", QString::number(uncompressedSize));
 | 
						|
    }
 | 
						|
 | 
						|
    // write component variables
 | 
						|
    foreach (QInstallerComponent *component, m_components) {
 | 
						|
        variablesOffsets.append(out->size()); // record start of variables
 | 
						|
        appendDictionary(out, component->d->m_vars);
 | 
						|
    }
 | 
						|
 | 
						|
    // append variables except temporary ones
 | 
						|
    qint64 variableDataStart = out->size();
 | 
						|
    Dictionary dict = m_vars;
 | 
						|
    dict.removeTemporaryKeys();
 | 
						|
    appendDictionary(out, dict);
 | 
						|
 | 
						|
    // append recorded list of component task offsets
 | 
						|
    qint64 taskOffsetsStart = out->size();
 | 
						|
    foreach (qint64 offset, tasksOffsets)
 | 
						|
        appendInt(out, offset);
 | 
						|
 | 
						|
    // append recorded list of component varaibles offsets
 | 
						|
    qint64 variablesOffsetsStart = out->size();
 | 
						|
    foreach (qint64 offset, variablesOffsets)
 | 
						|
        appendInt(out, offset);
 | 
						|
 | 
						|
    // append trailer
 | 
						|
    appendInt(out, tasksOffsets[0]);
 | 
						|
    appendInt(out, variablesOffsets[0]);
 | 
						|
    appendInt(out, m_components.size());
 | 
						|
    appendInt(out, taskOffsetsStart);
 | 
						|
    appendInt(out, variablesOffsetsStart);
 | 
						|
    appendInt(out, variableDataStart);
 | 
						|
    appendInt(out, magicInstallerMarker);
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::statusCanceledOrFailed() const
 | 
						|
{
 | 
						|
    return m_status == QInstaller::InstallerCanceledByUser
 | 
						|
        || m_status == QInstaller::InstallerFailed;
 | 
						|
}
 | 
						|
 | 
						|
QInstallerTask *QInstaller::Private::createTaskFromCode(int code)
 | 
						|
{
 | 
						|
    if (code >= 0 && code < m_taskCreators.size())
 | 
						|
        return m_taskCreators[code](q);
 | 
						|
    throw Error("NO TASK WITH CODE %1  REGISTERED");
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::undo(const QList<QInstallerTask *> &tasks)
 | 
						|
{
 | 
						|
    //qDebug() << "REMOVING" << files.size();
 | 
						|
    // tasks.size() corresponds to m_installationProgress;
 | 
						|
    m_totalProgress = tasks.size() * m_installationProgress / 100 + 1;
 | 
						|
    for (int i = tasks.size(); --i >= 0; ) {
 | 
						|
        QInstallerTask *task = tasks.at(i);
 | 
						|
        setInstallationProgress(i);
 | 
						|
        task->undo();
 | 
						|
    }
 | 
						|
    setInstallationProgress(0);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::appendCode(QIODevice *out)
 | 
						|
{
 | 
						|
    QFile in(installerBinaryPath());
 | 
						|
    openForRead(in);
 | 
						|
    if (m_verbose)
 | 
						|
        qDebug() << "CODE SIZE: " << m_codeSize;
 | 
						|
    appendData(out, &in, m_codeSize);
 | 
						|
    in.close();
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::Private::replaceVariables(const QString &str) const
 | 
						|
{
 | 
						|
    QString res;
 | 
						|
    int pos = 0;
 | 
						|
    while (true) {
 | 
						|
        int pos1 = str.indexOf('@', pos);
 | 
						|
        if (pos1 == -1)
 | 
						|
            break;
 | 
						|
        int pos2 = str.indexOf('@', pos1 + 1);
 | 
						|
        if (pos2 == -1)
 | 
						|
            break;
 | 
						|
        res += str.mid(pos, pos1 - pos);
 | 
						|
        QString name = str.mid(pos1 + 1, pos2 - pos1 - 1);
 | 
						|
        res += m_vars.value(name);
 | 
						|
        pos = pos2 + 1;
 | 
						|
    }
 | 
						|
    res += str.mid(pos);
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
QByteArray QInstaller::Private::replaceVariables(const QByteArray &ba) const
 | 
						|
{
 | 
						|
    QByteArray res;
 | 
						|
    int pos = 0;
 | 
						|
    while (true) {
 | 
						|
        int pos1 = ba.indexOf('@', pos);
 | 
						|
        if (pos1 == -1)
 | 
						|
            break;
 | 
						|
        int pos2 = ba.indexOf('@', pos1 + 1);
 | 
						|
        if (pos2 == -1)
 | 
						|
            break;
 | 
						|
        res += ba.mid(pos, pos1 - pos);
 | 
						|
        QString name = QString::fromLocal8Bit(ba.mid(pos1 + 1, pos2 - pos1 - 1));
 | 
						|
        res += m_vars.value(name).toLocal8Bit();
 | 
						|
        pos = pos2 + 1;
 | 
						|
    }
 | 
						|
    res += ba.mid(pos);
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::Private::uninstallerName() const
 | 
						|
{
 | 
						|
    QString name = m_vars["TargetDir"];
 | 
						|
    name += "/uninstall";
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    name += QLatin1String(".exe");
 | 
						|
#endif
 | 
						|
    return name;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::writeUninstaller(const QList<QInstallerTask *> &tasks)
 | 
						|
{
 | 
						|
    QFile out(uninstallerName());
 | 
						|
    try {
 | 
						|
        ifVerbose("CREATING UNINSTALLER " << tasks.size());
 | 
						|
        // Create uninstaller. this is basically a clone of ourselves
 | 
						|
        // with a few changed variables
 | 
						|
        openForWrite(out);
 | 
						|
        appendCode(&out);
 | 
						|
        qint64 tasksStart = out.size();
 | 
						|
        appendInt(&out, tasks.size());
 | 
						|
 | 
						|
        for (int i = tasks.size(); --i >= 0; ) {
 | 
						|
            QInstallerTask *task = tasks.at(i);
 | 
						|
            appendInt(&out, m_taskCreators.indexOf(task->creator()));
 | 
						|
            task->writeToUninstaller(&out); // might throw
 | 
						|
        }
 | 
						|
 | 
						|
        // append variables except temporary ones
 | 
						|
        qint64 variableDataStart = out.size();
 | 
						|
        Dictionary dict = m_vars;
 | 
						|
        dict.removeTemporaryKeys();
 | 
						|
        dict.setValue(QLatin1String("UninstallerPath"), uninstallerName());
 | 
						|
        appendDictionary(&out, dict);
 | 
						|
 | 
						|
        // append trailer
 | 
						|
        appendInt(&out, tasksStart);
 | 
						|
        appendInt(&out, variableDataStart); // variablesStart
 | 
						|
        appendInt(&out, 0); // componentCount
 | 
						|
        appendInt(&out, 0); // tasksOffsetsStart
 | 
						|
        appendInt(&out, 0); // variablesOffsetsStart
 | 
						|
        appendInt(&out, variableDataStart);
 | 
						|
        appendInt(&out, magicUninstallerMarker);
 | 
						|
 | 
						|
        out.setPermissions(out.permissions() | QFile::WriteUser
 | 
						|
            | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser);
 | 
						|
    }
 | 
						|
    catch (const QInstallerError &err) {
 | 
						|
        m_status = QInstaller::InstallerFailed;
 | 
						|
        // local roll back
 | 
						|
        qDebug() << "WRITING TO UNINSTALLER FAILED: " << err.message();
 | 
						|
        out.close();
 | 
						|
        out.remove();
 | 
						|
        throw;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::Private::registerPath() const
 | 
						|
{
 | 
						|
    QString productName = m_vars["ProductName"];
 | 
						|
    if (productName.isEmpty())
 | 
						|
        throw QInstallerError("ProductName should be set");
 | 
						|
    QString path;
 | 
						|
    if (m_vars["AllUsers"] == "true")
 | 
						|
        path += "HKEY_LOCAL_MACHINE";
 | 
						|
    else
 | 
						|
        path += "HKEY_CURRENT_USER";
 | 
						|
    path += "\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
 | 
						|
    path += productName;
 | 
						|
    return path;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::registerInstaller()
 | 
						|
{
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    QSettings settings(registerPath(), QSettings::NativeFormat);
 | 
						|
    settings.beginGroup("CurrentVersion");
 | 
						|
    settings.beginGroup("Uninstall");
 | 
						|
    settings.beginGroup(m_vars["ProductName"]);
 | 
						|
    settings.setValue("Comments", m_vars["Comments"]);
 | 
						|
    settings.setValue("Contact", m_vars["Contact"]);
 | 
						|
    settings.setValue("DisplayName", m_vars["ProductName"]);
 | 
						|
    settings.setValue("DisplayVersion", m_vars["DisplayVersion"]);
 | 
						|
    settings.setValue("EstimatedSize", "X4957efb0");
 | 
						|
    settings.setValue("HelpLink", m_vars["HelpLink"]);
 | 
						|
    settings.setValue("InstallDate", QDateTime::currentDateTime().toString());
 | 
						|
    settings.setValue("InstallLocation", m_vars["TargetDir"]);
 | 
						|
    settings.setValue("NoModify", "1");
 | 
						|
    settings.setValue("NoRepair", "1");
 | 
						|
    settings.setValue("Publisher", m_vars["Publisher"]);
 | 
						|
    settings.setValue("UninstallString", uninstallerName());
 | 
						|
    settings.setValue("UrlInfoAbout", m_vars["UrlInfoAbout"]);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::unregisterInstaller()
 | 
						|
{
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    QSettings settings(registerPath(), QSettings::NativeFormat);
 | 
						|
    settings.remove(QString());
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::runInstaller()
 | 
						|
{
 | 
						|
    QList<QInstallerTask *> tasks;
 | 
						|
 | 
						|
    try {
 | 
						|
        emit installationStarted();
 | 
						|
        if (!m_vars.contains("TargetDir"))
 | 
						|
            throw QInstallerError(QLatin1String("Variable 'TargetDir' not set."));
 | 
						|
 | 
						|
        QFile in(installerBinaryPath());
 | 
						|
        openForRead(in);
 | 
						|
 | 
						|
        m_totalProgress = 0;
 | 
						|
        QList<QInstallerComponent *> componentsToInstall;
 | 
						|
 | 
						|
        for (int i = 0; i != m_componentCount; ++i) {
 | 
						|
            QInstallerComponent *comp = m_components.at(i);
 | 
						|
            QString wantedState = comp->value("WantedState");
 | 
						|
            ifVerbose("HANDLING COMPONENT" << i << "WANTED: " << wantedState);
 | 
						|
            if (wantedState == "Uninstalled") {
 | 
						|
                qDebug() << "SKIPPING COMPONENT" << comp->value("DisplayName");
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            componentsToInstall.append(comp);
 | 
						|
            m_totalProgress += comp->value("UncompressedSize").toInt();
 | 
						|
        }
 | 
						|
            
 | 
						|
        qDebug() << "Install size: " << m_totalProgress
 | 
						|
            << "in " << componentsToInstall.size() << "components";
 | 
						|
 | 
						|
        qint64 lastProgressBase = 0;
 | 
						|
        foreach (QInstallerComponent *comp, componentsToInstall) {
 | 
						|
            int taskCount = comp->value("TaskCount").toInt();
 | 
						|
            quint64 componentStart = comp->value("ComponentStart").toInt();
 | 
						|
            in.seek(componentStart);
 | 
						|
            if (statusCanceledOrFailed())
 | 
						|
                throw Error("Installation canceled by user");
 | 
						|
            m_installationProgressText =
 | 
						|
                tr("Decompressing component %1").arg(comp->value("DisplayName"));
 | 
						|
            qApp->processEvents();
 | 
						|
            QByteArray compressed = retrieveByteArray(&in);
 | 
						|
            QByteArray uncompressed = qUncompress(compressed);
 | 
						|
            if (uncompressed.isEmpty()) {
 | 
						|
                qDebug() << "SIZE: " << compressed.size() << " TASK COUNT: " << taskCount
 | 
						|
                    << uncompressed.size();
 | 
						|
                throw Error("DECOMPRESSION FAILED");
 | 
						|
            }
 | 
						|
            QBuffer buffer(&uncompressed);
 | 
						|
            buffer.open(QIODevice::ReadOnly);
 | 
						|
            for (int j = 0; j != taskCount; ++j) {
 | 
						|
                int code = retrieveInt(&buffer);
 | 
						|
                QInstallerTask *task = createTaskFromCode(code); // might throw
 | 
						|
                task->readAndExecuteFromInstaller(&buffer); // might throw
 | 
						|
                tasks.append(task);
 | 
						|
                if (statusCanceledOrFailed())
 | 
						|
                    throw Error("Installation canceled by user");
 | 
						|
                setInstallationProgress(lastProgressBase + buffer.pos());
 | 
						|
            }
 | 
						|
            comp->setValue("CurrentState", "Installed");
 | 
						|
            lastProgressBase += uncompressed.size();
 | 
						|
        }
 | 
						|
        in.close();
 | 
						|
 | 
						|
        registerInstaller();
 | 
						|
        writeUninstaller(tasks);
 | 
						|
 | 
						|
        m_status = InstallerSucceeded;
 | 
						|
        m_installationProgressText = tr("Installation finished!");
 | 
						|
        qApp->processEvents();
 | 
						|
        emit installationFinished();
 | 
						|
    }
 | 
						|
    catch (const QInstallerError &err) {
 | 
						|
        installer()->showWarning(err.message());
 | 
						|
        qDebug() << "INSTALLER FAILED: " << err.message() << "\nROLLING BACK";
 | 
						|
        undo(tasks);
 | 
						|
        m_installationProgressText = tr("Installation aborted");
 | 
						|
        qApp->processEvents();
 | 
						|
        emit installationFinished();
 | 
						|
        throw;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::Private::restartTempUninstaller(const QStringList &args)
 | 
						|
{
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    ifVerbose("Running uninstaller on Windows.");
 | 
						|
    if (isUninstaller()) {
 | 
						|
        QString uninstallerFile = installerBinaryPath();
 | 
						|
        QDir tmpDir = QDir::temp();
 | 
						|
        QString tmpDirName = QLatin1String("qtcreator_uninst");
 | 
						|
        QString tmpAppName = QLatin1String("uninst.exe");
 | 
						|
        if (!tmpDir.exists(tmpDirName)) {
 | 
						|
            tmpDir.mkdir(tmpDirName);
 | 
						|
            if (!tmpDir.exists(tmpDirName)) {
 | 
						|
                ifVerbose("Could not create temporary folder!");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            tmpDir.cd(tmpDirName);
 | 
						|
        }
 | 
						|
 | 
						|
        if (tmpDir.exists(tmpAppName) && !tmpDir.remove(tmpAppName)) {
 | 
						|
            ifVerbose("Could not remove old temporary uninstaller!");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        QString tmpUninstaller = tmpDir.absoluteFilePath(tmpAppName);
 | 
						|
 | 
						|
        QFile in(uninstallerFile);
 | 
						|
        if (!in.open(QIODevice::ReadOnly)) {
 | 
						|
            ifVerbose("Cannot open uninstall.exe!");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        QFile out(tmpUninstaller);
 | 
						|
        if (!out.open(QIODevice::WriteOnly)) {
 | 
						|
            ifVerbose("Cannot open temporary uninstall.exe!");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        QByteArray ba = in.readAll();
 | 
						|
        QBuffer buf(&ba);
 | 
						|
        buf.open(QIODevice::ReadWrite);
 | 
						|
        buf.seek(buf.size() - sizeof(qint64));
 | 
						|
        appendInt(&buf, magicTempUninstallerMarker);
 | 
						|
        if (in.size() != out.write(buf.data())) {
 | 
						|
            ifVerbose("Could not copy uninstaller!");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        in.close();
 | 
						|
        out.close();
 | 
						|
 | 
						|
        MoveFileExW((TCHAR*)tmpUninstaller.utf16(),
 | 
						|
            0, MOVEFILE_DELAY_UNTIL_REBOOT|MOVEFILE_REPLACE_EXISTING);
 | 
						|
 | 
						|
        STARTUPINFOW sInfo;
 | 
						|
        PROCESS_INFORMATION pInfo;
 | 
						|
        memset(&sInfo, 0, sizeof(sInfo));
 | 
						|
        memset(&pInfo, 0, sizeof(pInfo));
 | 
						|
        sInfo.cb = sizeof(sInfo);
 | 
						|
 | 
						|
        QString cmd = QString("\"%1\"").arg(tmpUninstaller);
 | 
						|
        foreach (const QString &s, args)
 | 
						|
            cmd.append(QLatin1String(" \"") + s + QLatin1String("\""));
 | 
						|
        if (CreateProcessW(0, (TCHAR*)cmd.utf16(), 0, 0, false, 0, 0, 0, &sInfo, &pInfo)) {
 | 
						|
            CloseHandle(pInfo.hThread);
 | 
						|
            CloseHandle(pInfo.hProcess);
 | 
						|
            ifVerbose("Started temp uninstaller.");
 | 
						|
        } else {
 | 
						|
            ifVerbose("Cannot launch uninstaller!");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
#else
 | 
						|
    Q_UNUSED(args);
 | 
						|
#endif
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::Private::runUninstaller()
 | 
						|
{
 | 
						|
    QFile uninstaller(installerBinaryPath());
 | 
						|
    openForRead(uninstaller);
 | 
						|
    QByteArray ba = uninstaller.readAll();
 | 
						|
    uninstaller.close();
 | 
						|
 | 
						|
    emit uninstallationStarted();
 | 
						|
#ifndef Q_OS_WIN
 | 
						|
    // remove uninstaller binary itself. Also necessary for successful
 | 
						|
    // removal of the application directory.
 | 
						|
    uninstaller.remove();
 | 
						|
#else
 | 
						|
    if (m_vars.contains(QLatin1String("UninstallerPath"))) {
 | 
						|
        QFile orgUninstaller(m_vars.value(QLatin1String("UninstallerPath")));
 | 
						|
        orgUninstaller.remove();
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // read file
 | 
						|
    QBuffer in(&ba);
 | 
						|
    in.open(QIODevice::ReadOnly);
 | 
						|
    in.seek(m_tasksStart);
 | 
						|
    qint64 taskCount = retrieveInt(&in);
 | 
						|
    ifVerbose("FOUND " << taskCount << "UNINSTALLER TASKS");
 | 
						|
 | 
						|
    m_totalProgress = m_variablesStart;
 | 
						|
    for (int i = 0; i != taskCount; ++i) {
 | 
						|
        int code = retrieveInt(&in);
 | 
						|
        QInstallerTask *task = createTaskFromCode(code);
 | 
						|
        task->readAndExecuteFromUninstaller(&in);
 | 
						|
        setInstallationProgress(in.pos());
 | 
						|
    }
 | 
						|
    in.close();
 | 
						|
 | 
						|
    unregisterInstaller();
 | 
						|
 | 
						|
    m_installationProgressText = tr("Deinstallation finished");
 | 
						|
    qApp->processEvents();
 | 
						|
    emit uninstallationFinished();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstaller
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::QInstaller()
 | 
						|
{
 | 
						|
    d = new Private(this);
 | 
						|
    d->initialize();
 | 
						|
    registerTaskType(createCopyFileTask);
 | 
						|
    registerTaskType(createLinkFileTask);
 | 
						|
    registerTaskType(createPatchFileTask);
 | 
						|
    registerTaskType(createWriteSettingsTask);
 | 
						|
    registerTaskType(createMenuShortcutTask);
 | 
						|
}
 | 
						|
 | 
						|
QInstaller::~QInstaller()
 | 
						|
{
 | 
						|
    delete d;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::appendComponent(QInstallerComponent *component)
 | 
						|
{
 | 
						|
    d->m_components.append(component);
 | 
						|
}
 | 
						|
 | 
						|
int QInstaller::componentCount() const
 | 
						|
{
 | 
						|
    return d->m_components.size();
 | 
						|
}
 | 
						|
 | 
						|
QInstallerComponent *QInstaller::component(int i) const
 | 
						|
{
 | 
						|
    return d->m_components.at(i);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::registerTaskType(TaskCreator tc)
 | 
						|
{
 | 
						|
    d->m_taskCreators.append(tc);
 | 
						|
}
 | 
						|
 | 
						|
int QInstaller::indexOfTaskType(TaskCreator tc) const
 | 
						|
{
 | 
						|
    return d->m_taskCreators.indexOf(tc);
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::value(const QString &key, const QString &defaultValue) const
 | 
						|
{
 | 
						|
    return d->m_vars.value(key, defaultValue);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::setValue(const QString &key, const QString &value)
 | 
						|
{
 | 
						|
    d->m_vars[key] = value;
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::containsValue(const QString &key) const
 | 
						|
{
 | 
						|
    return d->m_vars.contains(key);
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::isVerbose() const
 | 
						|
{
 | 
						|
    return d->m_verbose;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::setVerbose(bool on)
 | 
						|
{
 | 
						|
    d->m_verbose = on;
 | 
						|
}
 | 
						|
 | 
						|
QInstaller::InstallerStatus QInstaller::status() const
 | 
						|
{
 | 
						|
    return d->m_status;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::interrupt()
 | 
						|
{
 | 
						|
    qDebug() << "INTERRUPT INSTALLER";
 | 
						|
    d->m_status = InstallerCanceledByUser;
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::replaceVariables(const QString &str) const
 | 
						|
{
 | 
						|
    return d->replaceVariables(str);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray QInstaller::replaceVariables(const QByteArray &ba) const
 | 
						|
{
 | 
						|
    return d->replaceVariables(ba);
 | 
						|
}
 | 
						|
 | 
						|
int QInstaller::installationProgress() const
 | 
						|
{
 | 
						|
    return d->m_installationProgress;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::setInstallationProgressText(const QString &value)
 | 
						|
{
 | 
						|
    d->m_installationProgressText = value;
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::installationProgressText() const
 | 
						|
{
 | 
						|
    return d->m_installationProgressText;
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::installerBinaryPath() const
 | 
						|
{
 | 
						|
    return d->installerBinaryPath();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::isCreator() const
 | 
						|
{
 | 
						|
    return d->isCreator();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::isInstaller() const
 | 
						|
{
 | 
						|
    return d->isInstaller();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::isUninstaller() const
 | 
						|
{
 | 
						|
    return d->isUninstaller();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::isTempUninstaller() const
 | 
						|
{
 | 
						|
    return d->isTempUninstaller();
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::runInstaller()
 | 
						|
{
 | 
						|
    try { d->runInstaller(); return true; } catch (...) { return false; }
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::runUninstaller()
 | 
						|
{
 | 
						|
    try { d->runUninstaller(); return true; } catch (...) { return false; }
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::showWarning(const QString &str)
 | 
						|
{
 | 
						|
    emit warning(str);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::dump() const
 | 
						|
{
 | 
						|
    qDebug() << "COMMAND LINE VARIABLES:" << d->m_vars;
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::appendInt(QIODevice *out, qint64 n)
 | 
						|
{
 | 
						|
    QT_PREPEND_NAMESPACE(appendInt)(out, n);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::appendString(QIODevice *out, const QString &str)
 | 
						|
{
 | 
						|
    QT_PREPEND_NAMESPACE(appendString)(out, str);
 | 
						|
}
 | 
						|
 | 
						|
void QInstaller::appendByteArray(QIODevice *out, const QByteArray &str)
 | 
						|
{
 | 
						|
    QT_PREPEND_NAMESPACE(appendByteArray)(out, str);
 | 
						|
}
 | 
						|
 | 
						|
qint64 QInstaller::retrieveInt(QIODevice *in)
 | 
						|
{
 | 
						|
    return QT_PREPEND_NAMESPACE(retrieveInt)(in);
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::retrieveString(QIODevice *in)
 | 
						|
{
 | 
						|
    return QT_PREPEND_NAMESPACE(retrieveString)(in);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray QInstaller::retrieveByteArray(QIODevice *in)
 | 
						|
{
 | 
						|
    return QT_PREPEND_NAMESPACE(retrieveByteArray)(in);
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::run()
 | 
						|
{
 | 
						|
    try {
 | 
						|
        if (isCreator()) {
 | 
						|
            createTasks(); // implemented in derived classes
 | 
						|
            d->writeInstaller();
 | 
						|
        } else if (isInstaller()) {
 | 
						|
            d->runInstaller();
 | 
						|
        } else if (isUninstaller() || isTempUninstaller()) {
 | 
						|
            runUninstaller();
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    } catch (const QInstallerError &err) {
 | 
						|
        qDebug() << "Caught Installer Error: " << err.message();
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::uninstallerName() const
 | 
						|
{
 | 
						|
    return d->uninstallerName();
 | 
						|
}
 | 
						|
 | 
						|
QString QInstaller::libraryName(const QString &baseName, const QString &version)
 | 
						|
{
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    return baseName + QLatin1String(".dll");
 | 
						|
#elif Q_OS_MAC
 | 
						|
    return QString("lib%1.dylib").arg(baseName);
 | 
						|
#else
 | 
						|
    return QString("lib%1.so.%2").arg(baseName).arg(version);    
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
bool QInstaller::restartTempUninstaller(const QStringList &args)
 | 
						|
{
 | 
						|
    return d->restartTempUninstaller(args);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerTask
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstallerTask::QInstallerTask(QInstaller *parent)
 | 
						|
  : m_installer(parent)
 | 
						|
{}
 | 
						|
 | 
						|
QInstaller *QInstallerTask::installer() const
 | 
						|
{
 | 
						|
    return m_installer;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerCopyFileTask
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::TaskCreator QInstallerCopyFileTask::creator() const
 | 
						|
{
 | 
						|
    return &createCopyFileTask;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::writeToInstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendInt(out, m_permissions);
 | 
						|
    QFile file(m_sourcePath);
 | 
						|
    openForRead(file);
 | 
						|
    appendFileData(out, &file);
 | 
						|
}
 | 
						|
 | 
						|
static int createParentDirs(const QString &absFileName)
 | 
						|
{
 | 
						|
    QFileInfo fi(absFileName);
 | 
						|
    if (fi.isDir())
 | 
						|
        return 0;
 | 
						|
    QString dirName = fi.path();
 | 
						|
    int n = createParentDirs(dirName);
 | 
						|
    QDir dir(dirName);
 | 
						|
    dir.mkdir(fi.fileName());
 | 
						|
    return n + 1;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::readAndExecuteFromInstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_permissions = retrieveInt(in);
 | 
						|
    ifVerbose("EXECUTE COPY FILE, TARGET " << m_targetPath);
 | 
						|
 | 
						|
    QString path = QDir::cleanPath(QFileInfo(m_targetPath).absolutePath());
 | 
						|
    m_parentDirCount = createParentDirs(path);
 | 
						|
    QString msg = QInstaller::tr("Copying file %1").arg(m_targetPath);
 | 
						|
    installer()->setInstallationProgressText(msg);
 | 
						|
 | 
						|
    QFile file(m_targetPath);
 | 
						|
    bool res = file.open(QIODevice::WriteOnly);
 | 
						|
    if (!res) {
 | 
						|
        // try to make it writeable, and try again
 | 
						|
        bool res1 = file.setPermissions(file.permissions()|QFile::WriteOwner);
 | 
						|
        ifVerbose("MAKE WRITABLE: " << res1);
 | 
						|
        res = file.open(QIODevice::WriteOnly);
 | 
						|
    }
 | 
						|
    if (!res) {
 | 
						|
        // try to remove it, and try again
 | 
						|
        bool res1 = file.remove();
 | 
						|
        ifVerbose("REMOVING FILE: " << res1);
 | 
						|
        res = file.open(QIODevice::WriteOnly);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!res) {
 | 
						|
        QString msg = QInstaller::tr("The file %1 is not writeable.")
 | 
						|
            .arg(m_targetPath);
 | 
						|
        installer()->showWarning(msg);
 | 
						|
        throw Error(msg);
 | 
						|
    }
 | 
						|
    retrieveFileData(&file, in);
 | 
						|
    QFile::Permissions perms(m_permissions | QFile::WriteOwner);
 | 
						|
    file.close();
 | 
						|
    file.setPermissions(perms);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::writeToUninstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendInt(out, m_parentDirCount);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::readAndExecuteFromUninstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = retrieveString(in);
 | 
						|
    m_parentDirCount = retrieveInt(in);
 | 
						|
    undo();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::undo()
 | 
						|
{
 | 
						|
    ifVerbose("UNLINKING FILE" << m_targetPath << m_parentDirCount);
 | 
						|
    QString msg = QInstaller::tr("Removing %1").arg(m_targetPath);
 | 
						|
    installer()->setInstallationProgressText(msg);
 | 
						|
 | 
						|
    QFileInfo fi(m_targetPath);
 | 
						|
    QDir dir(fi.path());
 | 
						|
 | 
						|
    QFile file(m_targetPath);
 | 
						|
    bool res = file.remove();
 | 
						|
    if (!res) {
 | 
						|
        // try to make it writeable, and try again
 | 
						|
        bool res1 = file.setPermissions(file.permissions()|QFile::WriteOwner);
 | 
						|
        ifVerbose("MAKE WRITABLE: " << res1);
 | 
						|
        res = file.remove();
 | 
						|
    }
 | 
						|
    
 | 
						|
    while (res && --m_parentDirCount >= 0) {
 | 
						|
        QString dirName = dir.dirName();
 | 
						|
        dir.cdUp();
 | 
						|
        res = dir.rmdir(dirName);
 | 
						|
        msg = QInstaller::tr("Removing file %1").arg(m_targetPath);
 | 
						|
        installer()->setInstallationProgressText(msg);
 | 
						|
        ifVerbose("REMOVING DIR " << dir.path() << dirName << res);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerCopyFileTask::dump(QDebug & os) const
 | 
						|
{
 | 
						|
    os << "c|" + sourcePath() + '|' + targetPath();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerLinkFileTask
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::TaskCreator QInstallerLinkFileTask::creator() const
 | 
						|
{
 | 
						|
    return &createLinkFileTask;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::writeToInstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendString(out, m_linkTargetPath);
 | 
						|
    appendInt(out, m_permissions);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::readAndExecuteFromInstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_linkTargetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_permissions = retrieveInt(in);
 | 
						|
 | 
						|
    ifVerbose("LINK " << m_targetPath << " TARGET " << m_linkTargetPath);
 | 
						|
 | 
						|
    QFile file(m_linkTargetPath);
 | 
						|
    if (file.link(m_targetPath))
 | 
						|
        return;
 | 
						|
 | 
						|
    // ok. linking failed. try to remove targetPath and link again
 | 
						|
    bool res1 = QFile::remove(m_targetPath);
 | 
						|
    ifVerbose("TARGET EXITS, REMOVE: " << m_targetPath << res1);
 | 
						|
    if (file.link(m_targetPath))
 | 
						|
        return;
 | 
						|
 | 
						|
    // nothing helped.
 | 
						|
    throw Error(QInstaller::tr("Cannot link file %1 to %2:\n")
 | 
						|
            .arg(m_linkTargetPath).arg(m_targetPath));
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::writeToUninstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::readAndExecuteFromUninstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = retrieveString(in);
 | 
						|
    ifVerbose("UNLINKING LINK" << m_targetPath);
 | 
						|
    undo();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::undo()
 | 
						|
{
 | 
						|
    QFile file(m_targetPath);
 | 
						|
    file.remove();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerLinkFileTask::dump(QDebug & os) const
 | 
						|
{
 | 
						|
     os << "l|" + targetPath() + '|' + linkTargetPath();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerWriteSettingsTask
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::TaskCreator QInstallerWriteSettingsTask::creator() const
 | 
						|
{
 | 
						|
    return &createWriteSettingsTask;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::writeToInstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_key);
 | 
						|
    appendString(out, m_value);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::readAndExecuteFromInstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_key = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_value = installer()->replaceVariables(retrieveString(in));
 | 
						|
    QSettings settings;
 | 
						|
    settings.setValue(m_key, m_value);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::writeToUninstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_key);
 | 
						|
    appendString(out, m_value);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::readAndExecuteFromUninstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_key = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_value = installer()->replaceVariables(retrieveString(in));
 | 
						|
    undo();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::undo()
 | 
						|
{
 | 
						|
    QSettings settings;
 | 
						|
    settings.setValue(m_key, QString());
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerWriteSettingsTask::dump(QDebug & os) const
 | 
						|
{
 | 
						|
    os << "s|" + key() + '|' + value();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerPatchFileTask
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::TaskCreator QInstallerPatchFileTask::creator() const
 | 
						|
{
 | 
						|
    return &createPatchFileTask;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerPatchFileTask::writeToInstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendByteArray(out, m_needle);
 | 
						|
    appendByteArray(out, m_replacement);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerPatchFileTask::readAndExecuteFromInstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_needle = retrieveByteArray(in);
 | 
						|
    m_replacement = installer()->replaceVariables(retrieveByteArray(in));
 | 
						|
    ifVerbose("PATCHING" << m_replacement << m_needle << m_targetPath);
 | 
						|
 | 
						|
    QFile file;
 | 
						|
    file.setFileName(m_targetPath);
 | 
						|
    if (!file.open(QIODevice::ReadWrite))
 | 
						|
        throw Error(QInstaller::tr("Cannot open file %1 for reading").arg(file.fileName()));
 | 
						|
 | 
						|
    uchar *data = file.map(0, file.size());
 | 
						|
    if (!data)
 | 
						|
        throw Error(QInstaller::tr("Cannot map file %1").arg(file.fileName()));
 | 
						|
    QByteArray ba = QByteArray::fromRawData((const char *)data, file.size());
 | 
						|
    int pos = ba.indexOf(m_needle);
 | 
						|
    if (pos != -1) {
 | 
						|
        for (int i = m_replacement.size(); --i >= 0; )
 | 
						|
            data[pos + i] = m_replacement.at(i);
 | 
						|
    }
 | 
						|
    if (!file.unmap(data))
 | 
						|
        throw Error(QInstaller::tr("Cannot unmap file %1").arg(file.fileName()));
 | 
						|
    file.close();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerPatchFileTask::dump(QDebug & os) const
 | 
						|
{
 | 
						|
     os << "p|" + targetPath() + '|' + needle() + '|' + replacement();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// QInstallerMenuShortcutTask
 | 
						|
//
 | 
						|
//
 | 
						|
// Usage:
 | 
						|
//
 | 
						|
// static const struct
 | 
						|
// {
 | 
						|
//     const char *target;
 | 
						|
//     const char *linkTarget;
 | 
						|
// } menuShortcuts[] = {
 | 
						|
//     {"Qt Creator by Nokia\\Run Qt Creator", "bin\\qtcreator.exe"},
 | 
						|
//     {"Qt Creator by Nokia\\Readme", "readme.txt"},
 | 
						|
//     {"Qt Creator by Nokia\\Uninstall", "uninstall.exe"}
 | 
						|
// };
 | 
						|
//
 | 
						|
// for (int i = 0; i != sizeof(menuShortcuts) / sizeof(menuShortcuts[0]); ++i) {
 | 
						|
//     QInstallerMenuShortcutTask *task = new QInstallerMenuShortcutTask(this);
 | 
						|
//     task->setTargetPath(menuShortcuts[i].target);
 | 
						|
//     task->setLinkTargetPath(QLatin1String("@TargetDir@\\") + menuShortcuts[i].linkTarget);
 | 
						|
// }
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
QInstaller::TaskCreator QInstallerMenuShortcutTask::creator() const
 | 
						|
{
 | 
						|
    return &createMenuShortcutTask;
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::writeToInstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendString(out, m_linkTargetPath);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::readAndExecuteFromInstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
    m_linkTargetPath = installer()->replaceVariables(retrieveString(in));
 | 
						|
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    QString workingDir = installer()->value(QLatin1String("TargetDir"));
 | 
						|
    bool res = false;
 | 
						|
    HRESULT hres;
 | 
						|
    IShellLink *psl;
 | 
						|
    bool neededCoInit = false;
 | 
						|
 | 
						|
    ifVerbose("CREATE MENU SHORTCUT: " << m_targetPath << " TARGET " << m_linkTargetPath);
 | 
						|
 | 
						|
    if (installer()->value(QLatin1String("AllUsers")) == "true") {
 | 
						|
        QSettings registry(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows"
 | 
						|
            "\\CurrentVersion\\Explorer\\Shell Folders"), QSettings::NativeFormat);
 | 
						|
        m_startMenuPath = registry.value(QLatin1String("Common Programs"), QString()).toString();
 | 
						|
    } else {
 | 
						|
        QSettings registry(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows"
 | 
						|
            "\\CurrentVersion\\Explorer\\Shell Folders"), QSettings::NativeFormat);
 | 
						|
        m_startMenuPath = registry.value(QLatin1String("Programs"), QString()).toString();
 | 
						|
    }
 | 
						|
    if (m_startMenuPath.isEmpty()) {
 | 
						|
        ifVerbose("CREATE MENU SHORTCUT: Cannot find start menu folder!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_targetPath.isEmpty()) {
 | 
						|
        if (!m_targetPath.endsWith(QLatin1String(".lnk")))
 | 
						|
            m_targetPath.append(QLatin1String(".lnk"));
 | 
						|
        m_targetPath = m_targetPath.replace('/', '\\');
 | 
						|
        int i = m_targetPath.lastIndexOf('\\');
 | 
						|
        if (i > -1) {
 | 
						|
            QDir dir(m_startMenuPath);
 | 
						|
            if (!dir.exists(m_targetPath.left(i)))
 | 
						|
                dir.mkpath(m_targetPath.left(i));
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_linkTargetPath.isEmpty())
 | 
						|
            return;
 | 
						|
 | 
						|
        QString trgt = m_linkTargetPath;
 | 
						|
        if (trgt.startsWith('\"')) {
 | 
						|
            trgt = trgt.left(trgt.indexOf('\"', 1) + 1);
 | 
						|
        } else {
 | 
						|
            trgt = trgt.left(trgt.indexOf(' '));
 | 
						|
        }
 | 
						|
        if (trgt.isEmpty())
 | 
						|
            trgt = m_linkTargetPath;
 | 
						|
 | 
						|
        hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
 | 
						|
        if (hres == CO_E_NOTINITIALIZED) {
 | 
						|
            neededCoInit = true;
 | 
						|
            CoInitialize(NULL);
 | 
						|
            hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
 | 
						|
                (void **)&psl);
 | 
						|
        }
 | 
						|
        if (SUCCEEDED(hres)) {
 | 
						|
            hres = psl->SetPath((wchar_t *)trgt.utf16());
 | 
						|
            if (SUCCEEDED(hres)) {
 | 
						|
                hres = psl->SetArguments((wchar_t *)m_linkTargetPath.mid(trgt.length()).utf16());
 | 
						|
                if (SUCCEEDED(hres)) {
 | 
						|
                    hres = psl->SetWorkingDirectory((wchar_t *)workingDir.utf16());
 | 
						|
                    if (SUCCEEDED(hres)) {
 | 
						|
                        IPersistFile *ppf;
 | 
						|
                        hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
 | 
						|
                        if (SUCCEEDED(hres)) {
 | 
						|
                            hres = ppf->Save((TCHAR*)QString(m_startMenuPath
 | 
						|
                                + QDir::separator() + m_targetPath).utf16(), TRUE);
 | 
						|
                            if (SUCCEEDED(hres))
 | 
						|
                                res = true;
 | 
						|
                            ppf->Release();
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            psl->Release();
 | 
						|
        }
 | 
						|
        if (neededCoInit)
 | 
						|
            CoUninitialize();
 | 
						|
    }
 | 
						|
    if (!res) {
 | 
						|
        QString msg = QInstaller::tr("Cannot create menu shortcut %1 to %2:\n")
 | 
						|
            .arg(m_linkTargetPath).arg(m_targetPath);
 | 
						|
        installer()->showWarning(msg);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::writeToUninstaller(QIODevice *out) const
 | 
						|
{
 | 
						|
    appendString(out, m_targetPath);
 | 
						|
    appendString(out, m_startMenuPath);
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::readAndExecuteFromUninstaller(QIODevice *in)
 | 
						|
{
 | 
						|
    m_targetPath = retrieveString(in);
 | 
						|
    m_startMenuPath = retrieveString(in);
 | 
						|
    ifVerbose("REMOVE MENU SHORTCUT: " << m_targetPath);
 | 
						|
    undo();
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::undo()
 | 
						|
{
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    QFileInfo fi(m_startMenuPath + QDir::separator() + m_targetPath);
 | 
						|
    QString path = fi.absoluteFilePath();
 | 
						|
    if (fi.isFile()) {
 | 
						|
        path = fi.absolutePath();
 | 
						|
        QFile file(fi.absoluteFilePath());
 | 
						|
        file.remove();
 | 
						|
    }
 | 
						|
    QDir dir(m_startMenuPath);
 | 
						|
    dir.rmpath(path);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void QInstallerMenuShortcutTask::dump(QDebug & os) const
 | 
						|
{
 | 
						|
     os << "msc|" + targetPath() + '|' + linkTargetPath();
 | 
						|
}
 | 
						|
 | 
						|
QT_END_NAMESPACE
 | 
						|
 | 
						|
#include "qinstaller.moc"
 |