Skip to content

Commit

Permalink
Implement XEP-0405 (MIX): Roster IQ extension
Browse files Browse the repository at this point in the history
This adds the MIX extensions for roster queries as defined in XEP-0405:
Mediated Information eXchange (MIX): Participant Server Requirements
[v0.3.1].

https://xmpp.org/extensions/xep-0405.html#mix-roster-capability-sharing
  • Loading branch information
lnjX committed Jan 20, 2019
1 parent 31b0e5c commit b0642a4
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 3 deletions.
77 changes: 74 additions & 3 deletions src/base/QXmppRosterIq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ void QXmppRosterIq::setVersion(const QString &version)
m_version = version;
}

/// Whether to annotate which items are MIX channels.

bool QXmppRosterIq::mixAnnotate() const
{
return m_mixAnnotate;
}

/// Sets whether to include which roster items are MIX channels. This MUST only
/// be enabled in get requests.

void QXmppRosterIq::setMixAnnotate(bool mixAnnotate)
{
m_mixAnnotate = mixAnnotate;
}

/// \cond
bool QXmppRosterIq::isRosterIq(const QDomElement &element)
{
Expand All @@ -74,16 +89,20 @@ bool QXmppRosterIq::isRosterIq(const QDomElement &element)
void QXmppRosterIq::parseElementFromChild(const QDomElement &element)
{
QDomElement queryElement = element.firstChildElement("query");
QDomElement itemElement = queryElement.firstChildElement("item");

setVersion(queryElement.attribute("ver"));

QDomElement itemElement = queryElement.firstChildElement("item");
while(!itemElement.isNull())
{
QXmppRosterIq::Item item;
item.parse(itemElement);
m_items.append(item);
itemElement = itemElement.nextSiblingElement();
itemElement = itemElement.nextSiblingElement("item");
}

QDomElement annotateElement = queryElement.firstChildElement("annotate");
setMixAnnotate(!annotateElement.isNull() && annotateElement.namespaceURI()
== ns_mix_roster);
}

void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
Expand All @@ -94,6 +113,14 @@ void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
// XEP-0237 roster versioning - If the server does not advertise support for roster versioning, the client MUST NOT include the 'ver' attribute.
if(!version().isEmpty())
writer->writeAttribute( "ver", version());

// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
if (m_mixAnnotate) {
writer->writeStartElement("annotate");
writer->writeAttribute("xmlns", ns_mix_roster);
writer->writeEndElement();
}

for(int i = 0; i < m_items.count(); ++i)
m_items.at(i).toXml(writer);
writer->writeEndElement();
Expand Down Expand Up @@ -253,6 +280,34 @@ void QXmppRosterIq::Item::setSubscriptionTypeFromStr(const QString& type)
qWarning("QXmppRosterIq::Item::setTypeFromStr(): invalid type");
}

/// Returns whether this is a MIX channel.

bool QXmppRosterIq::Item::isMixChannel() const
{
return m_isMixChannel;
}

/// Sets whether this is a MIX channel.

void QXmppRosterIq::Item::setIsMixChannel(bool isMixChannel)
{
m_isMixChannel = isMixChannel;
}

/// Returns the participant id for this MIX channel.

QString QXmppRosterIq::Item::mixParticipantId() const
{
return m_mixParticipantId;
}

/// Sets the participant id for this MIX channel.

void QXmppRosterIq::Item::setMixParticipantId(const QString& participantId)
{
m_mixParticipantId = participantId;
}

/// \cond
void QXmppRosterIq::Item::parse(const QDomElement &element)
{
Expand All @@ -267,6 +322,13 @@ void QXmppRosterIq::Item::parse(const QDomElement &element)
m_groups << groupElement.text();
groupElement = groupElement.nextSiblingElement("group");
}

// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
QDomElement channelElement = element.firstChildElement("channel");
if (!channelElement.isNull() && channelElement.namespaceURI() == ns_mix_roster) {
m_isMixChannel = true;
m_mixParticipantId = channelElement.attribute("participant-id");
}
}

