aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppMucManager.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2011-04-28 23:02:16 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2011-04-28 23:02:16 +0000
commitd9c96b1688a2f3af0df05b453bd4282b7c3e99f3 (patch)
tree7a7b6098a898eb34f400cc3dd70f3f5039b0067c /src/QXmppMucManager.cpp
parent4242e9521cbdddffd17353953406f98151a11a93 (diff)
downloadqxmpp-d9c96b1688a2f3af0df05b453bd4282b7c3e99f3.tar.gz
overhaul multi-user chat API by introducing a QXmppMucRoom class
Diffstat (limited to 'src/QXmppMucManager.cpp')
-rw-r--r--src/QXmppMucManager.cpp510
1 files changed, 363 insertions, 147 deletions
diff --git a/src/QXmppMucManager.cpp b/src/QXmppMucManager.cpp
index 8f7553e1..d6b71592 100644
--- a/src/QXmppMucManager.cpp
+++ b/src/QXmppMucManager.cpp
@@ -30,6 +30,35 @@
#include "QXmppMucManager.h"
#include "QXmppUtils.h"
+class QXmppMucRoomPrivate
+{
+public:
+ QString ownJid() const { return jid + "/" + nickName; }
+ QXmppClient *client;
+ QXmppMucRoom::Actions allowedActions;
+ QString jid;
+ QMap<QString, QXmppPresence> participants;
+ QString password;
+ QMap<QString, QXmppMucAdminIq::Item::Affiliation> affiliations;
+ QString nickName;
+ QXmppPresence::Status status;
+ QString subject;
+};
+
+/// Adds the given chat room to the set of manged rooms.
+///
+/// \param roomJid
+
+QXmppMucRoom *QXmppMucManager::addRoom(const QString &roomJid)
+{
+ QXmppMucRoom *room = m_rooms.value(roomJid);
+ if (!room) {
+ room = new QXmppMucRoom(client(), roomJid, this);
+ m_rooms.insert(roomJid, room);
+ }
+ return room;
+}
+
void QXmppMucManager::setClient(QXmppClient* client)
{
QXmppClientExtension::setClient(client);
@@ -37,10 +66,6 @@ void QXmppMucManager::setClient(QXmppClient* client)
bool check = connect(client, SIGNAL(messageReceived(QXmppMessage)),
this, SLOT(messageReceived(QXmppMessage)));
Q_ASSERT(check);
-
- check = QObject::connect(client, SIGNAL(presenceReceived(QXmppPresence)),
- this, SLOT(presenceReceived(QXmppPresence)));
- Q_ASSERT(check);
}
QStringList QXmppMucManager::discoveryFeatures() const
@@ -61,249 +86,440 @@ bool QXmppMucManager::handleStanza(const QDomElement &element)
{
QXmppMucAdminIq iq;
iq.parse(element);
- mucAdminIqReceived(iq);
+
+ QXmppMucRoom *room = m_rooms.value(iq.from());
+ if (room && iq.type() == QXmppIq::Result) {
+ foreach (const QXmppMucAdminIq::Item &item, iq.items()) {
+ const QString jid = item.jid();
+ if (!room->d->affiliations.contains(jid))
+ room->d->affiliations.insert(jid, item.affiliation());
+ }
+ emit room->permissionsReceived(iq.items());
+ }
return true;
}
else if (QXmppMucOwnerIq::isMucOwnerIq(element))
{
QXmppMucOwnerIq iq;
iq.parse(element);
- mucOwnerIqReceived(iq);
+
+ QXmppMucRoom *room = m_rooms.value(iq.from());
+ if (room && iq.type() == QXmppIq::Result && !iq.form().isNull())
+ emit room->configurationReceived(iq.form());
return true;
}
}
return false;
}
-/// Joins the given chat room with the requested nickname.
-///
-/// \param roomJid
-/// \param nickName
-/// \param password an optional password if the room is password-protected
-///
-/// \return true if the request was sent, false otherwise
+void QXmppMucManager::messageReceived(const QXmppMessage &msg)
+{
+ if (msg.type() != QXmppMessage::Normal)
+ return;
+
+ // process room invitations
+ foreach (const QXmppElement &extension, msg.extensions())
+ {
+ if (extension.tagName() == "x" && extension.attribute("xmlns") == ns_conference)
+ {
+ const QString roomJid = extension.attribute("jid");
+ if (!roomJid.isEmpty() && !m_rooms.contains(roomJid))
+ emit invitationReceived(roomJid, msg.from(), extension.attribute("reason"));
+ break;
+ }
+ }
+}
+
+/// Constructs a new QXmppMucRoom.
///
+/// \param parent
+
+QXmppMucRoom::QXmppMucRoom(QXmppClient *client, const QString &jid, QObject *parent)
+ : QObject(parent)
+{
+ bool check;
+
+ d = new QXmppMucRoomPrivate;
+ d->allowedActions = NoAction;
+ d->client = client;
+ d->jid = jid;
+
+ check = connect(d->client, SIGNAL(disconnected()),
+ this, SLOT(_q_disconnected()));
+ Q_ASSERT(check);
+
+ check = connect(d->client, SIGNAL(messageReceived(QXmppMessage)),
+ this, SLOT(_q_messageReceived(QXmppMessage)));
+ Q_ASSERT(check);
+
+ check = connect(d->client, SIGNAL(presenceReceived(QXmppPresence)),
+ this, SLOT(_q_presenceReceived(QXmppPresence)));
+ Q_ASSERT(check);
+}
+
+/// Destroys a QXmppMucRoom.
+
+QXmppMucRoom::~QXmppMucRoom()
+{
+ delete d;
+}
+
+/// Returns the actions you are allowed to perform on the room.
+
+QXmppMucRoom::Actions QXmppMucRoom::allowedActions() const
+{
+ return d->allowedActions;
+}
+
+/// Returns true if you are currently in the room.
-bool QXmppMucManager::joinRoom(const QString &roomJid, const QString &nickName, const QString &password)
+bool QXmppMucRoom::isJoined() const
{
+ return d->participants.contains(d->ownJid());
+}
+
+/// Returns the chat room's bare JID.
+
+QString QXmppMucRoom::jid() const
+{
+ return d->jid;
+}
+
+/// Joins the chat room.
+
+bool QXmppMucRoom::join()
+{
+ if (isJoined() || d->nickName.isEmpty())
+ return false;
+
QXmppPresence packet;
- packet.setTo(roomJid + "/" + nickName);
+ packet.setTo(d->ownJid());
packet.setType(QXmppPresence::Available);
+ packet.setStatus(d->status);
QXmppElement x;
x.setTagName("x");
x.setAttribute("xmlns", ns_muc);
- if (!password.isEmpty())
+ if (!d->password.isEmpty())
{
QXmppElement p;
p.setTagName("password");
- p.setValue(password);
+ p.setValue(d->password);
x.appendChild(p);
}
packet.setExtensions(x);
- if (client()->sendPacket(packet))
- {
- m_nickNames[roomJid] = nickName;
- return true;
- } else {
- return false;
- }
+ return d->client->sendPacket(packet);
}
-/// Leaves the given chat room.
-///
-/// \param roomJid
-///
-/// \return true if the request was sent, false otherwise
-///
+/// Leaves the chat room.
-bool QXmppMucManager::leaveRoom(const QString &roomJid)
+bool QXmppMucRoom::leave()
{
- if (!m_nickNames.contains(roomJid))
- return false;
- QString nickName = m_nickNames.take(roomJid);
QXmppPresence packet;
- packet.setTo(roomJid + "/" + nickName);
+ packet.setTo(d->ownJid());
packet.setType(QXmppPresence::Unavailable);
- return client()->sendPacket(packet);
+ return d->client->sendPacket(packet);
}
-/// Retrieves the list of participants for the given room.
+/// Returns your own nickname.
+
+QString QXmppMucRoom::nickName() const
+{
+ return d->nickName;
+}
+
+/// Invites a user to the chat room.
///
-/// \param roomJid
+/// \param jid
+/// \param reason
+
+bool QXmppMucRoom::sendInvitation(const QString &jid, const QString &reason)
+{
+ QXmppElement x;
+ x.setTagName("x");
+ x.setAttribute("xmlns", ns_conference);
+ x.setAttribute("jid", d->jid);
+ x.setAttribute("reason", reason);
+
+ QXmppMessage message;
+ message.setTo(jid);
+ message.setType(QXmppMessage::Normal);
+ message.setExtensions(x);
+ return d->client->sendPacket(message);
+}
+
+/// Sends a message to the room.
+
+bool QXmppMucRoom::sendMessage(const QString &text)
+{
+ QXmppMessage msg;
+ msg.setTo(d->jid);
+ msg.setType(QXmppMessage::GroupChat);
+ msg.setBody(text);
+ return d->client->sendPacket(msg);
+}
+
+/// Sets your own nickname.
+
+void QXmppMucRoom::setNickName(const QString &nickName)
+{
+ d->nickName = nickName;
+}
+
+/// Returns the presence for the given participant.
///
+/// \param jid
-QMap<QString, QXmppPresence> QXmppMucManager::roomParticipants(const QString& roomJid) const
+QXmppPresence QXmppMucRoom::participantPresence(const QString &jid) const
{
- return m_participants.value(roomJid);
+ if (d->participants.contains(jid))
+ return d->participants.value(jid);
+
+ QXmppPresence presence;
+ presence.setFrom(jid);
+ presence.setType(QXmppPresence::Unavailable);
+ return presence;
}
-/// Request the configuration form for the given room.
+/// Returns the list of participant JIDs.
+
+QStringList QXmppMucRoom::participants() const
+{
+ return d->participants.keys();
+}
+
+/// Returns the chat room password.
+
+QString QXmppMucRoom::password() const
+{
+ return d->password;
+}
+
+/// Sets the chat room password.
///
-/// \param roomJid
+/// \param password
+
+void QXmppMucRoom::setPassword(const QString &password)
+{
+ d->password = password;
+}
+
+QXmppPresence::Status QXmppMucRoom::status() const
+{
+ return d->status;
+}
+
+void QXmppMucRoom::setStatus(const QXmppPresence::Status &status)
+{
+ d->status = status;
+}
+
+/// Returns the room's subject.
+
+QString QXmppMucRoom::subject() const
+{
+ return d->subject;
+}
+
+/// Sets the chat room's subject.
+///
+/// \param subject
+
+void QXmppMucRoom::setSubject(const QString &subject)
+{
+ QXmppMessage msg;
+ msg.setTo(d->jid);
+ msg.setType(QXmppMessage::GroupChat);
+ msg.setSubject(subject);
+ d->client->sendPacket(msg);
+}
+
+/// Request the configuration form for the chat room.
///
/// \return true if the request was sent, false otherwise
///
-/// \sa roomConfigurationReceived()
+/// \sa configurationReceived()
-bool QXmppMucManager::requestRoomConfiguration(const QString &roomJid)
+bool QXmppMucRoom::requestConfiguration()
{
QXmppMucOwnerIq iq;
- iq.setTo(roomJid);
- return client()->sendPacket(iq);
+ iq.setTo(d->jid);
+ return d->client->sendPacket(iq);
}
-/// Send the configuration form for the given room.
+/// Send the configuration form for the chat room.
///
-/// \param roomJid
/// \param form
///
/// \return true if the request was sent, false otherwise
-bool QXmppMucManager::setRoomConfiguration(const QString &roomJid, const QXmppDataForm &form)
+bool QXmppMucRoom::setConfiguration(const QXmppDataForm &form)
{
QXmppMucOwnerIq iqPacket;
iqPacket.setType(QXmppIq::Set);
- iqPacket.setTo(roomJid);
+ iqPacket.setTo(d->jid);
iqPacket.setForm(form);
- return client()->sendPacket(iqPacket);
+ return d->client->sendPacket(iqPacket);
}
/// Request the room's permissions.
///
-/// \param roomJid
-///
/// \return true if the request was sent, false otherwise
+///
+/// \sa permissionsReceived()
-bool QXmppMucManager::requestRoomPermissions(const QString &roomJid)
+bool QXmppMucRoom::requestPermissions()
{
QList<QXmppMucAdminIq::Item::Affiliation> affiliations;
affiliations << QXmppMucAdminIq::Item::OwnerAffiliation;
affiliations << QXmppMucAdminIq::Item::AdminAffiliation;
affiliations << QXmppMucAdminIq::Item::MemberAffiliation;
affiliations << QXmppMucAdminIq::Item::OutcastAffiliation;
- foreach (QXmppMucAdminIq::Item::Affiliation affiliation, affiliations)
- {
+ foreach (QXmppMucAdminIq::Item::Affiliation affiliation, affiliations) {
QXmppMucAdminIq::Item item;
item.setAffiliation(affiliation);
QXmppMucAdminIq iq;
- iq.setTo(roomJid);
+ iq.setTo(d->jid);
iq.setItems(QList<QXmppMucAdminIq::Item>() << item);
- if (!client()->sendPacket(iq))
+ if (!d->client->sendPacket(iq))
return false;
}
return true;
}
-/// Sets the subject for the given room.
-///
-/// \param roomJid
-/// \param subject
+/// Sets the room's permissions.
///
/// \return true if the request was sent, false otherwise
-///
-bool QXmppMucManager::setRoomSubject(const QString &roomJid, const QString &subject)
+bool QXmppMucRoom::setPermissions(const QList<QXmppMucAdminIq::Item> &permissions)
{
- QXmppMessage msg;
- msg.setTo(roomJid);
- msg.setType(QXmppMessage::GroupChat);
- msg.setSubject(subject);
- return client()->sendPacket(msg);
-}
+ QList<QXmppMucAdminIq::Item> items;
+
+ // Process changed members
+ foreach (const QXmppMucAdminIq::Item &item, permissions) {
+ const QString jid = item.jid();
+ if (d->affiliations.value(jid) != item.affiliation())
+ items << item;
+ d->affiliations.remove(jid);
+ }
-/// Invite a user to a chat room.
-///
-/// \param roomJid
-/// \param jid
-/// \param reason
-///
-/// \return true if the message was sent, false otherwise
-///
+ // Process deleted members
+ foreach (const QString &jid, d->affiliations.keys()) {
+ QXmppMucAdminIq::Item item;
+ item.setAffiliation(QXmppMucAdminIq::Item::NoAffiliation);
+ item.setJid(jid);
+ items << item;
+ d->affiliations.remove(jid);
+ }
-bool QXmppMucManager::sendInvitation(const QString &roomJid, const QString &jid, const QString &reason)
-{
- QXmppElement x;
- x.setTagName("x");
- x.setAttribute("xmlns", ns_conference);
- x.setAttribute("jid", roomJid);
- x.setAttribute("reason", reason);
+ // Don't send request if there are no changes
+ if (items.isEmpty())
+ return false;
- QXmppMessage message;
- message.setTo(jid);
- message.setType(QXmppMessage::Normal);
- message.setExtensions(x);
- return client()->sendPacket(message);
+ QXmppMucAdminIq iq;
+ iq.setTo(d->jid);
+ iq.setType(QXmppIq::Set);
+ iq.setItems(items);
+ return d->client->sendPacket(iq);
}
-/// Send a message to a chat room.
-///
-/// \param roomJid
-/// \param text
-///
-/// \return true if the message was sent, false otherwise
-///
-
-bool QXmppMucManager::sendMessage(const QString &roomJid, const QString &text)
+void QXmppMucRoom::_q_disconnected()
{
- if (!m_nickNames.contains(roomJid))
- {
- qWarning("Cannot send message to unknown chat room");
- return false;
- }
- QXmppMessage msg;
- msg.setBody(text);
- msg.setTo(roomJid);
- msg.setType(QXmppMessage::GroupChat);
- return client()->sendPacket(msg);
-}
+ const bool wasJoined = isJoined();
-void QXmppMucManager::messageReceived(const QXmppMessage &msg)
-{
- if (msg.type() != QXmppMessage::Normal)
- return;
+ // clear chat room participants
+ const QStringList removed = d->participants.keys();
+ d->participants.clear();
+ foreach (const QString &jid, removed)
+ emit participantRemoved(jid);
- // process room invitations
- foreach (const QXmppElement &extension, msg.extensions())
- {
- if (extension.tagName() == "x" && extension.attribute("xmlns") == ns_conference)
- {
- const QString roomJid = extension.attribute("jid");
- if (!roomJid.isEmpty() && !m_nickNames.contains(roomJid))
- emit invitationReceived(roomJid, msg.from(), extension.attribute("reason"));
- break;
- }
- }
+ // emit "left" signal if we had joined the room
+ if (wasJoined)
+ emit left();
}
-void QXmppMucManager::mucAdminIqReceived(const QXmppMucAdminIq &iq)
+void QXmppMucRoom::_q_messageReceived(const QXmppMessage &message)
{
- if (iq.type() != QXmppIq::Result)
+ if (jidToBareJid(message.from())!= d->jid ||
+ message.type() != QXmppMessage::GroupChat)
return;
- emit roomPermissionsReceived(iq.from(), iq.items());
-}
-void QXmppMucManager::mucOwnerIqReceived(const QXmppMucOwnerIq &iq)
-{
- if (iq.type() == QXmppIq::Result && !iq.form().isNull())
- emit roomConfigurationReceived(iq.from(), iq.form());
+ // handle message subject
+ const QString subject = message.subject();
+ if (!subject.isEmpty()) {
+ d->subject = subject;
+ emit subjectChanged(subject);
+ }
+
+ emit messageReceived(message);
}
-void QXmppMucManager::presenceReceived(const QXmppPresence &presence)
+void QXmppMucRoom::_q_presenceReceived(const QXmppPresence &presence)
{
- QString jid = presence.from();
- QString bareJid = jidToBareJid(jid);
- QString resource = jidToResource(jid);
- if (!m_nickNames.contains(bareJid))
- return;
-
- if (presence.type() == QXmppPresence::Available)
- m_participants[bareJid][resource] = presence;
- else if (presence.type() == QXmppPresence::Unavailable)
- m_participants[bareJid].remove(resource);
- else
+ const QString jid = presence.from();
+ if (jidToBareJid(jid)!= d->jid)
return;
- emit roomParticipantChanged(bareJid, resource);
+ if (presence.type() == QXmppPresence::Available) {
+ const bool added = !d->participants.contains(jid);
+ d->participants.insert(jid, presence);
+ if (added) {
+ emit participantAdded(jid);
+ if (jid == d->ownJid()) {
+
+ // check whether we own the room
+ foreach (const QXmppElement &x, presence.extensions()) {
+ if (x.tagName() == "x" && x.attribute("xmlns") == ns_muc_user)
+ {
+ QXmppElement item = x.firstChildElement("item");
+ if (item.attribute("jid") == d->client->configuration().jid()) {
+ d->allowedActions = NoAction;
+
+ // role
+ if (item.attribute("role") == "moderator")
+ d->allowedActions |= (KickAction | SubjectAction);
+
+ // affiliation
+ if (item.attribute("affiliation") == "owner")
+ d->allowedActions |= (ConfigurationAction | PermissionsAction | SubjectAction);
+ else if (item.attribute("affiliation") == "admin")
+ d->allowedActions |= (PermissionsAction | SubjectAction);
+ }
+ }
+ }
+
+ emit joined();
+ }
+ } else {
+ emit participantChanged(jid);
+ }
+ }
+ else if (presence.type() == QXmppPresence::Unavailable) {
+ if (d->participants.remove(jid)) {
+ emit participantRemoved(jid);
+
+ // check whether this was our own presence
+ if (jid == d->ownJid()) {
+
+ // check whether we were kicked
+ foreach (const QXmppElement &extension, presence.extensions()) {
+ if (extension.tagName() == "x" &&
+ extension.attribute("xmlns") == ns_muc_user) {
+ QXmppElement status = extension.firstChildElement("status");
+ while (!status.isNull()) {
+ if (status.attribute("code").toInt() == 307) {
+ QXmppElement reason = extension.firstChildElement("item").firstChildElement("reason");
+ emit kicked(reason.value());
+ break;
+ }
+ status = status.nextSiblingElement("status");
+ }
+ }
+ }
+
+ // notify user we left the room
+ emit left();
+ }
+ }
+ }
}
-