diff options
Diffstat (limited to 'src/libs/installer')
-rw-r--r-- | src/libs/installer/abstractfiletask.cpp | 117 | ||||
-rw-r--r-- | src/libs/installer/abstractfiletask.h | 152 | ||||
-rw-r--r-- | src/libs/installer/abstracttask.h | 76 | ||||
-rw-r--r-- | src/libs/installer/copyfiletask.cpp | 130 | ||||
-rw-r--r-- | src/libs/installer/copyfiletask.h | 66 | ||||
-rw-r--r-- | src/libs/installer/downloadfiletask.cpp | 380 | ||||
-rw-r--r-- | src/libs/installer/downloadfiletask.h | 87 | ||||
-rw-r--r-- | src/libs/installer/downloadfiletask_p.h | 116 | ||||
-rw-r--r-- | src/libs/installer/installer.pro | 17 | ||||
-rw-r--r-- | src/libs/installer/observer.cpp | 191 | ||||
-rw-r--r-- | src/libs/installer/observer.h | 105 | ||||
-rw-r--r-- | src/libs/installer/runextensions.h | 435 | ||||
-rw-r--r-- | src/libs/installer/unziptask.cpp | 304 | ||||
-rw-r--r-- | src/libs/installer/unziptask.h | 70 |
14 files changed, 2244 insertions, 2 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..6c6e7810e --- /dev/null +++ b/src/libs/installer/abstractfiletask.h @@ -0,0 +1,152 @@ +/************************************************************************** +** +** 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 +#endif + +namespace QInstaller { + +namespace FileTaskRole { +enum +{ + Checksum, + SourceFile, + TargetFile, + UserRole = 1000 +}; +} + +class FileTaskItem : public TaskData +{ +public: + FileTaskItem() {} + explicit FileTaskItem(const QString &s) + { + insert(FileTaskRole::SourceFile, s); + } + FileTaskItem(const QString &s, const QString &t) + { + insert(FileTaskRole::SourceFile, s); + insert(FileTaskRole::TargetFile, t); + } + + QString source() const { return value(FileTaskRole::SourceFile).toString(); } + QString target() const { return value(FileTaskRole::TargetFile).toString(); } +}; + +class FileTaskResult : public TaskData +{ +public: + FileTaskResult() {} + FileTaskResult(const QString &t, const QByteArray &c) + { + insert(FileTaskRole::Checksum, c); + insert(FileTaskRole::TargetFile, t); + } + + QString target() const { return value(FileTaskRole::TargetFile).toString(); } + QByteArray checkSum() const { return value(FileTaskRole::Checksum).toByteArray(); } +}; + +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..75fc1fa78 --- /dev/null +++ b/src/libs/installer/abstracttask.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** 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 TaskData +{ +public: + TaskData() {} + 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; +}; + +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/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp new file mode 100644 index 000000000..ca31b7873 --- /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()), 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..3e1c903d2 --- /dev/null +++ b/src/libs/installer/downloadfiletask.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 "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, + const QAuthenticator &authenticator, QNetworkProxyFactory *networkProxyFactory) +{ + m_items = items; + m_futureInterface = &fi; + m_authenticator = authenticator; + + 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(url.toString(), filename); + taskItem.insert(FileTaskRole::Checksum, data.expectedCheckSum); + 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()); + } + m_futureInterface->reportResult(FileTaskResult(filename, data.observer->checkSum())); + + if (!data.expectedCheckSum.isEmpty()) { + if (data.expectedCheckSum != data.observer->checkSum().toHex()) { + m_futureInterface->reportException(FileTaskException(QString::fromLatin1("Checksum" + " mismatch detected '%1'.").arg(reply->url().toString()))); + } + } + + 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) +{ + Q_UNUSED(reply) + if (!authenticator) + return; + + if (!m_authenticator.user().isEmpty()) { + authenticator->setUser(m_authenticator.user()); + authenticator->setPassword(m_authenticator.password()); + m_authenticator = QAuthenticator(); // 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.value(FileTaskRole::Checksum).toByteArray())); + + connect(reply, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, + SLOT(onError(QNetworkReply::NetworkError))); +#ifndef QT_NO_SSL + 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())); + downloader.download(fi, taskItems(), m_authenticator, (m_proxyFactory.isNull() ? 0 + : m_proxyFactory->clone())); // Downloader takes ownership of this copy. + el.exec(); // That's tricky here, we 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..4e2cd2312 --- /dev/null +++ b/src/libs/installer/downloadfiletask.h @@ -0,0 +1,87 @@ +/************************************************************************** +** +** 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> + +namespace QInstaller { + +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..66b14767c --- /dev/null +++ b/src/libs/installer/downloadfiletask_p.h @@ -0,0 +1,116 @@ +/************************************************************************** +** +** 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) + : file(f), observer(o) {} + Data(QFile *f, FileTaskObserver *o, const QByteArray &e) + : file(f), observer(o), expectedCheckSum(e) + {} + QFile *file; + FileTaskObserver *observer; + QByteArray expectedCheckSum; +}; + +class Downloader : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Downloader) + +public: + Downloader(); + ~Downloader(); + + void download(QFutureInterface<FileTaskResult> &fi, const QList<FileTaskItem> &items, + const QAuthenticator &authenticator, 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; + QAuthenticator m_authenticator; + QHash<QNetworkReply*, Data> m_downloads; + QMultiHash<QNetworkReply*, QUrl> m_redirects; +}; + +} // namespace QInstaller + +#endif // DOWNLOADFILETASK_P_H diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 149287428..766525e17 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -104,7 +104,15 @@ 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 SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ @@ -169,7 +177,12 @@ HEADERS += packagemanagercore.h \ globals.cpp \ settingsoperation.cpp \ testrepository.cpp \ - packagemanagerpagefactory.cpp + packagemanagerpagefactory.cpp \ + abstractfiletask.cpp \ + copyfiletask.cpp \ + downloadfiletask.cpp \ + unziptask.cpp \ + observer.cpp RESOURCES += resources/patch_file_lists.qrc \ resources/installer.qrc 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/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/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 |