From a42c11570b9bba3465fb79dc936de6cb3c7c48cb Mon Sep 17 00:00:00 2001 From: Melvin Keskin Date: Thu, 7 Apr 2022 18:10:42 +0200 Subject: PubSubManager: Add requestFeatures() --- src/client/QXmppPubSubManager.cpp | 59 ++++++++++++ src/client/QXmppPubSubManager.h | 18 ++++ .../qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp | 103 +++++++++++++++++++++ 3 files changed, 180 insertions(+) diff --git a/src/client/QXmppPubSubManager.cpp b/src/client/QXmppPubSubManager.cpp index 8e2a1b6f..00d537db 100644 --- a/src/client/QXmppPubSubManager.cpp +++ b/src/client/QXmppPubSubManager.cpp @@ -78,6 +78,13 @@ using namespace QXmpp::Private; /// QXmppStanza::Error is reported. /// +/// +/// \typedef QXmppPubSubManager::FeaturesResult +/// +/// Type containing service discovery features, InvalidServiceType if the service is not of the +/// desired type or the returned IQ error (QXmppStanza::Error). +/// + /// /// \typedef QXmppPubSubManager::NodesResult /// @@ -170,6 +177,58 @@ QXmppPubSubManager::~QXmppPubSubManager() { } +/// +/// Requests all features of a pubsub service and checks the identities via service discovery. +/// +/// This uses a \xep{0030, Service Discovery} info request to get the service +/// identities and features. +/// +/// The features are only returned if the service is of type serviceType, +/// otherwise InvalidServiceType is returned. +/// +/// \warning THIS API IS NOT FINALIZED YET! +/// +/// \param serviceJid JID of the entity hosting the pubsub service +/// \param serviceType type of service to retrieve features for +/// +QFuture QXmppPubSubManager::requestFeatures(const QString &serviceJid, ServiceType serviceType) +{ + QXmppDiscoveryIq request; + request.setType(QXmppIq::Get); + request.setQueryType(QXmppDiscoveryIq::InfoQuery); + request.setTo(serviceJid); + + return chainIq(client()->sendIq(std::move(request)), this, [=](QXmppDiscoveryIq &&iq) -> FeaturesResult { + const auto identities = iq.identities(); + + const auto isPubSubServiceFound = std::any_of(identities.cbegin(), identities.cend(), [=](const QXmppDiscoveryIq::Identity &identity) { + if (identity.category() == QStringLiteral("pubsub")) { + const auto identityType = identity.type(); + + switch (serviceType) { + case PubSubOrPep: + return identityType == QStringLiteral("service") || identityType == QStringLiteral("pep"); + case PubSub: + return identityType == QStringLiteral("service"); + case Pep: + return identityType == QStringLiteral("pep"); + } + } + return false; + }); + + if (isPubSubServiceFound) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return iq.features(); +#else + return iq.features().toVector(); +#endif + } + + return InvalidServiceType(); + }); +} + /// /// Requests all listed nodes of a pubsub service via service discovery. /// diff --git a/src/client/QXmppPubSubManager.h b/src/client/QXmppPubSubManager.h index 8e02f801..8c7a014b 100644 --- a/src/client/QXmppPubSubManager.h +++ b/src/client/QXmppPubSubManager.h @@ -25,6 +25,15 @@ class QXMPP_EXPORT QXmppPubSubManager : public QXmppClientExtension Q_OBJECT public: + /// + /// Type of PubSub service + /// + enum ServiceType { + PubSubOrPep, ///< PubSub service or PEP service + PubSub, ///< PubSub service only + Pep ///< PEP service only + }; + /// /// Pre-defined ID of a PubSub item /// @@ -32,6 +41,13 @@ public: Current ///< Item of a singleton node }; + /// + /// Used to indicate a service type mismatch. + /// + struct InvalidServiceType + { + }; + template struct Items { @@ -40,6 +56,7 @@ public: }; using Result = std::variant; + using FeaturesResult = std::variant, InvalidServiceType, QXmppStanza::Error>; using NodesResult = std::variant, QXmppStanza::Error>; using InstantNodeResult = std::variant; template @@ -58,6 +75,7 @@ public: ~QXmppPubSubManager(); // Generic PubSub (the PubSub service is the given entity) + QFuture requestFeatures(const QString &serviceJid, ServiceType serviceType = PubSubOrPep); QFuture fetchNodes(const QString &jid); QFuture createNode(const QString &jid, const QString &nodeName); QFuture createNode(const QString &jid, const QString &nodeName, const QXmppPubSubNodeConfig &config); diff --git a/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp b/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp index 602e9c39..11c41dd2 100644 --- a/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp +++ b/tests/qxmpppubsubmanager/tst_qxmpppubsubmanager.cpp @@ -26,6 +26,9 @@ using PSManager = QXmppPubSubManager; using Affiliation = QXmppPubSubAffiliation; using AffiliationType = QXmppPubSubAffiliation::Affiliation; +const char *ns_pubsub = "http://jabber.org/protocol/pubsub"; +const char *ns_pubsub_auto_create = "http://jabber.org/protocol/pubsub#auto-create"; + class TestEventManager : public QXmppPubSubEventManager { public: @@ -67,6 +70,7 @@ class tst_QXmppPubSubManager : public QObject private: Q_SLOT void testDiscoFeatures(); + Q_SLOT void testRequestFeatures(); Q_SLOT void testFetchNodes(); Q_SLOT void testFetchPepNodes(); Q_SLOT void testCreateNodes_data(); @@ -115,6 +119,105 @@ void tst_QXmppPubSubManager::testDiscoFeatures() QCOMPARE(manager.discoveryFeatures(), QStringList { "http://jabber.org/protocol/pubsub#rsm" }); } +void tst_QXmppPubSubManager::testRequestFeatures() +{ + auto [test, psManager] = Client(); + + auto future = psManager->requestFeatures("pubsub.shakespeare.lit"); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "")); + + expectFutureVariant(future); + + future = psManager->requestFeatures("pubsub.shakespeare.lit"); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + auto features = expectFutureVariant>(future); + QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); + + future = psManager->requestFeatures("juliet@capulet.lit"); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + features = expectFutureVariant>(future); + QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); + + future = psManager->requestFeatures("juliet@capulet.lit", QXmppPubSubManager::PubSub); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + expectFutureVariant(future); + + future = psManager->requestFeatures("pubsub.shakespeare.lit", QXmppPubSubManager::PubSub); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + features = expectFutureVariant>(future); + QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); + + future = psManager->requestFeatures("pubsub.shakespeare.lit", QXmppPubSubManager::Pep); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + expectFutureVariant(future); + + future = psManager->requestFeatures("juliet@capulet.lit", QXmppPubSubManager::Pep); + test.expect(QStringLiteral("" + "" + "")); + test.inject(QStringLiteral("" + "" + "" + "" + "" + "")); + + features = expectFutureVariant>(future); + QCOMPARE(features, (QVector { ns_pubsub, ns_pubsub_auto_create })); +} + void tst_QXmppPubSubManager::testFetchNodes() { auto [test, psManager] = Client(); -- cgit v1.2.3