diff options
Diffstat (limited to 'src/libs/installer/libarchivewrapper_p.cpp')
-rw-r--r-- | src/libs/installer/libarchivewrapper_p.cpp | 405 |
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 |