Skip to content
This repository was archived by the owner on Mar 20, 2025. It is now read-only.

Commit 524a02d

Browse files
authored
Implement XEP-0272: Multiparty Jingle (Muji) stanza extensions (#457)
Adds the presence extension and the extension of the Jingle IQ. https://xmpp.org/extensions/xep-0272.html
1 parent 48bc077 commit 524a02d

File tree

9 files changed

+196
-0
lines changed

9 files changed

+196
-0
lines changed

doc/doap.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,15 @@ SPDX-License-Identifier: CC0-1.0
390390
<xmpp:since>1.5</xmpp:since>
391391
</xmpp:SupportedXep>
392392
</implements>
393+
<implements>
394+
<xmpp:SupportedXep>
395+
<xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0272.html'/>
396+
<xmpp:status>complete</xmpp:status>
397+
<xmpp:version>0.2</xmpp:version>
398+
<xmpp:since>1.5</xmpp:since>
399+
<xmpp:note>Based on https://github.com/xsf/xeps/pull/1139</xmpp:note>
400+
</xmpp:SupportedXep>
401+
</implements>
393402
<implements>
394403
<xmpp:SupportedXep>
395404
<xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0280.html'/>

src/base/QXmppConstants.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ const char *ns_bob = "urn:xmpp:bob";
129129
const char *ns_conference = "jabber:x:conference";
130130
// XEP-0264: Jingle Content Thumbnails
131131
const char *ns_thumbs = "urn:xmpp:thumbs:1";
132+
// XEP-0272: Multiparty Jingle (Muji)
133+
const char *ns_muji = "urn:xmpp:jingle:muji:0";
132134
// XEP-0280: Message Carbons
133135
const char *ns_carbons = "urn:xmpp:carbons:2";
134136
// XEP-0297: Stanza Forwarding

src/base/QXmppConstants_p.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ extern const char *ns_bob;
141141
extern const char *ns_conference;
142142
// XEP-0264: Jingle Content Thumbnails
143143
extern const char *ns_thumbs;
144+
// XEP-0272: Multiparty Jingle (Muji)
145+
extern const char *ns_muji;
144146
// XEP-0280: Message Carbons
145147
extern const char *ns_carbons;
146148
// XEP-0297: Stanza Forwarding

src/base/QXmppJingleIq.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-FileCopyrightText: 2010 Jeremy Lainé <jeremy.laine@m4x.org>
2+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
23
//
34
// SPDX-License-Identifier: LGPL-2.1-or-later
45

@@ -718,6 +719,8 @@ class QXmppJingleIqPrivate : public QSharedData
718719
QString responder;
719720
QString sid;
720721

722+
QString mujiGroupChatJid;
723+
721724
QList<QXmppJingleIq::Content> contents;
722725
QXmppJingleIq::Reason reason;
723726
bool ringing;
@@ -871,6 +874,30 @@ void QXmppJingleIq::setSid(const QString &sid)
871874
d->sid = sid;
872875
}
873876

