aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/QXmppEncryptedHttpFileSharingProvider.cpp88
-rw-r--r--src/client/QXmppEncryptedHttpFileSharingProvider.h12
-rw-r--r--src/client/QXmppFileSharingManager.cpp198
-rw-r--r--src/client/QXmppFileSharingManager.h4
-rw-r--r--src/client/QXmppFileSharingProvider.h52
-rw-r--r--src/client/QXmppHttpFileSharingProvider.cpp218
-rw-r--r--src/client/QXmppHttpFileSharingProvider.h10
-rw-r--r--src/client/QXmppUpload.cpp2
-rw-r--r--src/client/QXmppUpload.h13
9 files changed, 325 insertions, 272 deletions
diff --git a/src/client/QXmppEncryptedHttpFileSharingProvider.cpp b/src/client/QXmppEncryptedHttpFileSharingProvider.cpp
index b6e82621..82aa351e 100644
--- a/src/client/QXmppEncryptedHttpFileSharingProvider.cpp
+++ b/src/client/QXmppEncryptedHttpFileSharingProvider.cpp
@@ -6,8 +6,9 @@
#include "QXmppClient.h"
#include "QXmppFileEncryption.h"
+#include "QXmppFileMetadata.h"
+#include "QXmppFutureUtils_p.h"
#include "QXmppHttpUploadManager.h"
-#include "QXmppUpload.h"
#include "QXmppUtils.h"
#include "QcaInitializer_p.h"
@@ -16,43 +17,6 @@
using namespace QXmpp;
using namespace QXmpp::Private;
-class QXmppSfsEncryptedHttpUpload : public QXmppUpload
-{
- Q_OBJECT
-
-public:
- QXmppSfsEncryptedHttpUpload(std::shared_ptr<QXmppHttpUpload> &&httpUpload, const QByteArray &key, const QByteArray &iv)
- : inner(std::move(httpUpload))
- {
- connect(inner.get(), &QXmppHttpUpload::finished, this, [=](const QXmppHttpUpload::Result &result) {
- Q_EMIT uploadFinished(std::visit([=](auto &&value) -> QXmpp::Private::UploadResult {
- using T = std::decay_t<decltype(value)>;
- if constexpr (std::is_same_v<T, QUrl>) {
- QXmppEncryptedFileSource encryptedSource;
- encryptedSource.setKey(key);
- encryptedSource.setIv(iv);
- encryptedSource.setHttpSources({ QXmppHttpFileSource(value) });
- return std::any(encryptedSource);
- } else if constexpr (std::is_same_v<T, Cancelled>) {
- return Cancelled {};
- } else if constexpr (std::is_same_v<T, QXmppError>) {
- return value;
- }
- },
- result));
- });
- }
-
- 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<QXmppHttpUpload> inner;
-};
-
///
/// \class QXmppEncryptedHttpFileSharingProvider
///
@@ -65,7 +29,6 @@ class QXmppEncryptedHttpFileSharingProviderPrivate
{
public:
QXmpp::Private::QcaInitializer init;
- QXmppHttpUploadManager *manager;
QXmppHttpFileSharingProvider *httpProvider;
};
@@ -77,16 +40,16 @@ public:
QXmppEncryptedHttpFileSharingProvider::QXmppEncryptedHttpFileSharingProvider(QXmppClient *client, QNetworkAccessManager *netManager)
: d(std::make_unique<QXmppEncryptedHttpFileSharingProviderPrivate>())
{
- qRegisterMetaType<QXmpp::Private::UploadResult>();
- Q_ASSERT(client);
- d->manager = client->findExtension<QXmppHttpUploadManager>();
- Q_ASSERT(d->manager);
d->httpProvider = new QXmppHttpFileSharingProvider(client, netManager);
}
QXmppEncryptedHttpFileSharingProvider::~QXmppEncryptedHttpFileSharingProvider() = default;
-std::shared_ptr<QXmppDownload> QXmppEncryptedHttpFileSharingProvider::downloadFile(const std::any &source, std::unique_ptr<QIODevice> &&target)
+auto QXmppEncryptedHttpFileSharingProvider::downloadFile(const std::any &source,
+ std::unique_ptr<QIODevice> target,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(DownloadResult)> reportFinished)
+ -> std::shared_ptr<Download>
{
QXmppEncryptedFileSource encryptedSource;
try {
@@ -97,12 +60,14 @@ std::shared_ptr<QXmppDownload> QXmppEncryptedHttpFileSharingProvider::downloadFi
auto httpSource = encryptedSource.httpSources().front();
auto output = std::make_unique<Encryption::DecryptionDevice>(std::move(target), encryptedSource.cipher(), encryptedSource.iv(), encryptedSource.key());
- return d->httpProvider->downloadFile(httpSource, std::move(output));
+ return d->httpProvider->downloadFile(httpSource, std::move(output), std::move(reportProgress), std::move(reportFinished));
}
-std::shared_ptr<QXmppUpload> QXmppEncryptedHttpFileSharingProvider::uploadFile(
- std::unique_ptr<QIODevice> data,
- const QXmppFileMetadata &info)
+auto QXmppEncryptedHttpFileSharingProvider::uploadFile(std::unique_ptr<QIODevice> data,
+ const QXmppFileMetadata &,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(UploadResult)> reportFinished)
+ -> std::shared_ptr<Upload>
{
auto cipher = Aes256CbcPkcs7;
auto key = Encryption::generateKey(cipher);
@@ -111,13 +76,24 @@ std::shared_ptr<QXmppUpload> QXmppEncryptedHttpFileSharingProvider::uploadFile(
auto encDevice = std::make_unique<Encryption::EncryptionDevice>(std::move(data), cipher, key, iv);
auto encryptedSize = encDevice->size();
- auto upload = d->manager->uploadFile(
- std::move(encDevice),
- QXmppUtils::generateStanzaHash(10),
- QMimeDatabase().mimeTypeForName("application/octet-stream"),
- encryptedSize);
+ QXmppFileMetadata metadata;
+ metadata.setFilename(QXmppUtils::generateStanzaHash(10));
+ metadata.setMediaType(QMimeDatabase().mimeTypeForName("application/octet-stream"));
+ metadata.setSize(encryptedSize);
- return std::make_shared<QXmppSfsEncryptedHttpUpload>(std::move(upload), key, iv);
+ return d->httpProvider->uploadFile(
+ std::move(encDevice),
+ metadata,
+ std::move(reportProgress),
+ [=, reportFinished = std::move(reportFinished)](UploadResult result) {
+ auto encryptedResult = visitForward<UploadResult>(std::move(result), [&](std::any httpSourceAny) {
+ QXmppEncryptedFileSource encryptedSource;
+ encryptedSource.setKey(key);
+ encryptedSource.setIv(iv);
+ encryptedSource.setHttpSources({ std::any_cast<QXmppHttpFileSource>(std::move(httpSourceAny)) });
+
+ return encryptedSource;
+ });
+ reportFinished(std::move(encryptedResult));
+ });
}
-
-#include "QXmppEncryptedHttpFileSharingProvider.moc"
diff --git a/src/client/QXmppEncryptedHttpFileSharingProvider.h b/src/client/QXmppEncryptedHttpFileSharingProvider.h
index 6c43d8cd..8f018c71 100644
--- a/src/client/QXmppEncryptedHttpFileSharingProvider.h
+++ b/src/client/QXmppEncryptedHttpFileSharingProvider.h
@@ -21,10 +21,14 @@ public:
~QXmppEncryptedHttpFileSharingProvider() override;
auto downloadFile(const std::any &source,
- std::unique_ptr<QIODevice> &&target) -> std::shared_ptr<QXmppDownload> override;
- auto uploadFile(
- std::unique_ptr<QIODevice> data,
- const QXmppFileMetadata &info) -> std::shared_ptr<QXmppUpload> override;
+ std::unique_ptr<QIODevice> target,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(DownloadResult)> reportFinished) -> std::shared_ptr<Download> override;
+
+ auto uploadFile(std::unique_ptr<QIODevice> source,
+ const QXmppFileMetadata &info,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(UploadResult)> reportFinished) -> std::shared_ptr<Upload> override;
private:
std::unique_ptr<QXmppEncryptedHttpFileSharingProviderPrivate> d;
diff --git a/src/client/QXmppFileSharingManager.cpp b/src/client/QXmppFileSharingManager.cpp
index 54e29086..370b3094 100644
--- a/src/client/QXmppFileSharingManager.cpp
+++ b/src/client/QXmppFileSharingManager.cpp
@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im>
+// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
@@ -15,9 +16,11 @@
#include "QXmppThumbnail.h"
#include "QXmppUpload.h"
#include "QXmppUploadRequestManager.h"
+#include "QXmppUtils_p.h"
#include <any>
#include <unordered_map>
+#include <utility>
#include <QFile>
#include <QFileInfo>
@@ -45,6 +48,74 @@ static std::vector<HashAlgorithm> hashAlgorithms()
};
}
+class UploadImpl : public QXmppUpload
+{
+ Q_OBJECT
+public:
+ float progress() override { return calculateProgress(m_bytesSent, m_bytesTotal); }
+ void cancel() override
+ {
+ if (m_providerUpload) {
+ m_providerUpload->cancel();
+ }
+ m_metadataFuture.cancel();
+ m_hashesFuture.cancel();
+ }
+ bool isFinished() override { return m_finished; }
+ quint64 bytesTransferred() override { return m_bytesSent; }
+ quint64 bytesTotal() override { return m_bytesTotal; }
+
+ void reportFinished(Result result)
+ {
+ if (!m_finished) {
+ m_finished = true;
+ Q_EMIT finished(std::move(result));
+ }
+ }
+
+ std::shared_ptr<QXmppFileSharingProvider::Upload> m_providerUpload;
+ QFuture<std::shared_ptr<MetadataGeneratorResult>> m_metadataFuture;
+ QFuture<HashingResultPtr> m_hashesFuture;
+ QXmppFileMetadata m_metadata;
+ QXmppBitsOfBinaryDataList m_dataBlobs;
+ std::any m_source;
+ quint64 m_bytesSent = 0;
+ quint64 m_bytesTotal = 0;
+ bool m_finished = false;
+};
+
+class DownloadImpl : public QXmppDownload
+{
+ float progress() override { return calculateProgress(m_bytesReceived, m_bytesTotal); }
+ void cancel() override
+ {
+ if (m_providerDownload) {
+ m_providerDownload->cancel();
+ }
+ }
+ bool isFinished() override { return m_finished; }
+ quint64 bytesTransferred() override { return m_bytesReceived; }
+ quint64 bytesTotal() override { return m_bytesTotal; }
+
+public:
+ void reportProgress(quint64 bytesReceived, quint64 bytesTotal)
+ {
+ m_bytesReceived = bytesReceived;
+ m_bytesTotal = bytesTotal;
+ Q_EMIT progressChanged();
+ }
+ void reportFinished(Result result)
+ {
+ m_finished = true;
+ Q_EMIT finished(std::move(result));
+ }
+
+ std::shared_ptr<QXmppFileSharingProvider::Download> m_providerDownload;
+ quint64 m_bytesReceived = 0;
+ quint64 m_bytesTotal = 0;
+ bool m_finished = false;
+};
+
class QXmppFileSharingManagerPrivate
{
public:
@@ -112,89 +183,105 @@ std::shared_ptr<QXmppUpload> QXmppFileSharingManager::sendFile(std::shared_ptr<Q
const QString &filePath,
const std::optional<QString> &description)
{
- std::shared_ptr<QXmppUpload> upload;
-
QFileInfo fileInfo(filePath);
auto metadata = QXmppFileMetadata::fromFileInfo(fileInfo);
- if (description) {
- metadata.setDescription(description);
- }
+ metadata.setDescription(description);
+
+ auto upload = std::make_shared<UploadImpl>();
+ upload->m_metadata = std::move(metadata);
auto openFile = [=]() -> std::unique_ptr<QIODevice> {
auto device = std::make_unique<QFile>(fileInfo.absoluteFilePath());
if (!device->open(QIODevice::ReadOnly)) {
- Q_EMIT upload->finished(QXmppError::fromIoDevice(*device));
+ upload->reportFinished(QXmppError::fromIoDevice(*device));
}
return device;
};
- auto metadataFuture = d->metadataGenerator(openFile());
- auto hashesFuture = calculateHashes(openFile(), hashAlgorithms());
+ auto metadataIoDevice = openFile();
+ auto hashesIoDevice = openFile();
+ auto uploadIoDevice = openFile();
- upload = provider->uploadFile(openFile(), metadata);
- upload->metadata = metadata;
+ if (upload->m_finished) {
+ // error occurred while opening file
+ return upload;
+ }
- connect(upload.get(), &QXmppUpload::uploadFinished, this, [=](auto &&uploadResult) {
+ upload->m_metadataFuture = d->metadataGenerator(std::move(metadataIoDevice));
+ upload->m_hashesFuture = calculateHashes(std::move(hashesIoDevice), hashAlgorithms());
+
+ auto onProgress = [upload](quint64 sent, quint64 total) {
+ upload->m_bytesSent = sent;
+ upload->m_bytesTotal = total;
+ Q_EMIT upload->progressChanged();
+ };
+ auto onFinished = [this, upload](QXmppFileSharingProvider::UploadResult uploadResult) {
+ // free memory
+ upload->m_providerUpload.reset();
if (std::holds_alternative<std::any>(uploadResult)) {
- auto source = std::get<std::any>(uploadResult);
- await(metadataFuture, this, [=](auto &&result) mutable {
+ upload->m_source = std::get<std::any>(std::move(uploadResult));
+ await(upload->m_metadataFuture, this, [this, upload](auto &&result) mutable {
if (result->dimensions) {
- upload->metadata.setWidth(result->dimensions->width());
- upload->metadata.setHeight(result->dimensions->height());
+ upload->m_metadata.setWidth(result->dimensions->width());
+ upload->m_metadata.setHeight(result->dimensions->height());
}
if (result->length) {
- upload->metadata.setLength(*result->length);
+ upload->m_metadata.setLength(*result->length);
}
- QVector<QXmppThumbnail> thumbnails;
- thumbnails.reserve(result->thumbnails.size());
- QXmppBitsOfBinaryDataList dataBlobs;
- dataBlobs.reserve(result->thumbnails.size());
+ if (!result->thumbnails.empty()) {
+ QVector<QXmppThumbnail> thumbnails;
+ thumbnails.reserve(result->thumbnails.size());
+ upload->m_dataBlobs.reserve(result->thumbnails.size());
- for (const auto &metadataThumb : result->thumbnails) {
- auto bobData = QXmppBitsOfBinaryData::fromByteArray(metadataThumb.data);
- bobData.setContentType(metadataThumb.mimeType);
+ for (const auto &metadataThumb : result->thumbnails) {
+ auto bobData = QXmppBitsOfBinaryData::fromByteArray(metadataThumb.data);
+ bobData.setContentType(metadataThumb.mimeType);
- QXmppThumbnail thumbnail;
- thumbnail.setHeight(metadataThumb.height);
- thumbnail.setWidth(metadataThumb.width);
- thumbnail.setMediaType(metadataThumb.mimeType);
- thumbnail.setUri(bobData.cid().toCidUrl());
+ QXmppThumbnail thumbnail;
+ thumbnail.setHeight(metadataThumb.height);
+ thumbnail.setWidth(metadataThumb.width);
+ thumbnail.setMediaType(metadataThumb.mimeType);
+ thumbnail.setUri(bobData.cid().toCidUrl());
- thumbnails.append(std::move(thumbnail));
- dataBlobs.append(std::move(bobData));
+ thumbnails.append(std::move(thumbnail));
+ upload->m_dataBlobs.append(std::move(bobData));
+ }
+ upload->m_metadata.setThumbnails(thumbnails);
}
- upload->metadata.setThumbnails(thumbnails);
-
- await(hashesFuture, this, [=, dataBlobs = std::move(dataBlobs)](const auto &hashResult) mutable {
- QVector<QXmppHash> hashes;
- const auto hashValue = hashResult->result;
- if (std::holds_alternative<Cancelled>(hashValue)) {
- Q_EMIT upload->finished(Cancelled {});
- } else if (std::holds_alternative<std::vector<QXmppHash>>(hashValue)) {
- auto hashesVector = std::get<std::vector<QXmppHash>>(hashValue);
+
+ await(upload->m_hashesFuture, this, [upload](auto hashResult) mutable {
+ auto &hashValue = hashResult->result;
+ if (std::holds_alternative<std::vector<QXmppHash>>(hashValue)) {
+ const auto &hashesVector = std::get<std::vector<QXmppHash>>(hashValue);
+ QVector<QXmppHash> hashes;
+ hashes.reserve(hashesVector.size());
std::transform(hashesVector.begin(), hashesVector.end(),
std::back_inserter(hashes), [](auto &&hash) {
return hash;
});
-
- upload->metadata.setHashes(hashes);
+ upload->m_metadata.setHashes(hashes);
QXmppFileShare fs;
- fs.setMetadata(upload->metadata);
- fs.addSource(std::move(source));
+ fs.setMetadata(upload->m_metadata);
+ fs.addSource(upload->m_source);
- Q_EMIT upload->finished(QXmppUpload::FileResult { fs, std::move(dataBlobs) });
+ upload->reportFinished(QXmppUpload::FileResult { fs, std::move(upload->m_dataBlobs) });
+ } else if (std::holds_alternative<Cancelled>(hashValue)) {
+ upload->reportFinished(Cancelled());
} else if (std::holds_alternative<QXmppError>(hashValue)) {
- Q_EMIT upload->finished(std::get<QXmppError>(hashValue));
+ upload->reportFinished(std::get<QXmppError>(std::move(hashValue)));
}
});
});
+ } else if (std::holds_alternative<Cancelled>(uploadResult)) {
+ upload->reportFinished(Cancelled());
} else if (std::holds_alternative<QXmppError>(uploadResult)) {
- Q_EMIT upload->finished(std::get<QXmppError>(uploadResult));
+ upload->reportFinished(std::get<QXmppError>(std::move(uploadResult)));
}
- });
+ };
+ upload->m_providerUpload = provider->uploadFile(std::move(uploadIoDevice), upload->m_metadata, std::move(onProgress), std::move(onFinished));
return upload;
}
@@ -213,14 +300,21 @@ std::shared_ptr<QXmppUpload> QXmppFileSharingManager::sendFile(std::shared_ptr<Q
///
std::shared_ptr<QXmppDownload> QXmppFileSharingManager::downloadFile(
const QXmppFileShare &fileShare,
- std::unique_ptr<QIODevice> &&output)
+ std::unique_ptr<QIODevice> output)
{
- std::shared_ptr<QXmppDownload> download;
+ auto download = std::make_shared<DownloadImpl>();
+
+ auto onProgress = [download](quint64 received, quint64 total) {
+ download->reportProgress(received, total);
+ };
+ auto onFinished = [download](QXmppFileSharingProvider::DownloadResult result) {
+ download->reportFinished(std::move(result));
+ };
+
fileShare.visitSources([&](const std::any &source) {
std::type_index index(source.type());
try {
- auto provider = d->providers.at(index);
- download = provider->downloadFile(source, std::move(output));
+ download->m_providerDownload = d->providers.at(index)->downloadFile(source, std::move(output), std::move(onProgress), std::move(onFinished));
return true;
} catch (const std::out_of_range &) {
return false;
@@ -234,3 +328,5 @@ void QXmppFileSharingManager::internalRegisterProvider(std::type_index index, st
{
d->providers.insert_or_assign(index, provider);
}
+
+#include "QXmppFileSharingManager.moc"
diff --git a/src/client/QXmppFileSharingManager.h b/src/client/QXmppFileSharingManager.h
index 20a1cdc4..05039f79 100644
--- a/src/client/QXmppFileSharingManager.h
+++ b/src/client/QXmppFileSharingManager.h
@@ -6,7 +6,6 @@
#define QXMPPFILESHARINGMANAGER_H
#include "QXmppClientExtension.h"
-#include "QXmppFileShare.h"
#include "QXmppFileSharingProvider.h"
#include "QXmppGlobal.h"
@@ -21,6 +20,7 @@
class QIODevice;
class QXmppFileMetadata;
+class QXmppFileShare;
class QXmppFileSharingManagerPrivate;
class QXMPP_EXPORT QXmppFileSharingManager : public QXmppClientExtension
@@ -66,7 +66,7 @@ public:
const std::optional<QString> &description = {});
std::shared_ptr<QXmppDownload> downloadFile(const QXmppFileShare &fileShare,
- std::unique_ptr<QIODevice> &&output);
+ std::unique_ptr<QIODevice> output);
private:
void internalRegisterProvider(std::type_index, std::shared_ptr<QXmppFileSharingProvider> provider);
diff --git a/src/client/QXmppFileSharingProvider.h b/src/client/QXmppFileSharingProvider.h
index cde719db..02e6252a 100644
--- a/src/client/QXmppFileSharingProvider.h
+++ b/src/client/QXmppFileSharingProvider.h
@@ -1,14 +1,18 @@
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im>
+// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#ifndef QXMPPFILESHARINGPROVIDER_H
#define QXMPPFILESHARINGPROVIDER_H
+#include "QXmppError.h"
#include "QXmppGlobal.h"
#include <any>
+#include <functional>
#include <memory>
+#include <variant>
class QIODevice;
class QXmppFileMetadata;
@@ -16,7 +20,7 @@ class QXmppUpload;
class QXmppDownload;
///
-/// \brief The interface of a provider for the FileSharingManager
+/// \brief The interface of a provider for the QXmppFileSharingManager
///
/// To use it, implement all the pure virtual functions,
/// and add a using declaration for the type of source you want to handle.
@@ -27,26 +31,60 @@ class QXmppDownload;
class QXMPP_EXPORT QXmppFileSharingProvider
{
public:
+ /// Contains QXmpp::Success (successfully finished), QXmpp::Cancelled (manually cancelled) or
+ /// QXmppError (an error occured while downloading).
+ using DownloadResult = std::variant<QXmpp::Success, QXmpp::Cancelled, QXmppError>;
+
+ /// Contains std::any (created file source), QXmpp::Cancelled (manually cancelled) or
+ /// QXmppError (an error occured while uploading).
+ using UploadResult = std::variant<std::any /* source */, QXmpp::Cancelled, QXmppError>;
+
+ /// Used to control ongoing downloads
+ class Download
+ {
+ public:
+ virtual ~Download() = default;
+ /// Cancels the download.
+ virtual void cancel() = 0;
+ };
+
+ /// Used to control ongoing uploads
+ class Upload
+ {
+ public:
+ virtual ~Upload() = default;
+ /// Cancels the upload.
+ virtual void cancel() = 0;
+ };
+
/// \cond
virtual ~QXmppFileSharingProvider() = default;
/// \endcond
///
/// \brief Handles the download of files for this provider
- /// \param source A type-erased source object. The provider will only ever have to handle its own sources,
- /// so this can be safely casted to a concrete type.
+ /// \param source A type-erased source object. The provider will only ever have to handle
+ /// its own sources, so this can safely be casted to the defined source type.
/// \param target QIODevice into which the received data should be written
- /// \return A subclass of QXmppDownload
+ /// \param reportProgress Can be called to report received bytes and total bytes
+ /// \param reportFinished Finalizes the download, no more progress must be reported after this
///
- virtual auto downloadFile(const std::any &source, std::unique_ptr<QIODevice> &&target) -> std::shared_ptr<QXmppDownload> = 0;
+ virtual auto downloadFile(const std::any &source,
+ std::unique_ptr<QIODevice> target,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(DownloadResult)> reportFinished) -> std::shared_ptr<Download> = 0;
///
/// \brief Handles the upload of a file for this provider
/// \param source A QIODevice from which data for uploading should be read.
/// \param info Metadata of the file
- /// \return A subclass of QXmppUpload
+ /// \param reportProgress Can be called to report sent bytes and total bytes
+ /// \param reportFinished Finalizes the upload, no more progress must be reported after this
///
- virtual auto uploadFile(std::unique_ptr<QIODevice> source, const QXmppFileMetadata &info) -> std::shared_ptr<QXmppUpload> = 0;
+ virtual auto uploadFile(std::unique_ptr<QIODevice> source,
+ const QXmppFileMetadata &info,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(UploadResult)> reportFinished) -> std::shared_ptr<Upload> = 0;
};
#endif // QXMPPFILESHARINGPROVIDER_H
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 <jbb@kaidan.im>
+// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
//
// 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 <QMimeDatabase>
#include <QNetworkReply>
@@ -25,96 +25,6 @@ using namespace QXmpp::Private;
/// \since QXmpp 1.5
///
-class QXmppSfsHttpUpload : public QXmppUpload
-{
- Q_OBJECT
-
-public:
- QXmppSfsHttpUpload(std::shared_ptr<QXmppHttpUpload> &&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<decltype(value)>;
- if constexpr (std::is_same_v<T, QUrl>) {
- return std::any(QXmppHttpFileSource(value));
- } else if constexpr (std::is_same_v<T, Cancelled>) {
- return Cancelled {};
- } else if constexpr (std::is_same_v<T, QXmppError>) {
- 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<QXmppHttpUpload> 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<QXmppHttpFileSharingProviderPrivate>())
{
- qRegisterMetaType<QXmpp::Private::UploadResult>();
Q_ASSERT(client);
d->manager = client->findExtension<QXmppHttpUploadManager>();
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<QIODevice> &&target)
- -> std::shared_ptr<QXmppDownload>
+auto QXmppHttpFileSharingProvider::downloadFile(const std::any &source,
+ std::unique_ptr<QIODevice> target,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(DownloadResult)> reportFinished)
+ -> std::shared_ptr<Download>
{
+ 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<QXmppHttpFileSource>(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<QXmppHttpDownload>();
-
- 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>();
+ 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<QIODevice>(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<QNetworkReply::NetworkError>::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<QXmppFileSharingProvider::Download>(state);
}
auto QXmppHttpFileSharingProvider::uploadFile(std::unique_ptr<QIODevice> data,
- const QXmppFileMetadata &info)
- -> std::shared_ptr<QXmppUpload>
+ const QXmppFileMetadata &info,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(UploadResult)> reportFinished)
+ -> std::shared_ptr<Upload>
{
+ struct State : Upload
+ {
+ ~State() override = default;
+
+ std::shared_ptr<QXmppHttpUpload> upload;
+ void cancel() override { upload->cancel(); }
+ };
+
Q_ASSERT(d->manager);
- auto upload = d->manager->uploadFile(
+ auto state = std::make_shared<State>();
+ 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<QXmppSfsHttpUpload>(std::move(upload));
-}
+ QObject::connect(state->upload.get(), &QXmppHttpUpload::finished, [state, reportFinished = std::move(reportFinished)](const QXmppHttpUpload::Result &result) mutable {
+ reportFinished(visitForward<UploadResult>(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<QXmppFileSharingProvider::Upload>(state);
+}
diff --git a/src/client/QXmppHttpFileSharingProvider.h b/src/client/QXmppHttpFileSharingProvider.h
index 748c1277..64329659 100644
--- a/src/client/QXmppHttpFileSharingProvider.h
+++ b/src/client/QXmppHttpFileSharingProvider.h
@@ -29,9 +29,13 @@ public:
~QXmppHttpFileSharingProvider() override;
auto downloadFile(const std::any &source,
- std::unique_ptr<QIODevice> &&target) -> std::shared_ptr<QXmppDownload> override;
- auto uploadFile(std::unique_ptr<QIODevice> data,
- const QXmppFileMetadata &info) -> std::shared_ptr<QXmppUpload> override;
+ std::unique_ptr<QIODevice> target,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(DownloadResult)> reportFinished) -> std::shared_ptr<Download> override;
+ auto uploadFile(std::unique_ptr<QIODevice> source,
+ const QXmppFileMetadata &info,
+ std::function<void(quint64, quint64)> reportProgress,
+ std::function<void(UploadResult)> reportFinished) -> std::shared_ptr<Upload> override;
private:
std::unique_ptr<QXmppHttpFileSharingProviderPrivate> d;
diff --git a/src/client/QXmppUpload.cpp b/src/client/QXmppUpload.cpp
index e7b876e5..cd4a19e4 100644
--- a/src/client/QXmppUpload.cpp
+++ b/src/client/QXmppUpload.cpp
@@ -37,7 +37,7 @@
/// \typedef QXmppUpload::Result
///
/// \brief Contains FileResult (successfully finished), QXmpp::Cancelled (manually cancelled)
-/// or QXmppError (an error occured while downloading).
+/// or QXmppError (an error occured while uploading).
///
///
diff --git a/src/client/QXmppUpload.h b/src/client/QXmppUpload.h
index c948496f..ec423bd9 100644
--- a/src/client/QXmppUpload.h
+++ b/src/client/QXmppUpload.h
@@ -7,16 +7,11 @@
#include "QXmppBitsOfBinaryDataList.h"
#include "QXmppError.h"
-#include "QXmppFileMetadata.h"
#include "QXmppFileShare.h"
#include "QXmppFileTransfer.h"
#include <variant>
-namespace QXmpp::Private {
-using UploadResult = std::variant<std::any /* source */, QXmpp::Cancelled, QXmppError>;
-}
-
class QXMPP_EXPORT QXmppUpload : public QXmppFileTransfer
{
Q_OBJECT
@@ -30,18 +25,10 @@ public:
using Result = std::variant<FileResult, QXmpp::Cancelled, QXmppError>;
Q_SIGNAL void finished(QXmppUpload::Result);
-protected:
- /// \cond
- Q_SIGNAL void uploadFinished(QXmpp::Private::UploadResult);
- /// \endcond
-
private:
- QXmppFileMetadata metadata;
-
friend class QXmppFileSharingManager;
};
Q_DECLARE_METATYPE(QXmppUpload::Result);
-Q_DECLARE_METATYPE(QXmpp::Private::UploadResult);
#endif // QXMPPUPLOAD_H