From 68f167995e7ba71a6f2e556a7a0eab3d234e2d1a Mon Sep 17 00:00:00 2001 From: Jonah BrĂ¼chert Date: Fri, 9 Sep 2022 23:15:10 +0200 Subject: Implement XEP-0448: Stateless File Sharing This adds a file sharing manager that is capable of using multiple back ends. Currently implemented are a normal HTTP File Upload backend and an encrypted HTTP File Upload. Jingle File Transfer could be implemented later. Co-authored-by: Linus Jahn --- .../QXmppEncryptedHttpFileSharingProvider.cpp | 123 +++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/client/QXmppEncryptedHttpFileSharingProvider.cpp (limited to 'src/client/QXmppEncryptedHttpFileSharingProvider.cpp') diff --git a/src/client/QXmppEncryptedHttpFileSharingProvider.cpp b/src/client/QXmppEncryptedHttpFileSharingProvider.cpp new file mode 100644 index 00000000..b6e82621 --- /dev/null +++ b/src/client/QXmppEncryptedHttpFileSharingProvider.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppEncryptedHttpFileSharingProvider.h" + +#include "QXmppClient.h" +#include "QXmppFileEncryption.h" +#include "QXmppHttpUploadManager.h" +#include "QXmppUpload.h" +#include "QXmppUtils.h" + +#include "QcaInitializer_p.h" +#include + +using namespace QXmpp; +using namespace QXmpp::Private; + +class QXmppSfsEncryptedHttpUpload : public QXmppUpload +{ + Q_OBJECT + +public: + QXmppSfsEncryptedHttpUpload(std::shared_ptr &&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; + if constexpr (std::is_same_v) { + QXmppEncryptedFileSource encryptedSource; + encryptedSource.setKey(key); + encryptedSource.setIv(iv); + encryptedSource.setHttpSources({ QXmppHttpFileSource(value) }); + return std::any(encryptedSource); + } else if constexpr (std::is_same_v) { + return Cancelled {}; + } else if constexpr (std::is_same_v) { + 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 inner; +}; + +/// +/// \class QXmppEncryptedHttpFileSharingProvider +/// +/// Support for storing files encrypted on an HTTP server +/// +/// \since QXmpp 1.5 +/// + +class QXmppEncryptedHttpFileSharingProviderPrivate +{ +public: + QXmpp::Private::QcaInitializer init; + QXmppHttpUploadManager *manager; + QXmppHttpFileSharingProvider *httpProvider; +}; + +/// +/// \brief Create a new QXmppEncryptedHttpFileSharingProvider +/// \param client +/// \param netManager QNetworkAccessManager that can be reused all over your application. +/// +QXmppEncryptedHttpFileSharingProvider::QXmppEncryptedHttpFileSharingProvider(QXmppClient *client, QNetworkAccessManager *netManager) + : d(std::make_unique()) +{ + qRegisterMetaType(); + Q_ASSERT(client); + d->manager = client->findExtension(); + Q_ASSERT(d->manager); + d->httpProvider = new QXmppHttpFileSharingProvider(client, netManager); +} + +QXmppEncryptedHttpFileSharingProvider::~QXmppEncryptedHttpFileSharingProvider() = default; + +std::shared_ptr QXmppEncryptedHttpFileSharingProvider::downloadFile(const std::any &source, std::unique_ptr &&target) +{ + QXmppEncryptedFileSource encryptedSource; + try { + encryptedSource = std::any_cast(source); + } catch (const std::bad_any_cast &) { + qFatal("QXmppEncryptedHttpFileSharingProvider::downloadFile can only handle QXmppEncryptedFileSource sources"); + } + + auto httpSource = encryptedSource.httpSources().front(); + auto output = std::make_unique(std::move(target), encryptedSource.cipher(), encryptedSource.iv(), encryptedSource.key()); + return d->httpProvider->downloadFile(httpSource, std::move(output)); +} + +std::shared_ptr QXmppEncryptedHttpFileSharingProvider::uploadFile( + std::unique_ptr data, + const QXmppFileMetadata &info) +{ + auto cipher = Aes256CbcPkcs7; + auto key = Encryption::generateKey(cipher); + auto iv = Encryption::generateInitializationVector(cipher); + + auto encDevice = std::make_unique(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); + + return std::make_shared(std::move(upload), key, iv); +} + +#include "QXmppEncryptedHttpFileSharingProvider.moc" -- cgit v1.2.3