void QXmppRosterIq::Item::toXml(QXmlStreamWriter *writer) const
Expand All @@ -283,6 +345,15 @@ void QXmppRosterIq::Item::toXml(QXmlStreamWriter *writer) const
helperToXmlAddTextElement(writer,"group", *i);
++i;
}

// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
if (m_isMixChannel) {
writer->writeStartElement("channel");
writer->writeAttribute("xmlns", ns_mix_roster);
helperToXmlAddAttribute(writer, "participant-id", m_mixParticipantId);
writer->writeEndElement();
}

writer->writeEndElement();
}
/// \endcond
16 changes: 16 additions & 0 deletions src/base/QXmppRosterIq.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ class QXMPP_EXPORT QXmppRosterIq : public QXmppIq
void setSubscriptionStatus(const QString&);
void setSubscriptionType(SubscriptionType);

// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
bool isMixChannel() const;
void setIsMixChannel(bool);

QString mixParticipantId() const;
void setMixParticipantId(const QString&);

/// \cond
void parse(const QDomElement &element);
void toXml(QXmlStreamWriter *writer) const;
Expand All @@ -86,6 +93,9 @@ class QXMPP_EXPORT QXmppRosterIq : public QXmppIq
// can be subscribe/unsubscribe (attribute "ask")
QString m_subscriptionStatus;
QSet<QString> m_groups;
// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
bool m_isMixChannel = false;
QString m_mixParticipantId;
};

QString version() const;
Expand All @@ -94,6 +104,10 @@ class QXMPP_EXPORT QXmppRosterIq : public QXmppIq
void addItem(const Item&);
QList<Item> items() const;

// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
bool mixAnnotate() const;
void setMixAnnotate(bool);

/// \cond
static bool isRosterIq(const QDomElement &element);
/// \endcond
Expand All @@ -108,6 +122,8 @@ class QXMPP_EXPORT QXmppRosterIq : public QXmppIq
QList<Item> m_items;
// XEP-0237 Roster Versioning
QString m_version;
// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements
bool m_mixAnnotate = false;
};

#endif // QXMPPROSTERIQ_H
42 changes: 42 additions & 0 deletions tests/qxmpprosteriq/tst_qxmpprosteriq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ private slots:
void testItem();
void testVersion_data();
void testVersion();
void testMixAnnotate();
void testMixChannel();
};

void tst_QXmppRosterIq::testItem_data()
Expand Down Expand Up @@ -114,5 +116,45 @@ void tst_QXmppRosterIq::testVersion()
serializePacket(iq, xml);
}

void tst_QXmppRosterIq::testMixAnnotate()
{
const QByteArray xml(
"<iq from=\"juliet@example.com/balcony\" "
"type=\"get\">"
"<query xmlns=\"jabber:iq:roster\">"
"<annotate xmlns=\"urn:xmpp:mix:roster:0\"/>"
"</query>"
"</iq>"
);

QXmppRosterIq iq;
parsePacket(iq, xml);
QCOMPARE(iq.mixAnnotate(), true);
serializePacket(iq, xml);

iq.setMixAnnotate(false);
QCOMPARE(iq.mixAnnotate(), false);
}

void tst_QXmppRosterIq::testMixChannel()
{
const QByteArray xml(
"<item jid=\"balcony@example.net\">"
"<channel xmlns=\"urn:xmpp:mix:roster:0\" participant-id=\"123456\"/>"
"</item>"
);

QXmppRosterIq::Item item;
parsePacket(item, xml);
QCOMPARE(item.isMixChannel(), true);
QCOMPARE(item.mixParticipantId(), QString("123456"));
serializePacket(item, xml);

item.setIsMixChannel(false);
QCOMPARE(item.isMixChannel(), false);
item.setMixParticipantId("23a7n");
QCOMPARE(item.mixParticipantId(), QString("23a7n"));
}

QTEST_MAIN(tst_QXmppRosterIq)
#include "tst_qxmpprosteriq.moc"

0 comments on commit b0642a4

Please sign in to comment.