diff options
| author | taiBsu <dev@taibsu.de> | 2023-03-09 16:45:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-09 16:45:10 +0100 |
| commit | 6ea3edfd83a0bf1558d43e48eac563730276c175 (patch) | |
| tree | 0139fb92aca9da9f637237b7bedbd34393b0292e /src/base | |
| parent | 37d56016ac7940f5c876d0293cd9e3f7802ea63a (diff) | |
| download | qxmpp-6ea3edfd83a0bf1558d43e48eac563730276c175.tar.gz | |
Implement XEP-0215: External Service Discovery (#542)
Implements https://xmpp.org/extensions/xep-0215.html in version 1.0.
Diffstat (limited to 'src/base')
| -rw-r--r-- | src/base/QXmppConstants.cpp | 2 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppExternalService.h | 83 | ||||
| -rw-r--r-- | src/base/QXmppExternalServiceDiscoveryIq.cpp | 452 | ||||
| -rw-r--r-- | src/base/QXmppExternalServiceDiscoveryIq.h | 37 |
5 files changed, 576 insertions, 0 deletions
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index 3d1afe88..be5b62c7 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -120,6 +120,8 @@ const char *ns_ping = "urn:xmpp:ping"; const char *ns_entity_time = "urn:xmpp:time"; // XEP-0203: Delayed Delivery const char *ns_delayed_delivery = "urn:xmpp:delay"; +// XEP-0215: External Service Discovery +const char *ns_external_service_discovery = "urn:xmpp:extdisco:2"; // XEP-0220: Server Dialback const char *ns_server_dialback = "jabber:server:dialback"; // XEP-0221: Data Forms Media Element diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index 88489e0c..b90346b6 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -132,6 +132,8 @@ extern const char *ns_ping; extern const char *ns_entity_time; // XEP-0203: Delayed Delivery extern const char *ns_delayed_delivery; +// XEP-0215: External Service Discovery +extern const char *ns_external_service_discovery; // XEP-0220: Server Dialback extern const char *ns_server_dialback; // XEP-0221: Data Forms Media Element diff --git a/src/base/QXmppExternalService.h b/src/base/QXmppExternalService.h new file mode 100644 index 00000000..fb0b7d58 --- /dev/null +++ b/src/base/QXmppExternalService.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "QXmppGlobal.h" + +#include <optional> + +#include <QSharedDataPointer> + +class QDateTime; +class QDomElement; +class QXmlStreamWriter; +class QXmppExternalServicePrivate; + +class QXMPP_EXPORT QXmppExternalService +{ +public: + /// + /// Describes the action type of an external service IQ. + /// + /// \since QXmpp 1.6 + /// + enum class Action { + Add, + Delete, + Modify + }; + + /// + /// Describes the packet type of an external service IQ. + /// + /// \since QXmpp 1.6 + /// + enum class Transport { + Tcp, + Udp + }; + + QXmppExternalService(); + + QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppExternalService) + + QString host() const; + void setHost(const QString &); + + QString type() const; + void setType(const QString &); + + std::optional<Action> action() const; + void setAction(std::optional<Action>); + + std::optional<QDateTime> expires() const; + void setExpires(std::optional<QDateTime>); + + std::optional<QString> name() const; + void setName(std::optional<QString>); + + std::optional<QString> password() const; + void setPassword(std::optional<QString>); + + std::optional<int> port() const; + void setPort(std::optional<int>); + + std::optional<bool> restricted() const; + void setRestricted(std::optional<bool>); + + std::optional<Transport> transport() const; + void setTransport(std::optional<Transport>); + + std::optional<QString> username() const; + void setUsername(std::optional<QString>); + + static bool isExternalService(const QDomElement &); + + void parse(const QDomElement &); + void toXml(QXmlStreamWriter *) const; + +private: + QSharedDataPointer<QXmppExternalServicePrivate> d; +}; diff --git a/src/base/QXmppExternalServiceDiscoveryIq.cpp b/src/base/QXmppExternalServiceDiscoveryIq.cpp new file mode 100644 index 00000000..6b518129 --- /dev/null +++ b/src/base/QXmppExternalServiceDiscoveryIq.cpp @@ -0,0 +1,452 @@ +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppExternalServiceDiscoveryIq.h" + +#include "QXmppConstants_p.h" +#include "QXmppUtils.h" + +#include <QDateTime> +#include <QDomElement> + +using Action = QXmppExternalService::Action; +using Transport = QXmppExternalService::Transport; + +QString actionToString(Action action) +{ + switch (action) { + case Action::Add: + return QStringLiteral("add"); + case Action::Delete: + return QStringLiteral("delete"); + case Action::Modify: + return QStringLiteral("modify"); + } + return {}; +} + +std::optional<Action> actionFromString(const QString &string) +{ + if (string == QStringLiteral("add")) { + return Action::Add; + } else if (string == QStringLiteral("delete")) { + return Action::Delete; + } else if (string == QStringLiteral("modify")) { + return Action::Modify; + } + + return std::nullopt; +} + +QString transportToString(Transport transport) +{ + switch (transport) { + case Transport::Tcp: + return QStringLiteral("tcp"); + case Transport::Udp: + return QStringLiteral("udp"); + } + + return {}; +} + +std::optional<Transport> transportFromString(const QString &string) +{ + if (string == QStringLiteral("tcp")) { + return Transport::Tcp; + } else if (string == QStringLiteral("udp")) { + return Transport::Udp; + } + + return std::nullopt; +} + +class QXmppExternalServicePrivate : public QSharedData +{ +public: + QString host; + QString type; + std::optional<Action> action; + std::optional<QDateTime> expires; + std::optional<QString> name; + std::optional<QString> password; + std::optional<int> port; // recommended + std::optional<bool> restricted; + std::optional<Transport> transport; // recommended + std::optional<QString> username; +}; + +class QXmppExternalServiceDiscoveryIqPrivate : public QSharedData +{ +public: + QVector<QXmppExternalService> externalServices; +}; + +/// +/// \class QXmppExternalService +/// +/// QXmppExternalService represents a related XMPP entity that can be queried using \xep{0215, +/// External Service Discovery}. +/// +/// \since QXmpp 1.6 +/// + +QXmppExternalService::QXmppExternalService() + : d(new QXmppExternalServicePrivate) +{ +} + +QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppExternalService) + +/// +/// Returns the host of the external service. +/// +QString QXmppExternalService::host() const +{ + return d->host; +} + +/// +/// Sets the host of the external service. +/// +void QXmppExternalService::setHost(const QString &host) +{ + d->host = host; +} + +/// +/// Returns the type of the external service. +/// +QString QXmppExternalService::type() const +{ + return d->type; +} + +/// +/// Sets the type of the external service. +/// +void QXmppExternalService::setType(const QString &type) +{ + d->type = type; +} + +/// +/// Returns the action of the external service. +/// +std::optional<Action> QXmppExternalService::action() const +{ + return d->action; +} + +/// +/// Sets the action of the external service. +/// +void QXmppExternalService::setAction(std::optional<Action> action) +{ + d->action = action; +} + +/// +/// Returns the expiration date of the external service. +/// +std::optional<QDateTime> QXmppExternalService::expires() const +{ + return d->expires; +} + +/// +/// Sets the expiration date of the external service. +/// +void QXmppExternalService::setExpires(std::optional<QDateTime> expires) +{ + d->expires = std::move(expires); +} + +/// +/// Returns the name of the external service. +/// +std::optional<QString> QXmppExternalService::name() const +{ + return d->name; +} + +/// +/// Sets the name of the external service. +/// +void QXmppExternalService::setName(std::optional<QString> name) +{ + d->name = std::move(name); +} + +/// +/// Returns the password of the external service. +/// +std::optional<QString> QXmppExternalService::password() const +{ + return d->password; +} + +/// +/// Sets the password of the external service. +/// +void QXmppExternalService::setPassword(std::optional<QString> password) +{ + d->password = std::move(password); +} + +/// +/// Returns the port of the external service. +/// +std::optional<int> QXmppExternalService::port() const +{ + return d->port; +} + +/// +/// Sets the port of the external service. +/// +void QXmppExternalService::setPort(std::optional<int> port) +{ + d->port = port; +} + +/// +/// Returns the restricted mode of the external service. +/// +std::optional<bool> QXmppExternalService::restricted() const +{ + return d->restricted; +} +/// +/// Sets the restricted mode of the external service. +/// +void QXmppExternalService::setRestricted(std::optional<bool> restricted) +{ + d->restricted = restricted; +} + +/// +/// Returns the transport type of the external service. +/// +std::optional<QXmppExternalService::Transport> QXmppExternalService::transport() const +{ + return d->transport; +} + +/// +/// Sets the transport type of the external service. +/// +void QXmppExternalService::setTransport(std::optional<Transport> transport) +{ + d->transport = transport; +} + +/// +/// Returns the username of the external service. +/// +std::optional<QString> QXmppExternalService::username() const +{ + return d->username; +} + +/// +/// Sets the username of the external service. +/// +void QXmppExternalService::setUsername(std::optional<QString> username) +{ + d->username = std::move(username); +} + +/// +/// Returns true if the element is a valid external service and can be parsed. +/// +bool QXmppExternalService::isExternalService(const QDomElement &element) +{ + if (element.tagName() != "service") { + return false; + } + + return element.hasAttribute("host") && !element.attribute("host").isEmpty() && + element.hasAttribute("type") && !element.attribute("type").isEmpty(); +} + +/// +/// Parses given DOM element as an external service. +/// +void QXmppExternalService::parse(const QDomElement &el) +{ + QDomNamedNodeMap attributes = el.attributes(); + + setHost(el.attribute("host")); + setType(el.attribute("type")); + + d->action = actionFromString(el.attribute("action")); + + if (attributes.contains("expires")) { + setExpires(QXmppUtils::datetimeFromString(el.attribute("expires"))); + } + + if (attributes.contains("name")) { + setName(el.attribute("name")); + } + + if (attributes.contains("password")) { + setPassword(el.attribute("password")); + } + + if (attributes.contains("port")) { + setPort(el.attribute("port").toInt()); + } + + if (attributes.contains("restricted")) { + bool isRestricted { + el.attribute("restricted") == "true" || + el.attribute("restricted") == "1" + }; + + setRestricted(isRestricted); + } + + d->transport = transportFromString(el.attribute("transport")); + + if (attributes.contains("username")) { + setUsername(el.attribute("username")); + } +} + +/// +/// \brief QXmppExternalService::toXml +/// +/// Translates the external service to XML using the provided XML stream writer. +/// +/// \param writer +/// + +void QXmppExternalService::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("service"); + helperToXmlAddAttribute(writer, "host", d->host); + helperToXmlAddAttribute(writer, "type", d->type); + + if (d->action) { + helperToXmlAddAttribute(writer, "action", actionToString(d->action.value())); + } + + if (d->expires) { + helperToXmlAddAttribute(writer, "expires", d->expires->toString(Qt::ISODateWithMs)); + } + + if (d->name) { + helperToXmlAddAttribute(writer, "name", d->name.value()); + } + + if (d->password) { + helperToXmlAddAttribute(writer, "password", d->password.value()); + } + + if (d->port) { + helperToXmlAddAttribute(writer, "port", QString::number(d->port.value())); + } + + if (d->restricted) { + helperToXmlAddAttribute(writer, "restricted", d->restricted.value() ? "true" : "false"); + } + + if (d->transport) { + helperToXmlAddAttribute(writer, "transport", transportToString(d->transport.value())); + } + + if (d->username) { + helperToXmlAddAttribute(writer, "username", d->username.value()); + } + + writer->writeEndElement(); +} + +/// +/// \brief The QXmppExternalServiceDiscoveryIq class represents an IQ used to discover external +/// services as defined by \xep{0215}: External Service Discovery. +/// +/// \ingroup Stanzas +/// +/// \since QXmpp 1.6 +/// + +/// +/// Constructs an external service discovery IQ. +/// +QXmppExternalServiceDiscoveryIq::QXmppExternalServiceDiscoveryIq() + : d(new QXmppExternalServiceDiscoveryIqPrivate) +{ +} + +QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppExternalServiceDiscoveryIq) + +/// +/// Returns the external services of the IQ. +/// +QVector<QXmppExternalService> QXmppExternalServiceDiscoveryIq::externalServices() +{ + return d->externalServices; +} + +/// +/// Sets the external services of the IQ. +/// +void QXmppExternalServiceDiscoveryIq::setExternalServices(const QVector<QXmppExternalService> &externalServices) +{ + d->externalServices = externalServices; +} + +/// +/// Adds an external service to the list of external services in the IQ. +/// +void QXmppExternalServiceDiscoveryIq::addExternalService(const QXmppExternalService &externalService) +{ + d->externalServices.append(externalService); +} + +/// +/// Returns true if the provided DOM element is an external service discovery IQ. +/// +bool QXmppExternalServiceDiscoveryIq::isExternalServiceDiscoveryIq(const QDomElement &element) +{ + auto child = element.firstChildElement(); + return checkIqType(child.tagName(), child.namespaceURI()); +} + +/// +/// Returns true if the IQ is a valid external service discovery IQ. +/// +bool QXmppExternalServiceDiscoveryIq::checkIqType(const QString &tagName, const QString &xmlNamespace) +{ + return tagName == QStringLiteral("services") && (xmlNamespace == ns_external_service_discovery); +} + +/// \cond +void QXmppExternalServiceDiscoveryIq::parseElementFromChild(const QDomElement &element) +{ + for (auto el = element.firstChildElement("services").firstChildElement(); + !el.isNull(); + el = el.nextSiblingElement()) { + if (QXmppExternalService::isExternalService(el)) { + QXmppExternalService service; + service.parse(el); + d->externalServices.append(std::move(service)); + } + } +} + +void QXmppExternalServiceDiscoveryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("services"); + writer->writeDefaultNamespace(ns_external_service_discovery); + + for (const QXmppExternalService &item : d->externalServices) { + item.toXml(writer); + } + + writer->writeEndElement(); +} +/// \endcond diff --git a/src/base/QXmppExternalServiceDiscoveryIq.h b/src/base/QXmppExternalServiceDiscoveryIq.h new file mode 100644 index 00000000..9be44066 --- /dev/null +++ b/src/base/QXmppExternalServiceDiscoveryIq.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef QXMPPEXTERNALSERVICEDISCOVERYIQ_H +#define QXMPPEXTERNALSERVICEDISCOVERYIQ_H + +#include "QXmppExternalService.h" +#include "QXmppIq.h" + +class QXmppExternalServiceDiscoveryIqPrivate; + +class QXMPP_EXPORT QXmppExternalServiceDiscoveryIq : public QXmppIq +{ +public: + QXmppExternalServiceDiscoveryIq(); + + QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppExternalServiceDiscoveryIq) + + QVector<QXmppExternalService> externalServices(); + void setExternalServices(const QVector<QXmppExternalService> &); + void addExternalService(const QXmppExternalService &); + + static bool isExternalServiceDiscoveryIq(const QDomElement &); + static bool checkIqType(const QString &tagName, const QString &xmlNamespace); + +protected: + /// \cond + void parseElementFromChild(const QDomElement &) override; + void toXmlElementFromChild(QXmlStreamWriter *) const override; + /// \endcond + +private: + QSharedDataPointer<QXmppExternalServiceDiscoveryIqPrivate> d; +}; + +#endif // QXMPPEXTERNALSERVICEDISCOVERYIQ_H |
