summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/libarchivewrapper_p.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/libarchivewrapper_p.cpp')
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
new file mode 100644
index 000000000..b4325243d
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper_p.cpp
@@ -0,0 +1,405 @@
+/**************************************************************************
+**
+** 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
+*/
+
+/*!
+ \internal
+ \fn QInstaller::LibArchiveWrapperPrivate::dataBlockRequested()
+
+ Emitted when the server process has requested another data block.
+*/
+
+/*!
+ \internal
+ \fn QInstaller::LibArchiveWrapperPrivate::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()
+{
+}
+
+/*!
+ 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();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilename), filename);
+ 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)
+{
+ const quint64 total = totalFiles ? totalFiles : m_archive.totalFiles();
+ if (connectToServer()) {
+ QTimer timer;
+ connect(&timer, &QTimer::timeout, this, &LibArchiveWrapperPrivate::processSignals);
+ timer.start();
+
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
+ m_lock.unlock();
+ {
+ QEventLoop loop;
+ connect(this, &LibArchiveWrapperPrivate::remoteWorkerFinished, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ timer.stop();
+ return (workerStatus() == ExtractWorker::Success);
+ }
+ return m_archive.extract(dirPath, total);
+}
+
+/*!
+ 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();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level);
+ 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();
+ callRemoteMethodDefaultReply(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::AbstractArchiveSignalSeekRequested)) {
+ const qint64 offset = receivedSignals.takeFirst().value<qint64>();
+ const int whence = receivedSignals.takeFirst().value<int>();
+ emit seekRequested(offset, whence);
+ } 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 = 1024 * 1024; // 1MB
+
+ 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);
+}
+
+/*!
+ Seeks to specified \a offset in the underlying file device. Possible \a whence
+ values are \c SEEK_SET, \c SEEK_CUR, and \c SEEK_END.
+*/
+void LibArchiveWrapperPrivate::onSeekRequested(qint64 offset, int whence)
+{
+ QFile *const file = &m_archive.m_data->file;
+ if (!file->isOpen() || file->isSequential()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientFilePosition(ARCHIVE_FATAL);
+ return;
+ }
+ bool success = false;
+ switch (whence) {
+ case SEEK_SET: // moves file pointer position to the beginning of the file
+ success = file->seek(offset);
+ break;
+ case SEEK_CUR: // moves file pointer position to given location
+ success = file->seek(file->pos() + offset);
+ break;
+ case SEEK_END: // moves file pointer position to the end of file
+ success = file->seek(file->size() + offset);
+ break;
+ default:
+ break;
+ }
+ setClientFilePosition(success ? file->pos() : ARCHIVE_FATAL);
+}
+
+/*!
+ Connects handler signals for the matching signals of the wrapper object.
+*/
+void LibArchiveWrapperPrivate::init()
+{
+ 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);
+ QObject::connect(this, &LibArchiveWrapperPrivate::seekRequested,
+ this, &LibArchiveWrapperPrivate::onSeekRequested);
+}
+
+/*!
+ Calls a remote method to add a \a buffer for reading.
+*/
+void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer);
+ 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();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to set new file position \a pos.
+*/
+void LibArchiveWrapperPrivate::setClientFilePosition(qint64 pos)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos);
+ 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