diff options
| author | Melvin Keskin <melvo@olomono.de> | 2022-10-02 21:46:52 +0200 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2022-10-02 21:48:01 +0200 |
| commit | 9ea4dc9cb762f8108b47124250e0c9e76a5b2cde (patch) | |
| tree | 4908525119aa115e1e1b66af84563829b9342d45 | |
| parent | 9f9d672e350ef4ce65983d2cac590aa23d74ac96 (diff) | |
| download | qxmpp-9ea4dc9cb762f8108b47124250e0c9e76a5b2cde.tar.gz | |
Implement XEP-0167: Jingle RTP Sessions error conditions (#485)
| -rw-r--r-- | src/base/QXmppConstants.cpp | 1 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 1 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.cpp | 57 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.h | 15 | ||||
| -rw-r--r-- | tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 98 |
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" |
