From b871f6d3557c20fc7d1456e5080c33ea90795c84 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Sun, 2 Oct 2022 02:45:09 +0200 Subject: Generate QXmppUpload/Download only by FileSharingManager Previously all the providers had to subclass the QXmppUpload/Download. It should be much easier to do additional tasks (e.g. hashing after downloading) now because the manager (and not the provider) decides when to emit the finished signal. Making the encrypted source provider able to handle arbitrary unencrypted sources should be possible too. --- src/client/QXmppHttpFileSharingProvider.cpp | 218 +++++++++++----------------- 1 file changed, 83 insertions(+), 135 deletions(-) (limited to 'src/client/QXmppHttpFileSharingProvider.cpp') diff --git a/src/client/QXmppHttpFileSharingProvider.cpp b/src/client/QXmppHttpFileSharingProvider.cpp index 8222dab1..4b7cd37e 100644 --- a/src/client/QXmppHttpFileSharingProvider.cpp +++ b/src/client/QXmppHttpFileSharingProvider.cpp @@ -1,15 +1,15 @@ // SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert +// SPDX-FileCopyrightText: 2022 Linus Jahn // // SPDX-License-Identifier: LGPL-2.1-or-later #include "QXmppHttpFileSharingProvider.h" #include "QXmppClient.h" -#include "QXmppDownload.h" +#include "QXmppFileMetadata.h" +#include "QXmppFutureUtils_p.h" #include "QXmppHttpUploadManager.h" -#include "QXmppUpload.h" #include "QXmppUtils.h" -#include "QXmppUtils_p.h" #include #include @@ -25,96 +25,6 @@ using namespace QXmpp::Private; /// \since QXmpp 1.5 /// -class QXmppSfsHttpUpload : public QXmppUpload -{ - Q_OBJECT - -public: - QXmppSfsHttpUpload(std::shared_ptr &&httpUpload) - : inner(std::move(httpUpload)) - { - connect(inner.get(), &QXmppHttpUpload::finished, this, [this](const QXmppHttpUpload::Result &result) { - Q_EMIT uploadFinished(std::visit([](auto &&value) -> QXmpp::Private::UploadResult { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return std::any(QXmppHttpFileSource(value)); - } else if constexpr (std::is_same_v) { - return Cancelled {}; - } else if constexpr (std::is_same_v) { - return value; - } - }, - result)); - }); - connect(inner.get(), &QXmppHttpUpload::progressChanged, - this, &QXmppSfsHttpUpload::progressChanged); - } - - float progress() override { return inner->progress(); } - void cancel() override { inner->cancel(); } - bool isFinished() override { return inner->isFinished(); } - quint64 bytesTransferred() override { return inner->bytesSent(); } - quint64 bytesTotal() override { return inner->bytesTotal(); } - -private: - std::shared_ptr inner; -}; - -class QXmppHttpDownload : public QXmppDownload -{ - float progress() override - { - return calculateProgress(m_bytesSent, m_bytesTotal); - } - - void cancel() override - { - m_aborted = true; - reply->abort(); - } - - bool isFinished() override - { - return m_isFinished; - } - - quint64 bytesTransferred() override - { - return m_bytesSent; - } - - quint64 bytesTotal() override - { - return m_bytesTotal; - } - -public: - void reportProgress(quint64 bytesSent, quint64 bytesTotal) - { - m_bytesSent = bytesSent; - m_bytesTotal = bytesTotal; - Q_EMIT progressChanged(); - } - - void reportFinished(Result &&result) - { - Q_EMIT finished(result); - m_isFinished = true; - } - - [[nodiscard]] bool aborted() const - { - return m_aborted; - } - -private: - QNetworkReply *reply = nullptr; - quint64 m_bytesSent = 0; - quint64 m_bytesTotal = 0; - bool m_isFinished = false; - bool m_aborted = false; -}; - class QXmppHttpFileSharingProviderPrivate { public: @@ -130,7 +40,6 @@ public: QXmppHttpFileSharingProvider::QXmppHttpFileSharingProvider(QXmppClient *client, QNetworkAccessManager *netManager) : d(std::make_unique()) { - qRegisterMetaType(); Q_ASSERT(client); d->manager = client->findExtension(); Q_ASSERT(d->manager); @@ -139,77 +48,116 @@ QXmppHttpFileSharingProvider::QXmppHttpFileSharingProvider(QXmppClient *client, QXmppHttpFileSharingProvider::~QXmppHttpFileSharingProvider() = default; -auto QXmppHttpFileSharingProvider::downloadFile(const std::any &source, std::unique_ptr &&target) - -> std::shared_ptr +auto QXmppHttpFileSharingProvider::downloadFile(const std::any &source, + std::unique_ptr target, + std::function reportProgress, + std::function reportFinished) + -> std::shared_ptr { + struct State : Download + { + ~State() override = default; + + QNetworkReply *reply = nullptr; + bool finished = false; + bool cancelled = false; + + void cancel() override + { + if (!cancelled) { + cancelled = true; + reply->abort(); + } + } + }; + QXmppHttpFileSource httpSource; try { httpSource = std::any_cast(source); } catch (const std::bad_any_cast &) { - qFatal("QXmppHttpFileSharingProvider::downloadFile can only handle QXmppHttpFileSharingProvider sources"); + qFatal("QXmppHttpFileSharingProvider::downloadFile can only handle QXmppHttpFileSource."); } - auto *reply = d->netManager->get(QNetworkRequest(httpSource.url())); - - auto download = std::make_shared(); - - QObject::connect(reply, &QNetworkReply::finished, [=]() { - if (reply->error() != QNetworkReply::NoError) { - download->reportFinished(QXmppError::fromIoDevice(*reply)); - } else if (download->aborted()) { - download->reportFinished(Cancelled()); - } else { - download->reportFinished(Success()); + auto state = std::make_shared(); + state->reply = d->netManager->get(QNetworkRequest(httpSource.url())); + + QObject::connect(state->reply, &QNetworkReply::finished, [state, reportFinished = std::move(reportFinished)]() mutable { + Q_ASSERT(state); + + if (!state->finished) { + if (state->reply->error() != QNetworkReply::NoError) { + reportFinished(QXmppError::fromNetworkReply(*state->reply)); + } else if (state->cancelled) { + reportFinished(Cancelled()); + } else { + reportFinished(Success()); + } + state->finished = true; } - reply->deleteLater(); + state->reply->deleteLater(); + + // reduce ref count + state.reset(); }); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - QObject::connect(reply, &QNetworkReply::readyRead, [file = std::move(target), reply]() { + QObject::connect(state->reply, &QNetworkReply::readyRead, [file = std::move(target), reply = state->reply]() { file->write(reply->readAll()); }); #else auto file = std::shared_ptr(std::move(target)); - QObject::connect(reply, &QNetworkReply::readyRead, [file, reply]() { + QObject::connect(state->reply, &QNetworkReply::readyRead, [file, reply = state->reply]() { file->write(reply->readAll()); }); #endif - QObject::connect(reply, &QNetworkReply::downloadProgress, [=](qint64 bytesReceived, qint64 bytesTotal) { - download->reportProgress(bytesReceived, bytesTotal); - }); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - QObject::connect(reply, &QNetworkReply::errorOccurred, -#else - QObject::connect(reply, QOverload::of(&QNetworkReply::error), -#endif - [download, reply]() { - download->reportFinished(QXmppError::fromNetworkReply(*reply)); - }); - - QObject::connect(reply, &QNetworkReply::uploadProgress, [download](qint64 sent, qint64 total) { - quint64 sentBytes = sent < 0 ? 0 : quint64(sent); - quint64 totalBytes = total < 0 ? 0 : quint64(total); - download->reportProgress(sentBytes, totalBytes); + QObject::connect(state->reply, &QNetworkReply::downloadProgress, [stateRef = std::weak_ptr(state), reportProgress = std::move(reportProgress)](qint64 bytesReceived, qint64 bytesTotal) { + if (auto state = stateRef.lock()) { + if (!state->finished) { + reportProgress(bytesReceived, bytesTotal); + } + } }); - return download; + return std::dynamic_pointer_cast(state); } auto QXmppHttpFileSharingProvider::uploadFile(std::unique_ptr data, - const QXmppFileMetadata &info) - -> std::shared_ptr + const QXmppFileMetadata &info, + std::function reportProgress, + std::function reportFinished) + -> std::shared_ptr { + struct State : Upload + { + ~State() override = default; + + std::shared_ptr upload; + void cancel() override { upload->cancel(); } + }; + Q_ASSERT(d->manager); - auto upload = d->manager->uploadFile( + auto state = std::make_shared(); + state->upload = d->manager->uploadFile( std::move(data), info.filename().value_or(QXmppUtils::generateStanzaHash(10)), info.mediaType().value_or(QMimeDatabase().mimeTypeForName("application/octet-stream")), info.size() ? info.size().value() : -1); - return std::make_shared(std::move(upload)); -} + QObject::connect(state->upload.get(), &QXmppHttpUpload::finished, [state, reportFinished = std::move(reportFinished)](const QXmppHttpUpload::Result &result) mutable { + reportFinished(visitForward(result, [](QUrl url) { + return std::any(QXmppHttpFileSource(std::move(url))); + })); -#include "QXmppHttpFileSharingProvider.moc" + // reduce ref count, so the signal connection doesn't keep the state alive forever + state.reset(); + }); + QObject::connect(state->upload.get(), &QXmppHttpUpload::progressChanged, [stateRef = std::weak_ptr(state), reportProgress = std::move(reportProgress)]() { + if (auto state = stateRef.lock()) { + reportProgress(state->upload->bytesSent(), state->upload->bytesTotal()); + } + }); + + return std::dynamic_pointer_cast(state); +} -- cgit v1.2.3