From fa7637e7dc89eea896e2b6b229f570f52c5324e3 Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 1 Jun 2020 19:43:51 +0200 Subject: [PATCH] Do not use libcurl for reading local files We originally used libcurl for both downloading images from Internet and reading local files to have the same code path for both. It doesn't work that well in practice, as Qt and libcurl are not on the same page how special characters such as Chinese characters are represented in a local file URL. So create a new class to handle local files. Closes #76 --- CMakeLists.txt | 4 +-- downloadextractthread.cpp | 4 +-- downloadextractthread.h | 6 ++-- imagewriter.cpp | 7 +++- localfileextractthread.cpp | 74 ++++++++++++++++++++++++++++++++++++++ localfileextractthread.h | 28 +++++++++++++++ 6 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 localfileextractthread.cpp create mode 100644 localfileextractthread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cf2dfd0..2d10c32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(CMAKE_AUTORCC ON) # Adding headers explicity so they are displayed in Qt Creator set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h driveformatthread.h powersaveblocker.h - downloadthread.h downloadextractthread.h dependencies/mountutils/src/mountutils.hpp) + downloadthread.h downloadextractthread.h localfileextractthread.h dependencies/mountutils/src/mountutils.hpp) # Add dependencies if (APPLE) @@ -68,7 +68,7 @@ endif() set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp" "drivelistitem.cpp" "drivelistmodel.cpp" "downloadthread.cpp" "downloadextractthread.cpp" - "driveformatthread.cpp" "powersaveblocker.cpp" "qml.qrc") + "driveformatthread.cpp" "localfileextractthread.cpp" "powersaveblocker.cpp" "qml.qrc") find_package(Qt5 COMPONENTS Core Quick LinguistTools Svg OPTIONAL_COMPONENTS Widgets) if (Qt5Widgets_FOUND) diff --git a/downloadextractthread.cpp b/downloadextractthread.cpp index 1e69036..09399f2 100644 --- a/downloadextractthread.cpp +++ b/downloadextractthread.cpp @@ -382,12 +382,12 @@ int DownloadExtractThread::_on_close(struct archive *) // static callback functions that call object oriented equivalents ssize_t DownloadExtractThread::_archive_read(struct archive *a, void *client_data, const void **buff) { - return static_cast(client_data)->_on_read(a, buff); + return qobject_cast((QObject *) client_data)->_on_read(a, buff); } int DownloadExtractThread::_archive_close(struct archive *a, void *client_data) { - return static_cast(client_data)->_on_close(a); + return qobject_cast((QObject *) client_data)->_on_close(a); } bool DownloadExtractThread::isImage() diff --git a/downloadextractthread.h b/downloadextractthread.h index 1d7e6bf..e4744a0 100644 --- a/downloadextractthread.h +++ b/downloadextractthread.h @@ -48,13 +48,13 @@ protected: QByteArray _popQueue(); void _pushQueue(const char *data, size_t len); - void _cancelExtract(); + virtual void _cancelExtract(); virtual size_t _writeData(const char *buf, size_t len); virtual void _onDownloadSuccess(); virtual void _onDownloadError(const QString &msg); - ssize_t _on_read(struct archive *a, const void **buff); - int _on_close(struct archive *a); + virtual ssize_t _on_read(struct archive *a, const void **buff); + virtual int _on_close(struct archive *a); static ssize_t _archive_read(struct archive *a, void *client_data, const void **buff); static int _archive_close(struct archive *a, void *client_data); diff --git a/imagewriter.cpp b/imagewriter.cpp index e0d6e40..f9bf421 100644 --- a/imagewriter.cpp +++ b/imagewriter.cpp @@ -8,6 +8,7 @@ #include "downloadextractthread.h" #include "dependencies/drivelist/src/drivelist.hpp" #include "driveformatthread.h" +#include "localfileextractthread.h" #include #include #include @@ -182,7 +183,11 @@ void ImageWriter::startWrite() urlstr = QUrl::fromLocalFile(_cacheFileName).toString(_src.FullyEncoded).toLatin1(); } - if (compressed) + if (QUrl(urlstr).isLocalFile()) + { + _thread = new LocalFileExtractThread(urlstr, _dst.toLatin1(), _expectedHash, this); + } + else if (compressed) { _thread = new DownloadExtractThread(urlstr, _dst.toLatin1(), _expectedHash, this); } diff --git a/localfileextractthread.cpp b/localfileextractthread.cpp new file mode 100644 index 0000000..839def6 --- /dev/null +++ b/localfileextractthread.cpp @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi (Trading) Limited + */ + +#include "localfileextractthread.h" +#include "config.h" + +LocalFileExtractThread::LocalFileExtractThread(const QByteArray &url, const QByteArray &dst, const QByteArray &expectedHash, QObject *parent) + : DownloadExtractThread(url, dst, expectedHash, parent) +{ + _inputBuf = (char *) qMallocAligned(IMAGEWRITER_UNCOMPRESSED_BLOCKSIZE, 4096); +} + +LocalFileExtractThread::~LocalFileExtractThread() +{ + _cancelled = true; + wait(); + qFreeAligned(_inputBuf); +} + +void LocalFileExtractThread::_cancelExtract() +{ + _cancelled = true; + if (_inputfile.isOpen()) + _inputfile.close(); +} + +void LocalFileExtractThread::run() +{ + if (isImage() && !_openAndPrepareDevice()) + return; + + _timer.start(); + _inputfile.setFileName( QUrl(_url).toLocalFile() ); + if (!_inputfile.open(_inputfile.ReadOnly)) + { + _onDownloadError(tr("Error opening image file")); + return; + } + _lastDlTotal = _inputfile.size(); + + if (isImage()) + extractImageRun(); + else + extractMultiFileRun(); + +} + +ssize_t LocalFileExtractThread::_on_read(struct archive *, const void **buff) +{ + if (_cancelled) + return -1; + + *buff = _inputBuf; + ssize_t len = _inputfile.read(_inputBuf, IMAGEWRITER_UNCOMPRESSED_BLOCKSIZE); + + if (len > 0) + { + _lastDlNow += len; + if (!_isImage) + { + _inputHash.addData(_inputBuf, len); + } + } + + return len; +} + +int LocalFileExtractThread::_on_close(struct archive *) +{ + _inputfile.close(); + return 0; +} diff --git a/localfileextractthread.h b/localfileextractthread.h new file mode 100644 index 0000000..4375e58 --- /dev/null +++ b/localfileextractthread.h @@ -0,0 +1,28 @@ +#ifndef LOCALFILEEXTRACTTHREAD_H +#define LOCALFILEEXTRACTTHREAD_H + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi (Trading) Limited + */ + +#include "downloadextractthread.h" +#include + +class LocalFileExtractThread : public DownloadExtractThread +{ + Q_OBJECT +public: + explicit LocalFileExtractThread(const QByteArray &url, const QByteArray &dst = "", const QByteArray &expectedHash = "", QObject *parent = nullptr); + virtual ~LocalFileExtractThread(); + +protected: + virtual void _cancelExtract(); + virtual void run(); + virtual ssize_t _on_read(struct archive *a, const void **buff); + virtual int _on_close(struct archive *a); + QFile _inputfile; + char *_inputBuf; +}; + +#endif // LOCALFILEEXTRACTTHREAD_H