diff options
| author | Tibor Csötönyi <work@taibsu.de> | 2023-05-03 15:33:35 +0200 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2023-05-14 23:58:00 +0200 |
| commit | 2fde987d39dc66f028ea3ff44929ebd6e2b37f90 (patch) | |
| tree | 14ce02a68b801caf984f74700ea28c0a85fc5b3b | |
| parent | 44e9657c4e1551697f496cc9415f3d458103ca5c (diff) | |
Add XEP-0353: Jingle Message Initiation data classes
| -rw-r--r-- | src/base/QXmppConstants.cpp | 2 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.cpp | 275 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.h | 51 | ||||
| -rw-r--r-- | src/base/QXmppMessage.cpp | 35 | ||||
| -rw-r--r-- | src/base/QXmppMessage.h | 6 | ||||
| -rw-r--r-- | tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 284 | ||||
| -rw-r--r-- | tests/qxmppmessage/tst_qxmppmessage.cpp | 24 |
8 files changed, 665 insertions, 14 deletions
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index be5b62c7..0ddd3db2 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -160,6 +160,8 @@ const char *ns_chat_markers = "urn:xmpp:chat-markers:0"; const char *ns_message_processing_hints = "urn:xmpp:hints"; // XEP-0352: Client State Indication const char *ns_csi = "urn:xmpp:csi:0"; +// XEP-0353: Jingle Message Initiation +const char *ns_jingle_message_initiation = "urn:xmpp:jingle-message:0"; // XEP-0357: Push Notifications const char *ns_push = "urn:xmpp:push:0"; // XEP-0359: Unique and Stable Stanza IDs diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index b90346b6..c859de96 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -172,6 +172,8 @@ extern const char *ns_chat_markers; extern const char *ns_message_processing_hints; // XEP-0352: Client State Indication extern const char *ns_csi; +// XEP-0353: Jingle Message Initiation +extern const char *ns_jingle_message_initiation; // XEP-0357: Push Notifications extern const char *ns_push; // XEP-0359: Unique and Stable Stanza IDs diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index d769e809..67883fc8 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -2930,3 +2930,278 @@ bool QXmppJingleRtpHeaderExtensionProperty::isJingleRtpHeaderExtensionProperty(c return element.tagName() == QStringLiteral("rtp-hdrext") && element.namespaceURI() == ns_jingle_rtp_header_extensions_negotiation; } + +class QXmppJingleMessageInitiationElementPrivate : public QSharedData +{ +public: + QXmppJingleMessageInitiationElementPrivate() = default; + + QXmppJingleMessageInitiationElement::Type type { QXmppJingleMessageInitiationElement::Type::None }; + QString id; + + std::optional<QXmppJingleDescription> description; + std::optional<QXmppJingleReason> reason; + QString migratedTo; + + bool containsTieBreak; +}; + +QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppJingleReason) + +/// +/// \enum QXmppJingleMessageInitiationElement::Type +/// +/// Possible types of Jingle Message Initiation elements +/// + +/// +/// \class QXmppJingleMessageInitiationElement +/// +/// \brief The QXmppJingleMessageInitiationElement class represents a Jingle Message Initiation +/// element as specified by \xep{0353}: Jingle Message Initiation. +/// +/// \ingroup Stanzas +/// +/// \since QXmpp 1.6 +/// + +/// +/// \brief Constructs a Jingle Message Initiation element. +/// \param type The JMI element type +/// +QXmppJingleMessageInitiationElement::QXmppJingleMessageInitiationElement() + : d(new QXmppJingleMessageInitiationElementPrivate()) +{ +} + +/// +/// Returns the Jingle Message Initiation element type +/// +QXmppJingleMessageInitiationElement::Type QXmppJingleMessageInitiationElement::type() const +{ + return d->type; +} + +/// +/// Sets the Jingle Message Initiation element type. +/// +void QXmppJingleMessageInitiationElement::setType(Type type) +{ + d->type = type; +} + +/// +/// Returns the Jingle Message Initiation element id. +/// +QString QXmppJingleMessageInitiationElement::id() const +{ + return d->id; +} + +/// +/// Sets the Jingle Message Initiation element id. +/// +void QXmppJingleMessageInitiationElement::setId(const QString &id) +{ + d->id = id; +} + +/// +/// Returns the Jingle Message Initiation element description. +/// +std::optional<QXmppJingleDescription> QXmppJingleMessageInitiationElement::description() const +{ + return d->description; +} + +/// +/// Sets the Jingle Message Initiation element description. +/// +void QXmppJingleMessageInitiationElement::setDescription(std::optional<QXmppJingleDescription> description) +{ + d->description = description; +} + +/// +/// Returns the Jingle Message Initiation element reason. +/// +std::optional<QXmppJingleReason> QXmppJingleMessageInitiationElement::reason() const +{ + return d->reason; +} + +/// +/// Sets the Jingle Message Initiation element reason. +/// +void QXmppJingleMessageInitiationElement::setReason(std::optional<QXmppJingleReason> reason) +{ + d->reason = reason; + + if (d->reason) { + d->reason->setNamespaceUri(ns_jingle); + } +} + +/// +/// Returns true if the Jingle Message Initiation element contains a <tie-break/> tag. +/// +bool QXmppJingleMessageInitiationElement::containsTieBreak() const +{ + return d->containsTieBreak; +} + +/// +/// Sets if the Jingle Message Initiation element contains a <tie-break/> tag. +/// +void QXmppJingleMessageInitiationElement::setContainsTieBreak(bool containsTieBreak) +{ + d->containsTieBreak = containsTieBreak; +} + +/// +/// Returns the Jingle Message Initiation element ID migrated to if the Jingle is being migrated +/// to a different device. +/// +QString QXmppJingleMessageInitiationElement::migratedTo() const +{ + return d->migratedTo; +} + +/// +/// Sets the Jingle Message Initiation element ID migrated to if the Jingle is being migrated +/// to a different device. +/// +void QXmppJingleMessageInitiationElement::setMigratedTo(const QString &migratedTo) +{ + d->migratedTo = migratedTo; +} + +/// \cond +void QXmppJingleMessageInitiationElement::parse(const QDomElement &element) +{ + d->type = stringToJmiElementType(element.nodeName()); + + if (d->type == Type::None) { + return; + } + + d->id = element.attribute(QStringLiteral("id")); + + // Type::Proceed and Type::Ringing don't need any parsing aside of the id. + switch (d->type) { + case Type::Propose: { + if (const auto &descriptionElement = element.firstChildElement("description"); !descriptionElement.isNull()) { + d->description = QXmppJingleDescription(); + d->description->parse(descriptionElement); + } + + break; + } + case Type::Reject: + case Type::Retract: + d->containsTieBreak = !element.firstChildElement("tie-break").isNull(); + + if (const auto &reasonElement = element.firstChildElement("reason"); !reasonElement.isNull()) { + d->reason = QXmppJingleReason(); + d->reason->parse(reasonElement); + } + + break; + case Type::Finish: + if (const auto &reasonElement = element.firstChildElement("reason"); !reasonElement.isNull()) { + d->reason = QXmppJingleReason(); + d->reason->parse(reasonElement); + } + + if (const auto &migratedToElement = element.firstChildElement("migrated"); !migratedToElement.isNull()) { + d->migratedTo = migratedToElement.attribute("to"); + } + + break; + default: + break; + } +} + +void QXmppJingleMessageInitiationElement::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(jmiElementTypeToString(d->type)); + writer->writeDefaultNamespace(ns_jingle_message_initiation); + + helperToXmlAddAttribute(writer, QStringLiteral("id"), d->id); + + if (d->description) { + d->description->toXml(writer); + } + + if (d->reason) { + d->reason->toXml(writer); + } + + if (d->containsTieBreak) { + writer->writeEmptyElement(QStringLiteral("tie-break")); + } + + if (!d->migratedTo.isEmpty()) { + writer->writeEmptyElement(QStringLiteral("migrated")); + helperToXmlAddAttribute(writer, QStringLiteral("to"), d->migratedTo); + } + + writer->writeEndElement(); +} +/// \endcond + +QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppJingleMessageInitiationElement) + +/// +/// Returns true if passed QDomElement is a Jingle Message Initiation element +/// +bool QXmppJingleMessageInitiationElement::isJingleMessageInitiationElement(const QDomElement &element) +{ + return (element.nodeName() == QStringLiteral("propose") || element.nodeName() == QStringLiteral("ringing") || element.nodeName() == QStringLiteral("proceed") || element.nodeName() == QStringLiteral("retract") || element.nodeName() == QStringLiteral("reject") || element.nodeName() == QStringLiteral("finish")) && element.hasAttribute(QStringLiteral("id")) && element.namespaceURI() == ns_jingle_message_initiation; +} + +/// +/// Takes a Jingle Message Initiation element type and parses it to a string. +/// +QString QXmppJingleMessageInitiationElement::jmiElementTypeToString(Type type) const +{ + switch (type) { + case Type::Propose: + return "propose"; + case Type::Ringing: + return "ringing"; + case Type::Proceed: + return "proceed"; + case Type::Reject: + return "reject"; + case Type::Retract: + return "retract"; + case Type::Finish: + return "finish"; + default: + return {}; + } +} + +/// +/// Takes a string and parses it to a Jingle Message Initiation element type. +/// +QXmppJingleMessageInitiationElement::Type QXmppJingleMessageInitiationElement::stringToJmiElementType(const QString &typeStr) const +{ + if (typeStr == "propose") { + return Type::Propose; + } else if (typeStr == "ringing") { + return Type::Ringing; + } else if (typeStr == "proceed") { + return Type::Proceed; + } else if (typeStr == "reject") { + return Type::Reject; + } else if (typeStr == "retract") { + return Type::Retract; + } else if (typeStr == "finish") { + return Type::Finish; + } + + return Type::None; +} diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 6589a5cb..1e425f4d 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -24,6 +24,7 @@ class QXmppJingleRtpEncryptionPrivate; class QXmppJingleRtpFeedbackPropertyPrivate; class QXmppJingleRtpHeaderExtensionPropertyPrivate; class QXmppSdpParameterPrivate; +class QXmppJingleMessageInitiationElementPrivate; class QXMPP_EXPORT QXmppSdpParameter { @@ -595,6 +596,54 @@ private: QSharedDataPointer<QXmppJingleIqPrivate> d; }; -Q_DECLARE_METATYPE(QXmppJingleIq::Reason::RtpErrorCondition) +class QXMPP_EXPORT QXmppJingleMessageInitiationElement +{ +public: + enum class Type { + None, + Propose, + Ringing, + Proceed, + Reject, + Retract, + Finish + }; + + QXmppJingleMessageInitiationElement(); + QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppJingleMessageInitiationElement) + + Type type() const; + void setType(Type type); + + QString id() const; + void setId(const QString &id); + + std::optional<QXmppJingleDescription> description() const; + void setDescription(std::optional<QXmppJingleDescription> description); + + std::optional<QXmppJingleReason> reason() const; + void setReason(std::optional<QXmppJingleReason> reason); + + bool containsTieBreak() const; + void setContainsTieBreak(bool containsTieBreak); + + QString migratedTo() const; + void setMigratedTo(const QString &migratedTo); + + /// \cond + void parse(const QDomElement &element); + void toXml(QXmlStreamWriter *writer) const; + /// \endcond + + static bool isJingleMessageInitiationElement(const QDomElement &); + +private: + QString jmiElementTypeToString(Type type) const; + Type stringToJmiElementType(const QString &typeStr) const; + + QSharedDataPointer<QXmppJingleMessageInitiationElementPrivate> d; +}; + +Q_DECLARE_METATYPE(QXmppJingleReason::RtpErrorCondition) #endif diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp index 9527f5ed..be6cd83e 100644 --- a/src/base/QXmppMessage.cpp +++ b/src/base/QXmppMessage.cpp @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2010 Jeremy Lainé <jeremy.laine@m4x.org> // SPDX-FileCopyrightText: 2018 Linus Jahn <lnj@kaidan.im> // SPDX-FileCopyrightText: 2021 Melvin Keskin <melvo@olomono.de> +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> // // SPDX-License-Identifier: LGPL-2.1-or-later @@ -11,6 +12,7 @@ #include "QXmppConstants_p.h" #include "QXmppFileShare.h" #include "QXmppGlobal_p.h" +#include "QXmppJingleData.h" #include "QXmppMessageReaction.h" #include "QXmppMixInvitation.h" #ifdef BUILD_OMEMO @@ -123,6 +125,9 @@ public: // XEP-0334: Message Processing Hints quint8 hints; + // XEP-0353: Jingle Message Initiation + std::optional<QXmppJingleMessageInitiationElement> jingleMessageInitiationElement; + // XEP-0359: Unique and Stable Stanza IDs QString stanzaId; QString stanzaIdBy; @@ -872,6 +877,24 @@ void QXmppMessage::removeAllHints() } /// +/// Returns a Jingle Message Initiation element as defined in \xep{0353}: Jingle Message +/// Initiation. +/// +std::optional<QXmppJingleMessageInitiationElement> QXmppMessage::jingleMessageInitiationElement() const +{ + return d->jingleMessageInitiationElement; +} + +/// +/// Sets a Jingle Message Initiation element as defined in \xep{0353}: Jingle Message +/// Initiation. +/// +void QXmppMessage::setJingleMessageInitiationElement(const std::optional<QXmppJingleMessageInitiationElement> &jingleMessageInitiationElement) +{ + d->jingleMessageInitiationElement = jingleMessageInitiationElement; +} + +/// /// Returns the stanza ID of the message according to \xep{0359}: Unique and /// Stable Stanza IDs. /// @@ -1393,6 +1416,13 @@ bool QXmppMessage::parseExtension(const QDomElement &element, QXmpp::SceMode sce } return true; } + // XEP-0353: Jingle Message Initiation + if (QXmppJingleMessageInitiationElement::isJingleMessageInitiationElement(element)) { + QXmppJingleMessageInitiationElement jingleMessageInitiationElement; + jingleMessageInitiationElement.parse(element); + d->jingleMessageInitiationElement = jingleMessageInitiationElement; + return true; + } // XEP-0359: Unique and Stable Stanza IDs if (checkElement(element, QStringLiteral("stanza-id"), ns_sid)) { d->stanzaId = element.attribute(QStringLiteral("id")); @@ -1811,6 +1841,11 @@ void QXmppMessage::serializeExtensions(QXmlStreamWriter *writer, QXmpp::SceMode writer->writeEndElement(); } + // XEP-0353: Jingle Message Initiation + if (d->jingleMessageInitiationElement) { + d->jingleMessageInitiationElement->toXml(writer); + } + // XEP-0367: Message Attaching if (!d->attachId.isEmpty()) { writer->writeStartElement(QStringLiteral("attach-to")); diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h index be9e9b96..cf6df18d 100644 --- a/src/base/QXmppMessage.h +++ b/src/base/QXmppMessage.h @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2010 Jeremy Lainé <jeremy.laine@m4x.org> // SPDX-FileCopyrightText: 2018 Linus Jahn <lnj@kaidan.im> // SPDX-FileCopyrightText: 2020 Melvin Keskin <melvo@olomono.de> +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> // // SPDX-License-Identifier: LGPL-2.1-or-later @@ -18,6 +19,7 @@ class QXmppMessagePrivate; class QXmppBitsOfBinaryDataList; +class QXmppJingleMessageInitiationElement; class QXmppMessageReaction; class QXmppMixInvitation; #ifdef BUILD_OMEMO @@ -205,6 +207,10 @@ public: void removeHint(const Hint hint); void removeAllHints(); + // XEP-0353: Jingle Message Initiation + std::optional<QXmppJingleMessageInitiationElement> jingleMessageInitiationElement() const; + void setJingleMessageInitiationElement(const std::optional<QXmppJingleMessageInitiationElement> &jingleMessageInitiationElement); + // XEP-0359: Unique and Stable Stanza IDs QString stanzaId() const; void setStanzaId(const QString &id); diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp index 1aa88866..1d9c47fc 100644 --- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp +++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp @@ -56,6 +56,10 @@ private: Q_SLOT void testPayloadTypeRtpFeedbackNegotiation(); Q_SLOT void testRtpErrorCondition_data(); Q_SLOT void testRtpErrorCondition(); + + Q_SLOT void testIsJingleMessageInitiationElement_data(); + Q_SLOT void testIsJingleMessageInitiationElement(); + Q_SLOT void testJingleMessageInitiationElement(); }; void tst_QXmppJingleIq::testIsSdpParameter_data() @@ -1390,34 +1394,288 @@ void tst_QXmppJingleIq::testRtpErrorCondition() iq2.setAction(QXmppJingleIq::SessionTerminate); switch (condition) { - case QXmppJingleIq::Reason::NoErrorCondition: - iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::NoErrorCondition); + case QXmppJingleReason::NoErrorCondition: + iq2.reason().setRtpErrorCondition(QXmppJingleReason::NoErrorCondition); break; - case QXmppJingleIq::Reason::InvalidCrypto: - iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::InvalidCrypto); + case QXmppJingleReason::InvalidCrypto: + iq2.reason().setRtpErrorCondition(QXmppJingleReason::InvalidCrypto); break; - case QXmppJingleIq::Reason::CryptoRequired: - iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::CryptoRequired); + case QXmppJingleReason::CryptoRequired: + iq2.reason().setRtpErrorCondition(QXmppJingleReason::CryptoRequired); break; } - iq2.reason().setType(QXmppJingleIq::Reason::SecurityError); + iq2.reason().setType(QXmppJingleReason::SecurityError); const auto rtpErrorCondition2 = iq2.reason().rtpErrorCondition(); switch (condition) { - case QXmppJingleIq::Reason::NoErrorCondition: - QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::NoErrorCondition); + case QXmppJingleReason::NoErrorCondition: + QVERIFY(rtpErrorCondition2 == QXmppJingleReason::NoErrorCondition); break; - case QXmppJingleIq::Reason::InvalidCrypto: - QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::InvalidCrypto); + case QXmppJingleReason::InvalidCrypto: + QVERIFY(rtpErrorCondition2 == QXmppJingleReason::InvalidCrypto); break; - case QXmppJingleIq::Reason::CryptoRequired: - QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::CryptoRequired); + case QXmppJingleReason::CryptoRequired: + QVERIFY(rtpErrorCondition2 == QXmppJingleReason::CryptoRequired); break; } serializePacket(iq2, xml); } +void tst_QXmppJingleIq::testIsJingleMessageInitiationElement_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("isValid"); + + // --- Propose --- + + QTest::newRow("validPropose") + << QByteArrayLiteral( + "<propose xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'/>" + "</propose>") + << true; + QTest::newRow("invalidProposeIdMissing") + << QByteArrayLiteral( + "<propose xmlns='urn:xmpp:jingle-message:0'>" + "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'/>" + "</propose>") + << false; + QTest::newRow("invalidProposeNamespaceMissing") + << QByteArrayLiteral( + "<propose id='a73sjjvkla37jfea'>" + "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'/>" + "</propose>") + << false; + + // --- Ringing --- + + QTest::newRow("validRinging") + << QByteArrayLiteral("<ringing xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'/>") + << true; + QTest::newRow("invalidRingingIdMissing") + << QByteArrayLiteral("<ringing xmlns='urn:xmpp:jingle-message:0'/>") + << false; + QTest::newRow("invalidRingingNamespaceMissing") + << QByteArrayLiteral("<ringing id='a73sjjvkla37jfea'/>") + << false; + + // --- Proceed --- + + QTest::newRow("validProceed") + << QByteArrayLiteral("<proceed xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'/>") + << true; + QTest::newRow("invalidProceedIdMissing") + << QByteArrayLiteral("<proceed xmlns='urn:xmpp:jingle-message:0'/>") + << false; + QTest::newRow("invalidProceedNamespaceMissing") + << QByteArrayLiteral("<proceed id='a73sjjvkla37jfea'/>") + << false; + + // --- Reject --- + + QTest::newRow("validReject") + << QByteArrayLiteral( + "<reject xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Busy</text>" + "<busy/>" + "</reason>" + "</reject>") + << true; + QTest::newRow("invalidRejectIdMissing") + << QByteArrayLiteral( + "<reject xmlns='urn:xmpp:jingle-message:0'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Busy</text>" + "<busy/>" + "</reason>" + "</reject>") + << false; + QTest::newRow("invalidRejectNamespaceMissing") + << QByteArrayLiteral( + "<reject id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Busy</text>" + "<busy/>" + "</reason>" + "</reject>") + << false; + + // --- Retract --- + + QTest::newRow("validRetract") + << QByteArrayLiteral( + "<retract xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Retracted</text>" + "<cancel/>" + "</reason>" + "</retract>") + << true; + QTest::newRow("invalidRetractIdMissing") + << QByteArrayLiteral( + "<retract xmlns='urn:xmpp:jingle-message:0'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Retracted</text>" + "<cancel/>" + "</reason>" + "</retract>") + << false; + QTest::newRow("invalidRetractNamespaceMissing") + << QByteArrayLiteral( + "<retract id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Retracted</text>" + "<cancel/>" + "</reason>" + "</retract>") + << false; + + // --- Finish --- + + QTest::newRow("validFinish") + << QByteArrayLiteral( + "<finish xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Success</text>" + "<success/>" + "</reason>" + "</finish>") + << true; + QTest::newRow("invalidFinishIdMissing") + << QByteArrayLiteral( + "<finish xmlns='urn:xmpp:jingle-message:0'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Success</text>" + "<success/>" + "</reason>" + "</finish>") + << false; + QTest::newRow("invalidFinishNamespaceMissing") + << QByteArrayLiteral( + "<finish id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Success</text>" + "<success/>" + "</reason>" + "</finish>") + << false; +} + +void tst_QXmppJingleIq::testIsJingleMessageInitiationElement() +{ + QFETCH(QByteArray, xml); + QFETCH(bool, isValid); + + QCOMPARE(QXmppJingleMessageInitiationElement::isJingleMessageInitiationElement(xmlToDom(xml)), isValid); +} + +void tst_QXmppJingleIq::testJingleMessageInitiationElement() +{ + using JmiType = QXmppJingleMessageInitiationElement::Type; + + // --- Propose --- + + const QByteArray proposeXml( + "<propose xmlns='urn:xmpp:jingle-message:0' id='ca3cf894-5325-482f-a412-a6e9f832298d'>" + "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'/>" + "</propose>"); + QXmppJingleMessageInitiationElement proposeElement; + proposeElement.setType(JmiType::Propose); + + parsePacket(proposeElement, proposeXml); + QCOMPARE(proposeElement.id(), QStringLiteral("ca3cf894-5325-482f-a412-a6e9f832298d")); + QCOMPARE(proposeElement.description()->type(), QStringLiteral("urn:xmpp:jingle:apps:rtp:1")); + QCOMPARE(proposeElement.description()->media(), QStringLiteral("audio")); + QCOMPARE(proposeElement.containsTieBreak(), false); // single check if containsTieBreak is set correctly when unused + QCOMPARE(proposeElement.reason(), std::nullopt); // single check if reason is set correctly when unused + serializePacket(proposeElement, proposeXml); + + // --- Ringing --- + + const QByteArray ringingXml("<ringing xmlns='urn:xmpp:jingle-message:0' id='ca3cf894-5325-482f-a412-a6e9f832298d'/>"); + QXmppJingleMessageInitiationElement ringingElement; + ringingElement.setType(JmiType::Ringing); + + parsePacket(ringingElement, ringingXml); + QCOMPARE(ringingElement.id(), QStringLiteral("ca3cf894-5325-482f-a412-a6e9f832298d")); + serializePacket(ringingElement, ringingXml); + + // --- Proceed --- + + const QByteArray proceedXml("<proceed xmlns='urn:xmpp:jingle-message:0' id='ca3cf894-5325-482f-a412-a6e9f832298d'/>"); + QXmppJingleMessageInitiationElement proceedElement; + proceedElement.setType(JmiType::Proceed); + + parsePacket(proceedElement, proceedXml); + QCOMPARE(proceedElement.id(), QStringLiteral("ca3cf894-5325-482f-a412-a6e9f832298d")); + serializePacket(proceedElement, proceedXml); + + // --- Reject --- + + using ReasonType = QXmppJingleReason::Type; + + const QByteArray rejectXml( + "<reject xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Busy</text>" + "<busy/>" + "</reason>" + "<tie-break/>" + "</reject>"); + QXmppJingleMessageInitiationElement rejectElement; + rejectElement.setType(JmiType::Reject); + + parsePacket(rejectElement, rejectXml); + QCOMPARE(rejectElement.id(), QStringLiteral("a73sjjvkla37jfea")); + QCOMPARE(rejectElement.reason()->text(), QStringLiteral("Busy")); + QCOMPARE(rejectElement.reason()->type(), ReasonType::Busy); + QCOMPARE(rejectElement.reason()->namespaceUri(), QStringLiteral("urn:xmpp:jingle:1")); + QCOMPARE(rejectElement.containsTieBreak(), true); + serializePacket(rejectElement, rejectXml); + + // --- Retract --- + + const QByteArray retractXml( + "<retract xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Retracted</text>" + "<cancel/>" + "</reason>" + "</retract>"); + QXmppJingleMessageInitiationElement retractElement; + retractElement.setType(JmiType::Retract); + + parsePacket(retractElement, retractXml); + QCOMPARE(retractElement.id(), QStringLiteral("a73sjjvkla37jfea")); + QCOMPARE(retractElement.reason()->text(), QStringLiteral("Retracted")); + QCOMPARE(retractElement.reason()->type(), ReasonType::Cancel); + QCOMPARE(retractElement.reason()->namespaceUri(), QStringLiteral("urn:xmpp:jingle:1")); + serializePacket(retractElement, retractXml); + + // --- Finish --- + + const QByteArray finishXml( + "<finish xmlns='urn:xmpp:jingle-message:0' id='a73sjjvkla37jfea'>" + "<reason xmlns=\"urn:xmpp:jingle:1\">" + "<text>Success</text>" + "<success/>" + "</reason>" + "<migrated to='989a46a6-f202-4910-a7c3-83c6ba3f3947'/>" + "</finish>"); + QXmppJingleMessageInitiationElement finishElement; + finishElement.setType(JmiType::Finish); + + parsePacket(finishElement, finishXml); + QCOMPARE(finishElement.id(), QStringLiteral("a73sjjvkla37jfea")); + QCOMPARE(finishElement.reason()->text(), QStringLiteral("Success")); + QCOMPARE(finishElement.reason()->type(), ReasonType::Success); + QCOMPARE(finishElement.reason()->namespaceUri(), QStringLiteral("urn:xmpp:jingle:1")); + QCOMPARE(finishElement.migratedTo(), QStringLiteral("989a46a6-f202-4910-a7c3-83c6ba3f3947")); + serializePacket(finishElement, finishXml); +} + QTEST_MAIN(tst_QXmppJingleIq) #include "tst_qxmppjingleiq.moc" diff --git a/tests/qxmppmessage/tst_qxmppmessage.cpp b/tests/qxmppmessage/tst_qxmppmessage.cpp index 98ca5670..61fbed6f 100644 --- a/tests/qxmppmessage/tst_qxmppmessage.cpp +++ b/tests/qxmppmessage/tst_qxmppmessage.cpp @@ -1,12 +1,14 @@ // SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org> // SPDX-FileCopyrightText: 2012 Manjeet Dahiya <manjeetdahiya@gmail.com> // SPDX-FileCopyrightText: 2021 Melvin Keskin <melvo@olomono.de> +// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de> // // SPDX-License-Identifier: LGPL-2.1-or-later #include "QXmppBitsOfBinaryContentId.h" #include "QXmppBitsOfBinaryDataList.h" #include "QXmppEncryptedFileSource.h" +#include "QXmppJingleData.h" #include "QXmppMessage.h" #include "QXmppMessageReaction.h" #include "QXmppMixInvitation.h" @@ -58,6 +60,7 @@ private: Q_SLOT void testE2eeFallbackBody(); Q_SLOT void testFileSharing(); Q_SLOT void testEncryptedFileSource(); + Q_SLOT void testJingleMessageInitiationElement(); }; void tst_QXmppMessage::testBasic_data() @@ -1285,5 +1288,26 @@ void tst_QXmppMessage::testEncryptedFileSource() } } +void tst_QXmppMessage::testJingleMessageInitiationElement() +{ + const QByteArray xml( + "<message to='romeo@montague.example' from='juliet@capulet.example/phone' type='chat'>" + "<store xmlns=\"urn:xmpp:hints\"/>" + "<proceed xmlns='urn:xmpp:jingle-message:0' id='ca3cf894-5325-482f-a412-a6e9f832298d'/>" + "</message>"); + + QXmppMessage message1; + QVERIFY(!message1.jingleMessageInitiationElement()); + + parsePacket(message1, xml); + QVERIFY(message1.jingleMessageInitiationElement()); + serializePacket(message1, xml); + + QXmppMessage message2; + message2.addHint(QXmppMessage::Store); + message2.setJingleMessageInitiationElement(QXmppJingleMessageInitiationElement()); + QVERIFY(message2.jingleMessageInitiationElement()); +} + QTEST_MAIN(tst_QXmppMessage) #include "tst_qxmppmessage.moc" |