877+
///
878+
/// Returns the JID of the \xep{0272, Multiparty Jingle (Muji)} group chat.
879+
///
880+
/// \return the Muji group chat JID
881+
///
882+
/// \since QXmpp 1.5
883+
///
884+
QString QXmppJingleIq::mujiGroupChatJid() const
885+
{
886+
return d->mujiGroupChatJid;
887+
}
888+
889+
///
890+
/// Sets the JID of the \xep{0272, Multiparty Jingle (Muji)} group chat.
891+
///
892+
/// \param mujiGroupChatJid Muji group chat JID
893+
///
894+
/// \since QXmpp 1.5
895+
///
896+
void QXmppJingleIq::setMujiGroupChatJid(const QString &mujiGroupChatJid)
897+
{
898+
d->mujiGroupChatJid = mujiGroupChatJid;
899+
}
900+
874901
/// \cond
875902
bool QXmppJingleIq::isJingleIq(const QDomElement &element)
876903
{
@@ -892,6 +919,12 @@ void QXmppJingleIq::parseElementFromChild(const QDomElement &element)
892919
d->responder = jingleElement.attribute(QStringLiteral("responder"));
893920
d->sid = jingleElement.attribute(QStringLiteral("sid"));
894921

922+
// XEP-0272: Multiparty Jingle (Muji)
923+
if (const auto mujiGroupChatElement = jingleElement.firstChildElement(QStringLiteral("muji"));
924+
mujiGroupChatElement.namespaceURI() == ns_muji) {
925+
d->mujiGroupChatJid = mujiGroupChatElement.attribute(QStringLiteral("room"));
926+
}
927+
895928
// content
896929
d->contents.clear();
897930
QDomElement contentElement = jingleElement.firstChildElement(QStringLiteral("content"));
@@ -917,9 +950,19 @@ void QXmppJingleIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
917950
helperToXmlAddAttribute(writer, QStringLiteral("initiator"), d->initiator);
918951
helperToXmlAddAttribute(writer, QStringLiteral("responder"), d->responder);
919952
helperToXmlAddAttribute(writer, QStringLiteral("sid"), d->sid);
953+
954+
// XEP-0272: Multiparty Jingle (Muji)
955+
if (!d->mujiGroupChatJid.isEmpty()) {
956+
writer->writeStartElement(QStringLiteral("muji"));
957+
writer->writeDefaultNamespace(ns_muji);
958+
helperToXmlAddAttribute(writer, QStringLiteral("room"), d->mujiGroupChatJid);
959+
writer->writeEndElement();
960+
}
961+
920962
for (const auto &content : d->contents) {
921963
content.toXml(writer);
922964
}
965+
923966
d->reason.toXml(writer);
924967

925968
// ringing

src/base/QXmppJingleIq.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-FileCopyrightText: 2010 Jeremy Lainé <jeremy.laine@m4x.org>
2+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
23
//
34
// SPDX-License-Identifier: LGPL-2.1-or-later
45

@@ -303,6 +304,9 @@ class QXMPP_EXPORT QXmppJingleIq : public QXmppIq
303304
QString sid() const;
304305
void setSid(const QString &sid);
305306

307+
QString mujiGroupChatJid() const;
308+
void setMujiGroupChatJid(const QString &mujiGroupChatJid);
309+
306310
/// \cond
307311
static bool isJingleIq(const QDomElement &element);
308312
/// \endcond

src/base/QXmppPresence.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// SPDX-FileCopyrightText: 2009 Manjeet Dahiya <manjeetdahiya@gmail.com>
2+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
23
//
34
// SPDX-License-Identifier: LGPL-2.1-or-later
45

56
#include "QXmppPresence.h"
67

78
#include "QXmppConstants_p.h"
9+
#include "QXmppJingleIq.h"
810
#include "QXmppUtils.h"
911

1012
#include <QDateTime>
@@ -59,6 +61,10 @@ class QXmppPresencePrivate : public QSharedData
5961
QByteArray photoHash;
6062
QXmppPresence::VCardUpdateType vCardUpdateType;
6163

64+
// XEP-0272: Multiparty Jingle (Muji)
65+
bool isPreparingMujiSession = false;
66+
QVector<QXmppJingleIq::Content> mujiContents;
67+
6268
// XEP-0319: Last User Interaction in Presence
6369
QDateTime lastUserInteraction;
6470

@@ -253,6 +259,54 @@ QStringList QXmppPresence::capabilityExt() const
253259
return d->capabilityExt;
254260
}
255261

262+
///
263+
/// Returns whether a \xep{0272, Multiparty Jingle (Muji)} session is being prepared.
264+
///
265+
/// \return whether a Muji session is being prepared
266+
///
267+
/// \since QXmpp 1.5
268+
///
269+
bool QXmppPresence::isPreparingMujiSession() const
270+
{
271+
return d->isPreparingMujiSession;
272+
}
273+
274+
///
275+
/// Sets whether a \xep{0272, Multiparty Jingle (Muji)} session is being prepared.
276+
///
277+
/// \param isPreparingMujiSession whether a Muji session is being prepared
278+
///
279+
/// \since QXmpp 1.5
280+
///
281+
void QXmppPresence::setIsPreparingMujiSession(bool isPreparingMujiSession)
282+
{
283+
d->isPreparingMujiSession = isPreparingMujiSession;
284+
}
285+
286+
///
287+
/// Returns \xep{0272, Multiparty Jingle (Muji)} contents.
288+
///
289+
/// \return Muji contents
290+
///
291+
/// \since QXmpp 1.5
292+
///
293+
QVector<QXmppJingleIq::Content> QXmppPresence::mujiContents() const
294+
{
295+
return d->mujiContents;
296+
}
297+
298+
///
299+
/// Sets \xep{0272, Multiparty Jingle (Muji)} contents.
300+
///
301+
/// \param mujiContents Muji contents
302+
///
303+
/// \since QXmpp 1.5
304+
///
305+
void QXmppPresence::setMujiContents(const QVector<QXmppJingleIq::Content> &mujiContents)
306+
{
307+
d->mujiContents = mujiContents;
308+
}
309+
256310
/// Returns the MUC item.
257311

