aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLinus Jahn <lnj@kaidan.im>2020-07-05 01:29:52 +0200
committerLinus Jahn <lnj@kaidan.im>2021-08-22 16:09:02 +0200
commit110be45f72c507135576e7fd2d6faf4f16d047ad (patch)
tree74097f3010d575bb3a1943a048975640aded1d70 /src
parent5cd52a0a979feaf8953f8583076f8df639b3481d (diff)
downloadqxmpp-110be45f72c507135576e7fd2d6faf4f16d047ad.tar.gz
Add QXmppPubSubEvent
The pubsub events are inheriting from QXmppMessage and are template classes with the item type as template parameter. Supports nearly everything from XEP-0060.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/base/QXmppPubSubEvent.cpp440
-rw-r--r--src/base/QXmppPubSubEvent.h166
3 files changed, 608 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 06db738e..ab5eb1a9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -47,6 +47,7 @@ set(INSTALL_HEADER_FILES
base/QXmppPingIq.h
base/QXmppPresence.h
base/QXmppPubSubAffiliation.h
+ base/QXmppPubSubEvent.h
base/QXmppPubSubIq.h
base/QXmppPubSubItem.h
base/QXmppPubSubSubscription.h
@@ -134,6 +135,7 @@ set(SOURCE_FILES
base/QXmppPingIq.cpp
base/QXmppPresence.cpp
base/QXmppPubSubAffiliation.cpp
+ base/QXmppPubSubEvent.cpp
base/QXmppPubSubIq.cpp
base/QXmppPubSubItem.cpp
base/QXmppPubSubSubscription.cpp
diff --git a/src/base/QXmppPubSubEvent.cpp b/src/base/QXmppPubSubEvent.cpp
new file mode 100644
index 00000000..012d4fb2
--- /dev/null
+++ b/src/base/QXmppPubSubEvent.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2008-2021 The QXmpp developers
+ *
+ * Author:
+ * Linus Jahn
+ *
+ * Source:
+ * https://github.com/qxmpp-project/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include "QXmppPubSubEvent.h"
+
+#include "QXmppConstants_p.h"
+#include "QXmppDataForm.h"
+#include "QXmppUtils.h"
+
+#include <QDomElement>
+
+///
+/// \class QXmppPubSubEventBase
+///
+/// The QXmppPubSubEventBase class is an abstract class used for parsing of
+/// generic PubSub event notifications as defined by \xep{0060, Publish-
+/// Subscribe}.
+///
+/// This class cannot be used directly. For a full-featured access to the event
+/// notifications, please use the QXmppPubSubEvent class.
+///
+/// \since QXmpp 1.5
+///
+
+///
+/// \class QXmppPubSubEvent
+///
+/// \brief The QXmppPubSubEvent class represents a PubSub event notification as
+/// defined by \xep{0060, Publish-Subscribe}.
+///
+/// This class has a template parameter that can be used to define the type of
+/// the contained items.
+///
+/// You can use QXmppPubSubEvent::isPubSubItem() to check whether an DOM element
+/// is a &lt;message/&gt; with a PubSub event notification. If you set a special
+/// type as a template parameter, validity of the items will also be checked. To
+/// check for an event notification with items from \xep{0118, User Tune} for
+/// example, you could use the following:
+/// \code
+/// QXmppPubSubEvent<QXmppTuneItem>::isPubSubEvent(element);
+/// \endcode
+///
+/// \ingroup Stanzas
+///
+/// \since QXmpp 1.5
+///
+
+static const QStringList PUBSUB_EVENTS = {
+ QStringLiteral("configuration"),
+ QStringLiteral("delete"),
+ QStringLiteral("items"),
+ QStringLiteral("purge"),
+ QStringLiteral("subscription"),
+};
+
+static QDomElement firstChildElement(const QDomElement &element, const QString &tagName, const QString &namespaceURI)
+{
+ for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling()) {
+ if (child.isElement() && child.namespaceURI() == namespaceURI) {
+ QDomElement elt = child.toElement();
+ if (tagName.isEmpty() || elt.tagName() == tagName) {
+ return elt;
+ }
+ }
+ }
+ return QDomElement();
+}
+
+class QXmppPubSubEventPrivate : public QSharedData
+{
+public:
+ QXmppPubSubEventPrivate(QXmppPubSubEventBase::EventType type,
+ const QString &node);
+
+ QXmppPubSubEventBase::EventType eventType;
+ QString node;
+ QStringList retractIds;
+ QString redirectUri;
+ std::optional<QXmppPubSubSubscription> subscription;
+ std::optional<QXmppDataForm> configurationForm;
+};
+
+QXmppPubSubEventPrivate::QXmppPubSubEventPrivate(QXmppPubSubEventBase::EventType type,
+ const QString &node)
+ : eventType(type), node(node)
+{
+}
+
+///
+/// Constructs a PubSub event.
+///
+QXmppPubSubEventBase::QXmppPubSubEventBase(EventType type, const QString &node)
+ : d(new QXmppPubSubEventPrivate(type, node))
+{
+ setType(QXmppMessage::Normal);
+}
+
+/// Default copy-constructor.
+QXmppPubSubEventBase::QXmppPubSubEventBase(const QXmppPubSubEventBase &other) = default;
+
+QXmppPubSubEventBase::~QXmppPubSubEventBase() = default;
+
+/// Default assignment operator.
+QXmppPubSubEventBase &QXmppPubSubEventBase::operator=(const QXmppPubSubEventBase &other) = default;
+
+///
+/// Returns the event type of the PubSub event.
+///
+QXmppPubSubEventBase::EventType QXmppPubSubEventBase::eventType() const
+{
+ return d->eventType;
+}
+
+///
+/// Sets the event type of the PubSub event.
+///
+void QXmppPubSubEventBase::setEventType(EventType type)
+{
+ d->eventType = type;
+}
+
+///
+/// Returns the name of the event's node.
+///
+/// This does not work with Subscription events. In those cases you need to get
+/// the node of the subscription.
+///
+/// \sa subscription()
+/// \sa QXmppPubSubSubscription::node()
+///
+QString QXmppPubSubEventBase::node() const
+{
+ return d->node;
+}
+
+///
+/// Sets the name of the event's node.
+///
+/// This does not work with Subscription events. In those cases you need to set
+/// the node of the subscription.
+///
+/// \sa subscription()
+/// \sa QXmppPubSubSubscription::setNode()
+///
+void QXmppPubSubEventBase::setNode(const QString &node)
+{
+ d->node = node;
+}
+
+///
+/// Returns the item IDs that have been retracted.
+///
+/// This is only used for the Items event type.
+///
+QStringList QXmppPubSubEventBase::retractIds() const
+{
+ return d->retractIds;
+}
+
+///
+/// Sets the item IDs that have been retracted.
+///
+/// This is only used for the Items event type.
+///
+void QXmppPubSubEventBase::setRetractIds(const QStringList &retractIds)
+{
+ d->retractIds = retractIds;
+}
+
+///
+/// Returns the redirect URI to the new node.
+///
+/// This can be set for delete notifications to inform subscribers of the new
+/// node. Inclusion of this is of course optional.
+///
+QString QXmppPubSubEventBase::redirectUri() const
+{
+ return d->redirectUri;
+}
+
+///
+/// Sets the redirect URI to the new node.
+///
+/// This can be set for delete notifications to inform subscribers of the new
+/// node. Inclusion of this is of course optional.
+///
+void QXmppPubSubEventBase::setRedirectUri(const QString &redirectUri)
+{
+ d->redirectUri = redirectUri;
+}
+
+///
+/// Returns the subscription in case of a Subscription event.
+///
+std::optional<QXmppPubSubSubscription> QXmppPubSubEventBase::subscription() const
+{
+ return d->subscription;
+}
+
+///
+/// Sets the subscription in case of a Subscription event.
+///
+void QXmppPubSubEventBase::setSubscription(const std::optional<QXmppPubSubSubscription> &subscription)
+{
+ d->subscription = subscription;
+}
+
+///
+/// Returns a configuration data form if the event contains one.
+///
+std::optional<QXmppDataForm> QXmppPubSubEventBase::configurationForm() const
+{
+ return d->configurationForm;
+}
+
+///
+/// Sets a configuration data form (or clears it with std::nullopt).
+///
+void QXmppPubSubEventBase::setConfigurationForm(const std::optional<QXmppDataForm> &configurationForm)
+{
+ d->configurationForm = configurationForm;
+}
+
+/// \cond
+bool QXmppPubSubEventBase::isPubSubEvent(const QDomElement &stanza, std::function<bool(const QDomElement &)> isItemValid)
+{
+ if (stanza.tagName() != QStringLiteral("message")) {
+ return false;
+ }
+
+ // find correct "event" element
+ auto event = firstChildElement(stanza, QStringLiteral("event"), ns_pubsub_event);
+ auto eventTypeElement = event.firstChildElement();
+
+ // check for validity of the event type
+ EventType eventType;
+ if (const auto index = PUBSUB_EVENTS.indexOf(eventTypeElement.tagName());
+ index != -1) {
+ eventType = EventType(index);
+ } else {
+ return false;
+ }
+
+ // check for "node" attribute when required
+ switch (eventType) {
+ case Delete:
+ case Items:
+ case Purge:
+ if (!eventTypeElement.hasAttribute(QStringLiteral("node"))) {
+ return false;
+ }
+ break;
+ case Configuration:
+ case Subscription:
+ break;
+ }
+
+ // check individual content
+ switch (eventType) {
+ case Delete: {
+ if (const auto redirect = eventTypeElement.firstChildElement(QStringLiteral("redirect"));
+ !redirect.isNull() && !redirect.hasAttribute(QStringLiteral("uri"))) {
+ return false;
+ }
+ break;
+ }
+ case Items: {
+ // check validity of the items using isItemValid()
+ for (auto itemElement = eventTypeElement.firstChildElement(QStringLiteral("item"));
+ !itemElement.isNull();
+ itemElement = itemElement.nextSiblingElement(QStringLiteral("item"))) {
+ if (!isItemValid(itemElement)) {
+ return false;
+ }
+ }
+ break;
+ }
+ case Subscription: {
+ if (!QXmppPubSubSubscription::isSubscription(eventTypeElement)) {
+ return false;
+ }
+ }
+ case Configuration:
+ case Purge:
+ break;
+ }
+
+ return true;
+}
+
+bool QXmppPubSubEventBase::parseExtension(const QDomElement &eventElement)
+{
+ if (eventElement.tagName() == QStringLiteral("event") && eventElement.namespaceURI() == ns_pubsub_event) {
+ // check that the query type is valid
+ const auto eventTypeElement = eventElement.firstChildElement();
+ if (const auto index = PUBSUB_EVENTS.indexOf(eventTypeElement.tagName());
+ index != -1) {
+ d->eventType = EventType(index);
+ } else {
+ return false;
+ }
+
+ // parse "node" attribute
+ switch (d->eventType) {
+ case Configuration:
+ case Delete:
+ case Items:
+ case Purge:
+ d->node = eventTypeElement.attribute(QStringLiteral("node"));
+ break;
+ case Subscription:
+ break;
+ }
+
+ // check the items using isItemValid()
+ switch (d->eventType) {
+ case Delete:
+ if (auto redirect = eventTypeElement.firstChildElement(QStringLiteral("redirect"));
+ !redirect.isNull()) {
+ d->redirectUri = redirect.attribute(QStringLiteral("uri"));
+ }
+ break;
+ case Items:
+ // parse items
+ parseItems(eventTypeElement);
+
+ // parse retract ids
+ for (auto retract = eventTypeElement.firstChildElement(QStringLiteral("retract"));
+ !retract.isNull();
+ retract = retract.nextSiblingElement(QStringLiteral("retract"))) {
+ d->retractIds << retract.attribute(QStringLiteral("id"));
+ }
+ break;
+ case Subscription: {
+ QXmppPubSubSubscription subscription;
+ subscription.parse(eventTypeElement);
+ d->subscription = subscription;
+ break;
+ }
+ case Configuration:
+ if (auto formElement = firstChildElement(eventTypeElement, QStringLiteral("x"), ns_data);
+ !formElement.isNull()) {
+ QXmppDataForm form;
+ form.parse(formElement);
+ d->configurationForm = form;
+ }
+ break;
+ case Purge:
+ break;
+ }
+ } else {
+ // handles QXmppMessage default extensions
+ return QXmppMessage::parseExtension(eventElement);
+ }
+
+ return true;
+}
+
+void QXmppPubSubEventBase::serializeExtensions(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("event"));
+ writer->writeDefaultNamespace(ns_pubsub_event);
+
+ if (d->eventType == Subscription && d->subscription) {
+ d->subscription->toXml(writer);
+ } else {
+ writer->writeStartElement(PUBSUB_EVENTS.at(int(d->eventType)));
+
+ // write node attribute
+ switch (d->eventType) {
+ case Delete:
+ case Items:
+ case Purge:
+ // node attribute is required
+ writer->writeAttribute(QStringLiteral("node"), d->node);
+ break;
+ case Configuration:
+ // node attribute is optional
+ helperToXmlAddAttribute(writer, QStringLiteral("node"), d->node);
+ break;
+ case Subscription:
+ break;
+ }
+
+ switch (d->eventType) {
+ case Configuration:
+ if (d->configurationForm) {
+ d->configurationForm->toXml(writer);
+ }
+ break;
+ case Delete:
+ if (!d->redirectUri.isEmpty()) {
+ writer->writeStartElement(QStringLiteral("redirect"));
+ writer->writeAttribute(QStringLiteral("uri"), d->redirectUri);
+ writer->writeEndElement();
+ }
+ case Items:
+ // serialize items
+ serializeItems(writer);
+
+ // serialize retract ids
+ for (const auto &id : std::as_const(d->retractIds)) {
+ writer->writeStartElement(QStringLiteral("retract"));
+ writer->writeAttribute(QStringLiteral("id"), id);
+ writer->writeEndElement();
+ }
+
+ break;
+ case Purge:
+ case Subscription:
+ break;
+ }
+
+ writer->writeEndElement(); // close event's type element
+ }
+ writer->writeEndElement(); // </event>
+
+ QXmppMessage::serializeExtensions(writer);
+}
+/// \endcond
diff --git a/src/base/QXmppPubSubEvent.h b/src/base/QXmppPubSubEvent.h
new file mode 100644
index 00000000..06058a1e
--- /dev/null
+++ b/src/base/QXmppPubSubEvent.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008-2021 The QXmpp developers
+ *
+ * Author:
+ * Linus Jahn
+ *
+ * Source:
+ * https://github.com/qxmpp-project/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifndef QXMPPPUBSUBEVENT_H
+#define QXMPPPUBSUBEVENT_H
+
+#include "QXmppMessage.h"
+#include "QXmppPubSubSubscription.h"
+
+#include <functional>
+
+#include <QDomElement>
+#include <QSharedData>
+
+class QXmppDataForm;
+class QXmppPubSubEventPrivate;
+class QXmppPubSubItem;
+
+class QXMPP_EXPORT QXmppPubSubEventBase : public QXmppMessage
+{
+public:
+ ///
+ /// Enumeration of different event types
+ ///
+ enum EventType : uint8_t {
+ Configuration,
+ Delete,
+ Items,
+ Purge,
+ Subscription,
+ };
+
+ QXmppPubSubEventBase(EventType = Items, const QString &node = {});
+ QXmppPubSubEventBase(const QXmppPubSubEventBase &other);
+ virtual ~QXmppPubSubEventBase();
+
+ QXmppPubSubEventBase &operator=(const QXmppPubSubEventBase &other);
+
+ EventType eventType() const;
+ void setEventType(EventType);
+
+ QString node() const;
+ void setNode(const QString &node);
+
+ QStringList retractIds() const;
+ void setRetractIds(const QStringList &);
+
+ QString redirectUri() const;
+ void setRedirectUri(const QString &);
+
+ std::optional<QXmppPubSubSubscription> subscription() const;
+ void setSubscription(const std::optional<QXmppPubSubSubscription> &subscription);
+
+ std::optional<QXmppDataForm> configurationForm() const;
+ void setConfigurationForm(const std::optional<QXmppDataForm> &configurationForm);
+
+protected:
+ /// \cond
+ static bool isPubSubEvent(const QDomElement &element, std::function<bool(const QDomElement &)> isItemValid);
+
+ bool parseExtension(const QDomElement &element) override;
+ void serializeExtensions(QXmlStreamWriter *writer) const override;
+
+ virtual void parseItems(const QDomElement &) = 0;
+ virtual void serializeItems(QXmlStreamWriter *writer) const = 0;
+ /// \endcond
+
+private:
+ QSharedDataPointer<QXmppPubSubEventPrivate> d;
+};
+
+template<typename T = QXmppPubSubItem>
+class QXmppPubSubEvent : public QXmppPubSubEventBase
+{
+public:
+ QVector<T> items() const;
+ void setItems(const QVector<T> &items);
+
+ static bool isPubSubEvent(const QDomElement &element);
+
+protected:
+ /// \cond
+ void parseItems(const QDomElement &) override;
+ void serializeItems(QXmlStreamWriter *writer) const override;
+ /// \endcond
+
+private:
+ QVector<T> m_items;
+};
+
+///
+/// Returns the PubSub items of the event.
+///
+template<typename T>
+QVector<T> QXmppPubSubEvent<T>::items() const
+{
+ return m_items;
+}
+
+///
+/// Sets the PubSub items of the event.
+///
+template<typename T>
+void QXmppPubSubEvent<T>::setItems(const QVector<T> &items)
+{
+ m_items = items;
+}
+
+///
+/// Returns whether the element is a valid QXmppPubSubEvent and contains only
+/// valid items of type T.
+///
+template<typename T>
+bool QXmppPubSubEvent<T>::isPubSubEvent(const QDomElement &element)
+{
+ return QXmppPubSubEventBase::isPubSubEvent(element, [](const QDomElement &element) {
+ return T::isItem(element);
+ });
+}
+
+/// \cond
+template<typename T>
+void QXmppPubSubEvent<T>::parseItems(const QDomElement &parent)
+{
+ QDomElement child = parent.firstChildElement(QStringLiteral("item"));
+ while (!child.isNull()) {
+ T item;
+ item.parse(child);
+ m_items << item;
+
+ child = child.nextSiblingElement(QStringLiteral("item"));
+ }
+}
+
+template<typename T>
+void QXmppPubSubEvent<T>::serializeItems(QXmlStreamWriter *writer) const
+{
+ for (const auto &item : qAsConst(m_items)) {
+ item.toXml(writer);
+ }
+}
+/// \endcond
+
+Q_DECLARE_METATYPE(QXmppPubSubEventBase::EventType)
+
+#endif // QXMPPPUBSUBEVENT_H