diff options
Diffstat (limited to 'src')
39 files changed, 3042 insertions, 565 deletions
diff --git a/src/libs/installer/abstractfiletask.cpp b/src/libs/installer/abstractfiletask.cpp new file mode 100644 index 000000000..ce18422a0 --- /dev/null +++ b/src/libs/installer/abstractfiletask.cpp @@ -0,0 +1,117 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "abstractfiletask.h" + +namespace QInstaller { + +AbstractFileTask::AbstractFileTask() +{ + registerMetaTypes(); +} + +AbstractFileTask::AbstractFileTask(const QString &source) +{ + registerMetaTypes(); + setTaskItem(FileTaskItem(source)); +} + +AbstractFileTask::AbstractFileTask(const FileTaskItem &item) +{ + registerMetaTypes(); + setTaskItem(item); +} + +AbstractFileTask::AbstractFileTask(const QString &source, const QString &target) +{ + registerMetaTypes(); + setTaskItem(FileTaskItem(source, target)); +} + +QList<FileTaskItem> AbstractFileTask::taskItems() const +{ + QReadLocker _(&m_lock); + return m_items; +} + +void AbstractFileTask::setTaskItem(const FileTaskItem &item) +{ + clearTaskItems(); + addTaskItem(item); +} + + +// -- protected + +void AbstractFileTask::clearTaskItems() +{ + QWriteLocker _(&m_lock); + m_items.clear(); +} + +void AbstractFileTask::addTaskItem(const FileTaskItem &item) +{ + QWriteLocker _(&m_lock); + m_items.append(item); +} + +void AbstractFileTask::setTaskItems(const QList<FileTaskItem> &items) +{ + clearTaskItems(); + addTaskItems(items); +} + +void AbstractFileTask::addTaskItems(const QList<FileTaskItem> &items) +{ + QWriteLocker _(&m_lock); + m_items.append(items); +} + + +// -- private + +void AbstractFileTask::registerMetaTypes() +{ + qRegisterMetaType<QInstaller::FileTaskItem>(); + qRegisterMetaType<QInstaller::FileTaskResult>(); + qRegisterMetaType<QInstaller::FileTaskException>(); +} + +} // namespace QInstaller diff --git a/src/libs/installer/abstractfiletask.h b/src/libs/installer/abstractfiletask.h new file mode 100644 index 000000000..858a35f40 --- /dev/null +++ b/src/libs/installer/abstractfiletask.h @@ -0,0 +1,156 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef ABSTRACTFILETASK_H +#define ABSTRACTFILETASK_H + +#include "abstracttask.h" +#include "installer_global.h" + +#include <QObject> +#include <QReadWriteLock> + +#if QT_VERSION < 0x050000 +#include <qtconcurrentexception.h> +#define QException QtConcurrent::Exception +#define QUnhandledException QtConcurrent::UnhandledException +#endif + +namespace QInstaller { + +namespace TaskRole { +enum +{ + Checksum, + TaskItem, + SourceFile, + TargetFile, + UserRole = 1000 +}; +} + +class FileTaskItem : public AbstractTaskData +{ +public: + FileTaskItem() {} + explicit FileTaskItem(const QString &s) + { + insert(TaskRole::SourceFile, s); + } + FileTaskItem(const QString &s, const QString &t) + { + insert(TaskRole::SourceFile, s); + insert(TaskRole::TargetFile, t); + } + + QString source() const { return value(TaskRole::SourceFile).toString(); } + QString target() const { return value(TaskRole::TargetFile).toString(); } +}; + +class FileTaskResult : public AbstractTaskData +{ +public: + FileTaskResult() {} + FileTaskResult(const QString &t, const QByteArray &c, const FileTaskItem &i) + { + insert(TaskRole::Checksum, c); + insert(TaskRole::TargetFile, t); + insert(TaskRole::TaskItem, QVariant::fromValue(i)); + } + + QString target() const { return value(TaskRole::TargetFile).toString(); } + QByteArray checkSum() const { return value(TaskRole::Checksum).toByteArray(); } + FileTaskItem taskItem() const { return value(TaskRole::TaskItem).value<FileTaskItem>(); } +}; + +class FileTaskException : public QException +{ +public: + FileTaskException() {} + ~FileTaskException() throw() {} + explicit FileTaskException(const QString &message) + : m_message(message) {} + + void raise() const { throw *this; } + QString message() const { return m_message; } + FileTaskException *clone() const { return new FileTaskException(*this); } + +private: + QString m_message; +}; + +class INSTALLER_EXPORT AbstractFileTask : public AbstractTask<FileTaskResult> +{ + Q_OBJECT + Q_DISABLE_COPY(AbstractFileTask) + +public: + AbstractFileTask(); + virtual ~AbstractFileTask() {} + + explicit AbstractFileTask(const QString &source); + explicit AbstractFileTask(const FileTaskItem &item); + AbstractFileTask(const QString &source, const QString &target); + + QList<FileTaskItem> taskItems() const; + void setTaskItem(const FileTaskItem &item); + +protected: + void clearTaskItems(); + void addTaskItem(const FileTaskItem &item); + void setTaskItems(const QList<FileTaskItem> &items); + void addTaskItems(const QList<FileTaskItem> &items); + +private: + void registerMetaTypes(); + +private: + QList<FileTaskItem> m_items; + mutable QReadWriteLock m_lock; +}; + +} // namespace QInstaller + +Q_DECLARE_METATYPE(QInstaller::FileTaskItem) +Q_DECLARE_METATYPE(QInstaller::FileTaskResult) +Q_DECLARE_METATYPE(QInstaller::FileTaskException) + +#endif // ABSTRACTFILETASK_H diff --git a/src/libs/installer/abstracttask.h b/src/libs/installer/abstracttask.h new file mode 100644 index 000000000..2f458a4c2 --- /dev/null +++ b/src/libs/installer/abstracttask.h @@ -0,0 +1,79 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef ABSTRACTTASK_H +#define ABSTRACTTASK_H + +#include "runextensions.h" + +#include <QFutureInterface> + +namespace QInstaller { + +class AbstractTaskData +{ +public: + AbstractTaskData() {} + virtual ~AbstractTaskData() = 0; + + QVariant value(int role) const { return m_data.value(role); } + void insert(int key, const QVariant &value) { m_data.insert(key, value); } + +private: + QHash<int, QVariant> m_data; +}; +inline AbstractTaskData::~AbstractTaskData() {} + +template <typename T> +class AbstractTask : public QObject +{ + Q_DISABLE_COPY(AbstractTask) + +public: + AbstractTask() {} + virtual ~AbstractTask() {} + + virtual void doTask(QFutureInterface<T> &futureInterface) = 0; +}; + +} // namespace QInstaller + +#endif // ABSTRACTTASK_H diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp index 9cce8a08b..92567050a 100644 --- a/src/libs/installer/adminauthorization_x11.cpp +++ b/src/libs/installer/adminauthorization_x11.cpp @@ -208,11 +208,11 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const ::write(masterFD, pwd.data(), pwd.length()); ::write(masterFD, "\n", 1); ::read(masterFD, buf, pwd.length() + 1); - } + } } if (bytes == 0) ::usleep(100000); - } + } if (!errData.isEmpty()) { printError(parent, QString::fromLocal8Bit(errData.constData())); return false; @@ -225,9 +225,9 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const ::close(pipedData[1]); if (exited) return exitStatus == 0; - + return false; - } + } // child process else { @@ -236,7 +236,7 @@ bool AdminAuthorization::execute(QWidget *parent, const QString &program, const for (int sig = 1; sig < NSIG; ++sig) signal(sig, SIG_DFL); signal(SIGHUP, SIG_IGN); - + ::setsid(); ::ioctl(slaveFD, TIOCSCTTY, 1); diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index 7ea73bbb9..d537555fb 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -88,6 +88,8 @@ static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile"); static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters"); static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible"); static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath"); +static const QLatin1String scWizardStyle("WizardStyle"); +static const QLatin1String scTitleColor("TitleColor"); } #endif // CONSTANTS_H diff --git a/src/libs/installer/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp new file mode 100644 index 000000000..4fe2a17ac --- /dev/null +++ b/src/libs/installer/copyfiletask.cpp @@ -0,0 +1,130 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "copyfiletask.h" +#include "observer.h" + +#include <QFileInfo> +#include <QTemporaryFile> + +namespace QInstaller { + +CopyFileTask::CopyFileTask(const FileTaskItem &item) + : AbstractFileTask(item) +{ +} + +CopyFileTask::CopyFileTask(const QString &source) + : AbstractFileTask(source) +{ +} + +CopyFileTask::CopyFileTask(const QString &source, const QString &target) + : AbstractFileTask(source, target) +{ +} + +void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi) +{ + fi.reportStarted(); + fi.setExpectedResultCount(1); + + if (taskItems().isEmpty()) { + fi.reportException(FileTaskException(QLatin1String("Invalid task item count."))); + fi.reportFinished(); return; // error + } + + const FileTaskItem item = taskItems().first(); + FileTaskObserver observer(QCryptographicHash::Sha1); + + QFile source(item.source()); + if (!source.open(QIODevice::ReadOnly)) { + fi.reportException(FileTaskException(QString::fromLatin1("Could not open source '%1' " + "for read. Error: %2.").arg(source.fileName(), source.errorString()))); + fi.reportFinished(); return; // error + } + observer.setBytesToTransfer(source.size()); + + QScopedPointer<QFile> file; + const QString target = item.target(); + if (target.isEmpty()) { + QTemporaryFile *tmp = new QTemporaryFile; + tmp->setAutoRemove(false); + file.reset(tmp); + } else { + file.reset(new QFile(target)); + } + if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { + fi.reportException(FileTaskException(QString::fromLatin1("Could not open target '%1' " + "for write. Error: %2.").arg(file->fileName(), file->errorString()))); + fi.reportFinished(); return; // error + } + + QByteArray buffer(32768, Qt::Uninitialized); + while (!source.atEnd() && source.error() == QFile::NoError) { + if (fi.isCanceled()) + break; + if (fi.isPaused()) + fi.waitForResume(); + + const qint64 read = source.read(buffer.data(), buffer.size()); + qint64 written = 0; + while (written < read) { + const qint64 toWrite = file->write(buffer.constData() + written, read - written); + if (toWrite < 0) { + fi.reportException(FileTaskException(QString::fromLatin1("Writing to target " + "'%1' failed. Error: %2.").arg(file->fileName(), file->errorString()))); + } + written += toWrite; + } + + observer.addSample(read); + observer.timerEvent(NULL); + observer.addBytesTransfered(read); + observer.addCheckSumData(buffer.data(), read); + + fi.setProgressValueAndText(observer.progressValue(), observer.progressText()); + } + + fi.reportResult(FileTaskResult(file->fileName(), observer.checkSum(), item), 0); + fi.reportFinished(); +} + +} // namespace QInstaller diff --git a/src/libs/installer/copyfiletask.h b/src/libs/installer/copyfiletask.h new file mode 100644 index 000000000..d9cc20f93 --- /dev/null +++ b/src/libs/installer/copyfiletask.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef COPYFILETASK_H +#define COPYFILETASK_H + +#include "abstractfiletask.h" + +namespace QInstaller { + +class INSTALLER_EXPORT CopyFileTask : public AbstractFileTask +{ + Q_OBJECT + Q_DISABLE_COPY(CopyFileTask) + +public: + CopyFileTask() {} + explicit CopyFileTask(const FileTaskItem &item); + + explicit CopyFileTask(const QString &source); + CopyFileTask(const QString &source, const QString &target); + + void doTask(QFutureInterface<FileTaskResult> &fi); +}; + +} // namespace QInstaller + +#endif // COPYFILETASK_H diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp new file mode 100644 index 000000000..8d15a835d --- /dev/null +++ b/src/libs/installer/downloadfiletask.cpp @@ -0,0 +1,388 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "downloadfiletask.h" + +#include "downloadfiletask_p.h" +#include "observer.h" + +#include <QEventLoop> +#include <QFile> +#include <QNetworkProxyFactory> +#include <QSslError> +#include <QTemporaryFile> +#include <QTimer> + +namespace QInstaller { + +Downloader::Downloader() + : m_finished(0) +{ + connect(&m_nam, SIGNAL(finished(QNetworkReply*)), SLOT(onFinished(QNetworkReply*))); +} + +Downloader::~Downloader() +{ + m_nam.disconnect(); + foreach (QNetworkReply *const reply, m_downloads.keys()) { + reply->disconnect(); + reply->abort(); + reply->deleteLater(); + } + + foreach (const Data &data, m_downloads.values()) { + data.file->close(); + delete data.file; + delete data.observer; + } +} + +void Downloader::download(QFutureInterface<FileTaskResult> &fi, const QList<FileTaskItem> &items, + QNetworkProxyFactory *networkProxyFactory) +{ + m_items = items; + m_futureInterface = &fi; + + fi.reportStarted(); + fi.setExpectedResultCount(items.count()); + + m_nam.setProxyFactory(networkProxyFactory); + connect(&m_nam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, + SLOT(onAuthenticationRequired(QNetworkReply*, QAuthenticator*))); + QTimer::singleShot(0, this, SLOT(doDownload())); +} + +void Downloader::doDownload() +{ + foreach (const FileTaskItem &item, m_items) { + if (!startDownload(item)) + break; + } + + if (m_items.isEmpty() || m_futureInterface->isCanceled()) { + m_futureInterface->reportFinished(); + emit finished(); // emit finished, so the event loop can shutdown + } +} + + +// -- private slots + +void Downloader::onReadyRead() +{ + if (testCanceled()) { + m_futureInterface->reportFinished(); + emit finished(); return; // error + } + + QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender()); + if (!reply) + return; + + const Data &data = m_downloads[reply]; + if (!data.file->isOpen()) { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Target '%1' not " + "open for write. Error: %2.").arg(data.file->fileName(), data.file->errorString()))); + return; + } + + QByteArray buffer(32768, Qt::Uninitialized); + while (reply->bytesAvailable()) { + if (testCanceled()) { + m_futureInterface->reportFinished(); + emit finished(); return; // error + } + + const qint64 read = reply->read(buffer.data(), buffer.size()); + qint64 written = 0; + while (written < read) { + const qint64 toWrite = data.file->write(buffer.constData() + written, read - written); + if (toWrite < 0) { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Writing " + "to target '%1' failed. Error: %2.").arg(data.file->fileName(), + data.file->errorString()))); + return; + } + written += toWrite; + } + + data.observer->addSample(read); + data.observer->addBytesTransfered(read); + data.observer->addCheckSumData(buffer.data(), read); + + int progress = m_finished * 100; + foreach (const Data &data, m_downloads.values()) + progress += data.observer->progressValue(); + if (!reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) { + m_futureInterface->setProgressValueAndText(progress / m_items.count(), + data.observer->progressText()); + } + } +} + +void Downloader::onFinished(QNetworkReply *reply) +{ + const Data &data = m_downloads[reply]; + const QString filename = data.file->fileName(); + if (!m_futureInterface->isCanceled()) { + if (reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) { + const QUrl url = reply->url() + .resolved(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()); + const QList<QUrl> redirects = m_redirects.values(reply); + if (!redirects.contains(url)) { + data.file->close(); + data.file->remove(); + delete data.file; + delete data.observer; + + FileTaskItem taskItem = data.taskItem; + taskItem.insert(TaskRole::SourceFile, url.toString()); + QNetworkReply *const redirectReply = startDownload(taskItem); + + foreach (const QUrl &redirect, redirects) + m_redirects.insertMulti(redirectReply, redirect); + m_redirects.insertMulti(redirectReply, url); + + m_downloads.remove(reply); + m_redirects.remove(reply); + reply->deleteLater(); + return; + } else { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Redirect" + " loop detected '%1'.").arg(url.toString()))); + return; + } + } + } + + const QByteArray ba = reply->readAll(); + if (!ba.isEmpty()) { + data.observer->addSample(ba.size()); + data.observer->addBytesTransfered(ba.size()); + data.observer->addCheckSumData(ba.data(), ba.size()); + } + + const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray(); + if (!expectedCheckSum.isEmpty()) { + if (expectedCheckSum != data.observer->checkSum().toHex()) { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Checksum" + " mismatch detected '%1'.").arg(reply->url().toString()))); + } + } + m_futureInterface->reportResult(FileTaskResult(filename, data.observer->checkSum(), data.taskItem)); + + data.file->close(); + delete data.file; + delete data.observer; + + m_downloads.remove(reply); + m_redirects.remove(reply); + reply->deleteLater(); + + m_finished++; + if (m_downloads.isEmpty() || m_futureInterface->isCanceled()) { + m_futureInterface->reportFinished(); + emit finished(); // emit finished, so the event loop can shutdown + } +} + +void Downloader::onError(QNetworkReply::NetworkError error) +{ + QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender()); + if (reply) { + const Data &data = m_downloads[reply]; + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Network error " + "while downloading target '%1'. Error: %2.").arg(data.file->fileName(), + reply->errorString()))); + } else { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Unknown network " + "error while downloading. Error: %1.").arg(error))); + } +} + +void Downloader::onSslErrors(const QList<QSslError> &sslErrors) +{ +#if defined(QT_NO_SSL) || defined(QT_NO_OPENSSL) + Q_UNUSED(sslErrors); +#else + foreach (const QSslError &error, sslErrors) + qDebug() << QString::fromLatin1("SSL error: %s").arg(error.errorString()); +#endif +} + +void Downloader::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_UNUSED(bytesReceived) + QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender()); + if (reply) { + const Data &data = m_downloads[reply]; + data.observer->setBytesToTransfer(bytesTotal); + } +} + +void Downloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + if (!authenticator || !reply || !m_downloads.contains(reply)) + return; + + FileTaskItem *item = &m_downloads[reply].taskItem; + const QAuthenticator auth = item->value(TaskRole::Authenticator).value<QAuthenticator>(); + if (!auth.user().isEmpty()) { + authenticator->setUser(auth.user()); + authenticator->setPassword(auth.password()); + item->insert(TaskRole::Authenticator, QVariant()); // clear so we fail on next call + } else { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Could not " + "authenticate using the provided credentials. Source: '%1'.").arg(reply->url() + .toString()))); + } +} + + +// -- private + +bool Downloader::testCanceled() +{ + // TODO: figure out how to implement pause and resume + if (m_futureInterface->isPaused()) { + m_futureInterface->togglePaused(); // Note: this will trigger cancel + m_futureInterface->reportException(FileTaskException(QLatin1String("Pause and resume not " + "supported by network transfers."))); + } + return m_futureInterface->isCanceled(); +} + +QNetworkReply *Downloader::startDownload(const FileTaskItem &item) +{ + QUrl const source = item.source(); + if (!source.isValid()) { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Invalid source " + "'%1'. Error: %2.").arg(source.toString(), source.errorString()))); + return 0; + } + + QScopedPointer<QFile> file; + const QString target = item.target(); + if (target.isEmpty()) { + QTemporaryFile *tmp = new QTemporaryFile; + tmp->setAutoRemove(false); + file.reset(tmp); + } else { + file.reset(new QFile(target)); + } + if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { + file->remove(); + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Could not open " + "target '%1' for write. Error: %2.").arg(file->fileName(), file->errorString()))); + return 0; + } + + QNetworkReply *reply = m_nam.get(QNetworkRequest(source)); + m_downloads.insert(reply, Data(file.take(), new FileTaskObserver(QCryptographicHash::Sha1), item)); + + connect(reply, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, + SLOT(onError(QNetworkReply::NetworkError))); +#ifndef QT_NO_OPENSSL + // TODO: once we switch to Qt5, use QT_NO_SSL instead of QT_NO_OPENSSL + connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(onSslErrors(QList<QSslError>))); +#endif + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, + qint64))); + return reply; +} + + +// -- DownloadFileTask + +DownloadFileTask::DownloadFileTask(const QList<FileTaskItem> &items) + : AbstractFileTask() +{ + setTaskItems(items); +} + +void DownloadFileTask::setTaskItem(const FileTaskItem &item) +{ + AbstractFileTask::setTaskItem(item); +} + +void DownloadFileTask::addTaskItem(const FileTaskItem &item) +{ + AbstractFileTask::addTaskItem(item); +} + +void DownloadFileTask::setTaskItems(const QList<FileTaskItem> &items) +{ + AbstractFileTask::setTaskItems(items); +} + +void DownloadFileTask::addTaskItems(const QList<FileTaskItem> &items) +{ + AbstractFileTask::addTaskItems(items); +} + +void DownloadFileTask::setAuthenticator(const QAuthenticator &authenticator) +{ + m_authenticator = authenticator; +} + +void DownloadFileTask::setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory) +{ + m_proxyFactory.reset(factory); +} + +void DownloadFileTask::doTask(QFutureInterface<FileTaskResult> &fi) +{ + QEventLoop el; + Downloader downloader; + connect(&downloader, SIGNAL(finished()), &el, SLOT(quit())); + + QList<FileTaskItem> items = taskItems(); + if (!m_authenticator.isNull()) { + for (int i = 0; i < items.count(); ++i) { + if (items.at(i).value(TaskRole::Authenticator).isNull()) + items[i].insert(TaskRole::Authenticator, QVariant::fromValue(m_authenticator)); + } + } + downloader.download(fi, items, (m_proxyFactory.isNull() ? 0 : m_proxyFactory->clone())); + el.exec(); // That's tricky here, we need to run our own event loop to keep QNAM working. +} + +} // namespace QInstaller diff --git a/src/libs/installer/downloadfiletask.h b/src/libs/installer/downloadfiletask.h new file mode 100644 index 000000000..c320e6dba --- /dev/null +++ b/src/libs/installer/downloadfiletask.h @@ -0,0 +1,95 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef DOWNLOADFILETASK_H +#define DOWNLOADFILETASK_H + +#include "abstractfiletask.h" +#include "kdupdaterfiledownloaderfactory.h" + +#include <QAuthenticator> +Q_DECLARE_METATYPE(QAuthenticator) + +namespace QInstaller { + +namespace TaskRole { +enum +{ + Authenticator = TaskRole::TargetFile + 10 +}; +} + +class INSTALLER_EXPORT DownloadFileTask : public AbstractFileTask +{ + Q_OBJECT + Q_DISABLE_COPY(DownloadFileTask) + +public: + DownloadFileTask() {} + explicit DownloadFileTask(const FileTaskItem &item) + : AbstractFileTask(item) {} + explicit DownloadFileTask(const QList<FileTaskItem> &items); + + explicit DownloadFileTask(const QString &source) + : AbstractFileTask(source) {} + DownloadFileTask(const QString &source, const QString &target) + : AbstractFileTask(source, target) {} + + void addTaskItem(const FileTaskItem &items); + void addTaskItems(const QList<FileTaskItem> &items); + + void setTaskItem(const FileTaskItem &items); + void setTaskItems(const QList<FileTaskItem> &items); + + void setAuthenticator(const QAuthenticator &authenticator); + void setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory); + + void doTask(QFutureInterface<FileTaskResult> &fi); + +private: + friend class Downloader; + QAuthenticator m_authenticator; + QScopedPointer<KDUpdater::FileDownloaderProxyFactory> m_proxyFactory; +}; + +} // namespace QInstaller + +#endif // DOWNLOADFILETASK_H diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h new file mode 100644 index 000000000..855b32813 --- /dev/null +++ b/src/libs/installer/downloadfiletask_p.h @@ -0,0 +1,113 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef DOWNLOADFILETASK_P_H +#define DOWNLOADFILETASK_P_H + +#include "downloadfiletask.h" + +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QNetworkRequest> + +QT_BEGIN_NAMESPACE +class QFile; +class QSslError; +QT_END_NAMESPACE + +namespace QInstaller { + +class FileTaskObserver; + +struct Data +{ + Data() + : file(0), observer(0) {} + Data(QFile *f, FileTaskObserver *o, const FileTaskItem &fti) + : file(f), observer(o), taskItem(fti) + {} + QFile *file; + FileTaskObserver *observer; + FileTaskItem taskItem; +}; + +class Downloader : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Downloader) + +public: + Downloader(); + ~Downloader(); + + void download(QFutureInterface<FileTaskResult> &fi, const QList<FileTaskItem> &items, + QNetworkProxyFactory *networkProxyFactory); + +signals: + void finished(); + +private slots: + void doDownload(); + void onReadyRead(); + void onFinished(QNetworkReply *reply); + void onError(QNetworkReply::NetworkError error); + void onSslErrors(const QList<QSslError> &sslErrors); + void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + + +private: + bool testCanceled(); + QNetworkReply *startDownload(const FileTaskItem &item); + +private: + QFutureInterface<FileTaskResult> *m_futureInterface; + + int m_finished; + QNetworkAccessManager m_nam; + QList<FileTaskItem> m_items; + QHash<QNetworkReply*, Data> m_downloads; + QMultiHash<QNetworkReply*, QUrl> m_redirects; +}; + +} // namespace QInstaller + +#endif // DOWNLOADFILETASK_P_H diff --git a/src/libs/installer/getrepositoriesmetainfojob.cpp b/src/libs/installer/getrepositoriesmetainfojob.cpp deleted file mode 100644 index cde872064..000000000 --- a/src/libs/installer/getrepositoriesmetainfojob.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/************************************************************************** -** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Installer Framework. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 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 the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -**************************************************************************/ - -#include "getrepositoriesmetainfojob.h" - -#include "getrepositorymetainfojob.h" -#include "productkeycheck.h" -#include "packagemanagercore_p.h" - -#include <QtCore/QDebug> - -using namespace KDUpdater; -using namespace QInstaller; - - -// -- GetRepositoriesMetaInfoJob - -GetRepositoriesMetaInfoJob::GetRepositoriesMetaInfoJob(PackageManagerCore *core) - : KDJob(core) - , m_canceled(false) - , m_silentRetries(3) - , m_haveIgnoredError(false) - , m_core(core) -{ - setCapabilities(Cancelable); -} - -QStringList GetRepositoriesMetaInfoJob::temporaryDirectories() const -{ - return m_repositoryByTemporaryDirectory.keys(); -} - -QStringList GetRepositoriesMetaInfoJob::releaseTemporaryDirectories() const -{ - m_tempDirDeleter.releaseAll(); - return m_repositoryByTemporaryDirectory.keys(); -} - -Repository GetRepositoriesMetaInfoJob::repositoryForTemporaryDirectory(const QString &tmpDir) const -{ - return m_repositoryByTemporaryDirectory.value(tmpDir); -} - -int GetRepositoriesMetaInfoJob::numberOfRetrievedRepositories() const -{ - return m_repositoryByTemporaryDirectory.size(); -} - -int GetRepositoriesMetaInfoJob::silentRetries() const -{ - return m_silentRetries; -} - -void GetRepositoriesMetaInfoJob::setSilentRetries(int retries) -{ - m_silentRetries = retries; -} - -void GetRepositoriesMetaInfoJob::reset() -{ - m_canceled = false; - m_silentRetries = 3; - m_errorString.clear(); - m_haveIgnoredError = false; - - m_repositories.clear(); - m_tempDirDeleter.releaseAndDeleteAll(); - m_repositoryByTemporaryDirectory.clear(); - - setError(KDJob::NoError); - setErrorString(QString()); - setCapabilities(Cancelable); -} - -bool GetRepositoriesMetaInfoJob::isCanceled() const -{ - return m_canceled; -} - -// -- private Q_SLOTS - -void GetRepositoriesMetaInfoJob::doStart() -{ - if ((m_core->isInstaller() && !m_core->isOfflineOnly()) || (m_core->isUpdater() - || m_core->isPackageManager())) { - const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); - foreach (const Repository &repo, m_core->settings().repositories()) { - if (repo.isEnabled() && productKeyCheck->isValidRepository(repo)) - m_repositories += repo; - } - } - - fetchNextRepo(); -} - -void GetRepositoriesMetaInfoJob::doCancel() -{ - m_canceled = true; - if (m_job) - m_job->cancel(); -} - -void GetRepositoriesMetaInfoJob::fetchNextRepo() -{ - if (m_job) { - m_job->deleteLater(); - m_job = 0; - } - - if (m_canceled) { - emitFinishedWithError(KDJob::Canceled, m_errorString); - return; - } - - if (m_repositories.isEmpty()) { - if (m_haveIgnoredError) - emitFinishedWithError(QInstaller::UserIgnoreError, m_errorString); - else - emitFinished(); - return; - } - - m_job = new GetRepositoryMetaInfoJob(m_core, this); - connect(m_job, SIGNAL(finished(KDJob*)), this, SLOT(jobFinished(KDJob*))); - connect(m_job, SIGNAL(infoMessage(KDJob*, QString)), this, SIGNAL(infoMessage(KDJob*, QString))); - - m_job->setSilentRetries(silentRetries()); - m_job->setRepository(m_repositories.takeLast()); - m_job->start(); -} - -void GetRepositoriesMetaInfoJob::jobFinished(KDJob *j) -{ - const GetRepositoryMetaInfoJob *const job = qobject_cast<const GetRepositoryMetaInfoJob *>(j); - Q_ASSERT(job); - - if (job->error() != KDJob::NoError && !job->temporaryDirectory().isEmpty()) { - try { - removeDirectory(job->temporaryDirectory()); - } catch (...) { - } - } - - if (job->error() == KDJob::Canceled - || (job->error() >= KDJob::UserDefinedError && job->error() < QInstaller::UserIgnoreError)) { - emit infoMessage(j, job->errorString()); - qDebug() << job->errorString(); - emitFinishedWithError(job->error(), job->errorString()); - return; - } - - if (job->error() == QInstaller::UserIgnoreError) { - m_haveIgnoredError = true; - m_errorString = job->errorString(); - } else { - const QString &tmpdir = job->releaseTemporaryDirectory(); - job->m_tempDirDeleter.passAndRelease(m_tempDirDeleter, tmpdir); - m_repositoryByTemporaryDirectory.insert(tmpdir, job->repository()); - } - - if (job->error() == QInstaller::RepositoryUpdatesReceived) { - reset(); - QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); - } else { - QMetaObject::invokeMethod(this, "fetchNextRepo", Qt::QueuedConnection); - } -} diff --git a/src/libs/installer/getrepositorymetainfojob.cpp b/src/libs/installer/getrepositorymetainfojob.cpp index 14bcf26f2..8b6b53484 100644 --- a/src/libs/installer/getrepositorymetainfojob.cpp +++ b/src/libs/installer/getrepositorymetainfojob.cpp @@ -53,7 +53,6 @@ #include <QTimer> - using namespace KDUpdater; using namespace QInstaller; @@ -65,20 +64,13 @@ class GetRepositoryMetaInfoJob::ZipRunnable : public QObject, public QRunnable Q_OBJECT public: - ZipRunnable(const QString &archive, const QString &targetDir, QPointer<FileDownloader> downloader) + ZipRunnable(const QString &archive, const QString &targetDir) : QObject() , QRunnable() , m_archive(archive) , m_targetDir(targetDir) - , m_downloader(downloader) {} - ~ZipRunnable() - { - if (m_downloader) - m_downloader->deleteLater(); - } - void run() { QFile archive(m_archive); @@ -107,7 +99,6 @@ Q_SIGNALS: private: const QString m_archive; const QString m_targetDir; - QPointer<FileDownloader> m_downloader; }; @@ -119,9 +110,10 @@ GetRepositoryMetaInfoJob::GetRepositoryMetaInfoJob(PackageManagerCore *core, QOb , m_silentRetries(4) , m_retriesLeft(m_silentRetries) , m_downloader(0) - , m_waitForDone(false) , m_core(core) + , m_watcher(0) { + setTotalAmount(100); setCapabilities(Cancelable); } @@ -154,7 +146,13 @@ void GetRepositoryMetaInfoJob::setSilentRetries(int retries) void GetRepositoryMetaInfoJob::doStart() { + m_canceled = false; + setProcessedAmount(0); + m_packageHash.clear(); + m_packageNames.clear(); + m_packageVersions.clear(); m_retriesLeft = m_silentRetries; + startUpdatesXmlDownload(); } @@ -163,11 +161,16 @@ void GetRepositoryMetaInfoJob::doCancel() m_canceled = true; if (m_downloader) m_downloader->cancelDownload(); + if (m_watcher) + m_watcher->cancel(); +#if QT_VERSION >= 0x050200 + m_threadPool.clear(); +#endif + m_threadPool.waitForDone(); } void GetRepositoryMetaInfoJob::finished(int error, const QString &errorString) { - m_waitForDone = true; m_threadPool.waitForDone(); (error > KDJob::NoError) ? emitFinishedWithError(error, errorString) : emitFinished(); } @@ -384,14 +387,15 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() return; } - setTotalAmount(m_packageNames.count() + 1); setProcessedAmount(1); emit infoMessage(this, tr("Finished updating component meta information.")); - if (m_packageNames.isEmpty()) + if (m_packageNames.isEmpty()) { finished(KDJob::NoError); - else - fetchNextMetaInfo(); + setProcessedAmount(100); + } else { + downloadMetaInfo(); + } } void GetRepositoryMetaInfoJob::updatesXmlDownloadError(const QString &err) @@ -417,131 +421,83 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadError(const QString &err) // meta data download -void GetRepositoryMetaInfoJob::fetchNextMetaInfo() +void GetRepositoryMetaInfoJob::downloadMetaInfo() { emit infoMessage(this, tr("Retrieving component information from remote repository...")); - if (m_canceled) { - finished(KDJob::Canceled, m_downloader->errorString()); + finished(KDJob::Canceled, tr("Meta data download canceled.")); return; } - if (m_packageNames.isEmpty() && m_currentPackageName.isEmpty()) { + if (m_packageNames.isEmpty()) { finished(KDJob::NoError); return; } - QString next = m_currentPackageName; - QString nextVersion = m_currentPackageVersion; - if (next.isEmpty()) { - m_retriesLeft = m_silentRetries; - next = m_packageNames.takeLast(); - nextVersion = m_packageVersions.takeLast(); - } - - qDebug() << "fetching metadata of" << next << "in version" << nextVersion; - bool online = true; if (m_repository.url().scheme().isEmpty()) online = false; + QList<FileTaskItem> items; const QString repoUrl = m_repository.url().toString(); - const QUrl url = QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, next, - online ? nextVersion : QString()); - m_downloader = FileDownloaderFactory::instance().create(url.scheme(), this); - - if (!m_downloader) { - m_currentPackageName.clear(); - m_currentPackageVersion.clear(); - qWarning() << "Scheme not supported:" << m_repository.displayname(); - QMetaObject::invokeMethod(this, "fetchNextMetaInfo", Qt::QueuedConnection); - return; + for (int i = 0; i < m_packageNames.count(); ++i) { + items.append(FileTaskItem(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, + m_packageNames.at(i), (online ? m_packageVersions.at(i) : QString())))); + items[i].insert(TaskRole::Checksum, m_packageHash.value(i).toLatin1()); } - m_currentPackageName = next; - m_currentPackageVersion = nextVersion; - m_downloader->setUrl(url); - m_downloader->setAutoRemoveDownloadedFile(true); - QAuthenticator auth; auth.setUser(m_repository.username()); auth.setPassword(m_repository.password()); - m_downloader->setAuthenticator(auth); - connect(m_downloader, SIGNAL(downloadCanceled()), this, SLOT(metaDownloadCanceled())); - connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(metaDownloadFinished())); - connect(m_downloader, SIGNAL(downloadAborted(QString)), this, SLOT(metaDownloadError(QString)), - Qt::QueuedConnection); - connect(m_downloader, SIGNAL(authenticatorChanged(QAuthenticator)), this, - SLOT(onAuthenticatorChanged(QAuthenticator))); + m_metaDataTask.setTaskItems(items); + m_metaDataTask.setAuthenticator(auth); + m_metaDataTask.setProxyFactory(m_core->proxyFactory()->clone()); - m_downloader->download(); + m_watcher = new QFutureWatcher<FileTaskResult>(this); + connect(m_watcher, SIGNAL(finished()), this, SLOT(metaInfoDownloadFinished())); + connect(m_watcher, SIGNAL(progressValueChanged(int)), this, SLOT(onProgressValueChanged(int))); + m_watcher->setFuture(QtConcurrent::run(&DownloadFileTask::doTask, &m_metaDataTask)); } -void GetRepositoryMetaInfoJob::metaDownloadCanceled() +void GetRepositoryMetaInfoJob::metaInfoDownloadFinished() { - finished(KDJob::Canceled, m_downloader->errorString()); -} - -void GetRepositoryMetaInfoJob::metaDownloadFinished() -{ - const QString fn = m_downloader->downloadedFileName(); - Q_ASSERT(!fn.isEmpty()); - - QFile arch(fn); - if (!arch.open(QIODevice::ReadOnly)) { - finished(QInstaller::ExtractionError, tr("Could not open meta info archive: %1. Error: %2").arg(fn, - arch.errorString())); - return; - } - - if (!m_packageHash.isEmpty()) { - // verify file hash - const QByteArray expectedFileHash = m_packageHash.back().toLatin1(); - const QByteArray realFileHash = QInstaller::calculateHash(&arch, QCryptographicHash::Sha1).toHex(); - if (expectedFileHash != realFileHash) { - emit infoMessage(this, tr("The hash of one component does not match the expected one.")); - metaDownloadError(tr("Bad hash.")); - return; + QFutureWatcher<FileTaskResult> *task = static_cast<QFutureWatcher<FileTaskResult> *>(sender()); + try { + task->waitForFinished(); + QFuture<FileTaskResult> future = task->future(); + if (future.resultCount() > 0) { + foreach (const FileTaskResult &result, future.results()) { + ZipRunnable *runnable = new ZipRunnable(result.target(), m_temporaryDirectory); + connect(runnable, SIGNAL(finished(bool, QString)), this, SLOT(unzipFinished(bool, + QString))); + m_threadPool.start(runnable); + } } - m_packageHash.removeLast(); + finished(KDJob::NoError); + } catch (FileTaskException &e) { + doCancel(); + finished(KDJob::Canceled, e.message()); + } catch (QUnhandledException &e) { + doCancel(); + finished(KDJob::Canceled, QLatin1String(e.what())); + } catch (...) { + doCancel(); + finished(KDJob::Canceled, tr("Unknown exception.")); } - arch.close(); - m_currentPackageName.clear(); - - ZipRunnable *runnable = new ZipRunnable(fn, m_temporaryDirectory, m_downloader); - connect(runnable, SIGNAL(finished(bool,QString)), this, SLOT(unzipFinished(bool,QString))); - m_threadPool.start(runnable); - - if (!m_waitForDone) - fetchNextMetaInfo(); } -void GetRepositoryMetaInfoJob::metaDownloadError(const QString &err) +void GetRepositoryMetaInfoJob::onProgressValueChanged(int progress) { - if (m_retriesLeft <= 0) { - const QString msg = tr("Could not download meta information for component: %1. Error: %2") - .arg(m_currentPackageName, err); - - QMessageBox::StandardButtons buttons = QMessageBox::Retry | QMessageBox::Cancel; - const QMessageBox::StandardButton b = - MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), - QLatin1String("updatesXmlDownloadError"), tr("Download Error"), msg, buttons); - - if (b == QMessageBox::Cancel || b == QMessageBox::NoButton) { - finished(KDJob::Canceled, msg); - return; - } - } - - m_retriesLeft--; - QTimer::singleShot(1500, this, SLOT(fetchNextMetaInfo())); + setProcessedAmount(progress + 1); } void GetRepositoryMetaInfoJob::unzipFinished(bool ok, const QString &error) { - if (!ok) + if (!ok) { + doCancel(); finished(QInstaller::ExtractionError, error); + } } bool GetRepositoryMetaInfoJob::updateRepositories(QSet<Repository> *repositories, const QString &username, diff --git a/src/libs/installer/getrepositorymetainfojob.h b/src/libs/installer/getrepositorymetainfojob.h deleted file mode 100644 index 803f8b43f..000000000 --- a/src/libs/installer/getrepositorymetainfojob.h +++ /dev/null @@ -1,127 +0,0 @@ -/************************************************************************** -** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Installer Framework. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 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 the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -**************************************************************************/ -#ifndef GETREPOSITORYMETAINFOJOB_H -#define GETREPOSITORYMETAINFOJOB_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 <QAuthenticator> - -namespace KDUpdater { - class FileDownloader; -} - -namespace QInstaller { - -class GetRepositoriesMetaInfoJob; -class PackageManagerCore; - -class INSTALLER_EXPORT GetRepositoryMetaInfoJob : public KDJob -{ - Q_OBJECT - class ZipRunnable; - friend class QInstaller::GetRepositoriesMetaInfoJob; - -public: - explicit GetRepositoryMetaInfoJob(PackageManagerCore *core, QObject *parent = 0); - ~GetRepositoryMetaInfoJob(); - - Repository repository() const; - void setRepository(const Repository &r); - - int silentRetries() const; - void setSilentRetries(int retries); - - QString temporaryDirectory() const; - QString releaseTemporaryDirectory() const; - -private: - /* reimp */ void doStart(); - /* reimp */ void doCancel(); - void finished(int error, const QString &errorString = QString()); - bool updateRepositories(QSet<Repository> *repositories, const QString &username, - const QString &password, const QString &displayname = QString()); - -private Q_SLOTS: - void startUpdatesXmlDownload(); - void updatesXmlDownloadCanceled(); - void updatesXmlDownloadFinished(); - void updatesXmlDownloadError(const QString &error); - - void fetchNextMetaInfo(); - void metaDownloadCanceled(); - void metaDownloadFinished(); - void metaDownloadError(const QString &error); - - void unzipFinished(bool status, const QString &error); - void onAuthenticatorChanged(const QAuthenticator &authenticator); - -private: - bool m_canceled; - int m_silentRetries; - int m_retriesLeft; - Repository m_repository; - QStringList m_packageNames; - QStringList m_packageVersions; - QStringList m_packageHash; - QPointer<KDUpdater::FileDownloader> m_downloader; - QString m_currentPackageName; - QString m_currentPackageVersion; - QString m_temporaryDirectory; - mutable TempDirDeleter m_tempDirDeleter; - - bool m_waitForDone; - QThreadPool m_threadPool; - PackageManagerCore *m_core; -}; - -} // namespace QInstaller - -#endif // GETREPOSITORYMETAINFOJOB_H diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp index 12891f0e2..eedae80bd 100644 --- a/src/libs/installer/init.cpp +++ b/src/libs/installer/init.cpp @@ -188,6 +188,9 @@ static void messageHandler(QtMsgType type, const char *msg) #else void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + // suppress warning from QPA minimal plugin + if (msg.contains(QLatin1String("This plugin does not support propagateSizeHints"))) + return; QByteArray ba = trimAndPrepend(type, msg.toLocal8Bit()); if (type != QtDebugMsg) { ba += QString(QStringLiteral(" (%1:%2, %3)")).arg( diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 149287428..837196c5f 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -70,7 +70,6 @@ HEADERS += packagemanagercore.h \ installiconsoperation.h \ selfrestartoperation.h \ settings.h \ - getrepositorymetainfojob.h \ downloadarchivesjob.h \ init.h \ updater.h \ @@ -86,7 +85,6 @@ HEADERS += packagemanagercore.h \ minimumprogressoperation.h \ performinstallationform.h \ messageboxhandler.h \ - getrepositoriesmetainfojob.h \ licenseoperation.h \ component_p.h \ qtcreator_constants.h \ @@ -104,7 +102,17 @@ HEADERS += packagemanagercore.h \ graph.h \ settingsoperation.h \ testrepository.h \ - packagemanagerpagefactory.h + packagemanagerpagefactory.h \ + abstracttask.h\ + abstractfiletask.h \ + copyfiletask.h \ + downloadfiletask.h \ + downloadfiletask_p.h \ + unziptask.h \ + observer.h \ + runextensions.h \ + metadatajob.h \ + metadatajob_p.h SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ @@ -136,7 +144,6 @@ HEADERS += packagemanagercore.h \ environmentvariablesoperation.cpp \ installiconsoperation.cpp \ selfrestartoperation.cpp \ - getrepositorymetainfojob.cpp \ downloadarchivesjob.cpp \ init.cpp \ updater.cpp \ @@ -152,7 +159,6 @@ HEADERS += packagemanagercore.h \ minimumprogressoperation.cpp \ performinstallationform.cpp \ messageboxhandler.cpp \ - getrepositoriesmetainfojob.cpp \ licenseoperation.cpp \ component_p.cpp \ qprocesswrapper.cpp \ @@ -169,7 +175,13 @@ HEADERS += packagemanagercore.h \ globals.cpp \ settingsoperation.cpp \ testrepository.cpp \ - packagemanagerpagefactory.cpp + packagemanagerpagefactory.cpp \ + abstractfiletask.cpp \ + copyfiletask.cpp \ + downloadfiletask.cpp \ + unziptask.cpp \ + observer.cpp \ + metadatajob.cpp RESOURCES += resources/patch_file_lists.qrc \ resources/installer.qrc diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp index d7588d077..aebf665d1 100644 --- a/src/libs/installer/link.cpp +++ b/src/libs/installer/link.cpp @@ -45,7 +45,7 @@ #include <QDir> #include <QDebug> -#ifdef Q_OS_LINUX +#ifdef Q_OS_UNIX #include <unistd.h> #endif diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp new file mode 100644 index 000000000..d326b70bd --- /dev/null +++ b/src/libs/installer/metadatajob.cpp @@ -0,0 +1,380 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "metadatajob.h" +#include "errors.h" +#include "messageboxhandler.h" +#include "metadatajob_p.h" +#include "packagemanagercore.h" +#include "productkeycheck.h" +#include "qinstallerglobal.h" +#include "settings.h" + +namespace QInstaller { + +MetadataJob::MetadataJob(QObject *parent) + : KDJob(parent) + , m_core(0) +{ + setCapabilities(Cancelable); + connect(&m_xmlTask, SIGNAL(finished()), this, SLOT(xmlTaskFinished())); + connect(&m_metadataTask, SIGNAL(finished()), this, SLOT(metadataTaskFinished())); + connect(&m_metadataTask, SIGNAL(progressValueChanged(int)), this, SLOT(progressChanged(int))); +} + +MetadataJob::~MetadataJob() +{ + reset(); +} + +Repository MetadataJob::repositoryForDirectory(const QString &directory) const +{ + return m_metadata.value(directory).repository; +} + + +// -- private slots + +void MetadataJob::doStart() +{ + reset(); + if (!m_core) { + emitFinishedWithError(KDJob::Canceled, tr("Missing package manager core engine.")); + return; // We can't do anything here without core, so avoid tons of !m_core checks. + } + + emit infoMessage(this, tr("Preparing meta information download...")); + const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly(); + if (onlineInstaller || (m_core->isUpdater() || m_core->isPackageManager())) { + QList<FileTaskItem> items; + const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); + foreach (const Repository &repo, m_core->settings().repositories()) { + if (repo.isEnabled() && productKeyCheck->isValidRepository(repo)) { + QAuthenticator authenticator; + authenticator.setUser(repo.username()); + authenticator.setPassword(repo.password()); + + // append a random string to avoid proxy caches + FileTaskItem item(repo.url().toString() + QString::fromLatin1("/Updates.xml?") + .append(QString::number(qrand() * qrand()))); + item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); + item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); + items.append(item); + } + } + DownloadFileTask *const xmlTask = new DownloadFileTask(items); + m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); + } else { + emitFinished(); + } +} + +void MetadataJob::doCancel() +{ + reset(); + emitFinishedWithError(KDJob::Canceled, tr("Meta data download canceled.")); +} + +void MetadataJob::xmlTaskFinished() +{ + Status status = XmlDownloadFailure; + try { + m_xmlTask.waitForFinished(); + status = parseUpdatesXml(m_xmlTask.future().results()); + } catch (const FileTaskException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, e.message()); + } catch (const QUnhandledException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); + } catch (...) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during download.")); + } + + if (error() != KDJob::NoError) + return; + + if (status == XmlDownloadSuccess) { + setProcessedAmount(0); + DownloadFileTask *const metadataTask = new DownloadFileTask(m_packages); + m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); + emit infoMessage(this, tr("Retrieving meta information from remote repository...")); + } else if (status == XmlDownloadRetry) { + QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); + } else { + reset(); + emitFinishedWithError(QInstaller::DownloadError, tr("Failure to fetch repositories.")); + } +} + +void MetadataJob::unzipTaskFinished() +{ + QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender()); + try { + watcher->waitForFinished(); // trigger possible exceptions + } catch (const UnzipArchiveException &e) { + reset(); + emitFinishedWithError(QInstaller::ExtractionError, e.message()); + } catch (const QUnhandledException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); + } catch (...) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during extracting.")); + } + delete m_unzipTasks.value(watcher); + m_unzipTasks.remove(watcher); + delete watcher; + + if (error() != KDJob::NoError) + return; + if (m_unzipTasks.isEmpty()) { + setProcessedAmount(100); + emitFinished(); + } +} + +void MetadataJob::progressChanged(int progress) +{ + setProcessedAmount(progress); +} + +void MetadataJob::metadataTaskFinished() +{ + try { + m_metadataTask.waitForFinished(); + QFuture<FileTaskResult> future = m_metadataTask.future(); + if (future.resultCount() > 0) { + emit infoMessage(this, tr("Extracting meta information...")); + foreach (const FileTaskResult &result, future.results()) { + const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>(); + UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), + item.value(TaskRole::UserRole).toString()); + + QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); + m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task)); + connect(watcher, SIGNAL(finished()), this, SLOT(unzipTaskFinished())); + watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); + } + } else { + emitFinished(); + } + } catch (const FileTaskException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, e.message()); + } catch (const QUnhandledException &e) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); + } catch (...) { + reset(); + emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during download.")); + } +} + + +// -- private + +void MetadataJob::reset() +{ + m_packages.clear(); + m_metadata.clear(); + + setError(KDJob::NoError); + setErrorString(QString()); + setCapabilities(Cancelable); + + try { + m_xmlTask.cancel(); + m_metadataTask.cancel(); + foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) + watcher->cancel(); + foreach (QObject *const object, m_unzipTasks) + object->deleteLater(); + } catch (...) {} + m_tempDirDeleter.releaseAndDeleteAll(); +} + +MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &results) +{ + foreach (const FileTaskResult &result, results) { + if (error() != KDJob::NoError) + return XmlDownloadFailure; + + Metadata metadata; + try { + metadata.directory = createTemporaryDirectory(QLatin1String("remoterepo-")); + m_tempDirDeleter.add(metadata.directory); + } catch (const QInstaller::Error &error) { + qDebug() << error.message(); + return XmlDownloadFailure; + } + + QFile file(result.target()); + if (!file.rename(metadata.directory + QLatin1String("/Updates.xml"))) { + qDebug() << "Could not rename target to Updates.xml. Error:" << file.errorString(); + return XmlDownloadFailure; + } + + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << "Could not open Updates.xml for reading. Error:" << file.errorString(); + return XmlDownloadFailure; + } + + QString error; + QDomDocument doc; + if (!doc.setContent(&file, &error)) { + qDebug() << QString::fromLatin1("Could not fetch a valid version of Updates.xml from " + "repository: %1. Error: %2").arg(metadata.repository.displayname(), error); + return XmlDownloadFailure; + } + file.close(); + + const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>(); + metadata.repository = item.value(TaskRole::UserRole).value<Repository>(); + const bool online = !(metadata.repository.url().scheme()).isEmpty(); + + const QDomElement root = doc.documentElement(); + QDomNodeList children = root.childNodes(); + for (int i = 0; i < children.count(); ++i) { + const QDomElement el = children.at(i).toElement(); + if (!el.isNull() && el.tagName() == QLatin1String("PackageUpdate")) { + const QDomNodeList c2 = el.childNodes(); + QString packageName, packageVersion, packageHash; + for (int j = 0; j < c2.count(); ++j) { + if (c2.at(j).toElement().tagName() == scName) + packageName = c2.at(j).toElement().text(); + else if (c2.at(j).toElement().tagName() == scRemoteVersion) + packageVersion = c2.at(j).toElement().text(); + else if (c2.at(j).toElement().tagName() == QLatin1String("SHA1")) + packageHash = c2.at(j).toElement().text(); + } + const QString repoUrl = metadata.repository.url().toString(); + FileTaskItem item(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, + packageName, (online ? packageVersion : QString()))); + item.insert(TaskRole::UserRole, metadata.directory); + item.insert(TaskRole::Checksum, packageHash.toLatin1()); + m_packages.append(item); + } + } + m_metadata.insert(metadata.directory, metadata); + + // search for additional repositories that we might need to check + const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate")); + if (repositoryUpdate.isNull()) + continue; + + QHash<QString, QPair<Repository, Repository> > repositoryUpdates; + children = repositoryUpdate.toElement().childNodes(); + for (int i = 0; i < children.count(); ++i) { + const QDomElement el = children.at(i).toElement(); + if (!el.isNull() && el.tagName() == QLatin1String("Repository")) { + const QString action = el.attribute(QLatin1String("action")); + if (action == QLatin1String("add")) { + // add a new repository to the defaults list + Repository repository(el.attribute(QLatin1String("url")), true); + repository.setUsername(el.attribute(QLatin1String("username"))); + repository.setPassword(el.attribute(QLatin1String("password"))); + repository.setDisplayName(el.attribute(QLatin1String("displayname"))); + if (ProductKeyCheck::instance()->isValidRepository(repository)) { + repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); + qDebug() << "Repository to add:" << repository.displayname(); + } + } else if (action == QLatin1String("remove")) { + // remove possible default repositories using the given server url + Repository repository(el.attribute(QLatin1String("url")), true); + repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); + + qDebug() << "Repository to remove:" << repository.displayname(); + } else if (action == QLatin1String("replace")) { + // replace possible default repositories using the given server url + Repository oldRepository(el.attribute(QLatin1String("oldUrl")), true); + Repository newRepository(el.attribute(QLatin1String("newUrl")), true); + newRepository.setUsername(el.attribute(QLatin1String("username"))); + newRepository.setPassword(el.attribute(QLatin1String("password"))); + newRepository.setDisplayName(el.attribute(QLatin1String("displayname"))); + + if (ProductKeyCheck::instance()->isValidRepository(newRepository)) { + // store the new repository and the one old it replaces + repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository)); + qDebug() << "Replace repository:" << oldRepository.displayname() << "with:" + << newRepository.displayname(); + } + } else { + qDebug() << "Invalid additional repositories action set in Updates.xml fetched " + "from:" << metadata.repository.displayname() << "Line:" << el.lineNumber(); + } + } + } + + if (!repositoryUpdates.isEmpty()) { + Settings &s = m_core->settings(); + const QSet<Repository> temporaries = s.temporaryRepositories(); + // in case the temp repository introduced something new, we only want that temporary + if (temporaries.contains(metadata.repository)) { + QSet<Repository> tmpRepositories; + typedef QPair<Repository, Repository> RepositoryPair; + + QList<RepositoryPair> values = repositoryUpdates.values(QLatin1String("add")); + foreach (const RepositoryPair &value, values) + tmpRepositories.insert(value.first); + + values = repositoryUpdates.values(QLatin1String("replace")); + foreach (const RepositoryPair &value, values) + tmpRepositories.insert(value.first); + + tmpRepositories = tmpRepositories.subtract(temporaries); + if (tmpRepositories.count() > 0) { + s.addTemporaryRepositories(tmpRepositories, true); + QFile::remove(result.target()); + return XmlDownloadRetry; + } + } else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) { + if (m_core->isUpdater() || m_core->isPackageManager()) + m_core->writeMaintenanceConfigFiles(); + QFile::remove(result.target()); + return XmlDownloadRetry; + } + } + } + return XmlDownloadSuccess; +} + +} // namespace QInstaller diff --git a/src/libs/installer/getrepositoriesmetainfojob.h b/src/libs/installer/metadatajob.h index 2cc53b82a..a10eb083b 100644 --- a/src/libs/installer/getrepositoriesmetainfojob.h +++ b/src/libs/installer/metadatajob.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. @@ -38,68 +38,70 @@ ** $QT_END_LICENSE$ ** **************************************************************************/ -#ifndef GETREPOSITORIESMETAINFOJOB_H -#define GETREPOSITORIESMETAINFOJOB_H -#include "fileutils.h" -#include "installer_global.h" -#include "repository.h" +#ifndef METADATAJOB_H +#define METADATAJOB_H +#include "downloadfiletask.h" +#include "fileutils.h" #include "kdjob.h" +#include "repository.h" -#include <QtCore/QList> -#include <QtCore/QPointer> -#include <QtCore/QString> -#include <QtCore/QStringList> - -namespace KDUpdater { - class FileDownloader; -} +#include <QFutureWatcher> namespace QInstaller { -class GetRepositoryMetaInfoJob; class PackageManagerCore; -class INSTALLER_EXPORT GetRepositoriesMetaInfoJob : public KDJob +struct Metadata +{ + QString directory; + Repository repository; +}; + +class INSTALLER_EXPORT MetadataJob : public KDJob { Q_OBJECT + Q_DISABLE_COPY(MetadataJob) + + enum Status { + XmlDownloadRetry, + XmlDownloadFailure, + XmlDownloadSuccess + }; public: - explicit GetRepositoriesMetaInfoJob(PackageManagerCore *core); + explicit MetadataJob(QObject *parent = 0); + ~MetadataJob(); - QStringList temporaryDirectories() const; - QStringList releaseTemporaryDirectories() const; - Repository repositoryForTemporaryDirectory(const QString &tmpDir) const; + QList<Metadata> metadata() const { return m_metadata.values(); } + Repository repositoryForDirectory(const QString &directory) const; + void setPackageManagerCore(PackageManagerCore *core) { m_core = core; } - int numberOfRetrievedRepositories() const; +private slots: + void doStart(); + void doCancel(); - int silentRetries() const; - void setSilentRetries(int retries); + void xmlTaskFinished(); + void unzipTaskFinished(); + void metadataTaskFinished(); + void progressChanged(int progress); +private: void reset(); - bool isCanceled() const; - -private Q_SLOTS: - /* reimp */ void doStart(); - /* reimp */ void doCancel(); - - void fetchNextRepo(); - void jobFinished(KDJob*); + Status parseUpdatesXml(const QList<FileTaskResult> &results); private: - bool m_canceled; - int m_silentRetries; - bool m_haveIgnoredError; PackageManagerCore *m_core; - QString m_errorString; - QList<Repository> m_repositories; - mutable TempDirDeleter m_tempDirDeleter; - QPointer<GetRepositoryMetaInfoJob> m_job; - QHash<QString, Repository> m_repositoryByTemporaryDirectory; + QList<FileTaskItem> m_packages; + TempDirDeleter m_tempDirDeleter; + QHash<QString, Metadata> m_metadata; + QFutureWatcher<FileTaskResult> m_xmlTask; + QFutureWatcher<FileTaskResult> m_metadataTask; + QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks; }; } // namespace QInstaller -#endif // GETREPOSITORIESMETAINFOJOB_H +#endif // METADATAJOB_H diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h new file mode 100644 index 000000000..3b1835a3c --- /dev/null +++ b/src/libs/installer/metadatajob_p.h @@ -0,0 +1,120 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef METADATAJOB_P_H +#define METADATAJOB_P_H + +#include "lib7z_facade.h" +#include "metadatajob.h" + +namespace QInstaller{ + +class UnzipArchiveException : public QException +{ +public: + UnzipArchiveException() {} + ~UnzipArchiveException() throw() {} + explicit UnzipArchiveException(const QString &message) + : m_message(message) + {} + + void raise() const { throw *this; } + QString message() const { return m_message; } + UnzipArchiveException *clone() const { return new UnzipArchiveException(*this); } + +private: + QString m_message; +}; + +class UnzipArchiveTask : public AbstractTask<void> +{ + Q_OBJECT + Q_DISABLE_COPY(UnzipArchiveTask) + +public: + UnzipArchiveTask(const QString &arcive, const QString &target) + : m_archive(arcive), m_targetDir(target) + {} + ~UnzipArchiveTask() + { + QFile file(m_archive); + if (!file.remove()) { + qWarning("Could not delete file %s: %s", qPrintable(m_archive), qPrintable(file + .errorString())); + } + } + void doTask(QFutureInterface<void> &fi) + { + fi.reportStarted(); + fi.setExpectedResultCount(1); + + if (fi.isCanceled()) { + fi.reportFinished(); + return; // ignore already canceled + } + + QFile archive(m_archive); + if (archive.open(QIODevice::ReadOnly)) { + try { + Lib7z::extractArchive(&archive, m_targetDir); + } catch (const Lib7z::SevenZipException& e) { + fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting " + "'%1': %2").arg(m_archive, e.message()))); + } catch (...) { + fi.reportException(UnzipArchiveException(MetadataJob::tr("Unknown exception " + "caught while extracting %1.").arg(m_archive))); + } + } else { + fi.reportException(UnzipArchiveException(MetadataJob::tr("Could not open %1 for " + "reading. Error: %2").arg(m_archive, archive.errorString()))); + } + + fi.reportFinished(); + } + +private: + QString m_archive; + QString m_targetDir; +}; + +} // namespace QInstaller + +#endif // METADATAJOB_P_H diff --git a/src/libs/installer/observer.cpp b/src/libs/installer/observer.cpp new file mode 100644 index 000000000..617cd744d --- /dev/null +++ b/src/libs/installer/observer.cpp @@ -0,0 +1,191 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "observer.h" + +#include <fileutils.h> + +namespace QInstaller { + +FileTaskObserver::FileTaskObserver(QCryptographicHash::Algorithm algorithm) + : m_hash(algorithm) +{ + init(); +} + +FileTaskObserver::~FileTaskObserver() +{ + if (m_timerId >= 0) + killTimer(m_timerId); +} + +int FileTaskObserver::progressValue() const +{ + if (m_bytesToTransfer <= 0 || m_bytesTransfered > m_bytesToTransfer) + return 0; + return 100 * m_bytesTransfered / m_bytesToTransfer; +} + +QString FileTaskObserver::progressText() const +{ + QString progressText; + if (m_bytesToTransfer > 0) { + QString bytesReceived = QInstaller::humanReadableSize(m_bytesTransfered); + const QString bytesToReceive = QInstaller::humanReadableSize(m_bytesToTransfer); + + // remove the unit from the bytesReceived value if bytesToReceive has the same + const QString tmp = bytesToReceive.mid(bytesToReceive.indexOf(QLatin1Char(' '))); + if (bytesReceived.endsWith(tmp)) + bytesReceived.chop(tmp.length()); + + progressText = bytesReceived + tr(" of ") + bytesToReceive; + } else { + if (m_bytesTransfered > 0) + progressText = QInstaller::humanReadableSize(m_bytesTransfered) + tr(" received."); + } + + progressText += QLatin1String(" (") + QInstaller::humanReadableSize(m_bytesPerSecond) + tr("/sec") + + QLatin1Char(')'); + if (m_bytesToTransfer > 0 && m_bytesPerSecond > 0) { + const qint64 time = (m_bytesToTransfer - m_bytesTransfered) / m_bytesPerSecond; + + int s = time % 60; + const int d = time / 86400; + const int h = (time / 3600) - (d * 24); + const int m = (time / 60) - (d * 1440) - (h * 60); + + QString days; + if (d > 0) + days = QString::number(d) + (d < 2 ? tr(" day") : tr(" days")) + QLatin1String(", "); + + QString hours; + if (h > 0) + hours = QString::number(h) + (h < 2 ? tr(" hour") : tr(" hours")) + QLatin1String(", "); + + QString minutes; + if (m > 0) + minutes = QString::number(m) + (m < 2 ? tr(" minute") : tr(" minutes")); + + QString seconds; + if (s >= 0 && minutes.isEmpty()) { + s = (s <= 0 ? 1 : s); + seconds = QString::number(s) + (s < 2 ? tr(" second") : tr(" seconds")); + } + progressText += tr(" - ") + days + hours + minutes + seconds + tr(" remaining."); + } else { + progressText += tr(" - unknown time remaining."); + } + + return progressText; +} + +QByteArray FileTaskObserver::checkSum() const +{ + return m_hash.result(); +} + +void FileTaskObserver::addCheckSumData(const char *data, int length) +{ + m_hash.addData(data, length); +} + +void FileTaskObserver::addSample(qint64 sample) +{ + m_currentSpeedBin += sample; +} + +void FileTaskObserver::setBytesTransfered(qint64 bytesReceived) +{ + m_bytesTransfered = bytesReceived; +} + +void FileTaskObserver::addBytesTransfered(qint64 bytesReceived) +{ + m_bytesTransfered += bytesReceived; +} + +void FileTaskObserver::setBytesToTransfer(qint64 bytesToReceive) +{ + m_bytesToTransfer = bytesToReceive; +} + + +// -- private + +void FileTaskObserver::init() +{ + m_hash.reset(); + m_sampleIndex = 0; + m_bytesTransfered = 0; + m_bytesToTransfer = 0; + m_bytesPerSecond = 0; + m_currentSpeedBin = 0; + + m_timerId = -1; + m_timerInterval = 100; + memset(m_samples, 0, sizeof(m_samples)); + m_timerId = startTimer(m_timerInterval); +} + +void FileTaskObserver::timerEvent(QTimerEvent *event) +{ + Q_UNUSED(event) + unsigned int windowSize = sizeof(m_samples) / sizeof(qint64); + + // add speed of last time bin to the window + m_samples[m_sampleIndex % windowSize] = m_currentSpeedBin; + m_currentSpeedBin = 0; // reset bin for next time interval + + // advance the sample index + m_sampleIndex++; + m_bytesPerSecond = 0; + + // dynamic window size until the window is completely filled + if (m_sampleIndex < windowSize) + windowSize = m_sampleIndex; + + for (unsigned int i = 0; i < windowSize; ++i) + m_bytesPerSecond += m_samples[i]; + + m_bytesPerSecond /= windowSize; // computer average + m_bytesPerSecond *= 1000.0 / m_timerInterval; // rescale to bytes/second +} + +} // namespace QInstaller diff --git a/src/libs/installer/observer.h b/src/libs/installer/observer.h new file mode 100644 index 000000000..539d6b2da --- /dev/null +++ b/src/libs/installer/observer.h @@ -0,0 +1,105 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef OBSERVER_H +#define OBSERVER_H + +#include <QCryptographicHash> +#include <QObject> + +namespace QInstaller { + +class Observer : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Observer) + +public: + Observer() {} + virtual ~Observer() {} + + virtual int progressValue() const = 0; + virtual QString progressText() const = 0; +}; + +class FileTaskObserver : public Observer +{ + Q_OBJECT + Q_DISABLE_COPY(FileTaskObserver) + +public: + FileTaskObserver(QCryptographicHash::Algorithm algorithm); + ~FileTaskObserver(); + + int progressValue() const; + QString progressText() const; + + QByteArray checkSum() const; + void addCheckSumData(const char *data, int length); + + void addSample(qint64 sample); + void timerEvent(QTimerEvent *event); + + void setBytesTransfered(qint64 bytesTransfered); + void addBytesTransfered(qint64 bytesTransfered); + void setBytesToTransfer(qint64 bytesToTransfer); + +private: + void init(); + +private: + int m_timerId; + int m_timerInterval; + + qint64 m_bytesTransfered; + qint64 m_bytesToTransfer; + + qint64 m_samples[50]; + quint32 m_sampleIndex; + qint64 m_bytesPerSecond; + qint64 m_currentSpeedBin; + + QCryptographicHash m_hash; +}; + +} // namespace QInstaller + +#endif // OBSERVER_H diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index a4883b651..b90435993 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -49,7 +49,6 @@ #include "errors.h" #include "fsengineclient.h" #include "globals.h" -#include "getrepositoriesmetainfojob.h" #include "messageboxhandler.h" #include "packagemanagerproxyfactory.h" #include "progresscoordinator.h" @@ -185,10 +184,16 @@ */ /*! + \qmlsignal QInstaller::metaJobProgress(int progress) + + Triggered with progress updates of the while communicating with a remote repository. Progress + ranges from 0 to 100. +*/ + +/*! \qmlsignal QInstaller::metaJobInfoMessage(string message) Triggered with informative updates of the communication with a remote repository. - This is only useful for debugging purposes. */ /*! @@ -459,8 +464,7 @@ void PackageManagerCore::setCompleteUninstallation(bool complete) */ void PackageManagerCore::cancelMetaInfoJob() { - if (d->m_repoMetaInfoJob) - d->m_repoMetaInfoJob->cancel(); + d->m_metadataJob.cancel(); } /*! @@ -1534,7 +1538,6 @@ bool PackageManagerCore::localInstallerBinaryUsed() QList<QVariant> PackageManagerCore::execute(const QString &program, const QStringList &arguments, const QString &stdIn) const { - QEventLoop loop; QProcessWrapper process; QString adjustedProgram = replaceVariables(program); @@ -1543,7 +1546,6 @@ QList<QVariant> PackageManagerCore::execute(const QString &program, const QStrin adjustedArguments.append(replaceVariables(argument)); QString adjustedStdIn = replaceVariables(stdIn); - connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit())); process.start(adjustedProgram, adjustedArguments, adjustedStdIn.isNull() ? QIODevice::ReadOnly : QIODevice::ReadWrite); @@ -1555,8 +1557,7 @@ QList<QVariant> PackageManagerCore::execute(const QString &program, const QStrin process.closeWriteChannel(); } - if (process.state() != QProcessWrapper::NotRunning) - loop.exec(); + process.waitForFinished(-1); return QList<QVariant>() << QString::fromLatin1(process.readAllStandardOutput()) << process.exitCode(); } @@ -2099,8 +2100,9 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo lastLocalPath = localPath; } - if (d->m_repoMetaInfoJob) { - const Repository &repo = d->m_repoMetaInfoJob->repositoryForTemporaryDirectory(localPath); + + const Repository repo = d->m_metadataJob.repositoryForDirectory(localPath); + if (repo.isValid()) { component->setRepositoryUrl(repo.url()); component->setValue(QLatin1String("username"), repo.username()); component->setValue(QLatin1String("password"), repo.password()); diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index 5f262e804..a3f2b7eb0 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -279,6 +279,7 @@ Q_SIGNALS: void currentPageChanged(int page); void finishButtonClicked(); + void metaJobProgress(int progress); void metaJobInfoMessage(const QString &message); void startAllComponentsReset(); diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index c54a0e242..c73a7fc82 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -207,7 +207,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_updaterApplication(new DummyConfigurationInterface) , m_FSEngineClientHandler(0) , m_core(core) - , m_repoMetaInfoJob(0) , m_updates(false) , m_repoFetched(false) , m_updateSourcesAdded(false) @@ -235,7 +234,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_performedOperationsOld(performedOperations) , m_dependsOnLocalInstallerBinary(false) , m_core(core) - , m_repoMetaInfoJob(0) , m_updates(false) , m_repoFetched(false) , m_updateSourcesAdded(false) @@ -654,12 +652,13 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms m_updaterApplication.updateSourcesInfo()->setModified(false); } - if (!m_repoMetaInfoJob) { - m_repoMetaInfoJob = new GetRepositoriesMetaInfoJob(m_core); - m_repoMetaInfoJob->setAutoDelete(false); - connect(m_repoMetaInfoJob, SIGNAL(infoMessage(KDJob*, QString)), this, SLOT(infoMessage(KDJob*, - QString))); - } + m_metadataJob.disconnect(); + m_metadataJob.setAutoDelete(false); + m_metadataJob.setPackageManagerCore(m_core); + connect(&m_metadataJob, SIGNAL(infoMessage(KDJob*, QString)), this, + SLOT(infoMessage(KDJob*, QString))); + connect(&m_metadataJob, SIGNAL(progress(KDJob *, quint64, quint64)), this, + SLOT(infoProgress(KDJob *, quint64, quint64))); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } @@ -945,7 +944,7 @@ void PackageManagerCorePrivate::stopProcessesForUpdates(const QList<Component*> foreach (const Component *component, components) processList << m_core->replaceVariables(component->stopProcessForUpdateRequests()); - qSort(processList); + std::sort(processList.begin(), processList.end()); processList.erase(std::unique(processList.begin(), processList.end()), processList.end()); if (processList.isEmpty()) return; @@ -2176,21 +2175,21 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories() m_repoFetched = false; m_updateSourcesAdded = false; - m_repoMetaInfoJob->reset(); try { - m_repoMetaInfoJob->start(); - m_repoMetaInfoJob->waitForFinished(); + m_metadataJob.start(); + m_metadataJob.waitForFinished(); } catch (Error &error) { - setStatus(PackageManagerCore::Failure, tr("Could not retrieve meta information: %1").arg(error.message())); + setStatus(PackageManagerCore::Failure, tr("Could not retrieve meta information: %1") + .arg(error.message())); return m_repoFetched; } - if (m_repoMetaInfoJob->isCanceled() || m_repoMetaInfoJob->error() != KDJob::NoError) { - switch (m_repoMetaInfoJob->error()) { + if (m_metadataJob.error() != KDJob::NoError) { + switch (m_metadataJob.error()) { case QInstaller::UserIgnoreError: break; // we can simply ignore this error, the user knows about it default: - setStatus(PackageManagerCore::Failure, m_repoMetaInfoJob->errorString()); + setStatus(PackageManagerCore::Failure, m_metadataJob.errorString()); return m_repoFetched; } } @@ -2204,7 +2203,8 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe if (m_updateSourcesAdded) return m_updateSourcesAdded; - if (m_repoMetaInfoJob->temporaryDirectories().isEmpty()) { + const QList<Metadata> metadata = m_metadataJob.metadata(); + if (metadata.isEmpty()) { m_updateSourcesAdded = true; return m_updateSourcesAdded; } @@ -2213,7 +2213,8 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe m_updaterApplication.updateSourcesInfo()->refresh(); if (isInstaller()) { m_updaterApplication.addUpdateSource(m_data.settings().applicationName(), - m_data.settings().applicationName(), QString(), QUrl(QLatin1String("resource://metadata/")), 0); + m_data.settings().applicationName(), QString(), + QUrl(QLatin1String("resource://metadata/")), 0); m_updaterApplication.updateSourcesInfo()->setModified(false); } @@ -2221,16 +2222,15 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe m_updateSourcesAdded = false; const QString &appName = m_data.settings().applicationName(); - const QStringList tempDirs = m_repoMetaInfoJob->temporaryDirectories(); - foreach (const QString &tmpDir, tempDirs) { + foreach (const Metadata &data, metadata) { if (statusCanceledOrFailed()) return false; - if (tmpDir.isEmpty()) + if (data.directory.isEmpty()) continue; if (parseChecksum) { - const QString updatesXmlPath = tmpDir + QLatin1String("/Updates.xml"); + const QString updatesXmlPath = data.directory + QLatin1String("/Updates.xml"); QFile updatesFile(updatesXmlPath); try { openForRead(&updatesFile, updatesFile.fileName()); @@ -2255,7 +2255,8 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe if (!checksum.isNull()) m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue); } - m_updaterApplication.addUpdateSource(appName, appName, QString(), QUrl::fromLocalFile(tmpDir), 1); + m_updaterApplication.addUpdateSource(appName, appName, QString(), + QUrl::fromLocalFile(data.directory), 1); } m_updaterApplication.updateSourcesInfo()->setModified(false); diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index a6b9d4438..1d41d9b21 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -42,7 +42,7 @@ #ifndef PACKAGEMANAGERCORE_P_H #define PACKAGEMANAGERCORE_P_H -#include "getrepositoriesmetainfojob.h" +#include "metadatajob.h" #include "packagemanagercore.h" #include "packagemanagercoredata.h" #include "qinstallerglobal.h" @@ -95,6 +95,7 @@ class PackageManagerCorePrivate : public QObject { Q_OBJECT friend class PackageManagerCore; + Q_DISABLE_COPY(PackageManagerCorePrivate) public: enum OperationType { @@ -234,6 +235,9 @@ private slots: void infoMessage(KDJob *, const QString &message) { emit m_core->metaJobInfoMessage(message); } + void infoProgress(KDJob *, quint64 progress, quint64) { + emit m_core->metaJobProgress(progress); + } void handleMethodInvocationRequest(const QString &invokableMethodName); @@ -258,7 +262,7 @@ private: private: PackageManagerCore *m_core; - GetRepositoriesMetaInfoJob *m_repoMetaInfoJob; + MetadataJob m_metadataJob; bool m_updates; bool m_repoFetched; diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index a59586e9e..1757f21f5 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -126,8 +126,8 @@ public: setPixmap(QWizard::WatermarkPixmap, QPixmap()); setLayout(new QVBoxLayout); - setSubTitle(QLatin1String(" ")); - setTitle(widget->windowTitle()); + setColoredSubTitle(QLatin1String(" ")); + setColoredTitle(widget->windowTitle()); m_widget->setProperty("complete", true); m_widget->setProperty("final", false); widget->installEventFilter(this); @@ -150,7 +150,7 @@ protected: if (obj == m_widget) { switch(event->type()) { case QEvent::WindowTitleChange: - setTitle(m_widget->windowTitle()); + setColoredTitle(m_widget->windowTitle()); break; case QEvent::DynamicPropertyChange: @@ -236,6 +236,10 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) setWizardStyle(QWizard::ModernStyle); setSizeGripEnabled(true); #endif + + if (!m_core->settings().wizardStyle().isEmpty()) + setWizardStyle(getStyle(m_core->settings().wizardStyle())); + setOption(QWizard::NoBackButtonOnStartPage); setOption(QWizard::NoBackButtonOnLastPage); @@ -285,6 +289,19 @@ PackageManagerGui::~PackageManagerGui() delete d; } +QWizard::WizardStyle PackageManagerGui::getStyle(const QString &name) +{ + if (name == QLatin1String("Classic")) + return QWizard::ClassicStyle; + else if (name == QLatin1String("Modern")) + return QWizard::ModernStyle; + else if (name == QLatin1String("Mac")) + return QWizard::MacStyle; + else if (name == QLatin1String("Aero")) + return QWizard::AeroStyle; + return QWizard::ModernStyle; +} + void PackageManagerGui::setAutomatedPageSwitchEnabled(bool request) { d->m_autoSwitchPage = request; @@ -625,6 +642,12 @@ PackageManagerPage::PackageManagerPage(PackageManagerCore *core) , m_core(core) , validatorComponent(0) { + if (!m_core->settings().titleColor().isEmpty()) + m_titleColor = m_core->settings().titleColor(); + else { + QColor defaultColor = style()->standardPalette().text().color(); + m_titleColor = defaultColor.name(); + } setPixmap(QWizard::WatermarkPixmap, watermarkPixmap()); setPixmap(QWizard::BannerPixmap, bannerPixmap()); setPixmap(QWizard::LogoPixmap, logoPixmap()); @@ -655,6 +678,18 @@ QString PackageManagerPage::productName() const return m_core->value(QLatin1String("ProductName")); } +void PackageManagerPage::setColoredTitle(const QString &title) +{ + QString coloredTitle = QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title); + setTitle(coloredTitle); +} + +void PackageManagerPage::setColoredSubTitle(const QString &subTitle) +{ + QString coloredTitle = QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle); + setSubTitle(coloredTitle); +} + bool PackageManagerPage::isComplete() const { return m_complete; @@ -758,7 +793,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core) , m_widget(0) { setObjectName(QLatin1String("IntroductionPage")); - setTitle(tr("Setup - %1").arg(productName())); + setColoredTitle(tr("Setup - %1").arg(productName())); m_msgLabel = new QLabel(this); m_msgLabel->setWordWrap(true); @@ -821,7 +856,7 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("LicenseAgreementPage")); - setTitle(tr("License Agreement")); + setColoredTitle(tr("License Agreement")); m_licenseListWidget = new QListWidget(this); m_licenseListWidget->setObjectName(QLatin1String("LicenseListWidget")); @@ -941,7 +976,7 @@ void LicenseAgreementPage::updateUi() rejectButtonText = tr("I do not accept the licenses."); } - setSubTitle(subTitleText); + setColoredSubTitle(subTitleText); m_acceptLabel->setText(acceptButtonText); m_rejectLabel->setText(rejectButtonText); @@ -1142,7 +1177,7 @@ ComponentSelectionPage::ComponentSelectionPage(PackageManagerCore *core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("ComponentSelectionPage")); - setTitle(tr("Select Components")); + setColoredTitle(tr("Select Components")); } ComponentSelectionPage::~ComponentSelectionPage() @@ -1164,7 +1199,7 @@ void ComponentSelectionPage::entering() if (core->isInstaller()) index = 1; if (core->isUninstaller()) index = 2; if (core->isPackageManager()) index = 3; - setSubTitle(tr(strings[index])); + setColoredSubTitle(tr(strings[index])); d->updateTreeView(); setModified(isComplete()); @@ -1234,7 +1269,7 @@ TargetDirectoryPage::TargetDirectoryPage(PackageManagerCore *core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("TargetDirectoryPage")); - setTitle(tr("Installation Folder")); + setColoredTitle(tr("Installation Folder")); QVBoxLayout *layout = new QVBoxLayout(this); @@ -1353,8 +1388,8 @@ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("StartMenuDirectoryPage")); - setTitle(tr("Start Menu shortcuts")); - setSubTitle(tr("Select the Start Menu in which you would like to create the program's shortcuts. You can " + setColoredTitle(tr("Start Menu shortcuts")); + setColoredSubTitle(tr("Select the Start Menu in which you would like to create the program's shortcuts. You can " "also enter a name to create a new folder.")); m_lineEdit = new QLineEdit(this); @@ -1489,7 +1524,7 @@ void ReadyForInstallationPage::entering() m_taskDetailsButton->setVisible(false); m_taskDetailsBrowser->setVisible(false); setButtonText(QWizard::CommitButton, tr("U&ninstall")); - setTitle(tr("Ready to Uninstall")); + setColoredTitle(tr("Ready to Uninstall")); m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>" "<font color=\"red\">The program directory %2 will be deleted completely</font>, " "including all content in that directory!") @@ -1499,12 +1534,12 @@ void ReadyForInstallationPage::entering() return; } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) { setButtonText(QWizard::CommitButton, tr("U&pdate")); - setTitle(tr("Ready to Update Packages")); + setColoredTitle(tr("Ready to Update Packages")); m_msgLabel->setText(tr("Setup is now ready to begin updating your installation.")); } else { Q_ASSERT(packageManagerCore()->isInstaller()); setButtonText(QWizard::CommitButton, tr("&Install")); - setTitle(tr("Ready to Install")); + setColoredTitle(tr("Ready to Install")); m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.") .arg(productName())); } @@ -1711,17 +1746,17 @@ void PerformInstallationPage::entering() if (packageManagerCore()->isUninstaller()) { setButtonText(QWizard::CommitButton, tr("U&ninstall")); - setTitle(tr("Uninstalling %1").arg(productName())); + setColoredTitle(tr("Uninstalling %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runUninstaller())); } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) { setButtonText(QWizard::CommitButton, tr("&Update")); - setTitle(tr("Updating components of %1").arg(productName())); + setColoredTitle(tr("Updating components of %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater())); } else { setButtonText(QWizard::CommitButton, tr("&Install")); - setTitle(tr("Installing %1").arg(productName())); + setColoredTitle(tr("Installing %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runInstaller())); } @@ -1739,7 +1774,7 @@ void PerformInstallationPage::leaving() void PerformInstallationPage::setTitleMessage(const QString &title) { - setTitle(title); + setColoredTitle(title); } // -- private slots @@ -1788,7 +1823,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core) , m_commitButton(0) { setObjectName(QLatin1String("FinishedPage")); - setTitle(tr("Completing the %1 Wizard").arg(productName())); + setColoredTitle(tr("Completing the %1 Wizard").arg(productName())); m_msgLabel = new QLabel(this); m_msgLabel->setWordWrap(true); @@ -1865,7 +1900,7 @@ void FinishedPage::entering() } } else { // TODO: how to handle this using the config.xml - setTitle(tr("The %1 Wizard failed.").arg(productName())); + setColoredTitle(tr("The %1 Wizard failed.").arg(productName())); } m_runItCheckBox->hide(); @@ -1916,7 +1951,7 @@ RestartPage::RestartPage(PackageManagerCore *core) : PackageManagerPage(core) { setObjectName(QLatin1String("RestartPage")); - setTitle(tr("Completing the %1 Setup Wizard").arg(productName())); + setColoredTitle(tr("Completing the %1 Setup Wizard").arg(productName())); setFinalPage(false); setCommitPage(false); diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h index 304f5f2a1..948fb346a 100644 --- a/src/libs/installer/packagemanagergui.h +++ b/src/libs/installer/packagemanagergui.h @@ -94,6 +94,7 @@ public: Q_INVOKABLE void setSettingsButtonEnabled(bool enable); void updateButtonLayout(); + static QWizard::WizardStyle getStyle(const QString &name); Q_SIGNALS: void interrupted(); @@ -153,6 +154,9 @@ public: virtual QPixmap watermarkPixmap() const; virtual QPixmap bannerPixmap() const; + void setColoredTitle(const QString &title); + void setColoredSubTitle(const QString &subTitle); + virtual bool isComplete() const; void setComplete(bool complete); @@ -189,6 +193,7 @@ protected: private: bool m_fresh; bool m_complete; + QString m_titleColor; bool m_needsSettingsButton; PackageManagerCore *m_core; diff --git a/src/libs/installer/runextensions.h b/src/libs/installer/runextensions.h new file mode 100644 index 000000000..e4c7fb2be --- /dev/null +++ b/src/libs/installer/runextensions.h @@ -0,0 +1,435 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef QTCONCURRENT_RUNEX_H +#define QTCONCURRENT_RUNEX_H + +#include <qrunnable.h> +#include <qfutureinterface.h> +#include <qthreadpool.h> + +QT_BEGIN_NAMESPACE + +namespace QtConcurrent { + +template <typename T, typename FunctionPointer> +class StoredInterfaceFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &)) + : fn(fn) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + +}; +template <typename T, typename FunctionPointer, typename Class> +class StoredInterfaceMemberFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object) + : fn(fn), object(object) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + +}; + +template <typename T, typename FunctionPointer, typename Arg1> +class StoredInterfaceFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) + : fn(fn), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1> +class StoredInterfaceMemberFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1) + : fn(fn), object(object), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2> +class StoredInterfaceFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) + : fn(fn), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2> +class StoredInterfaceMemberFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) + : fn(fn), object(object), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceMemberFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceMemberFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceMemberFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; + +template <typename T> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &)) +{ + return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start(); +} +template <typename T, typename Arg1> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) +{ + return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start(); +} +template <typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) +{ + return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) +{ + return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) +{ + return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) +{ + return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start(); +} + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object) +{ + return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start(); +} + +template <typename Class, typename T, typename Arg1> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1) +{ + return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) +{ + return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) +{ + return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) +{ + return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start(); +} + +template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) +{ + return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start(); +} +} // namespace QtConcurrent + +QT_END_NAMESPACE + +#endif // QTCONCURRENT_RUNEX_H diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp index 7d8f234cf..90cb431f4 100644 --- a/src/libs/installer/settings.cpp +++ b/src/libs/installer/settings.cpp @@ -230,7 +230,7 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, << scStartMenuDir << scUninstallerName << scUninstallerIniFile << scRemoveTargetDir << scRunProgram << scRunProgramArguments << scRunProgramDescription << scDependsOnLocalInstallerBinary - << scAllowSpaceInPath << scAllowNonAsciiCharacters + << scAllowSpaceInPath << scAllowNonAsciiCharacters << scWizardStyle << scTitleColor << scRepositorySettingsPageVisible << scTargetConfigurationFile << scRemoteRepositories << scTranslations; @@ -344,6 +344,16 @@ QString Settings::icon() const return d->makeAbsolutePath(d->m_data.value(scIcon).toString() + systemIconSuffix()); } +QString Settings::wizardStyle() const +{ + return d->m_data.value(scWizardStyle).toString(); +} + +QString Settings::titleColor() const +{ + return d->m_data.value(scTitleColor).toString(); +} + QString Settings::installerApplicationIcon() const { return d->makeAbsolutePath(d->m_data.value(scInstallerApplicationIcon).toString() + systemIconSuffix()); diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h index a86b5f5fc..9bce8bd70 100644 --- a/src/libs/installer/settings.h +++ b/src/libs/installer/settings.h @@ -100,6 +100,8 @@ public: QString installerApplicationIcon() const; QString installerWindowIcon() const; QString systemIconSuffix() const; + QString wizardStyle() const; + QString titleColor() const; QString applicationName() const; QString applicationVersion() const; diff --git a/src/libs/installer/unziptask.cpp b/src/libs/installer/unziptask.cpp new file mode 100644 index 000000000..b3a0b6bb2 --- /dev/null +++ b/src/libs/installer/unziptask.cpp @@ -0,0 +1,304 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ +#include "unziptask.h" + +#ifdef Q_OS_UNIX +# include "StdAfx.h" +#endif +#include "Common/ComTry.h" +// TODO: include once we switch from lib7z_fascade.h +//#include "Common/MyInitGuid.h" + +#include "7zip/IPassword.h" +#include "7zip/Common/FileStreams.h" +#include "7zip/UI/Common/OpenArchive.h" + +#include "Windows/FileDir.h" +#include "Windows/PropVariant.h" + +#include "7zCrc.h" + +#include <QDir> + +void registerArc7z(); +void registerCodecLZMA(); +void registerCodecLZMA2(); + +namespace QInstaller { + +class ArchiveExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + ArchiveExtractCallback(const QString &target, const CArc &arc, QFutureInterface<QString> &fi) + : m_target(target) + , m_arc(arc) + { + m_futureInterface = &fi; + } + + + // -- IProgress + + STDMETHOD(SetTotal)(UInt64 total) + { + COM_TRY_BEGIN + m_totalUnpacked = 0; + m_totalUnpackedExpected = total; + return S_OK; + COM_TRY_END + } + + STDMETHOD(SetCompleted)(const UInt64 *completeValue) + { + COM_TRY_BEGIN + Q_UNUSED(completeValue) + return S_OK; // return S_OK here as we do not support sub archive extracting + COM_TRY_END + } + + + // -- IArchiveExtractCallback + + STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) + { + if (m_futureInterface->isCanceled()) + return E_FAIL; + if (m_futureInterface->isPaused()) + m_futureInterface->waitForResume(); + + COM_TRY_BEGIN + *outStream = 0; + m_currentIndex = index; + if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) + return E_FAIL; + + bool isDir = false; + if (IsArchiveItemFolder(m_arc.Archive, m_currentIndex, isDir) != S_OK) + return E_FAIL; + + bool isEncrypted = false; + if (GetArchiveItemBoolProp(m_arc.Archive, m_currentIndex, kpidEncrypted, isEncrypted) != S_OK) + return E_FAIL; + + if (isDir || isEncrypted) + return E_FAIL; + + UString itemPath; + if (m_arc.GetItemPath(m_currentIndex, itemPath) != S_OK) + return E_FAIL; + + QDir().mkpath(m_target); + m_currentTarget = m_target + QLatin1Char('/') + QString::fromStdWString((const wchar_t*)(itemPath)) + .replace(QLatin1Char('\\'), QLatin1Char('/')); + + m_outFileStream = new COutFileStream; + CMyComPtr<ISequentialOutStream> scopedPointer(m_outFileStream); + if (!m_outFileStream->Open((wchar_t*)(m_currentTarget.utf16()), CREATE_ALWAYS)) + return E_FAIL; + + m_outFileStreamComPtr = scopedPointer; + *outStream = scopedPointer.Detach(); + + return S_OK; + COM_TRY_END + } + + STDMETHOD(PrepareOperation)(Int32 askExtractMode) + { + COM_TRY_BEGIN + Q_UNUSED(askExtractMode) + m_futureInterface->setProgressValueAndText(0, QLatin1String("Started to extract archive.")); + return S_OK; // return S_OK here as we do not need to prepare anything + COM_TRY_END + } + + STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) + { + COM_TRY_BEGIN + switch (resultEOperationResult) + { + case NArchive::NExtract::NOperationResult::kOK: + break; + + default: // fall through and bail + case NArchive::NExtract::NOperationResult::kCRCError: + case NArchive::NExtract::NOperationResult::kDataError: + case NArchive::NExtract::NOperationResult::kUnSupportedMethod: + m_outFileStream->Close(); + m_outFileStreamComPtr.Release(); + return E_FAIL; + } + + UInt32 attributes; + if (GetAttributes(&attributes)) + NWindows::NFile::NDirectory::MySetFileAttributes((wchar_t*)(m_currentTarget.utf16()), attributes); + + FILETIME accessTime, creationTime, modificationTime; + const bool writeAccessTime = GetTime(kpidATime, &accessTime); + const bool writeCreationTime = GetTime(kpidCTime, &creationTime); + const bool writeModificationTime = GetTime(kpidMTime, &modificationTime); + + m_outFileStream->SetTime((writeCreationTime ? &creationTime : NULL), + (writeAccessTime ? &accessTime : NULL), (writeModificationTime ? &modificationTime : NULL)); + + m_totalUnpacked += m_outFileStream->ProcessedSize; + m_outFileStream->Close(); + m_outFileStreamComPtr.Release(); + + m_futureInterface->reportResult(m_currentTarget); + m_futureInterface->setProgressValueAndText(100 * m_totalUnpacked / m_totalUnpackedExpected, + m_currentTarget); + return S_OK; + COM_TRY_END + } + +private: + bool GetAttributes(UInt32 *attributes) const + { + *attributes = 0; + NWindows::NCOM::CPropVariant property; + if (m_arc.Archive->GetProperty(m_currentIndex, kpidAttrib, &property) != S_OK) + return false; + + if (property.vt != VT_UI4) + return false; + + *attributes = property.ulVal; + return (property.vt == VT_UI4); + } + + bool GetTime(PROPID propertyId, FILETIME *filetime) const + { + if (!filetime) + return false; + + filetime->dwLowDateTime = 0; + filetime->dwHighDateTime = 0; + + NWindows::NCOM::CPropVariant property; + if (m_arc.Archive->GetProperty(m_currentIndex, propertyId, &property) != S_OK) + return false; + + if (property.vt != VT_FILETIME) + return false; + + *filetime = property.filetime; + return (filetime->dwHighDateTime != 0 || filetime->dwLowDateTime != 0); + } + +private: + QString m_target; + const CArc &m_arc; + UnzipTask *m_unzipTask; + QFutureInterface<QString> *m_futureInterface; + + UInt32 m_currentIndex; + QString m_currentTarget; + + UInt64 m_totalUnpacked; + UInt64 m_totalUnpackedExpected; + + COutFileStream *m_outFileStream; + CMyComPtr<ISequentialOutStream> m_outFileStreamComPtr; +}; + + +// -- UnzipTask + +UnzipTask::UnzipTask(const QString &source, const QString &target) + : m_source(source) + , m_target(target) +{ + { + CrcGenerateTable(); + + registerArc7z(); + registerCodecLZMA(); + registerCodecLZMA2(); + } +} + +void UnzipTask::doTask(QFutureInterface<QString> &fi) +{ + fi.reportStarted(); + + CCodecs codecs; + if (codecs.Load() != S_OK) + return; + + CIntVector formatIndices; + if (!codecs.FindFormatForArchiveType(L"", formatIndices)) + return; + + CInFileStream *fileStream = new CInFileStream; + fileStream->Open((wchar_t*) (m_source.utf16())); + + CArchiveLink archiveLink; + if (archiveLink.Open2(&codecs, formatIndices, false, fileStream, UString(), 0) != S_OK) + return; + + UINT32 count = 0; + for (int i = 0; i < archiveLink.Arcs.Size(); ++i) { + const CArc& arc = archiveLink.Arcs[i]; + UInt32 numItems = 0; + if (arc.Archive->GetNumberOfItems(&numItems) != S_OK) + break; + count += numItems; + } + fi.setExpectedResultCount(count); + + for (int i = 0; i < archiveLink.Arcs.Size(); ++i) { + if (fi.isCanceled()) + break; + if (fi.isPaused()) + fi.waitForResume(); + + const UInt32 extractAll = UInt32(-1); + const CArc &arc = archiveLink.Arcs[i]; + arc.Archive->Extract(0, extractAll, NArchive::NExtract::NAskMode::kExtract, + new ArchiveExtractCallback(m_target, arc, fi)); + } + + fi.reportFinished(); +} + +} // namespace QInstaller diff --git a/src/libs/installer/unziptask.h b/src/libs/installer/unziptask.h new file mode 100644 index 000000000..40e1c6722 --- /dev/null +++ b/src/libs/installer/unziptask.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef UNZIPTASK_H +#define UNZIPTASK_H + +#include "abstracttask.h" +#include "installer_global.h" + +namespace QInstaller { + +class INSTALLER_EXPORT UnzipTask : public AbstractTask<QString> +{ +public: + UnzipTask() {} + UnzipTask(const QString &source, const QString &target); + + void doTask(QFutureInterface<QString> &fi); + +private: + void setBytesToExtract(qint64 bytes); + void addBytesExtracted(qint64 bytes); + +private: + QString m_source; + QString m_target; + friend class ArchiveExtractCallback; +}; + +} // namespace QInstaller + +#endif // UNZIPTASK_H diff --git a/src/libs/kdtools/kdsavefile.cpp b/src/libs/kdtools/kdsavefile.cpp index 88b95f42e..8e2209604 100644 --- a/src/libs/kdtools/kdsavefile.cpp +++ b/src/libs/kdtools/kdsavefile.cpp @@ -54,7 +54,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> -#ifdef Q_OS_LINUX +#ifdef Q_OS_UNIX #include <unistd.h> #endif @@ -174,11 +174,10 @@ static QFile *createFile(const QString &path, QIODevice::OpenMode m, QFile::Perm */ static QString generateTempFileName(const QString &path) { - const QString tmp = path + QLatin1String("tmp.dsfdf.%1"); //TODO: use random suffix - int count = 1; - while (QFile::exists(tmp.arg(count))) - ++count; - return tmp.arg(count); + QTemporaryFile tmpfile(path); + tmpfile.open(); + const QString tmp = tmpfile.fileName(); + return tmp; } /*! @@ -384,7 +383,7 @@ bool KDSaveFile::commit(KDSaveFile::CommitMode mode) } if (mode == OverwriteExistingFile) { QFile tmp(backup); - const bool removed = !tmp.exists() || tmp.remove(backup); + const bool removed = !tmp.exists() || tmp.remove(); if (!removed) qWarning() << "Could not remove the backup: " << tmp.errorString(); } diff --git a/src/libs/kdtools/kdsysinfo.cpp b/src/libs/kdtools/kdsysinfo.cpp index 98a0d951e..46192e742 100644 --- a/src/libs/kdtools/kdsysinfo.cpp +++ b/src/libs/kdtools/kdsysinfo.cpp @@ -66,7 +66,7 @@ VolumeInfo VolumeInfo::fromPath(const QString &path) QList<VolumeInfo> volumes = mountedVolumes(); // sort by length to get the longest mount point (not just "/") first - qSort(volumes.begin(), volumes.end(), PathLongerThan()); + std::sort(volumes.begin(), volumes.end(), PathLongerThan()); foreach (const VolumeInfo &volume, volumes) { const QDir volumePath(volume.mountPath()); if (targetPath == volumePath) diff --git a/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp b/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp index 9acd09682..140577d3c 100644 --- a/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp +++ b/src/libs/kdtools/kdupdaterupdatesourcesinfo.cpp @@ -250,7 +250,7 @@ void UpdateSourcesInfo::addUpdateSourceInfo(const UpdateSourceInfo &info) if (d->updateSourceInfoList.contains(info)) return; d->updateSourceInfoList.push_back(info); - qSort(d->updateSourceInfoList.begin(), d->updateSourceInfoList.end(), UpdateSourceInfoPriorityHigherThan()); + std::sort(d->updateSourceInfoList.begin(), d->updateSourceInfoList.end(), UpdateSourceInfoPriorityHigherThan()); emit updateSourceInfoAdded(info); d->modified = true; } diff --git a/src/sdk/installerbasecommons.cpp b/src/sdk/installerbasecommons.cpp index 0cf85a0ec..263eb5767 100644 --- a/src/sdk/installerbasecommons.cpp +++ b/src/sdk/installerbasecommons.cpp @@ -116,6 +116,7 @@ IntroductionPageImpl::IntroductionPageImpl(QInstaller::PackageManagerCore *core) core->setCompleteUninstallation(core->isUninstaller()); + connect(core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int))); connect(core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); connect(core, SIGNAL(coreNetworkSettingsChanged()), this, SLOT(onCoreNetworkSettingsChanged())); @@ -249,6 +250,12 @@ void IntroductionPageImpl::setMessage(const QString &msg) m_label->setText(msg); } +void IntroductionPageImpl::onProgressChanged(int progress) +{ + m_progressBar->setRange(0, 100); + m_progressBar->setValue(progress); +} + void IntroductionPageImpl::setErrorMessage(const QString &error) { QPalette palette; @@ -335,6 +342,8 @@ void IntroductionPageImpl::entering() setErrorMessage(QString()); setButtonText(QWizard::CancelButton, tr("Quit")); + m_progressBar->setValue(0); + m_progressBar->setRange(0, 0); PackageManagerCore *core = packageManagerCore(); if (core->isUninstaller() ||core->isUpdater() || core->isPackageManager()) { showMaintenanceTools(); @@ -345,6 +354,8 @@ void IntroductionPageImpl::entering() void IntroductionPageImpl::leaving() { + m_progressBar->setValue(0); + m_progressBar->setRange(0, 0); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton)); } @@ -388,6 +399,19 @@ QString TargetDirectoryPageImpl::targetDirWarning() const "absolute path."); } + QDir target(targetDir()); + target = target.canonicalPath(); + + if (target.isRoot()) { + return TargetDirectoryPageImpl::tr("As the install directory is completely deleted, installing " + "in %1 is forbidden.").arg(QDir::toNativeSeparators(QDir::rootPath())); + } + + if (target == QDir::home()) { + return TargetDirectoryPageImpl::tr("As the install directory is completely deleted, installing " + "in %1 is forbidden.").arg(QDir::toNativeSeparators(QDir::homePath())); + } + QString dir = QDir::toNativeSeparators(targetDir()); #ifdef Q_OS_WIN // folder length (set by user) + maintenance tool name length (no extension) + extra padding @@ -468,11 +492,6 @@ bool TargetDirectoryPageImpl::validatePage() const QFileInfo fi(targetDir); if (fi.isDir()) { - if (dir == QDir::root() || dir == QDir::home()) { - return failWithError(QLatin1String("ForbiddenTargetDirectory"), tr("As the install directory " - "is completely deleted installing in %1 is forbidden.").arg(QDir::rootPath())); - } - QString fileName = packageManagerCore()->settings().uninstallerName(); #if defined(Q_OS_MAC) if (QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).isBundle()) diff --git a/src/sdk/installerbasecommons.h b/src/sdk/installerbasecommons.h index 2bcdce3f2..79330eaf7 100644 --- a/src/sdk/installerbasecommons.h +++ b/src/sdk/installerbasecommons.h @@ -72,6 +72,7 @@ public: public Q_SLOTS: void onCoreNetworkSettingsChanged(); void setMessage(const QString &msg); + void onProgressChanged(int progress); void setErrorMessage(const QString &error); Q_SIGNALS: diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro index 67fd77f2a..e75fbe3c2 100644 --- a/src/sdk/sdk.pro +++ b/src/sdk/sdk.pro @@ -8,6 +8,12 @@ QT += network script xml isEqual(QT_MAJOR_VERSION, 5) { QT += widgets + # add the minimal plugin in static case to be able to start the installer + # headless with: installer-binary -platform minimal + # using QT += qpa_minimal_plugin would result in a minimal only compiled version + !win32:CONFIG(static, static|shared) { + QTPLUGIN += qminimal + } } DESTDIR = $$IFW_APP_PATH diff --git a/src/sdk/settingsdialog.h b/src/sdk/settingsdialog.h index a2386acbb..942adfc2f 100644 --- a/src/sdk/settingsdialog.h +++ b/src/sdk/settingsdialog.h @@ -53,16 +53,16 @@ QT_BEGIN_NAMESPACE class QAuthenticator; class QLocale; class QVariant; + +namespace Ui { + class SettingsDialog; +} QT_END_NAMESPACE namespace QInstaller { class PackageManagerCore; } -namespace Ui { - class SettingsDialog; -} - // -- PasswordDelegate class PasswordDelegate : public QStyledItemDelegate |