diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-04-28 23:02:16 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-04-28 23:02:16 +0000 |
| commit | d9c96b1688a2f3af0df05b453bd4282b7c3e99f3 (patch) | |
| tree | 7a7b6098a898eb34f400cc3dd70f3f5039b0067c /src/QXmppMucManager.cpp | |
| parent | 4242e9521cbdddffd17353953406f98151a11a93 (diff) | |
| download | qxmpp-d9c96b1688a2f3af0df05b453bd4282b7c3e99f3.tar.gz | |
overhaul multi-user chat API by introducing a QXmppMucRoom class
Diffstat (limited to 'src/QXmppMucManager.cpp')
| -rw-r--r-- | src/QXmppMucManager.cpp | 510 |
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(); + } + } + } } - |
