Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement XEP-0447: Stateless file sharing: File sharing element #448

Merged
merged 10 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/doap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,14 @@ SPDX-License-Identifier: CC0-1.0
<xmpp:since>1.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0447.html'/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2</xmpp:version>
<xmpp:since>1.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0450.html'/>
Expand Down
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ set(INSTALL_HEADER_FILES
base/QXmppEntityTimeIq.h
base/QXmppError.h
base/QXmppExtension.h
base/QXmppFutureUtils_p.h
base/QXmppFileMetadata.h
base/QXmppFileShare.h
base/QXmppFutureUtils_p.h
base/QXmppGeolocItem.h
base/QXmppGlobal.h
base/QXmppHash.h
Expand Down Expand Up @@ -162,6 +163,7 @@ set(SOURCE_FILES
base/QXmppEntityTimeIq.cpp
base/QXmppError.cpp
base/QXmppFileMetadata.cpp
base/QXmppFileShare.cpp
base/QXmppGeolocItem.cpp
base/QXmppGlobal.cpp
base/QXmppHash.cpp
Expand Down
4 changes: 4 additions & 0 deletions src/base/QXmppConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const char *ns_data = "jabber:x:data";
// XEP-0095: Stream Initiation
const char *ns_stream_initiation = "http://jabber.org/protocol/si";
const char *ns_stream_initiation_file_transfer = "http://jabber.org/protocol/si/profile/file-transfer";
// XEP-0103: URL Address Information
const char *ns_url_data = "http://jabber.org/protocol/url-data";
// XEP-0108: User Activity
const char *ns_activity = "http://jabber.org/protocol/activity";
// XEP-0115: Entity Capabilities
Expand Down Expand Up @@ -188,5 +190,7 @@ const char *ns_fallback_indication = "urn:xmpp:fallback:0";
const char *ns_tm = "urn:xmpp:tm:1";
// XEP-0446: File metadata element
const char *ns_file_metadata = "urn:xmpp:file:metadata:0";
// XEP-0447: Stateless file sharing
const char *ns_sfs = "urn:xmpp:sfs:0";
// XEP-0450: Automatic Trust Management (ATM)
const char *ns_atm = "urn:xmpp:atm:1";
4 changes: 4 additions & 0 deletions src/base/QXmppConstants_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ extern const char *ns_data;
// XEP-0095: Stream Initiation
extern const char *ns_stream_initiation;
extern const char *ns_stream_initiation_file_transfer;
// XEP-0103: URL Address Information
extern const char *ns_url_data;
// XEP-0108: User Activity
extern const char *ns_activity;
// XEP-0115: Entity Capabilities
Expand Down Expand Up @@ -200,6 +202,8 @@ extern const char *ns_fallback_indication;
extern const char *ns_tm;
// XEP-0446: File metadata element
extern const char *ns_file_metadata;
// XEP-0447: Stateless file sharing
extern const char *ns_sfs;
// XEP-0450: Automatic Trust Management (ATM)
extern const char *ns_atm;

Expand Down
151 changes: 151 additions & 0 deletions src/base/QXmppFileShare.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "QXmppFileShare.h"

#include "QXmppConstants_p.h"
#include "QXmppFileMetadata.h"

#include <optional>

#include <QDomElement>
#include <QUrl>
#include <QXmlStreamWriter>

using Disposition = QXmppFileShare::Disposition;

static std::optional<Disposition> dispositionFromString(const QString &str)
{
if (str == "inline") {
return Disposition::Inline;
}
if (str == "attachment") {
return Disposition::Attachment;
}
return {};
}

static QString dispositionToString(Disposition value)
{
switch (value) {
case Disposition::Inline:
return QStringLiteral("inline");
case Disposition::Attachment:
return QStringLiteral("attachment");
}
Q_UNREACHABLE();
}

/// \cond
class QXmppFileSharePrivate : public QSharedData
{
public:
QXmppFileMetadata metadata;
QVector<QUrl> httpSources;
QXmppFileShare::Disposition disposition = Disposition::Inline;
};
/// \endcond

///
/// \class QXmppFileSharePrivate
///
/// File sharing element from \xep{0447, Stateless file sharing}. Contains
/// metadata and source URLs.
///
/// \note jinglepub references are currently missing
///
/// \since QXmpp 1.5
///

/// Default constructor
QXmppFileShare::QXmppFileShare()
: d(new QXmppFileSharePrivate)
{
}

QXmppFileShare::QXmppFileShare(const QXmppFileShare &) = default;
QXmppFileShare::QXmppFileShare(QXmppFileShare &&) noexcept = default;
QXmppFileShare::~QXmppFileShare() = default;
QXmppFileShare &QXmppFileShare::operator=(const QXmppFileShare &) = default;
QXmppFileShare &QXmppFileShare::operator=(QXmppFileShare &&) noexcept = default;

/// Returns the disposition setting for this file.
QXmppFileShare::Disposition QXmppFileShare::disposition() const
{
return d->disposition;
}

/// Sets the disposition setting for this file.
void QXmppFileShare::setDisposition(Disposition disp)
{
d->disposition = disp;
}

const QXmppFileMetadata &QXmppFileShare::metadata() const
{
return d->metadata;
}

void QXmppFileShare::setMetadata(const QXmppFileMetadata &metadata)
{
d->metadata = metadata;
}

const QVector<QUrl> &QXmppFileShare::httpSources() const
{
return d->httpSources;
}

void QXmppFileShare::setHttpSources(const QVector<QUrl> &newHttpSources)
{
d->httpSources = newHttpSources;
}

/// \cond
bool QXmppFileShare::parse(const QDomElement &el)
{
if (el.tagName() == "file-sharing" && el.namespaceURI() == ns_sfs) {
// disposition
d->disposition = dispositionFromString(el.attribute("disposition"))
.value_or(Disposition::Inline);

// file metadata
auto fileEl = el.firstChildElement("file");
d->metadata = QXmppFileMetadata();
if (!d->metadata.parse(fileEl)) {
return false;
}

// sources:
// expect that there's only one sources element with the correct namespace
auto sources = el.firstChildElement("sources");
for (auto urlEl = sources.firstChildElement("url-data");
!urlEl.isNull();
urlEl = urlEl.nextSiblingElement("url-data")) {
if (urlEl.namespaceURI() == "http://jabber.org/protocol/url-data") {
d->httpSources.append(QUrl(urlEl.attribute("target")));
}
}
return true;
}
return false;
}

void QXmppFileShare::toXml(QXmlStreamWriter *writer) const
{
writer->writeStartElement("file-sharing");
writer->writeDefaultNamespace(ns_sfs);
writer->writeAttribute("disposition", dispositionToString(d->disposition));
d->metadata.toXml(writer);
writer->writeStartElement("sources");
for (const auto &source : d->httpSources) {
writer->writeStartElement("url-data");
writer->writeDefaultNamespace(ns_url_data);
writer->writeAttribute("target", source.toString());
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeEndElement();
}
/// \endcond
52 changes: 52 additions & 0 deletions src/base/QXmppFileShare.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#ifndef QXMPPFILESHARE_H
#define QXMPPFILESHARE_H

#include "QXmppGlobal.h"

#include <QSharedDataPointer>

class QDomElement;
class QUrl;
class QXmlStreamWriter;
class QXmppFileSharePrivate;
class QXmppFileMetadata;

class QXMPP_EXPORT QXmppFileShare
{
public:
enum Disposition {
Inline,
Attachment,
};

QXmppFileShare();
QXmppFileShare(const QXmppFileShare &);
QXmppFileShare(QXmppFileShare &&) noexcept;
~QXmppFileShare();

QXmppFileShare &operator=(const QXmppFileShare &);
QXmppFileShare &operator=(QXmppFileShare &&) noexcept;

Disposition disposition() const;
void setDisposition(Disposition);

const QXmppFileMetadata &metadata() const;
void setMetadata(const QXmppFileMetadata &);

const QVector<QUrl> &httpSources() const;
void setHttpSources(const QVector<QUrl> &newHttpSources);

/// \cond
bool parse(const QDomElement &el);
void toXml(QXmlStreamWriter *writer) const;
/// \endcond

private:
QSharedDataPointer<QXmppFileSharePrivate> d;
};

#endif // QXMPPFILESHARE_H
37 changes: 37 additions & 0 deletions src/base/QXmppMessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "QXmppBitsOfBinaryDataList.h"
#include "QXmppConstants_p.h"
#include "QXmppFileShare.h"
#include "QXmppGlobal_p.h"
#include "QXmppMixInvitation.h"
#ifdef BUILD_OMEMO
Expand Down Expand Up @@ -151,6 +152,9 @@ class QXmppMessagePrivate : public QSharedData

// XEP-0434: Trust Messages (TM)
std::optional<QXmppTrustMessageElement> trustMessageElement;

// XEP-0448: Encryption for stateless file sharing
QVector<QXmppFileShare> sharedFiles;
};

QXmppMessagePrivate::QXmppMessagePrivate()
Expand Down Expand Up @@ -1205,6 +1209,26 @@ void QXmppMessage::setTrustMessageElement(const std::optional<QXmppTrustMessageE
d->trustMessageElement = trustMessageElement;
}

///
/// Returns the via \xep{0447, Stateless file sharing} shared files attached to this message.
///
/// \since QXmpp 1.5
///
const QVector<QXmppFileShare> &QXmppMessage::sharedFiles() const
{
return d->sharedFiles;
}

///
/// Sets the via \xep{0447, Stateless file sharing} shared files attached to this message.
///
/// \since QXmpp 1.5
///
void QXmppMessage::setSharedFiles(const QVector<QXmppFileShare> &sharedFiles)
{
d->sharedFiles = sharedFiles;
}

/// \cond
void QXmppMessage::parse(const QDomElement &element)
{
Expand Down Expand Up @@ -1489,6 +1513,14 @@ bool QXmppMessage::parseExtension(const QDomElement &element, QXmpp::SceMode sce
d->trustMessageElement = trustMessageElement;
return true;
}
// XEP-0448: Stateless file sharing
if (checkElement(element, QStringLiteral("file-sharing"), ns_sfs)) {
QXmppFileShare share;
if (share.parse(element)) {
d->sharedFiles.push_back(std::move(share));
}
return true;
}
}
return false;
}
Expand Down Expand Up @@ -1739,5 +1771,10 @@ void QXmppMessage::serializeExtensions(QXmlStreamWriter *writer, QXmpp::SceMode
if (d->trustMessageElement) {
d->trustMessageElement->toXml(writer);
}

// XEP-0448: Stateless file sharing
for (const auto &fileShare : d->sharedFiles) {
fileShare.toXml(writer);
}
}
}
5 changes: 5 additions & 0 deletions src/base/QXmppMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#ifndef QXMPPMESSAGE_H
#define QXMPPMESSAGE_H

#include "QXmppFileShare.h"
#include "QXmppStanza.h"

#include <optional>
Expand Down Expand Up @@ -248,6 +249,10 @@ class QXMPP_EXPORT QXmppMessage : public QXmppStanza
std::optional<QXmppTrustMessageElement> trustMessageElement() const;
void setTrustMessageElement(const std::optional<QXmppTrustMessageElement> &trustMessageElement);

// XEP-0447: Stateless file sharing
const QVector<QXmppFileShare> &sharedFiles() const;
void setSharedFiles(const QVector<QXmppFileShare> &sharedFiles);

/// \cond
#ifdef BUILD_OMEMO
// XEP-0384: OMEMO Encryption
Expand Down