aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTibor Csötönyi <work@taibsu.de>2023-05-03 15:33:35 +0200
committerLinus Jahn <lnj@kaidan.im>2023-05-14 23:58:00 +0200
commit2fde987d39dc66f028ea3ff44929ebd6e2b37f90 (patch)
tree14ce02a68b801caf984f74700ea28c0a85fc5b3b
parent44e9657c4e1551697f496cc9415f3d458103ca5c (diff)
Add XEP-0353: Jingle Message Initiation data classes
-rw-r--r--src/base/QXmppConstants.cpp2
-rw-r--r--src/base/QXmppConstants_p.h2
-rw-r--r--src/base/QXmppJingleIq.cpp275
-rw-r--r--src/base/QXmppJingleIq.h51
-rw-r--r--src/base/QXmppMessage.cpp35
-rw-r--r--src/base/QXmppMessage.h6
-rw-r--r--tests/qxmppjingleiq/tst_qxmppjingleiq.cpp284
-rw-r--r--tests/qxmppmessage/tst_qxmppmessage.cpp24
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"