summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/abstractarchive.cpp221
-rw-r--r--src/libs/installer/abstractarchive.h120
-rw-r--r--src/libs/installer/archivefactory.cpp147
-rw-r--r--src/libs/installer/archivefactory.h69
-rw-r--r--src/libs/installer/component.cpp5
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp17
-rw-r--r--src/libs/installer/directoryguard.cpp112
-rw-r--r--src/libs/installer/directoryguard.h55
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp29
-rw-r--r--src/libs/installer/extractarchiveoperation.h4
-rw-r--r--src/libs/installer/extractarchiveoperation_p.h136
-rw-r--r--src/libs/installer/installer.pro23
-rw-r--r--src/libs/installer/lib7z_create.h12
-rw-r--r--src/libs/installer/lib7z_facade.cpp107
-rw-r--r--src/libs/installer/lib7z_list.h17
-rw-r--r--src/libs/installer/lib7zarchive.cpp242
-rw-r--r--src/libs/installer/lib7zarchive.h95
-rw-r--r--src/libs/installer/libarchivearchive.cpp819
-rw-r--r--src/libs/installer/libarchivearchive.h189
-rw-r--r--src/libs/installer/libarchivewrapper.cpp204
-rw-r--r--src/libs/installer/libarchivewrapper.h71
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp356
-rw-r--r--src/libs/installer/libarchivewrapper_p.h94
-rw-r--r--src/libs/installer/metadatajob_p.h23
-rw-r--r--src/libs/installer/protocol.h25
-rw-r--r--src/libs/installer/remoteserverconnection.cpp103
-rw-r--r--src/libs/installer/remoteserverconnection.h8
-rw-r--r--src/libs/installer/remoteserverconnection_p.h57
28 files changed, 3134 insertions, 226 deletions
diff --git a/src/libs/installer/abstractarchive.cpp b/src/libs/installer/abstractarchive.cpp
new file mode 100644
index 000000000..dd9b8e625
--- /dev/null
+++ b/src/libs/installer/abstractarchive.cpp
@@ -0,0 +1,221 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "abstractarchive.h"
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ArchiveEntry
+ \brief The ArchiveEntry struct represents an entry in an archive file,
+ which can be for example a file or a directory.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AbstractArchive
+ \brief The AbstractArchive class is the base class for classes representing
+ different archive files. It cannot be instantiated on its own but
+ defines the API and provides common functionality when subclassed.
+*/
+
+/*!
+ \enum AbstractArchive::CompressionLevel
+ This enum holds the possible values for archive compression level.
+
+ \value Non
+ \value Fastest
+ \value Fast
+ \value Normal
+ \value Maximum
+ \value Ultra
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::currentEntryChanged(const QString &filename)
+
+ Current entry changed to \a filename. Subclasses should emit this signal whenever
+ the entry to process is changed.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::completedChanged(quint64 completed, quint64 total)
+
+ The ratio of \a completed entries from \a total changed. Subclasses should emit
+ this whenever the progress changes.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::cancel()
+
+ Cancels current operation. A subclass should implement this slot.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::close()
+
+ Closes the archive. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::create(const QStringList &data)
+
+ Creates an archive from \a data. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::extract(const QString &dirPath)
+
+ Extracts the archive to \a dirPath. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::extract(const QString &dirPath, const quint64 totalFiles)
+
+ Extracts the contents of an archive to \a dirPath with precalculated
+ count of \a totalFiles. Returns \c true on success; \c false otherwise.
+ A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::isSupported()
+
+ Returns \c true if the archive is supported; \c false otherwise.
+ A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::list()
+
+ Returns a list of entries in this archive. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::open(QIODevice::OpenMode mode)
+
+ Opens the file device for an archive in \a mode. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::setFilename(const QString &filename)
+
+ Sets the \a filename for the archive. A subclass should implement this method.
+*/
+
+/*!
+ Constructs a new archive object with \a parent as parent. Cannot be
+ called directly but instead from subclass constructors.
+*/
+AbstractArchive::AbstractArchive(QObject *parent)
+ : QObject(parent)
+ , m_compressionLevel(CompressionLevel::Normal)
+{
+}
+
+/*!
+ Virtual destructor for \c AbstractArchive.
+*/
+AbstractArchive::~AbstractArchive()
+{
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+*/
+QString AbstractArchive::errorString() const
+{
+ return m_error;
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+*/
+void AbstractArchive::setCompressionLevel(const CompressionLevel level)
+{
+ m_compressionLevel = level;
+}
+
+/*!
+ Sets a human-readable description of the current \a error.
+*/
+void AbstractArchive::setErrorString(const QString &error)
+{
+ m_error = error;
+}
+
+/*!
+ Returns the current compression level.
+*/
+AbstractArchive::CompressionLevel AbstractArchive::compressionLevel() const
+{
+ return m_compressionLevel;
+}
+
+/*!
+ Reads an \a entry from the specified \a istream. Returns a reference to \a istream.
+*/
+QDataStream &operator>>(QDataStream &istream, ArchiveEntry &entry)
+{
+ istream >> entry.path >> entry.utcTime >> entry.isDirectory
+ >> entry.uncompressedSize >> entry.permissions_mode >> entry.permissions_enum;
+
+ return istream;
+}
+
+/*!
+ Writes an \a entry to the specified \a ostream. Returns a reference to \a ostream.
+*/
+QDataStream &operator<<(QDataStream &ostream, const ArchiveEntry &entry)
+{
+ ostream << entry.path << entry.utcTime << entry.isDirectory
+ << entry.uncompressedSize << entry.permissions_mode << entry.permissions_enum;
+
+ return ostream;
+}
+
+/*!
+ Returns \c true if left-hand-side entry \a lhs is equal to right-hand-size entry \a rhs.
+*/
+bool operator==(const ArchiveEntry &lhs, const ArchiveEntry &rhs)
+{
+ return lhs.path == rhs.path
+ && lhs.utcTime == rhs.utcTime
+ && lhs.isDirectory == rhs.isDirectory
+ && lhs.compressedSize == rhs.compressedSize
+ && lhs.uncompressedSize == rhs.uncompressedSize
+ && lhs.permissions_mode == rhs.permissions_mode
+ && (lhs.permissions_enum == rhs.permissions_enum // ignore invalid permissions
+ || lhs.permissions_enum == static_cast<QFile::Permissions>(-1)
+ || rhs.permissions_enum == static_cast<QFile::Permissions>(-1));
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/abstractarchive.h b/src/libs/installer/abstractarchive.h
new file mode 100644
index 000000000..77cc35b61
--- /dev/null
+++ b/src/libs/installer/abstractarchive.h
@@ -0,0 +1,120 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef ABSTRACTARCHIVE_H
+#define ABSTRACTARCHIVE_H
+
+#include "installer_global.h"
+
+#include <QFile>
+#include <QDateTime>
+#include <QDataStream>
+#include <QPoint>
+
+#ifdef Q_OS_WIN
+typedef int mode_t;
+#endif
+
+namespace QInstaller {
+
+struct INSTALLER_EXPORT ArchiveEntry
+{
+ ArchiveEntry()
+ : isDirectory(false)
+ , compressedSize(0)
+ , uncompressedSize(0)
+ , permissions_mode(0)
+ , permissions_enum(0)
+ {}
+
+ QString path;
+ QDateTime utcTime;
+ QPoint archiveIndex;
+ bool isDirectory;
+ quint64 compressedSize;
+ quint64 uncompressedSize;
+ mode_t permissions_mode;
+ QFile::Permissions permissions_enum;
+};
+
+class INSTALLER_EXPORT AbstractArchive : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractArchive)
+
+public:
+ enum CompressionLevel {
+ Non = 0,
+ Fastest = 1,
+ Fast = 3,
+ Normal = 5,
+ Maximum = 7,
+ Ultra = 9
+ };
+ Q_ENUM(CompressionLevel)
+
+ explicit AbstractArchive(QObject *parent = nullptr);
+ virtual ~AbstractArchive() = 0;
+
+ virtual bool open(QIODevice::OpenMode mode) = 0;
+ virtual void close() = 0;
+ virtual void setFilename(const QString &filename) = 0;
+
+ virtual QString errorString() const;
+
+ virtual bool extract(const QString &dirPath) = 0;
+ virtual bool extract(const QString &dirPath, const quint64 totalFiles) = 0;
+ virtual bool create(const QStringList &data) = 0;
+ virtual QVector<ArchiveEntry> list() = 0;
+ virtual bool isSupported() = 0;
+
+ virtual void setCompressionLevel(const CompressionLevel level);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(const quint64 completed, const quint64 total);
+
+public Q_SLOTS:
+ virtual void cancel() = 0;
+
+protected:
+ void setErrorString(const QString &error);
+ CompressionLevel compressionLevel() const;
+
+private:
+ QString m_error;
+ CompressionLevel m_compressionLevel;
+};
+
+INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, ArchiveEntry &entry);
+INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const ArchiveEntry &entry);
+INSTALLER_EXPORT bool operator==(const ArchiveEntry &lhs, const ArchiveEntry &rhs);
+
+} // namespace QInstaller
+
+#endif // ABSTRACTARCHIVE_H
diff --git a/src/libs/installer/archivefactory.cpp b/src/libs/installer/archivefactory.cpp
new file mode 100644
index 000000000..a946f9416
--- /dev/null
+++ b/src/libs/installer/archivefactory.cpp
@@ -0,0 +1,147 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "archivefactory.h"
+#include "lib7zarchive.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivewrapper.h"
+#endif
+
+#include <QFileInfo>
+
+using namespace QInstaller;
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ArchiveFactory
+ \brief The ArchiveFactory class is used to create archive objects
+ based on the suffix of a given filename.
+
+ This class acts as a factory for \c QInstaller::AbstractArchive. You can
+ register one or more archive handlers with this factory and create
+ registered objects based on the file suffix.
+
+ This class follows the singleton design pattern. Only one instance
+ of this class can be created and its reference can be fetched from
+ the \c {instance()} method.
+
+ The following archive handlers are registered by default:
+ \list
+ \li Lib7z
+ \li LibArchive
+ \endlist
+*/
+
+/*!
+ \fn void QInstaller::ArchiveFactory::registerArchive(const QString &name, const QStringList &types)
+
+ Registers a new archive handler with the factory based on \a name and list
+ of supported file suffix \a types.
+*/
+
+
+/*!
+ Returns the only instance of this class.
+*/
+ArchiveFactory &ArchiveFactory::instance()
+{
+ static ArchiveFactory instance;
+ return instance;
+}
+
+/*!
+ Constructs and returns a pointer to an archive object with \a filename and \a parent.
+ If the archive type referenced by \a filename is not registered, a null pointer is
+ returned instead.
+*/
+AbstractArchive *ArchiveFactory::create(const QString &filename, QObject *parent) const
+{
+ const QString suffix = QFileInfo(filename).completeSuffix();
+ QString name;
+ for (auto &types : m_supportedTypesHash) {
+ QStringList::const_iterator it;
+ for (it = types.constBegin(); it != types.constEnd(); ++it) {
+ if (suffix.endsWith(*it, Qt::CaseInsensitive)) {
+ name = m_supportedTypesHash.key(types);
+ break;
+ }
+ }
+ }
+ if (name.isEmpty())
+ return nullptr;
+
+ AbstractArchive *archive = GenericFactory<AbstractArchive, QString, QString, QObject *>
+ ::create(name, filename, parent);
+
+ return archive;
+}
+
+/*!
+ Returns a list of supported archive types.
+*/
+QStringList ArchiveFactory::supportedTypes()
+{
+ QStringList types;
+ QHash<QString, QStringList> *const typesHash = &instance().m_supportedTypesHash;
+ for (auto &value : *typesHash)
+ types.append(value);
+
+ return types;
+}
+
+/*!
+ Returns \c true if the archive type from \a filename is registered with
+ an archive handler.
+*/
+bool ArchiveFactory::isSupportedType(const QString &filename)
+{
+ const QString suffix = QFileInfo(filename).completeSuffix();
+ QHash<QString, QStringList> *const typesHash = &instance().m_supportedTypesHash;
+ for (auto &types : *typesHash) {
+ QStringList::const_iterator it;
+ for (it = types.constBegin(); it != types.constEnd(); ++it) {
+ if (suffix.endsWith(*it, Qt::CaseInsensitive))
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Private constructor for ArchiveFactory. Registers default archive handlers.
+*/
+ArchiveFactory::ArchiveFactory()
+{
+ registerArchive<Lib7zArchive>(QLatin1String("Lib7z"), QStringList()
+ << QLatin1String("7z"));
+#ifdef IFW_LIBARCHIVE
+ registerArchive<LibArchiveWrapper>(QLatin1String("LibArchive"), QStringList()
+ << QLatin1String("tar.gz") << QLatin1String("tar.bz2")
+ << QLatin1String("tar.xz") << QLatin1String("zip") );
+#endif
+}
diff --git a/src/libs/installer/archivefactory.h b/src/libs/installer/archivefactory.h
new file mode 100644
index 000000000..6f545eefa
--- /dev/null
+++ b/src/libs/installer/archivefactory.h
@@ -0,0 +1,69 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef ARCHIVEFACTORY_H
+#define ARCHIVEFACTORY_H
+
+#include "installer_global.h"
+#include "genericfactory.h"
+#include "abstractarchive.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ArchiveFactory
+ : public GenericFactory<AbstractArchive, QString, QString, QObject *>
+{
+ Q_DISABLE_COPY(ArchiveFactory)
+
+public:
+ static ArchiveFactory &instance();
+
+ template <typename T>
+ void registerArchive(const QString &name, const QStringList &types)
+ {
+ if (containsProduct(name))
+ m_supportedTypesHash.remove(name);
+
+ registerProduct<T>(name);
+ m_supportedTypesHash.insert(name, types);
+ }
+ AbstractArchive *create(const QString &filename, QObject *parent = nullptr) const;
+
+ static QStringList supportedTypes();
+ static bool isSupportedType(const QString &filename);
+
+private:
+ ArchiveFactory();
+
+private:
+ QHash<QString, QStringList> m_supportedTypesHash;
+};
+
+} // namespace QInstaller
+
+#endif // ARCHIVEFACTORY_H
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 6e3856502..1a83291c1 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -31,7 +31,7 @@
#include "errors.h"
#include "fileutils.h"
#include "globals.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "messageboxhandler.h"
#include "packagemanagercore.h"
#include "remoteclient.h"
@@ -865,7 +865,8 @@ void Component::createOperationsForArchive(const QString &archive)
return;
}
- const bool isZip = Lib7z::isSupportedArchive(archive);
+ QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
+ const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported());
if (isZip) {
// component.xml can override this value
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
index e05c34b91..a40838178 100644
--- a/src/libs/installer/createlocalrepositoryoperation.cpp
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,8 +34,7 @@
#include "fileio.h"
#include "fileutils.h"
#include "copydirectoryoperation.h"
-#include "lib7z_create.h"
-#include "lib7z_facade.h"
+#include "lib7zarchive.h"
#include "packagemanagercore.h"
#include "productkeycheck.h"
#include "constants.h"
@@ -124,8 +123,12 @@ static QString createArchive(const QString repoPath, const QString &sourceDir, c
const QString fileName = QString::fromLatin1("/%1meta.7z").arg(version);
QFile archive(repoPath + fileName);
- QInstaller::openForWrite(&archive);
- Lib7z::createArchive(&archive, QStringList() << sourceDir);
+
+ Lib7zArchive archiveFile(archive.fileName());
+ if (!(archiveFile.open(QIODevice::WriteOnly) && archiveFile.create(QStringList() << sourceDir))) {
+ throw Error(CreateLocalRepositoryOperation::tr("Cannot create archive \"%1\": %2")
+ .arg(QDir::toNativeSeparators(archive.fileName()), archiveFile.errorString()));
+ }
removeFiles(sourceDir, helper); // cleanup the files we compressed
if (!archive.rename(sourceDir + fileName)) {
throw Error(CreateLocalRepositoryOperation::tr("Cannot move file \"%1\" to \"%2\": %3")
@@ -356,10 +359,6 @@ bool CreateLocalRepositoryOperation::performOperation()
}
} catch (...) {}
setValue(QLatin1String("local-repo"), repoPath);
- } catch (const Lib7z::SevenZipException &e) {
- setError(UserDefinedError);
- setErrorString(e.message());
- return false;
} catch (const QInstaller::Error &e) {
setError(UserDefinedError);
setErrorString(e.message());
diff --git a/src/libs/installer/directoryguard.cpp b/src/libs/installer/directoryguard.cpp
new file mode 100644
index 000000000..9c97130a4
--- /dev/null
+++ b/src/libs/installer/directoryguard.cpp
@@ -0,0 +1,112 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "directoryguard.h"
+
+#include "globals.h"
+#include "errors.h"
+
+#include <QCoreApplication>
+#include <QDir>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::DirectoryGuard
+ \brief RAII class to create a directory and delete it on destruction unless released.
+*/
+
+/*!
+ Constructs a new guard object for \a path.
+*/
+DirectoryGuard::DirectoryGuard(const QString &path)
+ : m_path(path)
+ , m_created(false)
+ , m_released(false)
+{
+ m_path.replace(QLatin1Char('\\'), QLatin1Char('/'));
+}
+
+/*!
+ Destroys the directory guard instance and removes the
+ guarded directory unless released.
+*/
+DirectoryGuard::~DirectoryGuard()
+{
+ if (!m_created || m_released)
+ return;
+ QDir dir(m_path);
+ if (!dir.rmdir(m_path))
+ qCWarning(lcInstallerInstallLog) << "Cannot delete directory" << m_path;
+}
+
+/*!
+ Tries to create the directory structure.
+ Returns a list of every directory created.
+*/
+QStringList DirectoryGuard::tryCreate()
+{
+ if (m_path.isEmpty())
+ return QStringList();
+
+ const QFileInfo fi(m_path);
+ if (fi.exists() && fi.isDir())
+ return QStringList();
+ if (fi.exists() && !fi.isDir()) {
+ throw Error(QCoreApplication::translate("DirectoryGuard",
+ "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path)));
+ }
+ QStringList created;
+
+ QDir toCreate(m_path);
+ while (!toCreate.exists()) {
+ QString p = toCreate.absolutePath();
+ created.push_front(p);
+ p = p.section(QLatin1Char('/'), 0, -2);
+ toCreate = QDir(p);
+ }
+
+ QDir dir(m_path);
+ m_created = dir.mkpath(m_path);
+ if (!m_created) {
+ throw Error(QCoreApplication::translate("DirectoryGuard",
+ "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
+ }
+ return created;
+}
+
+/*!
+ Marks the directory as released.
+*/
+void DirectoryGuard::release()
+{
+ m_released = true;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/directoryguard.h b/src/libs/installer/directoryguard.h
new file mode 100644
index 000000000..c18402557
--- /dev/null
+++ b/src/libs/installer/directoryguard.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef DIRECTORYGUARD_H
+#define DIRECTORYGUARD_H
+
+#include "installer_global.h"
+
+#include <QString>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT DirectoryGuard
+{
+public:
+ explicit DirectoryGuard(const QString &path);
+ ~DirectoryGuard();
+
+ QStringList tryCreate();
+ void release();
+
+private:
+ QString m_path;
+ bool m_created;
+ bool m_released;
+};
+
+} // namespace QInstaller
+
+#endif // DIRECTORYGUARD_H
diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
index c2d541929..b2bda24a2 100644
--- a/src/libs/installer/extractarchiveoperation.cpp
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -88,27 +88,28 @@ bool ExtractArchiveOperation::performOperation()
connect(&callback, &Callback::progressChanged, this, &ExtractArchiveOperation::progressChanged);
- if (PackageManagerCore *core = packageManager()) {
- connect(core, &PackageManagerCore::statusChanged, &callback, &Callback::statusChanged);
- }
-
- Runnable *runnable = new Runnable(archivePath, targetDir, &callback);
- connect(runnable, &Runnable::finished, &receiver, &Receiver::runnableFinished,
+ Worker *worker = new Worker(archivePath, targetDir, &callback);
+ connect(worker, &Worker::finished, &receiver, &Receiver::workerFinished,
Qt::QueuedConnection);
+ if (PackageManagerCore *core = packageManager())
+ connect(core, &PackageManagerCore::statusChanged, worker, &Worker::onStatusChanged);
+
QFileInfo fileInfo(archivePath);
emit outputTextChanged(tr("Extracting \"%1\"").arg(fileInfo.fileName()));
+ {
+ QEventLoop loop;
+ QThread workerThread;
+ worker->moveToThread(&workerThread);
- QEventLoop loop;
- connect(&receiver, &Receiver::finished, &loop, &QEventLoop::quit);
- if (QThreadPool::globalInstance()->tryStart(runnable)) {
+ connect(&workerThread, &QThread::started, worker, &Worker::run);
+ connect(&receiver, &Receiver::finished, &workerThread, &QThread::quit);
+ connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(&workerThread, &QThread::finished, &loop, &QEventLoop::quit);
+
+ workerThread.start();
loop.exec();
- } else {
- // HACK: In case there is no availabe thread we should call it directly.
- runnable->run();
- receiver.runnableFinished(true, QString());
}
-
// Write all file names which belongs to a package to a separate file and only the separate
// filename to a .dat file. There can be enormous amount of files in a package, which makes
// the dat file very slow to read and write. The .dat file is read into memory in startup,
diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h
index fa05d403a..7fc008887 100644
--- a/src/libs/installer/extractarchiveoperation.h
+++ b/src/libs/installer/extractarchiveoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -63,7 +63,7 @@ private:
private:
class Callback;
- class Runnable;
+ class Worker;
class Receiver;
};
diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h
index 9cc07246f..706187eb7 100644
--- a/src/libs/installer/extractarchiveoperation_p.h
+++ b/src/libs/installer/extractarchiveoperation_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,8 +31,7 @@
#include "extractarchiveoperation.h"
#include "fileutils.h"
-#include "lib7z_extract.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "packagemanagercore.h"
#include <QRunnable>
@@ -85,7 +84,7 @@ private:
typedef QPair<QString, QString> Backup;
typedef QVector<Backup> BackupFiles;
-class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractCallback
+class ExtractArchiveOperation::Callback : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Callback)
@@ -93,36 +92,14 @@ class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractC
public:
Callback() = default;
- BackupFiles backupFiles() const {
+ BackupFiles backupFiles() const
+ {
return m_backupFiles;
}
- QStringList extractedFiles() const {
- return m_extractedFiles;
- }
-
-public slots:
- void statusChanged(QInstaller::PackageManagerCore::Status status)
+ QStringList extractedFiles() const
{
- switch(status) {
- case PackageManagerCore::Canceled:
- m_state = E_ABORT;
- break;
- case PackageManagerCore::Failure:
- m_state = E_FAIL;
- break;
- default: // ignore all other status values
- break;
- }
- }
-
-signals:
- void progressChanged(double progress);
-
-private:
- void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE
- {
- m_extractedFiles.prepend(QDir::toNativeSeparators(filename));
+ return m_extractedFiles;
}
static QString generateBackupName(const QString &fn)
@@ -135,7 +112,7 @@ private:
return res;
}
- bool prepareForFile(const QString &filename) Q_DECL_OVERRIDE
+ bool prepareForFile(const QString &filename)
{
if (!QFile::exists(filename))
return true;
@@ -151,59 +128,110 @@ private:
return true;
}
- HRESULT setCompleted(quint64 completed, quint64 total) Q_DECL_OVERRIDE
+Q_SIGNALS:
+ void progressChanged(double progress);
+
+public Q_SLOTS:
+ void onCurrentEntryChanged(const QString &filename)
+ {
+ m_extractedFiles.prepend(QDir::toNativeSeparators(filename));
+ }
+
+ void onCompletedChanged(quint64 completed, quint64 total)
{
emit progressChanged(double(completed) / total);
- return m_state;
}
private:
- HRESULT m_state = S_OK;
BackupFiles m_backupFiles;
QStringList m_extractedFiles;
};
-class ExtractArchiveOperation::Runnable : public QObject, public QRunnable
+class ExtractArchiveOperation::Worker : public QObject
{
Q_OBJECT
- Q_DISABLE_COPY(Runnable)
+ Q_DISABLE_COPY(Worker)
public:
- Runnable(const QString &archivePath, const QString &targetDir,
- ExtractArchiveOperation::Callback *callback)
+ Worker(const QString &archivePath, const QString &targetDir, Callback *callback)
: m_archivePath(archivePath)
, m_targetDir(targetDir)
+ , m_canceled(false)
, m_callback(callback)
{}
+Q_SIGNALS:
+ void finished(bool success, const QString &errorString);
+
+public Q_SLOTS:
void run()
{
- QFile archive(m_archivePath);
- if (!archive.open(QIODevice::ReadOnly)) {
- emit finished(false, tr("Cannot open archive \"%1\" for reading: %2").arg(m_archivePath,
- archive.errorString()));
+ m_canceled = false;
+ m_archive.reset(ArchiveFactory::instance().create(m_archivePath));
+ if (!m_archive) {
+ emit finished(false, tr("Could not create handler object for archive \"%1\": \"%2\".")
+ .arg(m_archivePath, QLatin1String(Q_FUNC_INFO)));
return;
}
- try {
- Lib7z::extractArchive(&archive, m_targetDir, m_callback);
- emit finished(true, QString());
- } catch (const Lib7z::SevenZipException& e) {
+ connect(m_archive.get(), &AbstractArchive::currentEntryChanged, m_callback, &Callback::onCurrentEntryChanged);
+ connect(m_archive.get(), &AbstractArchive::completedChanged, m_callback, &Callback::onCompletedChanged);
+
+ if (!(m_archive->open(QIODevice::ReadOnly) && m_archive->isSupported())) {
+ emit finished(false, tr("Cannot open archive \"%1\" for reading: %2").arg(m_archivePath,
+ m_archive->errorString()));
+ return;
+ }
+ const QVector<ArchiveEntry> entries = m_archive->list();
+ if (entries.isEmpty()) {
+ emit finished(false, tr("Error while reading contents of archive \"%1\": %2").arg(m_archivePath,
+ m_archive->errorString()));
+ return;
+ }
+ for (auto &entry : entries) {
+ QString completeFilePath = m_targetDir + QDir::separator() + entry.path;
+ if (!entry.isDirectory && !m_callback->prepareForFile(completeFilePath)) {
+ emit finished(false, tr("Cannot prepare for file \"%1\"").arg(completeFilePath));
+ return;
+ }
+ }
+ if (m_canceled) {
+ // For large archives the reading takes some time, and the user might have
+ // canceled before we start the actual extracting.
+ emit finished(false, tr("Extract for archive \"%1\" canceled.").arg(m_archivePath));
+ } else if (!m_archive->extract(m_targetDir, entries.size())) {
emit finished(false, tr("Error while extracting archive \"%1\": %2").arg(m_archivePath,
- e.message()));
- } catch (...) {
- emit finished(false, tr("Unknown exception caught while extracting \"%1\".")
- .arg(m_archivePath));
+ m_archive->errorString()));
+ } else {
+ emit finished(true, QString());
}
}
-signals:
- void finished(bool success, const QString &errorString);
+ void onStatusChanged(PackageManagerCore::Status status)
+ {
+ if (!m_archive)
+ return;
+
+ switch (status) {
+ case PackageManagerCore::Canceled:
+ m_canceled = true;
+ m_archive->cancel();
+ break;
+ case PackageManagerCore::Failure:
+ m_canceled = true;
+ m_archive->cancel();
+ break;
+ default: // ignore all other status values
+ break;
+ }
+ }
private:
QString m_archivePath;
QString m_targetDir;
- ExtractArchiveOperation::Callback *m_callback;
+ QScopedPointer<AbstractArchive> m_archive;
+ bool m_canceled;
+ Callback *m_callback;
};
class ExtractArchiveOperation::Receiver : public QObject
@@ -223,7 +251,7 @@ public:
}
public slots:
- void runnableFinished(bool ok, const QString &msg)
+ void workerFinished(bool ok, const QString &msg)
{
m_success = ok;
m_errorString = msg;
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index c28a840bd..4721bb089 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -138,10 +138,18 @@ HEADERS += packagemanagercore.h \
repositorycategory.h \
componentselectionpage_p.h \
commandlineparser.h \
- commandlineparser_p.h
+ commandlineparser_p.h \
+ abstractarchive.h \
+ directoryguard.h \
+ lib7zarchive.h \
+ archivefactory.h
SOURCES += packagemanagercore.cpp \
+ abstractarchive.cpp \
+ archivefactory.cpp \
aspectratiolabel.cpp \
+ directoryguard.cpp \
+ lib7zarchive.cpp \
loggingutils.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \
@@ -229,8 +237,19 @@ unix {
else: SOURCES += adminauthorization_x11.cpp
}
+CONFIG(libarchive) {
+ HEADERS += libarchivearchive.h \
+ libarchivewrapper.h \
+ libarchivewrapper_p.h
+
+ SOURCES += libarchivearchive.cpp \
+ libarchivewrapper.cpp \
+ libarchivewrapper_p.cpp
+
+ LIBS += -llibarchive
+}
+
LIBS += -l7z
-CONFIG(libarchive): LIBS += -llibarchive
win32 {
SOURCES += adminauthorization_win.cpp sysinfo_win.cpp
diff --git a/src/libs/installer/lib7z_create.h b/src/libs/installer/lib7z_create.h
index bc61db7ab..72fc56c81 100644
--- a/src/libs/installer/lib7z_create.h
+++ b/src/libs/installer/lib7z_create.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#define LIB7Z_CREATE_H
#include "installer_global.h"
+#include "abstractarchive.h"
#include <Common/MyCom.h>
#include <7zip/UI/Common/Update.h>
@@ -46,14 +47,7 @@ namespace Lib7z
Yes
};
- enum struct Compression {
- Non = 0,
- Fastest = 1,
- Fast = 3,
- Normal = 5,
- Maximum = 7,
- Ultra = 9
- };
+ typedef QInstaller::AbstractArchive::CompressionLevel Compression;
class INSTALLER_EXPORT UpdateCallback : public IUpdateCallbackUI2, public CMyUnknownImp
{
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index 2f33c95e3..17176d7b0 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -36,6 +36,7 @@
#include "lib7z_list.h"
#include "lib7z_guid.h"
#include "globals.h"
+#include "directoryguard.h"
#ifndef Q_OS_WIN
# include "StdAfx.h"
@@ -294,73 +295,6 @@ QString errorMessageFrom7zResult(const LONG &extractResult)
return errorMessage;
}
-/*
- RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
-*/
-struct DirectoryGuard
-{
- explicit DirectoryGuard(const QString &path)
- : m_path(path)
- , m_created(false)
- , m_released(false)
- {
- m_path.replace(QLatin1Char('\\'), QLatin1Char('/'));
- }
-
- ~DirectoryGuard()
- {
- if (!m_created || m_released)
- return;
- QDir dir(m_path);
- if (!dir.rmdir(m_path))
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot delete directory " << m_path;
- }
-
- /*
- Tries to create the directory structure.
- Returns a list of every directory created.
- */
- QStringList tryCreate()
- {
- if (m_path.isEmpty())
- return QStringList();
-
- const QFileInfo fi(m_path);
- if (fi.exists() && fi.isDir())
- return QStringList();
- if (fi.exists() && !fi.isDir()) {
- throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path)));
- }
- QStringList created;
-
- QDir toCreate(m_path);
- while (!toCreate.exists()) {
- QString p = toCreate.absolutePath();
- created.push_front(p);
- p = p.section(QLatin1Char('/'), 0, -2);
- toCreate = QDir(p);
- }
-
- QDir dir(m_path);
- m_created = dir.mkpath(m_path);
- if (!m_created) {
- throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
- }
- return created;
- }
-
- void release()
- {
- m_released = true;
- }
-
- QString m_path;
- bool m_created;
- bool m_released;
-};
-
static UString QString2UString(const QString &str)
{
return str.toStdWString().c_str();
@@ -554,18 +488,6 @@ private:
QPointer<QIODevice> m_device;
};
-bool operator==(const File &lhs, const File &rhs)
-{
- return lhs.path == rhs.path
- && lhs.utcTime == rhs.utcTime
- && lhs.isDirectory == rhs.isDirectory
- && lhs.compressedSize == rhs.compressedSize
- && lhs.uncompressedSize == rhs.uncompressedSize
- && (lhs.permissions == rhs.permissions
- || lhs.permissions == static_cast<QFile::Permissions>(-1)
- || rhs.permissions == static_cast<QFile::Permissions>(-1));
-}
-
/*!
Returns a list of files belonging to an \a archive.
*/
@@ -586,6 +508,8 @@ QVector<File> listArchive(QFileDevice *archive)
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
@@ -620,7 +544,7 @@ QVector<File> listArchive(QFileDevice *archive)
f.archiveIndex.setY(item);
f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
Archive_IsItem_Folder(arch, item, f.isDirectory);
- f.permissions = getPermissions(arch, item, nullptr);
+ f.permissions_enum = getPermissions(arch, item, nullptr);
getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime));
f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0);
f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0);
@@ -692,7 +616,7 @@ STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **out
const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s)));
- DirectoryGuard guard(fi.absolutePath());
+ QInstaller::DirectoryGuard guard(fi.absolutePath());
const QStringList directories = guard.tryCreate();
bool isDir = false;
@@ -846,19 +770,6 @@ STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*
*/
/*!
- \enum Lib7z::Compression
-
- This enum specifies the compression ratio of an archive:
-
- \value Non
- \value Fastest
- \value Fast
- \value Normal
- \value Maximum
- \value Ultra
-*/
-
-/*!
\namespace Lib7z
\inmodule QtInstallerFramework
\brief The Lib7z namespace contains miscellaneous identifiers used throughout the Lib7z library.
@@ -1176,7 +1087,7 @@ void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallb
localCallback = callback;
}
- DirectoryGuard outDir(QFileInfo(directory).absolutePath());
+ QInstaller::DirectoryGuard outDir(QFileInfo(directory).absolutePath());
try {
outDir.tryCreate();
@@ -1191,6 +1102,8 @@ void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallb
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
@@ -1248,6 +1161,8 @@ bool isSupportedArchive(QFileDevice *archive)
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
diff --git a/src/libs/installer/lib7z_list.h b/src/libs/installer/lib7z_list.h
index 965a8b5ea..e09c73746 100644
--- a/src/libs/installer/lib7z_list.h
+++ b/src/libs/installer/lib7z_list.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,6 +40,7 @@
#define LIB7Z_LIST_H
#include "installer_global.h"
+#include "abstractarchive.h"
#include <QDateTime>
#include <QFile>
@@ -47,19 +48,7 @@
namespace Lib7z
{
- struct INSTALLER_EXPORT File
- {
- public:
- QString path;
- QDateTime utcTime;
- QPoint archiveIndex;
- bool isDirectory = false;
- quint64 compressedSize = 0;
- quint64 uncompressedSize = 0;
- QFile::Permissions permissions = 0;
- };
-
- INSTALLER_EXPORT bool operator==(const File &lhs, const File &rhs);
+ typedef QInstaller::ArchiveEntry File;
QVector<File> INSTALLER_EXPORT listArchive(QFileDevice *archive);
diff --git a/src/libs/installer/lib7zarchive.cpp b/src/libs/installer/lib7zarchive.cpp
new file mode 100644
index 000000000..d7b0c0dc9
--- /dev/null
+++ b/src/libs/installer/lib7zarchive.cpp
@@ -0,0 +1,242 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "lib7zarchive.h"
+
+#include "errors.h"
+#include "lib7z_facade.h"
+#include "lib7z_create.h"
+#include "lib7z_list.h"
+
+#include <QCoreApplication>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Lib7zArchive
+ \brief The Lib7zArchive class represents an archive file
+ handled with the LZMA software development kit.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Lib7zArchive::ExtractCallbackWrapper
+ \internal
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as parent object.
+*/
+Lib7zArchive::Lib7zArchive(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , m_extractCallback(new ExtractCallbackWrapper())
+{
+ Lib7zArchive::setFilename(filename);
+ listenExtractCallback();
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+Lib7zArchive::Lib7zArchive(QObject *parent)
+ : AbstractArchive(parent)
+ , m_extractCallback(new ExtractCallbackWrapper())
+{
+ listenExtractCallback();
+}
+
+/*!
+ Destroys the instance and releases resources.
+*/
+Lib7zArchive::~Lib7zArchive()
+{
+ delete m_extractCallback;
+}
+
+/*!
+ \reimp
+
+ Opens the underlying file device using \a mode. Returns \c true if
+ succesfull; otherwise \c false.
+*/
+bool Lib7zArchive::open(QIODevice::OpenMode mode)
+{
+ if (!m_file.open(mode)) {
+ setErrorString(m_file.errorString());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Closes the underlying file device.
+*/
+void Lib7zArchive::close()
+{
+ m_file.close();
+}
+
+/*!
+ \reimp
+
+ Sets the \a filename of the underlying file device.
+*/
+void Lib7zArchive::setFilename(const QString &filename)
+{
+ m_file.setFileName(filename);
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath.
+ Returns \c true on success; \c false otherwise.
+*/
+bool Lib7zArchive::extract(const QString &dirPath)
+{
+ m_extractCallback->setState(S_OK);
+ try {
+ Lib7z::extractArchive(&m_file, dirPath, m_extractCallback);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath. The \a totalFiles
+ parameter is unused. Returns \c true on success; \c false otherwise.
+*/
+bool Lib7zArchive::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ Q_UNUSED(totalFiles)
+ return extract(dirPath);
+}
+
+/*!
+ \reimp
+
+ Packages the given \a data into the archive and creates the file on disk.
+*/
+bool Lib7zArchive::create(const QStringList &data)
+{
+ try {
+ // No support for callback yet.
+ Lib7z::createArchive(&m_file, data, compressionLevel());
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> Lib7zArchive::list()
+{
+ try {
+ return Lib7z::listArchive(&m_file);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return QVector<ArchiveEntry>();
+ }
+}
+
+/*!
+ \reimp
+
+ Returns \c true if the current archive is of supported format;
+ \c false otherwise.
+*/
+bool Lib7zArchive::isSupported()
+{
+ try {
+ return Lib7z::isSupportedArchive(&m_file);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+}
+
+/*!
+ \reimp
+
+ Cancels the extract operation in progress.
+*/
+void Lib7zArchive::cancel()
+{
+ m_extractCallback->setState(E_ABORT);
+}
+
+/*!
+ \internal
+*/
+void Lib7zArchive::listenExtractCallback()
+{
+ connect(m_extractCallback, &ExtractCallbackWrapper::currentEntryChanged,
+ this, &Lib7zArchive::currentEntryChanged);
+ connect(m_extractCallback, &ExtractCallbackWrapper::completedChanged,
+ this, &Lib7zArchive::completedChanged);
+}
+
+
+Lib7zArchive::ExtractCallbackWrapper::ExtractCallbackWrapper()
+ : m_state(S_OK)
+{
+}
+
+void Lib7zArchive::ExtractCallbackWrapper::setState(HRESULT state)
+{
+ m_state = state;
+}
+
+void Lib7zArchive::ExtractCallbackWrapper::setCurrentFile(const QString &filename)
+{
+ emit currentEntryChanged(filename);
+}
+
+HRESULT Lib7zArchive::ExtractCallbackWrapper::setCompleted(quint64 completed, quint64 total)
+{
+ qApp->processEvents();
+
+ emit completedChanged(completed, total);
+ return m_state;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/lib7zarchive.h b/src/libs/installer/lib7zarchive.h
new file mode 100644
index 000000000..45f352aeb
--- /dev/null
+++ b/src/libs/installer/lib7zarchive.h
@@ -0,0 +1,95 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIB7ZARCHIVE_H
+#define LIB7ZARCHIVE_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+#include "lib7z_extract.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Lib7zArchive : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Lib7zArchive)
+
+public:
+ Lib7zArchive(const QString &filename, QObject *parent = nullptr);
+ explicit Lib7zArchive(QObject *parent = nullptr);
+ ~Lib7zArchive();
+
+ bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
+ void close() Q_DECL_OVERRIDE;
+ void setFilename(const QString &filename) Q_DECL_OVERRIDE;
+
+ bool extract(const QString &dirPath) Q_DECL_OVERRIDE;
+ bool extract(const QString &dirPath, const quint64 totalFiles) Q_DECL_OVERRIDE;
+ bool create(const QStringList &data) Q_DECL_OVERRIDE;
+ QVector<ArchiveEntry> list() Q_DECL_OVERRIDE;
+ bool isSupported() Q_DECL_OVERRIDE;
+
+public Q_SLOTS:
+ void cancel() Q_DECL_OVERRIDE;
+
+private:
+ void listenExtractCallback();
+
+ class ExtractCallbackWrapper;
+
+private:
+ QFile m_file;
+ ExtractCallbackWrapper *const m_extractCallback;
+};
+
+class Lib7zArchive::ExtractCallbackWrapper : public QObject, public Lib7z::ExtractCallback
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ExtractCallbackWrapper)
+
+public:
+ ExtractCallbackWrapper();
+
+ void setState(HRESULT state);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(quint64 completed, quint64 total);
+
+private:
+ void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE;
+ HRESULT setCompleted(quint64 completed, quint64 total) Q_DECL_OVERRIDE;
+
+private:
+ HRESULT m_state;
+};
+
+} // namespace QInstaller
+
+#endif // LIB7ZARCHIVE_H
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp
new file mode 100644
index 000000000..ad5609490
--- /dev/null
+++ b/src/libs/installer/libarchivearchive.cpp
@@ -0,0 +1,819 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivearchive.h"
+
+#include "directoryguard.h"
+#include "errors.h"
+#include "globals.h"
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QDir>
+#include <QTimer>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerReaderDeleter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerWriterDeleter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerEntryDeleter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ExtractWorker
+ \internal
+*/
+
+ExtractWorker::Status ExtractWorker::status() const
+{
+ return m_status;
+}
+
+void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ m_status = Unfinished;
+ quint64 completed = 0;
+
+ if (!totalFiles) {
+ m_status = Failure;
+ emit finished(QLatin1String("The file count for current archive is null!"));
+ return;
+ }
+
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_disk_new());
+ archive_entry *entry = nullptr;
+
+ LibArchiveArchive::configureReader(reader.get());
+ LibArchiveArchive::configureDiskWriter(writer.get());
+
+ DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath());
+ try {
+ const QStringList createdDirs = targetDir.tryCreate();
+ // Make sure that all leading directories created get removed as well
+ foreach (const QString &directory, createdDirs)
+ emit currentEntryChanged(directory);
+
+ int status = archive_read_open(reader.get(), this, nullptr, readCallback, nullptr);
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(QLatin1String(archive_error_string(reader.get())));
+ return;
+ }
+
+ forever {
+ if (m_status == Canceled) {
+ emit finished(QLatin1String("Extract canceled."));
+ return;
+ }
+ status = archive_read_next_header(reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(QLatin1String(archive_error_string(reader.get())));
+ return;
+ }
+ const char *current = archive_entry_pathname(entry);
+ const QString outputPath = dirPath + QDir::separator() + QString::fromLocal8Bit(current);
+ archive_entry_set_pathname(entry, outputPath.toLocal8Bit());
+
+ emit currentEntryChanged(outputPath);
+ if (!writeEntry(reader.get(), writer.get(), entry))
+ return;
+
+ ++completed;
+ emit completedChanged(completed, totalFiles);
+
+ qApp->processEvents();
+ }
+ } catch (const Error &e) {
+ m_status = Failure;
+ emit finished(e.message());
+ return;
+ }
+ targetDir.release();
+ m_status = Success;
+ emit finished();
+}
+
+void ExtractWorker::addDataBlock(const QByteArray buffer)
+{
+ m_buffer.append(buffer);
+ emit dataReadyForRead();
+}
+
+void ExtractWorker::cancel()
+{
+ m_status = Canceled;
+}
+
+ssize_t ExtractWorker::readCallback(archive *reader, void *caller, const void **buff)
+{
+ Q_UNUSED(reader)
+
+ ExtractWorker *obj;
+ if (!(obj = static_cast<ExtractWorker *>(caller)))
+ return ARCHIVE_FATAL;
+
+ QByteArray *buffer = &obj->m_buffer;
+ if (!buffer->isEmpty())
+ buffer->clear();
+
+ emit obj->dataBlockRequested();
+
+ // It's a bit bad that we have to wait here, but libarchive doesn't
+ // provide an event based reading method.
+ {
+ QEventLoop loop;
+ QTimer::singleShot(30000, &loop, &QEventLoop::quit);
+ connect(obj, &ExtractWorker::dataReadyForRead, &loop, &QEventLoop::quit);
+ connect(obj, &ExtractWorker::dataAtEnd, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+
+ if (!(*buff = static_cast<const void *>(buffer->constData())))
+ return ARCHIVE_FATAL;
+
+ return buffer->size();
+}
+
+bool ExtractWorker::writeEntry(archive *reader, archive *writer, archive_entry *entry)
+{
+ int status;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ status = archive_write_header(writer, entry);
+ if (status != ARCHIVE_OK) {
+ emit finished(QLatin1String(archive_error_string(writer)));
+ return false;
+ }
+
+ forever {
+ status = archive_read_data_block(reader, &buff, &size, &offset);
+ if (status == ARCHIVE_EOF)
+ return true;
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(QLatin1String(archive_error_string(reader)));
+ return false;
+ }
+ status = archive_write_data_block(writer, buff, size, offset);
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(QLatin1String(archive_error_string(writer)));
+ return false;
+ }
+ }
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveArchive
+ \brief The LibArchiveArchive class represents an archive file
+ handled with libarchive archive and compression library.
+
+ In addition to extracting data from the underlying file device,
+ the class supports a non-blocking mode of extracting from an external
+ data source. When using this mode, the calling client must pass the data
+ to be read in chunks of arbitrary size, and inform the object when there
+ is no more data to read.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveArchive::ArchiveData
+ \brief Bundles a file device and associated read buffer for access
+ as client data in libarchive callbacks.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::dataBlockRequested()
+
+ Emitted when the worker object requires more data to continue extracting.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerFinished()
+
+ Emitted when the worker object finished extracting an archive.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToExtract(const QString &dirPath, const quint64 totalFiles)
+
+ Emitted when the worker object is about to extract \a totalFiles
+ from an archive to \a dirPath.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToAddDataBlock(const QByteArray buffer)
+
+ Emitted when the worker object is about to add and read the data block in \a buffer.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToSetDataAtEnd()
+
+ Emitted when the worker object is about to set data-at-end, meaning there
+ will be no further read requests for the calling client.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToCancel()
+
+ Emitted when the worker object is about to cancel extracting.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as the parent object.
+*/
+LibArchiveArchive::LibArchiveArchive(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , m_data(new ArchiveData())
+ , m_cancelScheduled(false)
+{
+ LibArchiveArchive::setFilename(filename);
+ initExtractWorker();
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+LibArchiveArchive::LibArchiveArchive(QObject *parent)
+ : AbstractArchive(parent)
+ , m_data(new ArchiveData())
+ , m_cancelScheduled(false)
+{
+ initExtractWorker();
+}
+
+/*!
+ Destroys the instance and releases resources.
+*/
+LibArchiveArchive::~LibArchiveArchive()
+{
+ m_workerThread.quit();
+ m_workerThread.wait();
+
+ delete m_data;
+}
+
+/*!
+ \reimp
+
+ Opens the underlying file device using \a mode. Returns \c true if
+ succesfull; otherwise \c false.
+*/
+bool LibArchiveArchive::open(QIODevice::OpenMode mode)
+{
+ if (!m_data->file.open(mode)) {
+ setErrorString(m_data->file.errorString());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Closes the underlying file device.
+*/
+void LibArchiveArchive::close()
+{
+ m_data->file.close();
+}
+
+/*!
+ \reimp
+
+ Sets the \a filename of the underlying file device.
+*/
+void LibArchiveArchive::setFilename(const QString &filename)
+{
+ m_data->file.setFileName(filename);
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath.
+ Returns \c true on success; \c false otherwise.
+*/
+bool LibArchiveArchive::extract(const QString &dirPath)
+{
+ return extract(dirPath, totalFiles());
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath with
+ precalculated count of \a totalFiles. Returns \c true on
+ success; \c false otherwise.
+*/
+bool LibArchiveArchive::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ m_cancelScheduled = false;
+ quint64 completed = 0;
+ if (!totalFiles) {
+ setErrorString(QLatin1String("The file count for current archive is null!"));
+ return false;
+ }
+
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_disk_new());
+ archive_entry *entry = nullptr;
+
+ configureReader(reader.get());
+ configureDiskWriter(writer.get());
+
+ DirectoryGuard targetDir(QFileInfo(dirPath).absolutePath());
+ try {
+ const QStringList createdDirs = targetDir.tryCreate();
+ // Make sure that all leading directories created get removed as well
+ foreach (const QString &directory, createdDirs)
+ emit currentEntryChanged(directory);
+
+ int status = archive_read_open(reader.get(), m_data, nullptr, readCallback, nullptr);
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ forever {
+ if (m_cancelScheduled)
+ throw Error(QLatin1String("Extract canceled."));
+
+ status = archive_read_next_header(reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ const char *current = archive_entry_pathname(entry);
+ const QString outputPath = dirPath + QDir::separator() + QString::fromLocal8Bit(current);
+ archive_entry_set_pathname(entry, outputPath.toLocal8Bit());
+
+ emit currentEntryChanged(outputPath);
+ if (!writeEntry(reader.get(), writer.get(), entry))
+ throw Error(errorString()); // appropriate error string set in writeEntry()
+
+ ++completed;
+ emit completedChanged(completed, totalFiles);
+
+ qApp->processEvents();
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return false;
+ }
+ targetDir.release();
+ m_data->file.seek(0);
+ return true;
+}
+
+/*!
+ \reimp
+
+ Packages the given \a data into the archive and creates the file on disk.
+*/
+bool LibArchiveArchive::create(const QStringList &data)
+{
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_new());
+ configureWriter(writer.get());
+
+ try {
+ int status;
+ if ((status = archive_write_open_filename(writer.get(), m_data->file.fileName().toLocal8Bit())))
+ throw Error(QLatin1String(archive_error_string(writer.get())));
+
+ for (auto &dataEntry : data) {
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_disk_new());
+ configureDiskReader(reader.get());
+
+ if ((status = archive_read_disk_open(reader.get(), dataEntry.toLocal8Bit())))
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ QDir basePath = QFileInfo(dataEntry).dir();
+ forever {
+ QScopedPointer<archive_entry, ScopedPointerEntryDeleter> entry(archive_entry_new());
+ status = archive_read_next_header2(reader.get(), entry.get());
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ const QFileInfo fileOrDir(pathWithoutNamespace(QLatin1String(archive_entry_sourcepath(entry.get()))));
+ // Set new path name in archive, otherwise we add all directories from absolute path
+ const QString newPath = basePath.relativeFilePath(fileOrDir.filePath());
+ archive_entry_set_pathname(entry.get(), newPath.toLocal8Bit());
+
+ archive_read_disk_descend(reader.get());
+ status = archive_write_header(writer.get(), entry.get());
+ if (status < ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(writer.get())));
+
+ if (fileOrDir.isDir())
+ continue; // nothing to copy
+
+ QFile file(pathWithoutNamespace(QLatin1String(archive_entry_sourcepath(entry.get()))));
+ if (!file.open(QIODevice::ReadOnly))
+ throw Error(file.errorString());
+
+ QByteArray buffer;
+ constexpr qint64 blockSize = 4 * 1024;
+ buffer.resize(blockSize);
+
+ ssize_t bytesRead = readData(&file, buffer.data(), blockSize);
+ while (bytesRead > 0) {
+ archive_write_data(writer.get(), buffer.constData(), blockSize);
+ bytesRead = readData(&file, buffer.data(), blockSize);
+ }
+ file.close();
+ }
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveArchive::list()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ archive_entry *entry = nullptr;
+
+ configureReader(reader.get());
+
+ QVector<ArchiveEntry> entries;
+ try {
+ int status = archive_read_open(reader.get(), m_data, nullptr, readCallback, nullptr);
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ forever {
+ status = archive_read_next_header(reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ ArchiveEntry archiveEntry;
+ archiveEntry.path = QLatin1String(archive_entry_pathname(entry));
+ archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry));
+ archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR);
+ archiveEntry.uncompressedSize = archive_entry_size(entry);
+ archiveEntry.permissions_mode = archive_entry_perm(entry);
+
+ entries.append(archiveEntry);
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return QVector<ArchiveEntry>();
+ }
+ m_data->file.seek(0);
+ return entries;
+}
+
+/*!
+ \reimp
+
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveArchive::isSupported()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ configureReader(reader.get());
+
+ try {
+ const int status = archive_read_open(reader.get(), m_data, nullptr, readCallback, nullptr);
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return false;
+ }
+ m_data->file.seek(0);
+ return true;
+}
+
+/*!
+ Requests to extract the archive to \a dirPath with \a totalFiles
+ in a separate thread with a worker object.
+*/
+void LibArchiveArchive::workerExtract(const QString &dirPath, const quint64 totalFiles)
+{
+ emit workerAboutToExtract(dirPath, totalFiles);
+}
+
+/*!
+ Adds data to be read by the worker object in \a buffer.
+*/
+void LibArchiveArchive::workerAddDataBlock(const QByteArray buffer)
+{
+ emit workerAboutToAddDataBlock(buffer);
+}
+
+/*!
+ Signals the worker object that the client data is at end.
+*/
+void LibArchiveArchive::workerSetDataAtEnd()
+{
+ emit workerAboutToSetDataAtEnd();
+}
+
+/*!
+ Cancels the extract in progress for the worker object.
+*/
+void LibArchiveArchive::workerCancel()
+{
+ emit workerAboutToCancel();
+}
+
+/*!
+ Returns the status of the worker object.
+*/
+ExtractWorker::Status LibArchiveArchive::workerStatus() const
+{
+ return m_worker.status();
+}
+
+/*!
+ \reimp
+
+ Cancels the extract in progress.
+*/
+void LibArchiveArchive::cancel()
+{
+ m_cancelScheduled = true;
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::onWorkerFinished(const QString &errorString)
+{
+ setErrorString(errorString);
+ emit workerFinished();
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureReader(archive *archive)
+{
+ archive_read_support_filter_bzip2(archive);
+ archive_read_support_filter_gzip(archive);
+ archive_read_support_filter_xz(archive);
+
+ archive_read_support_format_tar(archive);
+ archive_read_support_format_zip(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureWriter(archive *archive)
+{
+ if (QFileInfo(m_data->file.fileName()).suffix() == QLatin1String("zip")) {
+ archive_write_set_format_zip(archive);
+ } else {
+ archive_write_set_format_pax_restricted(archive);
+ archive_write_set_format_filter_by_ext(archive, m_data->file.fileName().toLatin1());
+ }
+ const QByteArray options = "compression-level=" + QString::number(compressionLevel()).toLatin1();
+ if (archive_write_set_options(archive, options.constData())) { // not fatal
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Could not set options" << options
+ << "for archive" << m_data->file.fileName() << ":" << archive_error_string(archive);
+ }
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureDiskReader(archive *archive)
+{
+ archive_read_disk_set_standard_lookup(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureDiskWriter(archive *archive)
+{
+ constexpr int flags = ARCHIVE_EXTRACT_TIME
+ | ARCHIVE_EXTRACT_PERM
+ | ARCHIVE_EXTRACT_ACL
+ | ARCHIVE_EXTRACT_FFLAGS;
+
+ archive_write_disk_set_options(archive, flags);
+ archive_write_disk_set_standard_lookup(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::initExtractWorker()
+{
+ m_worker.moveToThread(&m_workerThread);
+
+ connect(this, &LibArchiveArchive::workerAboutToExtract, &m_worker, &ExtractWorker::extract);
+ connect(this, &LibArchiveArchive::workerAboutToAddDataBlock, &m_worker, &ExtractWorker::addDataBlock);
+ connect(this, &LibArchiveArchive::workerAboutToSetDataAtEnd, &m_worker, &ExtractWorker::dataAtEnd);
+ connect(this, &LibArchiveArchive::workerAboutToCancel, &m_worker, &ExtractWorker::cancel);
+
+ connect(&m_worker, &ExtractWorker::dataBlockRequested, this, &LibArchiveArchive::dataBlockRequested);
+ connect(&m_worker, &ExtractWorker::finished, this, &LibArchiveArchive::onWorkerFinished);
+
+ connect(&m_worker, &ExtractWorker::currentEntryChanged, this, &LibArchiveArchive::currentEntryChanged);
+ connect(&m_worker, &ExtractWorker::completedChanged, this, &LibArchiveArchive::completedChanged);
+
+ m_workerThread.start();
+}
+
+/*!
+ Writes the current \a entry header, then pulls data from the archive \a reader
+ and writes it to the \a writer handle.
+*/
+bool LibArchiveArchive::writeEntry(archive *reader, archive *writer, archive_entry *entry)
+{
+ int status;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ status = archive_write_header(writer, entry);
+ if (status != ARCHIVE_OK) {
+ setErrorString(QLatin1String(archive_error_string(writer)));
+ return false;
+ }
+
+ forever {
+ status = archive_read_data_block(reader, &buff, &size, &offset);
+ if (status == ARCHIVE_EOF)
+ return true;
+ if (status != ARCHIVE_OK) {
+ setErrorString(QLatin1String(archive_error_string(reader)));
+ return false;
+ }
+ status = archive_write_data_block(writer, buff, size, offset);
+ if (status != ARCHIVE_OK) {
+ setErrorString(QLatin1String(archive_error_string(writer)));
+ return false;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Reads \a data from the current position of \a file. The maximum bytes to
+ read are specified by \a maxSize. Returns the amount of bytes read.
+*/
+qint64 LibArchiveArchive::readData(QFile *file, char *data, qint64 maxSize)
+{
+ if (!file->isOpen() || file->isSequential())
+ return ARCHIVE_FATAL;
+
+ if (file->atEnd() && file->seek(0))
+ return ARCHIVE_OK;
+
+ const qint64 bytesRead = file->read(data, maxSize);
+ if (bytesRead == -1)
+ return ARCHIVE_FATAL;
+
+ return bytesRead;
+}
+
+/*!
+ \internal
+
+ Called by libarchive when new data is needed. Reads data from the file device
+ in \a archiveData into the buffer referenced by \a buff. Returns the number of bytes read.
+*/
+ssize_t LibArchiveArchive::readCallback(archive *reader, void *archiveData, const void **buff)
+{
+ Q_UNUSED(reader)
+ constexpr qint64 blockSize = 1024 * 1024; // 1MB
+
+ ArchiveData *data;
+ if (!(data = static_cast<ArchiveData *>(archiveData)))
+ return ARCHIVE_FATAL;
+
+ if (!data->buffer.isEmpty())
+ data->buffer.clear();
+
+ if (data->buffer.size() != blockSize)
+ data->buffer.resize(blockSize);
+
+ if (!(*buff = static_cast<const void *>(data->buffer.constData())))
+ return ARCHIVE_FATAL;
+
+ // Doesn't matter if the buffer size exceeds the actual data read,
+ // the return value indicates the length of relevant bytes.
+ return readData(&data->file, data->buffer.data(), data->buffer.size());
+}
+
+/*!
+ Returns the \a path to a file or directory, without the Win32 namespace prefix.
+ On Unix platforms, the \a path is returned unaltered.
+*/
+QString LibArchiveArchive::pathWithoutNamespace(const QString &path)
+{
+ QString aPath = path;
+#ifdef Q_OS_WIN
+ if (aPath.size() > 4 && aPath.at(0) == QLatin1Char('\\')
+ && aPath.at(2) == QLatin1Char('?') && aPath.at(3) == QLatin1Char('\\')) {
+ aPath = aPath.mid(4);
+ }
+#endif
+ return aPath;
+}
+
+/*!
+ Returns the number of files in this archive.
+*/
+quint64 LibArchiveArchive::totalFiles()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ archive_entry *entry = nullptr;
+ quint64 files = 0;
+
+ configureReader(reader.get());
+
+ try {
+ int status = archive_read_open(reader.get(), m_data, nullptr, readCallback, nullptr);
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ forever {
+ status = archive_read_next_header(reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK)
+ throw Error(QLatin1String(archive_error_string(reader.get())));
+
+ ++files;
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return 0;
+ }
+ m_data->file.seek(0);
+ return files;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivearchive.h b/src/libs/installer/libarchivearchive.h
new file mode 100644
index 000000000..e0281e655
--- /dev/null
+++ b/src/libs/installer/libarchivearchive.h
@@ -0,0 +1,189 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEARCHIVE_H
+#define LIBARCHIVEARCHIVE_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include <QThread>
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
+namespace QInstaller {
+
+class ExtractWorker : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ExtractWorker)
+
+public:
+ enum Status {
+ Success = 0,
+ Failure = 1,
+ Canceled = 2,
+ Unfinished = 3
+ };
+
+ ExtractWorker() = default;
+
+ Status status() const;
+
+public Q_SLOTS:
+ void extract(const QString &dirPath, const quint64 totalFiles);
+ void addDataBlock(const QByteArray buffer);
+ void cancel();
+
+Q_SIGNALS:
+ void dataBlockRequested();
+ void dataAtEnd();
+ void dataReadyForRead();
+ void finished(const QString &errorString = QString());
+
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(quint64 completed, quint64 total);
+
+private:
+ static ssize_t readCallback(archive *reader, void *caller, const void **buff);
+ bool writeEntry(archive *reader, archive *writer, archive_entry *entry);
+
+private:
+ QByteArray m_buffer;
+ Status m_status;
+};
+
+class INSTALLER_EXPORT LibArchiveArchive : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveArchive)
+
+public:
+ LibArchiveArchive(const QString &filename, QObject *parent = nullptr);
+ explicit LibArchiveArchive(QObject *parent = nullptr);
+ ~LibArchiveArchive();
+
+ bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
+ void close() Q_DECL_OVERRIDE;
+ void setFilename(const QString &filename) Q_DECL_OVERRIDE;
+
+ bool extract(const QString &dirPath) Q_DECL_OVERRIDE;
+ bool extract(const QString &dirPath, const quint64 totalFiles) Q_DECL_OVERRIDE;
+ bool create(const QStringList &data) Q_DECL_OVERRIDE;
+ QVector<ArchiveEntry> list() Q_DECL_OVERRIDE;
+ bool isSupported() Q_DECL_OVERRIDE;
+
+ void workerExtract(const QString &dirPath, const quint64 totalFiles);
+ void workerAddDataBlock(const QByteArray buffer);
+ void workerSetDataAtEnd();
+ void workerCancel();
+ ExtractWorker::Status workerStatus() const;
+
+Q_SIGNALS:
+ void dataBlockRequested();
+ void workerFinished();
+
+ void workerAboutToExtract(const QString &dirPath, const quint64 totalFiles);
+ void workerAboutToAddDataBlock(const QByteArray buffer);
+ void workerAboutToSetDataAtEnd();
+ void workerAboutToCancel();
+
+public Q_SLOTS:
+ void cancel() Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+ void onWorkerFinished(const QString &errorString);
+
+private:
+ static void configureReader(archive *archive);
+ void configureWriter(archive *archive);
+ static void configureDiskReader(archive *archive);
+ static void configureDiskWriter(archive *archive);
+
+ void initExtractWorker();
+
+ bool writeEntry(archive *reader, archive *writer, archive_entry *entry);
+
+ static qint64 readData(QFile *file, char *data, qint64 maxSize);
+ static ssize_t readCallback(archive *reader, void *archiveData, const void **buff);
+
+ static QString pathWithoutNamespace(const QString &path);
+
+ quint64 totalFiles();
+
+private:
+ friend class ExtractWorker;
+ friend class LibArchiveWrapperPrivate;
+
+ struct ArchiveData
+ {
+ QFile file;
+ QByteArray buffer;
+ };
+
+private:
+ ArchiveData *m_data;
+ ExtractWorker m_worker;
+ QThread m_workerThread;
+
+ bool m_cancelScheduled;
+};
+
+struct ScopedPointerReaderDeleter
+{
+ static inline void cleanup(archive *p)
+ {
+ archive_read_free(p);
+ }
+};
+
+struct ScopedPointerWriterDeleter
+{
+ static inline void cleanup(archive *p)
+ {
+ archive_write_free(p);
+ }
+};
+
+struct ScopedPointerEntryDeleter
+{
+ static inline void cleanup(archive_entry *p)
+ {
+ archive_entry_free(p);
+ }
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEARCHIVE_H
diff --git a/src/libs/installer/libarchivewrapper.cpp b/src/libs/installer/libarchivewrapper.cpp
new file mode 100644
index 000000000..c259678ca
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper.cpp
@@ -0,0 +1,204 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivewrapper.h"
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveWrapper
+ \brief The LibArchiveWrapper class provides an interface for interacting
+ with archives handled using the libarchive archive and compression library.
+
+ The invoked archive operations are performed in a normal user mode, or in an
+ on-demand elevated user mode through the remote client-server protocol of the framework.
+
+ This class is not thread-safe; extra care should be taken especially when
+ elevated mode is active to not call its functions from any thread other than
+ where the object was created.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveWrapper::currentEntryChanged(const QString &filename)
+
+ Current entry changed to \a filename. Emitted when the entry to process is changed.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveWrapper::completedChanged(quint64 completed, quint64 total)
+
+ The ratio of \a completed entries from \a total changed.
+ Emitted when the progress changes.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as the parent object.
+*/
+LibArchiveWrapper::LibArchiveWrapper(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , d(new LibArchiveWrapperPrivate(filename))
+{
+ connect(d, &LibArchiveWrapperPrivate::currentEntryChanged,
+ this, &LibArchiveWrapper::currentEntryChanged);
+ connect(d, &LibArchiveWrapperPrivate::completedChanged,
+ this, &LibArchiveWrapper::completedChanged);
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+LibArchiveWrapper::LibArchiveWrapper(QObject *parent)
+ : AbstractArchive(parent)
+ , d(new LibArchiveWrapperPrivate())
+{
+ connect(d, &LibArchiveWrapperPrivate::currentEntryChanged,
+ this, &LibArchiveWrapper::currentEntryChanged);
+ connect(d, &LibArchiveWrapperPrivate::completedChanged,
+ this, &LibArchiveWrapper::completedChanged);
+}
+
+/*!
+ Destroys the instance.
+*/
+LibArchiveWrapper::~LibArchiveWrapper()
+{
+ delete d;
+}
+
+/*!
+ Opens the file device using \a mode.
+ Returns \c true if succesfull; otherwise \c false.
+*/
+bool LibArchiveWrapper::open(QIODevice::OpenMode mode)
+{
+ return d->open(mode);
+}
+
+/*!
+ Closes the file device.
+*/
+void LibArchiveWrapper::close()
+{
+ d->close();
+}
+
+/*!
+ Sets the \a filename for the archive.
+
+ If the remote connection is active, the same method is called by the server.
+*/
+void LibArchiveWrapper::setFilename(const QString &filename)
+{
+ d->setFilename(filename);
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+QString LibArchiveWrapper::errorString() const
+{
+ return d->errorString();
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapper::extract(const QString &dirPath)
+{
+ return d->extract(dirPath);
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath with
+ precalculated count of \a totalFiles. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapper::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ return d->extract(dirPath, totalFiles);
+}
+
+/*!
+ Packages the given \a data into the archive and creates the file on disk.
+ Returns \c true on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+bool LibArchiveWrapper::create(const QStringList &data)
+{
+ return d->create(data);
+}
+
+/*!
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveWrapper::list()
+{
+ return d->list();
+}
+
+/*!
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveWrapper::isSupported()
+{
+ return d->isSupported();
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+*/
+void LibArchiveWrapper::setCompressionLevel(const CompressionLevel level)
+{
+ d->setCompressionLevel(level);
+}
+
+/*!
+ Cancels the extract operation in progress.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapper::cancel()
+{
+ d->cancel();
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivewrapper.h b/src/libs/installer/libarchivewrapper.h
new file mode 100644
index 000000000..c638d10dc
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper.h
@@ -0,0 +1,71 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEWRAPPER_H
+#define LIBARCHIVEWRAPPER_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+#include "libarchivewrapper_p.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT LibArchiveWrapper : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveWrapper)
+
+public:
+ LibArchiveWrapper(const QString &filename, QObject *parent = nullptr);
+ explicit LibArchiveWrapper(QObject *parent = nullptr);
+ ~LibArchiveWrapper();
+
+ bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
+ void close() Q_DECL_OVERRIDE;
+ void setFilename(const QString &filename) Q_DECL_OVERRIDE;
+
+ QString errorString() const Q_DECL_OVERRIDE;
+
+ bool extract(const QString &dirPath) Q_DECL_OVERRIDE;
+ bool extract(const QString &dirPath, const quint64 totalFiles) Q_DECL_OVERRIDE;
+ bool create(const QStringList &data) Q_DECL_OVERRIDE;
+ QVector<ArchiveEntry> list() Q_DECL_OVERRIDE;
+ bool isSupported() Q_DECL_OVERRIDE;
+
+ void setCompressionLevel(const AbstractArchive::CompressionLevel level) Q_DECL_OVERRIDE;
+
+public Q_SLOTS:
+ void cancel() Q_DECL_OVERRIDE;
+
+private:
+ LibArchiveWrapperPrivate *const d;
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEWRAPPER_H
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
new file mode 100644
index 000000000..5509812cf
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper_p.cpp
@@ -0,0 +1,356 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivewrapper_p.h"
+
+#include "globals.h"
+
+#include <QFileInfo>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveWrapperPrivate
+ \internal
+*/
+
+/*!
+ \fn QInstaller::ArchiveWrapper::dataBlockRequested()
+
+ Emitted when the server process has requested another data block.
+*/
+
+/*!
+ \fn QInstaller::ArchiveWrapper::remoteWorkerFinished()
+
+ Emitted when the server process has finished extracting an archive.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename.
+*/
+LibArchiveWrapperPrivate::LibArchiveWrapperPrivate(const QString &filename)
+ : RemoteObject(QLatin1String(Protocol::AbstractArchive))
+{
+ init();
+ LibArchiveWrapperPrivate::setFilename(filename);
+}
+
+/*!
+ Constructs the object.
+*/
+LibArchiveWrapperPrivate::LibArchiveWrapperPrivate()
+ : RemoteObject(QLatin1String(Protocol::AbstractArchive))
+{
+ init();
+}
+
+/*!
+ Destroys the instance.
+*/
+LibArchiveWrapperPrivate::~LibArchiveWrapperPrivate()
+{
+ m_timer.stop();
+}
+
+/*!
+ Opens the file device using \a mode.
+ Returns \c true if succesfull; otherwise \c false.
+*/
+bool LibArchiveWrapperPrivate::open(QIODevice::OpenMode mode)
+{
+ return m_archive.open(mode);
+}
+
+/*!
+ Closes the file device.
+*/
+void LibArchiveWrapperPrivate::close()
+{
+ m_archive.close();
+}
+
+/*!
+ Sets the \a filename for the archive.
+
+ If the remote connection is active, the same method is called by the server.
+*/
+void LibArchiveWrapperPrivate::setFilename(const QString &filename)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilename), filename, dummy);
+ m_lock.unlock();
+ }
+ m_archive.setFilename(filename);
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+QString LibArchiveWrapperPrivate::errorString() const
+{
+ if ((const_cast<LibArchiveWrapperPrivate *>(this))->connectToServer()) {
+ m_lock.lockForWrite();
+ const QString errorString
+ = callRemoteMethod<QString>(QLatin1String(Protocol::AbstractArchiveErrorString));
+ m_lock.unlock();
+ return errorString;
+ }
+ return m_archive.errorString();
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath with
+ an optional precalculated count of \a totalFiles. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapperPrivate::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, totalFiles);
+ m_lock.unlock();
+ {
+ QEventLoop loop;
+ connect(this, &LibArchiveWrapperPrivate::remoteWorkerFinished, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ return (workerStatus() == ExtractWorker::Success);
+ }
+ return m_archive.extract(dirPath, totalFiles);
+}
+
+/*!
+ Packages the given \a data into the archive and creates the file on disk.
+ Returns \c true on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+bool LibArchiveWrapperPrivate::create(const QStringList &data)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ const bool success
+ = callRemoteMethod<bool>(QLatin1String(Protocol::AbstractArchiveCreate), data);
+ m_lock.unlock();
+ return success;
+ }
+ return m_archive.create(data);
+}
+
+/*!
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveWrapperPrivate::list()
+{
+ return m_archive.list();
+}
+
+/*!
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveWrapperPrivate::isSupported()
+{
+ return m_archive.isSupported();
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapperPrivate::setCompressionLevel(const AbstractArchive::CompressionLevel level)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level, dummy);
+ m_lock.unlock();
+ return;
+ }
+ m_archive.setCompressionLevel(level);
+}
+
+/*!
+ Cancels the extract operation in progress.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapperPrivate::cancel()
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveCancel));
+ m_lock.unlock();
+ return;
+ }
+ m_archive.cancel();
+}
+
+/*!
+ Calls a remote method to get the associated queued signals from the server.
+ Signals are then processed and emitted client-side.
+*/
+void LibArchiveWrapperPrivate::processSignals()
+{
+ if (!isConnectedToServer())
+ return;
+
+ if (!m_lock.tryLockForRead())
+ return;
+
+ QList<QVariant> receivedSignals =
+ callRemoteMethod<QList<QVariant>>(QString::fromLatin1(Protocol::GetAbstractArchiveSignals));
+
+ m_lock.unlock();
+ while (!receivedSignals.isEmpty()) {
+ const QString name = receivedSignals.takeFirst().toString();
+ if (name == QLatin1String(Protocol::AbstractArchiveSignalCurrentEntryChanged)) {
+ emit currentEntryChanged(receivedSignals.takeFirst().toString());
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalCompletedChanged)) {
+ const quint64 completed = receivedSignals.takeFirst().value<quint64>();
+ const quint64 total = receivedSignals.takeFirst().value<quint64>();
+ emit completedChanged(completed, total);
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalDataBlockRequested)) {
+ emit dataBlockRequested();
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalWorkerFinished)) {
+ emit remoteWorkerFinished();
+ }
+ }
+}
+
+/*!
+ Reads a block of data from the current position of the underlying file device.
+*/
+void LibArchiveWrapperPrivate::onDataBlockRequested()
+{
+ constexpr quint64 blockSize = 10 * 1024 * 1024; // 10MB
+
+ QFile *const file = &m_archive.m_data->file;
+ if (!file->isOpen() || file->isSequential()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientDataAtEnd();
+ return;
+ }
+ if (file->atEnd() && file->seek(0)) {
+ setClientDataAtEnd();
+ return;
+ }
+
+ QByteArray *const buff = &m_archive.m_data->buffer;
+ if (!buff->isEmpty())
+ buff->clear();
+
+ if (buff->size() != blockSize)
+ buff->resize(blockSize);
+
+ const qint64 bytesRead = file->read(buff->data(), blockSize);
+ if (bytesRead == -1) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientDataAtEnd();
+ return;
+ }
+ // The read callback in ExtractWorker class expects the buffer size to
+ // match the number of bytes read. Some formats will fail if the buffer
+ // is larger than the actual data.
+ if (buff->size() != bytesRead)
+ buff->resize(bytesRead);
+
+ addDataBlock(*buff);
+}
+
+/*!
+ Starts the timer to process server-side signals and connects handler
+ signals for the matching signals of the wrapper object.
+*/
+void LibArchiveWrapperPrivate::init()
+{
+ m_timer.start(250);
+ QObject::connect(&m_timer, &QTimer::timeout,
+ this, &LibArchiveWrapperPrivate::processSignals);
+
+ QObject::connect(&m_archive, &LibArchiveArchive::currentEntryChanged,
+ this, &LibArchiveWrapperPrivate::currentEntryChanged);
+ QObject::connect(&m_archive, &LibArchiveArchive::completedChanged,
+ this, &LibArchiveWrapperPrivate::completedChanged);
+
+ QObject::connect(this, &LibArchiveWrapperPrivate::dataBlockRequested,
+ this, &LibArchiveWrapperPrivate::onDataBlockRequested);
+}
+
+/*!
+ Calls a remote method to add a \a buffer for reading.
+*/
+void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer, dummy);
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to inform that the client has finished
+ reading the current file.
+*/
+void LibArchiveWrapperPrivate::setClientDataAtEnd()
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to retrieve and return the status of
+ the extract worker on a server process.
+*/
+ExtractWorker::Status LibArchiveWrapperPrivate::workerStatus() const
+{
+ ExtractWorker::Status status = ExtractWorker::Unfinished;
+ if ((const_cast<LibArchiveWrapperPrivate *>(this))->connectToServer()) {
+ m_lock.lockForWrite();
+ status = static_cast<ExtractWorker::Status>(
+ callRemoteMethod<qint32>(QLatin1String(Protocol::AbstractArchiveWorkerStatus)));
+ m_lock.unlock();
+ }
+ return status;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivewrapper_p.h b/src/libs/installer/libarchivewrapper_p.h
new file mode 100644
index 000000000..ea8409da0
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper_p.h
@@ -0,0 +1,94 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEWRAPPER_P_H
+#define LIBARCHIVEWRAPPER_P_H
+
+#include "installer_global.h"
+#include "remoteobject.h"
+#include "libarchivearchive.h"
+#include "lib7zarchive.h"
+
+#include <QTimer>
+#include <QReadWriteLock>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT LibArchiveWrapperPrivate : public RemoteObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveWrapperPrivate)
+
+public:
+ explicit LibArchiveWrapperPrivate(const QString &filename);
+ LibArchiveWrapperPrivate();
+ ~LibArchiveWrapperPrivate();
+
+ bool open(QIODevice::OpenMode mode);
+ void close();
+ void setFilename(const QString &filename);
+
+ QString errorString() const;
+
+ bool extract(const QString &dirPath, const quint64 totalFiles = 0);
+ bool create(const QStringList &data);
+ QVector<ArchiveEntry> list();
+ bool isSupported();
+
+ void setCompressionLevel(const AbstractArchive::CompressionLevel level);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(const quint64 completed, const quint64 total);
+ void dataBlockRequested();
+ void remoteWorkerFinished();
+
+public Q_SLOTS:
+ void cancel();
+
+private Q_SLOTS:
+ void processSignals();
+ void onDataBlockRequested();
+
+private:
+ void init();
+
+ void addDataBlock(const QByteArray &buffer);
+ void setClientDataAtEnd();
+ ExtractWorker::Status workerStatus() const;
+
+private:
+ QTimer m_timer;
+ mutable QReadWriteLock m_lock;
+
+ LibArchiveArchive m_archive;
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEWRAPPER_P_H
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 9160f4cc9..99b48e626 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,8 +29,7 @@
#ifndef METADATAJOB_P_H
#define METADATAJOB_P_H
-#include "lib7z_extract.h"
-#include "lib7z_facade.h"
+#include "lib7zarchive.h"
#include "metadatajob.h"
#include <QDir>
@@ -76,21 +75,15 @@ public:
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 "
- "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), e.message())));
- } catch (...) {
- fi.reportException(UnzipArchiveException(MetadataJob::tr("Unknown exception "
- "caught while extracting archive \"%1\".").arg(QDir::toNativeSeparators(m_archive))));
- }
- } else {
+ Lib7zArchive archive(m_archive);
+ if (!archive.open(QIODevice::ReadOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
"reading: %2").arg(QDir::toNativeSeparators(m_archive), archive.errorString())));
}
+ if (!archive.extract(m_targetDir)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
+ "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive.errorString())));
+ }
fi.reportFinished();
}
diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h
index 9ead7943c..c7eb9a308 100644
--- a/src/libs/installer/protocol.h
+++ b/src/libs/installer/protocol.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -163,6 +163,29 @@ const char QAbstractFileEngineSyncToDisk[] = "QAbstractFileEngine::syncToDisk";
const char QAbstractFileEngineRenameOverwrite[] = "QAbstractFileEngine::renameOverwrite";
const char QAbstractFileEngineFileTime[] = "QAbstractFileEngine::fileTime";
+
+// LibArchiveWrapper
+const char AbstractArchive[] = "AbstractArchive";
+const char AbstractArchiveOpen[] = "AbstractArchive::open";
+const char AbstractArchiveClose[] = "AbstractArchive::close";
+const char AbstractArchiveSetFilename[] = "AbstractArchive::setFilename";
+const char AbstractArchiveErrorString[] = "AbstractArchive::errorString";
+const char AbstractArchiveExtract[] = "AbstractArchive::extract";
+const char AbstractArchiveCreate[] = "AbstractArchive::create";
+const char AbstractArchiveList[] = "AbstractArchive::list";
+const char AbstractArchiveIsSupported[] = "AbstractArchive::isSupported";
+const char AbstractArchiveSetCompressionLevel[] = "AbstractArchive::setCompressionLevel";
+const char AbstractArchiveAddDataBlock[] = "AbstractArchive::addDataBlock";
+const char AbstractArchiveSetClientDataAtEnd[] = "AbstractArchive::setClientDataAtEnd";
+const char AbstractArchiveWorkerStatus[] = "AbstractArchive::workerStatus";
+const char AbstractArchiveCancel[] = "AbstractArchive::cancel";
+
+const char GetAbstractArchiveSignals[] = "GetAbstractArchiveSignals";
+const char AbstractArchiveSignalCurrentEntryChanged[] = "AbstractArchive::currentEntryChanged";
+const char AbstractArchiveSignalCompletedChanged[] = "AbstractArchive::completedChanged";
+const char AbstractArchiveSignalDataBlockRequested[] = "AbstractArchive::dataBlockRequested";
+const char AbstractArchiveSignalWorkerFinished[] = "AbstractArchive::workerFinished";
+
} // namespace Protocol
void INSTALLER_EXPORT sendPacket(QIODevice *device, const QByteArray &command, const QByteArray &data);
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index a6b66c081..5d72f834d 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.cpp
@@ -34,6 +34,9 @@
#include "utils.h"
#include "permissionsettings.h"
#include "globals.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivearchive.h"
+#endif
#include <QCoreApplication>
#include <QDataStream>
@@ -59,8 +62,10 @@ RemoteServerConnection::RemoteServerConnection(qintptr socketDescriptor, const Q
, m_socketDescriptor(socketDescriptor)
, m_process(nullptr)
, m_engine(nullptr)
+ , m_archive(nullptr)
, m_authorizationKey(key)
- , m_signalReceiver(nullptr)
+ , m_processSignalReceiver(nullptr)
+ , m_archiveSignalReceiver(nullptr)
{
setObjectName(QString::fromLatin1("RemoteServerConnection(%1)").arg(socketDescriptor));
}
@@ -89,6 +94,7 @@ void RemoteServerConnection::run()
if (!receivePacket(&socket, &cmd, &data)) {
socket.waitForReadyRead(250);
+ qApp->processEvents();
continue;
}
@@ -142,11 +148,20 @@ void RemoteServerConnection::run()
if (m_process)
m_process->deleteLater();
m_process = new QProcess;
- m_signalReceiver = new QProcessSignalReceiver(m_process);
+ m_processSignalReceiver = new QProcessSignalReceiver(m_process);
} else if (type == QLatin1String(Protocol::QAbstractFileEngine)) {
if (m_engine)
delete m_engine;
m_engine = new QFSFileEngine;
+ } else if (type == QLatin1String(Protocol::AbstractArchive)) {
+#ifdef IFW_LIBARCHIVE
+ if (m_archive)
+ m_archive->deleteLater();
+ m_archive = new LibArchiveArchive;
+ m_archiveSignalReceiver = new AbstractArchiveSignalReceiver(static_cast<LibArchiveArchive *>(m_archive));
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
}
continue;
}
@@ -157,24 +172,44 @@ void RemoteServerConnection::run()
if (type == QLatin1String(Protocol::QSettings)) {
settings.reset();
} else if (type == QLatin1String(Protocol::QProcess)) {
- m_signalReceiver->m_receivedSignals.clear();
+ m_processSignalReceiver->m_receivedSignals.clear();
m_process->deleteLater();
m_process = nullptr;
} else if (type == QLatin1String(Protocol::QAbstractFileEngine)) {
delete m_engine;
m_engine = nullptr;
+ } else if (type == QLatin1String(Protocol::AbstractArchive)) {
+#ifdef IFW_LIBARCHIVE
+ m_archiveSignalReceiver->m_receivedSignals.clear();
+ m_archive->deleteLater();
+ m_archive = nullptr;
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
}
return;
}
if (command == QLatin1String(Protocol::GetQProcessSignals)) {
- if (m_signalReceiver) {
- QMutexLocker _(&m_signalReceiver->m_lock);
- sendData(&socket, m_signalReceiver->m_receivedSignals);
+ if (m_processSignalReceiver) {
+ QMutexLocker _(&m_processSignalReceiver->m_lock);
+ sendData(&socket, m_processSignalReceiver->m_receivedSignals);
socket.flush();
- m_signalReceiver->m_receivedSignals.clear();
+ m_processSignalReceiver->m_receivedSignals.clear();
}
continue;
+ } else if (command == QLatin1String(Protocol::GetAbstractArchiveSignals)) {
+#ifdef IFW_LIBARCHIVE
+ if (m_archiveSignalReceiver) {
+ QMutexLocker _(&m_archiveSignalReceiver->m_lock);
+ sendData(&socket, m_archiveSignalReceiver->m_receivedSignals);
+ socket.flush();
+ m_archiveSignalReceiver->m_receivedSignals.clear();
+ }
+ continue;
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
}
if (command.startsWith(QLatin1String(Protocol::QProcess))) {
@@ -183,6 +218,8 @@ void RemoteServerConnection::run()
handleQSettings(&socket, command, stream, settings.data());
} else if (command.startsWith(QLatin1String(Protocol::QAbstractFileEngine))) {
handleQFSFileEngine(&socket, command, stream);
+ } else if (command.startsWith(QLatin1String(Protocol::AbstractArchive))) {
+ handleArchive(&socket, command, stream);
} else {
qCDebug(QInstaller::lcServer) << "Unknown command:" << command;
}
@@ -519,4 +556,56 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
}
}
+void RemoteServerConnection::handleArchive(QIODevice *socket, const QString &command, QDataStream &data)
+{
+#ifdef IFW_LIBARCHIVE
+ LibArchiveArchive *archive = static_cast<LibArchiveArchive *>(m_archive);
+ if (command == QLatin1String(Protocol::AbstractArchiveOpen)) {
+ qint32 openMode;
+ data >> openMode;
+ sendData(socket, archive->open(static_cast<QIODevice::OpenMode>(openMode)));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveClose)) {
+ archive->close();
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetFilename)) {
+ QString fileName;
+ data >> fileName;
+ archive->setFilename(fileName);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveErrorString)) {
+ sendData(socket, archive->errorString());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveExtract)) {
+ QString dirPath;
+ quint64 total;
+ data >> dirPath;
+ data >> total;
+ archive->workerExtract(dirPath, total);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveCreate)) {
+ QStringList entries;
+ data >> entries;
+ sendData(socket, archive->create(entries));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveList)) {
+ sendData(socket, archive->list());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveIsSupported)) {
+ sendData(socket, archive->isSupported());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetCompressionLevel)) {
+ qint32 level;
+ data >> level;
+ archive->setCompressionLevel(static_cast<AbstractArchive::CompressionLevel>(level));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveAddDataBlock)) {
+ QByteArray buff;
+ data >> buff;
+ archive->workerAddDataBlock(buff);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd)) {
+ archive->workerSetDataAtEnd();
+ } else if (command == QLatin1String(Protocol::AbstractArchiveWorkerStatus)) {
+ sendData(socket, static_cast<qint32>(archive->workerStatus()));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveCancel)) {
+ archive->workerCancel();
+ } else if (!command.isEmpty()) {
+ qCDebug(QInstaller::lcServer) << "Unknown AbstractArchive command:" << command;
+ }
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/remoteserverconnection.h b/src/libs/installer/remoteserverconnection.h
index 07cfb98c0..ccb8e153d 100644
--- a/src/libs/installer/remoteserverconnection.h
+++ b/src/libs/installer/remoteserverconnection.h
@@ -29,6 +29,8 @@
#ifndef REMOTESERVERCONNECTION_H
#define REMOTESERVERCONNECTION_H
+#include "abstractarchive.h"
+
#include <QPointer>
#include <QThread>
@@ -44,6 +46,7 @@ namespace QInstaller {
class PermissionSettings;
class QProcessSignalReceiver;
+class AbstractArchiveSignalReceiver;
class RemoteServerConnection : public QThread
{
@@ -66,14 +69,17 @@ private:
void handleQSettings(QIODevice *device, const QString &command, QDataStream &data,
PermissionSettings *settings);
void handleQFSFileEngine(QIODevice *device, const QString &command, QDataStream &data);
+ void handleArchive(QIODevice *device, const QString &command, QDataStream &data);
private:
qintptr m_socketDescriptor;
QProcess *m_process;
QFSFileEngine *m_engine;
+ AbstractArchive *m_archive;
QString m_authorizationKey;
- QProcessSignalReceiver *m_signalReceiver;
+ QProcessSignalReceiver *m_processSignalReceiver;
+ AbstractArchiveSignalReceiver *m_archiveSignalReceiver;
};
} // namespace QInstaller
diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h
index dad5f4133..dc6d794b6 100644
--- a/src/libs/installer/remoteserverconnection_p.h
+++ b/src/libs/installer/remoteserverconnection_p.h
@@ -30,6 +30,9 @@
#define REMOTESERVERCONNECTION_P_H
#include "protocol.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivearchive.h"
+#endif
#include <QMutex>
#include <QProcess>
@@ -124,6 +127,60 @@ private:
QVariantList m_receivedSignals;
};
+#ifdef IFW_LIBARCHIVE
+class AbstractArchiveSignalReceiver : public QObject
+{
+ Q_OBJECT
+ friend class RemoteServerConnection;
+
+private:
+ explicit AbstractArchiveSignalReceiver(LibArchiveArchive *archive)
+ : QObject(archive)
+ {
+ connect(archive, &LibArchiveArchive::currentEntryChanged,
+ this, &AbstractArchiveSignalReceiver::onCurrentEntryChanged);
+ connect(archive, &LibArchiveArchive::completedChanged,
+ this, &AbstractArchiveSignalReceiver::onCompletedChanged);
+ connect(archive, &LibArchiveArchive::dataBlockRequested,
+ this, &AbstractArchiveSignalReceiver::onDataBlockRequested);
+ connect(archive, &LibArchiveArchive::workerFinished,
+ this, &AbstractArchiveSignalReceiver::onWorkerFinished);
+ }
+
+private Q_SLOTS:
+ void onCurrentEntryChanged(const QString &filename)
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalCurrentEntryChanged));
+ m_receivedSignals.append(filename);
+ }
+
+ void onCompletedChanged(quint64 completed, quint64 total)
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalCompletedChanged));
+ m_receivedSignals.append(completed);
+ m_receivedSignals.append(total);
+ }
+
+ void onDataBlockRequested()
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalDataBlockRequested));
+ }
+
+ void onWorkerFinished()
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalWorkerFinished));
+ }
+
+private:
+ QMutex m_lock;
+ QVariantList m_receivedSignals;
+};
+#endif
+
} // namespace QInstaller
#endif // REMOTESERVERCONNECTION_P_H