258312
QXmppMucItem QXmppPresence::mucItem() const
@@ -447,6 +501,19 @@ void QXmppPresence::parseExtension(const QDomElement &element, QXmppElementList
447501
d->vCardUpdateType = VCardUpdateValidPhoto;
448502
}
449503
}
504+
// XEP-0272: Multiparty Jingle (Muji)
505+
} else if (element.tagName() == QStringLiteral("muji") && element.namespaceURI() == ns_muji) {
506+
if (!element.firstChildElement(QStringLiteral("preparing")).isNull()) {
507+
d->isPreparingMujiSession = true;
508+
}
509+
510+
for (auto contentElement = element.firstChildElement(QStringLiteral("content"));
511+
!contentElement.isNull();
512+
contentElement = contentElement.nextSiblingElement(QStringLiteral("content"))) {
513+
QXmppJingleIq::Content content;
514+
content.parse(contentElement);
515+
d->mujiContents.append(content);
516+
}
450517
// XEP-0319: Last User Interaction in Presence
451518
} else if (element.tagName() == QStringLiteral("idle") && element.namespaceURI() == ns_idle) {
452519
if (element.hasAttribute(QStringLiteral("since"))) {
@@ -537,6 +604,22 @@ void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const
537604
xmlWriter->writeEndElement();
538605
}
539606

607+
// XEP-0272: Multiparty Jingle (Muji)
608+
if (d->isPreparingMujiSession || !d->mujiContents.isEmpty()) {
609+
xmlWriter->writeStartElement(QStringLiteral("muji"));
610+
xmlWriter->writeDefaultNamespace(ns_muji);
611+
612+
if (d->isPreparingMujiSession) {
613+
xmlWriter->writeEmptyElement(QStringLiteral("preparing"));
614+
}
615+
616+
for (const auto &mujiContent : d->mujiContents) {
617+
mujiContent.toXml(xmlWriter);
618+
}
619+
620+
xmlWriter->writeEndElement();
621+
}
622+
540623
// XEP-0319: Last User Interaction in Presence
541624
if (!d->lastUserInteraction.isNull() && d->lastUserInteraction.isValid()) {
542625
xmlWriter->writeStartElement(QStringLiteral("idle"));

src/base/QXmppPresence.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// SPDX-FileCopyrightText: 2009 Manjeet Dahiya <manjeetdahiya@gmail.com>
2+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
23
//
34
// SPDX-License-Identifier: LGPL-2.1-or-later
45

56
#ifndef QXMPPPRESENCE_H
67
#define QXMPPPRESENCE_H
78

9+
#include "QXmppJingleIq.h"
810
#include "QXmppMucIq.h"
911
#include "QXmppStanza.h"
1012

@@ -106,6 +108,13 @@ class QXMPP_EXPORT QXmppPresence : public QXmppStanza
106108

107109
QStringList capabilityExt() const;
108110

111+
// XEP-0272: Multiparty Jingle (Muji)
112+
bool isPreparingMujiSession() const;
113+
void setIsPreparingMujiSession(bool isPreparingMujiSession);
114+
115+
QVector<QXmppJingleIq::Content> mujiContents() const;
116+
void setMujiContents(const QVector<QXmppJingleIq::Content> &mujiContents);
117+
109118
// XEP-0319: Last User Interaction in Presence
110119
QDateTime lastUserInteraction() const;
111120
void setLastUserInteraction(const QDateTime &);

tests/qxmppjingleiq/tst_qxmppjingleiq.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org>
2+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
23
//
34
// SPDX-License-Identifier: LGPL-2.1-or-later
45

@@ -362,6 +363,7 @@ void tst_QXmppJingleIq::testSession()
362363
" action=\"session-initiate\""
363364
" initiator=\"romeo@montague.lit/orchard\""
364365
" sid=\"a73sjjvkla37jfea\">"
366+
"<muji xmlns=\"urn:xmpp:jingle:muji:0\" room=\"darkcave@chat.shakespeare.lit\"/>"
365367
"<content creator=\"initiator\" name=\"this-is-a-stub\">"
366368
"<description xmlns=\"urn:xmpp:jingle:apps:stub:0\"/>"
367369
"<transport xmlns=\"urn:xmpp:jingle:transports:stub:0\"/>"
@@ -374,6 +376,7 @@ void tst_QXmppJingleIq::testSession()
374376
QCOMPARE(session.action(), QXmppJingleIq::SessionInitiate);
375377
QCOMPARE(session.initiator(), QLatin1String("romeo@montague.lit/orchard"));
376378
QCOMPARE(session.sid(), QLatin1String("a73sjjvkla37jfea"));
379+
QCOMPARE(session.mujiGroupChatJid(), QStringLiteral("darkcave@chat.shakespeare.lit"));
377380
QCOMPARE(session.contents().size(), 1);
378381
QCOMPARE(session.contents()[0].creator(), QLatin1String("initiator"));
379382
QCOMPARE(session.contents()[0].name(), QLatin1String("this-is-a-stub"));

tests/qxmpppresence/tst_qxmpppresence.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-FileCopyrightText: 2012 Oliver Goffart <ogoffart@woboq.com>
22
// SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org>
3+
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
34
//
45
// SPDX-License-Identifier: LGPL-2.1-or-later
56

@@ -21,6 +22,7 @@ private slots:
2122
void testPresenceWithMucItem();
2223
void testPresenceWithMucPassword();
2324
void testPresenceWithMucSupport();
25+
void testPresenceWithMuji();
2426
void testPresenceWithLastUserInteraction();
2527
void testPresenceWithMix();
2628
void testPresenceWithVCard();
@@ -250,6 +252,45 @@ void tst_QXmppPresence::testPresenceWithMucSupport()
250252
serializePacket(presence, xml);
251253
}
252254

255+
void tst_QXmppPresence::testPresenceWithMuji()
256+
{
257+
const QByteArray xml(
258+
"<presence>"
259+
"<muji xmlns=\"urn:xmpp:jingle:muji:0\">"
260+
"<preparing/>"
261+
"<content creator=\"initiator\" name=\"video\"/>"
262+
"<content creator=\"initiator\" name=\"voice\"/>"
263+
"</muji>"
264+
"</presence>");
265+
266+
QXmppPresence presence1;
267+
QVERIFY(!presence1.isPreparingMujiSession());
268+
QVERIFY(presence1.mujiContents().isEmpty());
269+
parsePacket(presence1, xml);
270+
271+
QVERIFY(presence1.isPreparingMujiSession());
272+
QCOMPARE(presence1.mujiContents().size(), 2);
273+
QCOMPARE(presence1.mujiContents().at(0).name(), QStringLiteral("video"));
274+
QCOMPARE(presence1.mujiContents().at(1).name(), QStringLiteral("voice"));
275+
serializePacket(presence1, xml);
276+
277+
QXmppPresence presence2;
278+
presence2.setIsPreparingMujiSession(true);
279+
QXmppJingleIq::Content mujiContent1;
280+
mujiContent1.setCreator(QStringLiteral("initiator"));
281+
mujiContent1.setName(QStringLiteral("video"));
282+
QXmppJingleIq::Content mujiContent2;
283+
mujiContent2.setCreator(QStringLiteral("initiator"));
284+
mujiContent2.setName(QStringLiteral("voice"));
285+
presence2.setMujiContents({ mujiContent1, mujiContent2 });
286+
287+
QVERIFY(presence2.isPreparingMujiSession());
288+
QCOMPARE(presence2.mujiContents().size(), 2);
289+
QCOMPARE(presence2.mujiContents().at(0).name(), QStringLiteral("video"));
290+
QCOMPARE(presence2.mujiContents().at(1).name(), QStringLiteral("voice"));
291+
serializePacket(presence2, xml);
292+
}
293+
253294
void tst_QXmppPresence::testPresenceWithLastUserInteraction()
254295
{
255296
const QByteArray xml(

0 commit comments

Comments
 (0)