aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelvin Keskin <melvo@olomono.de>2022-10-02 21:46:52 +0200
committerLinus Jahn <lnj@kaidan.im>2022-10-02 21:48:01 +0200
commit9ea4dc9cb762f8108b47124250e0c9e76a5b2cde (patch)
tree4908525119aa115e1e1b66af84563829b9342d45
parent9f9d672e350ef4ce65983d2cac590aa23d74ac96 (diff)
downloadqxmpp-9ea4dc9cb762f8108b47124250e0c9e76a5b2cde.tar.gz
Implement XEP-0167: Jingle RTP Sessions error conditions (#485)
-rw-r--r--src/base/QXmppConstants.cpp1
-rw-r--r--src/base/QXmppConstants_p.h1
-rw-r--r--src/base/QXmppJingleIq.cpp57
-rw-r--r--src/base/QXmppJingleIq.h15
-rw-r--r--tests/qxmppjingleiq/tst_qxmppjingleiq.cpp98
5 files changed, 171 insertions, 1 deletions
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp
index d0dfa3dd..0a8152f9 100644
--- a/src/base/QXmppConstants.cpp
+++ b/src/base/QXmppConstants.cpp
@@ -109,6 +109,7 @@ const char *ns_jingle_rtp = "urn:xmpp:jingle:apps:rtp:1";
const char *ns_jingle_rtp_audio = "urn:xmpp:jingle:apps:rtp:audio";
const char *ns_jingle_rtp_video = "urn:xmpp:jingle:apps:rtp:video";
const char *ns_jingle_rtp_info = "urn:xmpp:jingle:apps:rtp:info:1";
+const char *ns_jingle_rtp_errors = "urn:xmpp:jingle:apps:rtp:errors:1";
// XEP-0184: Message Receipts
const char *ns_message_receipts = "urn:xmpp:receipts";
// XEP-0198: Stream Management
diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h
index ac6c18d8..f5086cc9 100644
--- a/src/base/QXmppConstants_p.h
+++ b/src/base/QXmppConstants_p.h
@@ -121,6 +121,7 @@ extern const char *ns_jingle_rtp;
extern const char *ns_jingle_rtp_audio;
extern const char *ns_jingle_rtp_video;
extern const char *ns_jingle_rtp_info;
+extern const char *ns_jingle_rtp_errors;
// XEP-0184: Message Receipts
extern const char *ns_message_receipts;
// XEP-0198: Stream Management
diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp
index 01e27a04..fc198278 100644
--- a/src/base/QXmppJingleIq.cpp
+++ b/src/base/QXmppJingleIq.cpp
@@ -54,6 +54,12 @@ static const char *jingle_reasons[] = {
"unsupported-transports",
};
+static const QStringList JINGLE_RTP_ERROR_CONDITIONS = {
+ {},
+ QStringLiteral("invalid-crypto"),
+ QStringLiteral("crypto-required")
+};
+
static const QStringList JINGLE_RTP_HEADER_EXTENSIONS_SENDERS = {
QStringLiteral("both"),
QStringLiteral("initiator"),
@@ -947,6 +953,14 @@ QString QXmppJingleIq::Content::toSdp() const
/// \endcond
+///
+/// \enum QXmppJingleIq::Reason::RtpErrorCondition
+///
+/// Condition of an RTP-specific error
+///
+/// \since QXmpp 1.5
+///
+
QXmppJingleIq::Reason::Reason()
: m_type(None)
{
@@ -980,6 +994,30 @@ void QXmppJingleIq::Reason::setType(QXmppJingleIq::Reason::Type type)
m_type = type;
}
+///
+/// Returns the RTP error condition as specified by \xep{0167, Jingle RTP Sessions}.
+///
+/// \return the RTP error condition
+///
+/// \since QXmpp 1.5
+///
+QXmppJingleIq::Reason::RtpErrorCondition QXmppJingleIq::Reason::rtpErrorCondition() const
+{
+ return m_rtpErrorCondition;
+}
+
+///
+/// Sets the RTP error condition as specified by \xep{0167, Jingle RTP Sessions}.
+///
+/// \param rtpErrorCondition RTP error condition
+///
+/// \since QXmpp 1.5
+///
+void QXmppJingleIq::Reason::setRtpErrorCondition(RtpErrorCondition rtpErrorCondition)
+{
+ m_rtpErrorCondition = rtpErrorCondition;
+}
+
/// \cond
void QXmppJingleIq::Reason::parse(const QDomElement &element)
{
@@ -990,6 +1028,18 @@ void QXmppJingleIq::Reason::parse(const QDomElement &element)
break;
}
}
+
+ for (auto child = element.firstChildElement();
+ !child.isNull();
+ child = child.nextSiblingElement()) {
+ if (child.namespaceURI() == ns_jingle_rtp_errors) {
+ if (const auto index = JINGLE_RTP_ERROR_CONDITIONS.indexOf(child.tagName());
+ index != -1) {
+ m_rtpErrorCondition = RtpErrorCondition(index);
+ }
+ break;
+ }
+ }
}
void QXmppJingleIq::Reason::toXml(QXmlStreamWriter *writer) const
@@ -1003,6 +1053,13 @@ void QXmppJingleIq::Reason::toXml(QXmlStreamWriter *writer) const
helperToXmlAddTextElement(writer, QStringLiteral("text"), m_text);
}
writer->writeEmptyElement(jingle_reasons[m_type]);
+
+ if (m_rtpErrorCondition != NoErrorCondition) {
+ writer->writeStartElement(JINGLE_RTP_ERROR_CONDITIONS.at(m_rtpErrorCondition));
+ writer->writeDefaultNamespace(ns_jingle_rtp_errors);
+ writer->writeEndElement();
+ }
+
writer->writeEndElement();
}
/// \endcond
diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h
index 8dd3e68f..5d8de1f8 100644
--- a/src/base/QXmppJingleIq.h
+++ b/src/base/QXmppJingleIq.h
@@ -428,6 +428,15 @@ public:
UnsupportedTransports
};
+ enum RtpErrorCondition {
+ /// There is no error condition.
+ NoErrorCondition,
+ /// The encryption offer is rejected.
+ InvalidCrypto,
+ /// Encryption is required but not offered.
+ CryptoRequired
+ };
+
Reason();
QString text() const;
@@ -436,6 +445,9 @@ public:
Type type() const;
void setType(Type type);
+ RtpErrorCondition rtpErrorCondition() const;
+ void setRtpErrorCondition(RtpErrorCondition rtpErrorCondition);
+
/// \cond
void parse(const QDomElement &element);
void toXml(QXmlStreamWriter *writer) const;
@@ -444,6 +456,7 @@ public:
private:
QString m_text;
Type m_type;
+ RtpErrorCondition m_rtpErrorCondition = NoErrorCondition;
};
QXmppJingleIq();
@@ -500,4 +513,6 @@ private:
QSharedDataPointer<QXmppJingleIqPrivate> d;
};
+Q_DECLARE_METATYPE(QXmppJingleIq::Reason::RtpErrorCondition)
+
#endif
diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
index 0a7b499a..b2d74c83 100644
--- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
+++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp
@@ -45,6 +45,8 @@ private slots:
void testAudioPayloadType();
void testVideoPayloadType();
void testPayloadTypeRtpFeedbackNegotiation();
+ void testRtpErrorCondition_data();
+ void testRtpErrorCondition();
};
void tst_QXmppJingleIq::testIsSdpParameter_data()
@@ -790,7 +792,6 @@ void tst_QXmppJingleIq::testContentRtpHeaderExtensionsNegotiation()
rtpHeaderExtensionProperty2.setId(uint32_t(2));
rtpHeaderExtensionProperty2.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:ntp-64"));
-
QXmppJinglePayloadType payloadType;
payloadType.setId(96);
payloadType.setName(QStringLiteral("speex"));
@@ -1099,5 +1100,100 @@ void tst_QXmppJingleIq::testPayloadTypeRtpFeedbackNegotiation()
serializePacket(payload2, xml);
}
+void tst_QXmppJingleIq::testRtpErrorCondition_data()
+{
+ QTest::addColumn<QByteArray>("xml");
+ QTest::addColumn<QXmppJingleIq::Reason::RtpErrorCondition>("condition");
+
+ QTest::newRow("NoErrorCondition")
+ << QByteArrayLiteral("<iq type=\"set\">"
+ "<jingle xmlns=\"urn:xmpp:jingle:1\" action=\"session-terminate\">"
+ "<reason>"
+ "<security-error/>"
+ "</reason>"
+ "</jingle>"
+ "</iq>")
+ << QXmppJingleIq::Reason::NoErrorCondition;
+ QTest::newRow("InvalidCrypto")
+ << QByteArrayLiteral("<iq type=\"set\">"
+ "<jingle xmlns=\"urn:xmpp:jingle:1\" action=\"session-terminate\">"
+ "<reason>"
+ "<security-error/>"
+ "<invalid-crypto xmlns=\"urn:xmpp:jingle:apps:rtp:errors:1\"/>"
+ "</reason>"
+ "</jingle>"
+ "</iq>")
+ << QXmppJingleIq::Reason::InvalidCrypto;
+ QTest::newRow("CryptoRequired")
+ << QByteArrayLiteral("<iq type=\"set\">"
+ "<jingle xmlns=\"urn:xmpp:jingle:1\" action=\"session-terminate\">"
+ "<reason>"
+ "<security-error/>"
+ "<crypto-required xmlns=\"urn:xmpp:jingle:apps:rtp:errors:1\"/>"
+ "</reason>"
+ "</jingle>"
+ "</iq>")
+ << QXmppJingleIq::Reason::CryptoRequired;
+}
+
+void tst_QXmppJingleIq::testRtpErrorCondition()
+{
+ QFETCH(QByteArray, xml);
+ QFETCH(QXmppJingleIq::Reason::RtpErrorCondition, condition);
+
+ QXmppJingleIq iq1;
+ QCOMPARE(iq1.reason().rtpErrorCondition(), QXmppJingleIq::Reason::NoErrorCondition);
+ parsePacket(iq1, xml);
+
+ const auto rtpErrorCondition1 = iq1.reason().rtpErrorCondition();
+ switch (condition) {
+ case QXmppJingleIq::Reason::NoErrorCondition:
+ QVERIFY(rtpErrorCondition1 == QXmppJingleIq::Reason::NoErrorCondition);
+ break;
+ case QXmppJingleIq::Reason::InvalidCrypto:
+ QVERIFY(rtpErrorCondition1 == QXmppJingleIq::Reason::InvalidCrypto);
+ break;
+ case QXmppJingleIq::Reason::CryptoRequired:
+ QVERIFY(rtpErrorCondition1 == QXmppJingleIq::Reason::CryptoRequired);
+ break;
+ }
+
+ serializePacket(iq1, xml);
+
+ QXmppJingleIq iq2;
+ iq2.setType(QXmppIq::Set);
+ iq2.setId({});
+ iq2.setAction(QXmppJingleIq::SessionTerminate);
+
+ switch (condition) {
+ case QXmppJingleIq::Reason::NoErrorCondition:
+ iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::NoErrorCondition);
+ break;
+ case QXmppJingleIq::Reason::InvalidCrypto:
+ iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::InvalidCrypto);
+ break;
+ case QXmppJingleIq::Reason::CryptoRequired:
+ iq2.reason().setRtpErrorCondition(QXmppJingleIq::Reason::CryptoRequired);
+ break;
+ }
+
+ iq2.reason().setType(QXmppJingleIq::Reason::SecurityError);
+
+ const auto rtpErrorCondition2 = iq2.reason().rtpErrorCondition();
+ switch (condition) {
+ case QXmppJingleIq::Reason::NoErrorCondition:
+ QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::NoErrorCondition);
+ break;
+ case QXmppJingleIq::Reason::InvalidCrypto:
+ QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::InvalidCrypto);
+ break;
+ case QXmppJingleIq::Reason::CryptoRequired:
+ QVERIFY(rtpErrorCondition2 == QXmppJingleIq::Reason::CryptoRequired);
+ break;
+ }
+
+ serializePacket(iq2, xml);
+}
+
QTEST_MAIN(tst_QXmppJingleIq)
#include "tst_qxmppjingleiq.moc"