summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-05-22 20:45:53 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-05-23 08:15:20 (GMT)
commita68530cbbfeb17e01fd684b1ef41b960bc173f66 (patch)
tree98b5cd195f04d781164cf1394cee9092bb7b2497
parentc9659b556b932e2f887cf1d8ab6c5a0bead835eb (diff)
downloadswift-contrib-a68530cbbfeb17e01fd684b1ef41b960bc173f66.zip
swift-contrib-a68530cbbfeb17e01fd684b1ef41b960bc173f66.tar.bz2
Implement XEP-0045 joining, and appropriate error handling.
Resolves: #211
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp120
-rw-r--r--Swift/Controllers/Chat/MUCController.h14
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp2
-rw-r--r--Swiften/Elements/MUCPayload.h10
-rw-r--r--Swiften/Elements/MUCUserPayload.h59
-rw-r--r--Swiften/MUC/MUC.cpp77
-rw-r--r--Swiften/MUC/MUC.h14
-rw-r--r--Swiften/MUC/MUCOccupant.cpp25
-rw-r--r--Swiften/MUC/MUCOccupant.h22
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp2
-rw-r--r--Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp94
-rw-r--r--Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h31
-rw-r--r--Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h17
-rw-r--r--Swiften/Parser/SConscript1
-rw-r--r--Swiften/SConscript1
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp1
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp10
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h7
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp74
-rw-r--r--Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h22
20 files changed, 529 insertions, 74 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index a8a6747..dbf03c9 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -45,7 +45,6 @@ MUCController::MUCController (
ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream),
muc_(new MUC(stanzaChannel, presenceSender, muc)),
nick_(nick) {
- loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
parting_ = false;
events_ = uiEventStream;
@@ -53,11 +52,16 @@ MUCController::MUCController (
chatWindow_->setRosterModel(roster_);
chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
+ muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
- loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
- loginCheckTimer_->start();
+ muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
+ if (timerFactory) {
+ loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
+ loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
+ loginCheckTimer_->start();
+ }
muc_->joinAs(nick);
chatWindow_->convertToMUC();
@@ -72,21 +76,51 @@ MUCController::~MUCController() {
delete muc_;
chatWindow_->setRosterModel(NULL);
delete roster_;
- loginCheckTimer_->stop();
+ if (loginCheckTimer_) {
+ loginCheckTimer_->stop();
+ }
}
void MUCController::handleJoinTimeoutTick() {
- loginCheckTimer_->stop();
+ receivedActivity();
chatWindow_->addSystemMessage("Room " + toJID_.toString() + " is not responding. This operation may never complete");
}
-void MUCController::handleJoinComplete(MUC::JoinResult result) {
- loginCheckTimer_->stop();
- if (result == MUC::JoinFailed) {
- chatWindow_->addErrorMessage("Unable to join this room");
- }
+void MUCController::receivedActivity() {
+ if (loginCheckTimer_) {
+ loginCheckTimer_->stop();
+ }
+}
+
+void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
+ receivedActivity();
+ String errorMessage = "Unable to join this room";
+ String rejoinNick;
+ if (error) {
+ switch (error->getCondition()) {
+ case ErrorPayload::Conflict: rejoinNick = nick_ + "_"; errorMessage += " as " + nick_ + ", retrying as " + rejoinNick; break;
+ case ErrorPayload::JIDMalformed: errorMessage += ", no nickname specified";break;
+ case ErrorPayload::NotAuthorized: errorMessage += ", a password needed";break;
+ case ErrorPayload::RegistrationRequired: errorMessage += ", only members may join"; break;
+ case ErrorPayload::Forbidden: errorMessage += ", you are banned from the room"; break;
+ case ErrorPayload::ServiceUnavailable: errorMessage += ", the room is full";break;
+ case ErrorPayload::ItemNotFound: errorMessage += ", the room does not exist";break;
+
+ default: break;
+ }
+ }
+ errorMessage += ".";
+ chatWindow_->addErrorMessage(errorMessage);
+ if (!rejoinNick.isEmpty()) {
+ muc_->joinAs(rejoinNick);
+ }
+}
+
+void MUCController::handleJoinComplete(const String& nick) {
+ receivedActivity();
joined_ = true;
- String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick_;
+ String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick;
+ nick_ = nick;
chatWindow_->addSystemMessage(joinMessage);
}
@@ -105,18 +139,76 @@ void MUCController::handleWindowClosed() {
}
void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
- JID jid(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
- roster_->addContact(jid, occupant.getNick(), "Occupants");
+ receivedActivity();
+ JID jid(nickToJID(occupant.getNick()));
+ roster_->addContact(jid, occupant.getNick(), roleToGroupName(occupant.getRole()));
+ if (joined_) {
+ String joinString = occupant.getNick() + " has joined the room";
+ MUCOccupant::Role role = occupant.getRole();
+ if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) {
+ joinString += " as a " + roleToFriendlyName(role);
+
+ }
+ joinString += ".";
+ chatWindow_->addSystemMessage(joinString);
+ }
if (avatarManager_ != NULL) {
handleAvatarChanged(jid, "dummy");
}
}
-void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& /*reason*/) {
+String MUCController::roleToFriendlyName(MUCOccupant::Role role) {
+ switch (role) {
+ case MUCOccupant::Moderator: return "moderator";
+ case MUCOccupant::Participant: return "participant";
+ case MUCOccupant::Visitor: return "visitor";
+ case MUCOccupant::NoRole: return "";
+ }
+ return "";
+}
+
+JID MUCController::nickToJID(const String& nick) {
+ return JID(toJID_.getNode(), toJID_.getDomain(), nick);
+}
+
+void MUCController::preHandleIncomingMessage(boost::shared_ptr<Message>) {
+ /*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/
+ receivedActivity();
+ joined_ = true;
+}
+
+void MUCController::handleOccupantRoleChanged(const String& nick, const MUCOccupant::Role& newRole, const MUCOccupant::Role& oldRole) {
+ receivedActivity();
+ JID jid(nickToJID(nick));
+ roster_->removeContactFromGroup(jid, roleToGroupName(oldRole));
+ roster_->addContact(jid, nick, roleToGroupName(newRole));
+ chatWindow_->addSystemMessage(nick + " is now a " + roleToFriendlyName(newRole));
+}
+
+String MUCController::roleToGroupName(MUCOccupant::Role role) {
+ String result;
+ switch (role) {
+ case MUCOccupant::Moderator: result = "Moderators"; break;
+ case MUCOccupant::Participant: result = "Participants"; break;
+ case MUCOccupant::Visitor: result = "Visitors"; break;
+ case MUCOccupant::NoRole: result = "Occupants"; break;
+ default: assert(false);
+ }
+ return result;
+}
+
+void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& reason) {
+ String partMessage = occupant.getNick() + " has left the room";
+ if (!reason.isEmpty()) {
+ partMessage += " (" + reason + ")";
+ }
+ partMessage += ".";
+ chatWindow_->addSystemMessage(partMessage);
roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
}
void MUCController::handleOccupantPresenceChange(boost::shared_ptr<Presence> presence) {
+ receivedActivity();
roster_->applyOnItems(SetPresence(presence, JID::WithResource));
}
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 9e79835..247a942 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -4,8 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_MUCController_H
-#define SWIFTEN_MUCController_H
+#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/signals.hpp>
@@ -47,9 +46,15 @@ namespace Swift {
void handleOccupantJoined(const MUCOccupant& occupant);
void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason);
void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
- void handleJoinComplete(MUC::JoinResult result);
+ void handleOccupantRoleChanged(const String& nick, const MUCOccupant::Role& newRole, const MUCOccupant::Role& oldRole);
+ void handleJoinComplete(const String& nick);
+ void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);
void handleJoinTimeoutTick();
-
+ String roleToGroupName(MUCOccupant::Role role);
+ JID nickToJID(const String& nick);
+ String roleToFriendlyName(MUCOccupant::Role role);
+ void receivedActivity();
+ void preHandleIncomingMessage(boost::shared_ptr<Message>);
private:
MUC* muc_;
UIEventStream* events_;
@@ -61,5 +66,4 @@ namespace Swift {
boost::shared_ptr<Timer> loginCheckTimer_;
};
}
-#endif
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 99b2f90..2204366 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -64,7 +64,7 @@ public:
uiEventStream_ = new UIEventStream();
chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createWindow).With(uiEventStream_).Return(NULL);
- manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true);
+ manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL);
avatarManager_ = new MockAvatarManager();
manager_->setAvatarManager(avatarManager_);
};
diff --git a/Swiften/Elements/MUCPayload.h b/Swiften/Elements/MUCPayload.h
index 5ce682e..4b48b45 100644
--- a/Swiften/Elements/MUCPayload.h
+++ b/Swiften/Elements/MUCPayload.h
@@ -1,17 +1,19 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
+#include <boost/optional.hpp>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
#include "Swiften/Elements/Payload.h"
namespace Swift {
class MUCPayload : public Payload {
- public:
- MUCPayload() {
- }
+
};
}
diff --git a/Swiften/Elements/MUCUserPayload.h b/Swiften/Elements/MUCUserPayload.h
new file mode 100644
index 0000000..3032b9f
--- /dev/null
+++ b/Swiften/Elements/MUCUserPayload.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/MUC/MUCOccupant.h"
+
+namespace Swift {
+ class MUCUserPayload : public Payload {
+ public:
+ struct Item {
+ Item() : affiliation(MUCOccupant::NoAffiliation), role(MUCOccupant::NoRole) {}
+ boost::optional<JID> realJID;
+ boost::optional<String> nick;
+ MUCOccupant::Affiliation affiliation;
+ MUCOccupant::Role role;
+ };
+
+ struct StatusCode {
+ StatusCode() {}
+ int code;
+ };
+
+ // struct Password {
+
+ // }
+
+ // struct History {
+
+ // }
+
+ // struct Invite {
+
+ // }
+
+ MUCUserPayload() {
+ }
+
+ void addItem(Item item) {items_.push_back(item);}
+
+ void addStatusCode(StatusCode code) {statusCodes_.push_back(code);}
+
+ const std::vector<Item> getItems() const {return items_;}
+
+ const std::vector<StatusCode> getStatusCodes() const {return statusCodes_;}
+
+ private:
+ std::vector<Item> items_;
+ std::vector<StatusCode> statusCodes_;
+ };
+}
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index e52eae4..60b81a0 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -1,17 +1,20 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include "Swiften/MUC/MUC.h"
+#include <iostream>
+
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include "Swiften/Presence/PresenceSender.h"
#include "Swiften/Client/StanzaChannel.h"
#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/MUCUserPayload.h"
#include "Swiften/Elements/MUCPayload.h"
namespace Swift {
@@ -25,9 +28,9 @@ MUC::MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID
//FIXME: discover reserved nickname
void MUC::joinAs(const String &nick) {
- //FIXME: password
- //FIXME: history request
- firstPresenceSeen = false;
+ //TODO: password
+ //TODO: history request
+ joinComplete_ = false;
ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
@@ -45,53 +48,73 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
if (!isFromMUC(presence->getFrom())) {
return;
}
-
- if (!firstPresenceSeen) {
+ boost::shared_ptr<MUCUserPayload> mucPayload;
+ foreach (boost::shared_ptr<MUCUserPayload> payload, presence->getPayloads<MUCUserPayload>()) {
+ if (payload->getItems().size() > 0 || payload->getStatusCodes().size() > 0) {
+ mucPayload = payload;
+ }
+ }
+
+ if (!joinComplete_) {
if (presence->getType() == Presence::Error) {
- onJoinComplete(JoinFailed);
- //FIXME: parse error element
- //Wrong password
- //Members-only
- //Banned
- //Nickname-conflict
- //Max-users
- //Locked-room
-
+ String reason;
+ onJoinFailed(presence->getPayload<ErrorPayload>());
return;
}
- firstPresenceSeen = true;
- onJoinComplete(JoinSucceeded);
- presenceSender->addDirectedPresenceReceiver(ownMUCJID);
}
String nick = presence->getFrom().getResource();
if (nick.isEmpty()) {
return;
}
- //FIXME: occupant affiliation, role.
- //FIXME: if status code='110', This is me, stop talking.
- //FIXME: what's status 210?
+ MUCOccupant::Role role(MUCOccupant::NoRole);
+ MUCOccupant::Affiliation affiliation(MUCOccupant::NoAffiliation);
+ if (mucPayload && mucPayload->getItems().size() > 0) {
+ role = mucPayload->getItems()[0].role;
+ affiliation = mucPayload->getItems()[0].affiliation;
+ }
+
//100 is non-anonymous
- //FIXME: 100 may also be specified in a <message/>
- //Once I've got my nick (110), everything new is a join
+ //TODO: 100 may also be specified in a <message/>
//170 is room logging to http
- //FIXME: full JIDs
- //FIXME: Nick changes
+ //TODO: Nick changes
if (presence->getType() == Presence::Unavailable) {
std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
if (i != occupants.end()) {
- //FIXME: part type
+ //TODO: part type
onOccupantLeft(i->second, Part, "");
occupants.erase(i);
}
}
else if (presence->getType() == Presence::Available) {
- std::pair<std::map<String,MUCOccupant>::iterator, bool> result = occupants.insert(std::make_pair(nick, MUCOccupant(nick)));
+ std::map<String, MUCOccupant>::iterator it = occupants.find(nick);
+ if (it != occupants.end()) {
+ MUCOccupant oldOccupant = it->second;
+ if (oldOccupant.getRole() != role) {
+ onOccupantRoleChanged(nick, role, oldOccupant.getRole());
+ }
+ if (oldOccupant.getAffiliation() != affiliation) {
+ onOccupantAffiliationChanged(nick, affiliation, oldOccupant.getAffiliation());
+ }
+ }
+ std::pair<std::map<String, MUCOccupant>::iterator, bool> result = occupants.insert(std::make_pair(nick, MUCOccupant(nick, role, affiliation)));
if (result.second) {
onOccupantJoined(result.first->second);
}
onOccupantPresenceChange(presence);
}
+ if (mucPayload && !joinComplete_) {
+ foreach (MUCUserPayload::StatusCode status, mucPayload->getStatusCodes()) {
+ if (status.code == 110) {
+ /* Simply knowing this is your presence is enough, 210 doesn't seem to be necessary. */
+ joinComplete_ = true;
+ ownMUCJID = presence->getFrom();
+ onJoinComplete(getOwnNick());
+ presenceSender->addDirectedPresenceReceiver(ownMUCJID);
+ }
+ }
+ }
+
}
//FIXME: Recognise Topic changes
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index e306f11..fc691f1 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -25,10 +25,7 @@ namespace Swift {
class MUC {
public:
enum JoinResult { JoinSucceeded, JoinFailed };
- enum LeavingType { Part };
- enum Roles {Moderator, Participant, Visitor, NoRole};
- enum Affiliations {Owner, Admin, Member, Outcast, NoAffiliation};
-
+ enum LeavingType { Part };
public:
MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc);
@@ -41,8 +38,11 @@ namespace Swift {
void handleIncomingMessage(boost::shared_ptr<Message> message);
public:
- boost::signal<void (JoinResult)> onJoinComplete;
+ boost::signal<void (const String& /*nick*/)> onJoinComplete;
+ boost::signal<void (boost::shared_ptr<ErrorPayload>)> onJoinFailed;
boost::signal<void (boost::shared_ptr<Presence>)> onOccupantPresenceChange;
+ boost::signal<void (const String&, const MUCOccupant::Role& /*new*/, const MUCOccupant::Role& /*old*/)> onOccupantRoleChanged;
+ boost::signal<void (const String&, const MUCOccupant::Affiliation& /*new*/, const MUCOccupant::Affiliation& /*old*/)> onOccupantAffiliationChanged;
boost::signal<void (const MUCOccupant&)> onOccupantJoined;
boost::signal<void (const MUCOccupant&, LeavingType, const String& /*reason*/)> onOccupantLeft;
/* boost::signal<void (const MUCInfo&)> onInfoResult; */
@@ -66,7 +66,7 @@ namespace Swift {
StanzaChannel* stanzaChannel;
PresenceSender* presenceSender;
std::map<String, MUCOccupant> occupants;
- bool firstPresenceSeen;
+ bool joinComplete_;
boost::bsignals::scoped_connection scopedConnection_;
};
}
diff --git a/Swiften/MUC/MUCOccupant.cpp b/Swiften/MUC/MUCOccupant.cpp
index 0bd9787..3e907ab 100644
--- a/Swiften/MUC/MUCOccupant.cpp
+++ b/Swiften/MUC/MUCOccupant.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -8,7 +8,7 @@
namespace Swift {
-MUCOccupant::MUCOccupant(const String &nick) : nick_(nick) {
+MUCOccupant::MUCOccupant(const String &nick, Role role, Affiliation affiliation) : nick_(nick), role_(role), affiliation_(affiliation) {
}
MUCOccupant::~MUCOccupant() {
@@ -18,4 +18,25 @@ String MUCOccupant::getNick() const {
return nick_;
}
+MUCOccupant::Role MUCOccupant::getRole() const {
+ return role_;
+}
+
+MUCOccupant::Affiliation MUCOccupant::getAffiliation() const {
+ return affiliation_;
+}
+
+void MUCOccupant::setRealJID(const JID& realJID) {
+ realJID_ = realJID;
+}
+
+void MUCOccupant::setNick(const String& nick) {
+ nick_ = nick;
+}
+
+
+boost::optional<JID> MUCOccupant::getRealJID() const {
+ return realJID_;
+}
+
}
diff --git a/Swiften/MUC/MUCOccupant.h b/Swiften/MUC/MUCOccupant.h
index 939d634..c9551de 100644
--- a/Swiften/MUC/MUCOccupant.h
+++ b/Swiften/MUC/MUCOccupant.h
@@ -1,27 +1,39 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_MUCOccupant_H
-#define SWIFTEN_MUCOccupant_H
+#pragma once
+
+#include <boost/optional.hpp>
#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
namespace Swift {
class Client;
class MUCOccupant {
public:
- MUCOccupant(const String &nick);
+ enum Role {Moderator, Participant, Visitor, NoRole};
+ enum Affiliation {Owner, Admin, Member, Outcast, NoAffiliation};
+
+ MUCOccupant(const String &nick, Role role, Affiliation affiliation);
~MUCOccupant();
String getNick() const;
+ Role getRole() const;
+ Affiliation getAffiliation() const;
+ boost::optional<JID> getRealJID() const;
+ void setRealJID(const JID& jid);
+ void setNick(const String& nick);
private:
String nick_;
+ Role role_;
+ Affiliation affiliation_;
+ boost::optional<JID> realJID_;
};
}
-#endif
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 8c570cb..1a45ed2 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -28,6 +28,7 @@
#include "Swiften/Parser/PayloadParsers/RawXMLPayloadParserFactory.h"
#include "Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h"
#include "Swiften/Parser/PayloadParsers/DelayParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h"
using namespace boost;
@@ -53,6 +54,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
factories_.push_back(shared_ptr<PayloadParserFactory>(new PrivateStorageParserFactory(this)));
factories_.push_back(shared_ptr<PayloadParserFactory>(new ChatStateParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new DelayParserFactory()));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new MUCUserPayloadParserFactory()));
foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
addFactory(factory.get());
}
diff --git a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp
new file mode 100644
index 0000000..5bb0e68
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include "Swiften/MUC/MUCOccupant.h"
+
+#include <cassert>
+#include <iostream>
+
+namespace Swift {
+
+MUCUserPayloadParser::MUCUserPayloadParser() : level(TopLevel) {
+}
+
+void MUCUserPayloadParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) {
+ if (level == ItemLevel) {
+ if (element == "item") {
+ MUCUserPayload::Item item;
+ String affiliation = attributes.getAttribute("affiliation");
+ String role = attributes.getAttribute("role");
+ String nick = attributes.getAttribute("nick");
+ String jid = attributes.getAttribute("jid");
+ item.affiliation = parseAffiliation(affiliation);
+ item.role = parseRole(role);
+ if (!jid.isEmpty()) {
+ item.realJID = JID(jid);
+ }
+ if (!nick.isEmpty()) {
+ item.nick = nick;
+ }
+ getPayloadInternal()->addItem(item);
+ } else if (element == "status") {
+ MUCUserPayload::StatusCode status;
+ try {
+ status.code = boost::lexical_cast<int>(attributes.getAttribute("code").getUTF8Data());
+ getPayloadInternal()->addStatusCode(status);
+ } catch (boost::bad_lexical_cast&) {
+ }
+ }
+ }
+ ++level;
+}
+
+MUCOccupant::Role MUCUserPayloadParser::parseRole(const String& roleString) const {
+ if (roleString == "moderator") {
+ return MUCOccupant::Moderator;
+ }
+ if (roleString == "participant") {
+ return MUCOccupant::Participant;
+ }
+ if (roleString == "visitor") {
+ return MUCOccupant::Visitor;
+ }
+ if (roleString == "none") {
+ return MUCOccupant::NoRole;
+ }
+ return MUCOccupant::NoRole;
+}
+
+MUCOccupant::Affiliation MUCUserPayloadParser::parseAffiliation(const String& affiliationString) const {
+ if (affiliationString == "owner") {
+ return MUCOccupant::Owner;
+ }
+ if (affiliationString == "admin") {
+ return MUCOccupant::Admin;
+ }
+ if (affiliationString == "member") {
+ return MUCOccupant::Member;
+ }
+ if (affiliationString == "outcast") {
+ return MUCOccupant::Outcast;
+ }
+ if (affiliationString == "none") {
+ return MUCOccupant::NoAffiliation;
+ }
+ return MUCOccupant::NoAffiliation;
+}
+
+
+void MUCUserPayloadParser::handleEndElement(const String& /*element*/, const String&) {
+ --level;
+}
+
+void MUCUserPayloadParser::handleCharacterData(const String& /*data*/) {
+
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h
new file mode 100644
index 0000000..01c7de1
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "Swiften/Elements/MUCUserPayload.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class MUCUserPayloadParser : public GenericPayloadParser<MUCUserPayload> {
+ public:
+ MUCUserPayloadParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+ MUCOccupant::Role parseRole(const String& itemString) const;
+ MUCOccupant::Affiliation parseAffiliation(const String& statusString) const;
+ private:
+ enum Level {
+ TopLevel = 0,
+ ItemLevel = 1
+ };
+ int level;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h
new file mode 100644
index 0000000..3946ece
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Parser/GenericPayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h"
+
+namespace Swift {
+ class MUCUserPayloadParserFactory : public GenericPayloadParserFactory<MUCUserPayloadParser> {
+ public:
+ MUCUserPayloadParserFactory() : GenericPayloadParserFactory<MUCUserPayloadParser>("x", "http://jabber.org/protocol/muc#user") {}
+ };
+}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 77f50be..430d827 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -37,6 +37,7 @@ sources = [
"PayloadParsers/VCardParser.cpp",
"PayloadParsers/VCardUpdateParser.cpp",
"PayloadParsers/DelayParser.cpp",
+ "PayloadParsers/MUCUserPayloadParser.cpp",
"PlatformXMLParserFactory.cpp",
"PresenceParser.cpp",
"SerializingParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 4d25dc5..f8bcc8c 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -74,6 +74,7 @@ if env["SCONS_STAGE"] == "build" :
"Serializer/PayloadSerializers/ErrorSerializer.cpp",
"Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp",
"Serializer/PayloadSerializers/MUCPayloadSerializer.cpp",
+ "Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp",
"Serializer/PayloadSerializers/ResourceBindSerializer.cpp",
"Serializer/PayloadSerializers/RosterSerializer.cpp",
"Serializer/PayloadSerializers/SecurityLabelSerializer.cpp",
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index caf1d9c..e407876 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -13,6 +13,7 @@
#include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/StatusSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h"
diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
index 9630014..36aa47f 100644
--- a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
@@ -1,19 +1,21 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+
namespace Swift {
MUCPayloadSerializer::MUCPayloadSerializer() : GenericPayloadSerializer<MUCPayload>() {
}
-String MUCPayloadSerializer::serializePayload(boost::shared_ptr<MUCPayload>) const {
- String result("<x xmlns='http://jabber.org/protocol/muc'/>");
- return result;
+String MUCPayloadSerializer::serializePayload(boost::shared_ptr<MUCPayload> payload) const {
+ XMLElement mucElement("x", "http://jabber.org/protocol/muc");
+ return mucElement.serialize();
}
}
diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h
index b015980..cd7f107 100644
--- a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h
+++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h
@@ -1,11 +1,10 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_MUCPayloadSerializer_H
-#define SWIFTEN_MUCPayloadSerializer_H
+#pragma once
#include "Swiften/Serializer/GenericPayloadSerializer.h"
#include "Swiften/Elements/MUCPayload.h"
@@ -14,9 +13,7 @@ namespace Swift {
class MUCPayloadSerializer : public GenericPayloadSerializer<MUCPayload> {
public:
MUCPayloadSerializer();
-
virtual String serializePayload(boost::shared_ptr<MUCPayload> version) const;
};
}
-#endif
diff --git a/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp
new file mode 100644
index 0000000..f4732ea
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h"
+
+#include <sstream>
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Serializer/XML/XMLElement.h"
+#include "Swiften/Serializer/XML/XMLTextNode.h"
+
+
+namespace Swift {
+
+MUCUserPayloadSerializer::MUCUserPayloadSerializer() : GenericPayloadSerializer<MUCUserPayload>() {
+}
+
+String MUCUserPayloadSerializer::serializePayload(boost::shared_ptr<MUCUserPayload> payload) const {
+ XMLElement mucElement("x", "http://jabber.org/protocol/muc");
+ foreach (const MUCUserPayload::StatusCode statusCode, payload->getStatusCodes()) {
+ boost::shared_ptr<XMLElement> statusElement(new XMLElement("status"));
+ std::ostringstream code;
+ code << statusCode.code;
+ statusElement->setAttribute("code", code.str());
+ mucElement.addNode(statusElement);
+ }
+ foreach (const MUCUserPayload::Item item, payload->getItems()) {
+ boost::shared_ptr<XMLElement> itemElement(new XMLElement("item"));
+ itemElement->setAttribute("affiliation", affiliationToString(item.affiliation));
+ itemElement->setAttribute("role", roleToString(item.role));
+ if (item.realJID) {
+ itemElement->setAttribute("jid", item.realJID.get());
+ }
+ if (item.nick) {
+ itemElement->setAttribute("nick", item.nick.get());
+ }
+ mucElement.addNode(itemElement);
+ }
+ return mucElement.serialize();
+}
+
+String MUCUserPayloadSerializer::affiliationToString(MUCOccupant::Affiliation affiliation) const {
+ String result;
+ switch (affiliation) {
+ case MUCOccupant::Owner: result = "owner"; break;
+ case MUCOccupant::Admin: result = "admin"; break;
+ case MUCOccupant::Member: result = "member"; break;
+ case MUCOccupant::Outcast: result = "outcast"; break;
+ case MUCOccupant::NoAffiliation: result = "none"; break;
+ default: assert(false);
+ }
+ return result;
+}
+
+String MUCUserPayloadSerializer::roleToString(MUCOccupant::Role role) const {
+ String result;
+ switch (role) {
+ case MUCOccupant::Moderator: result = "moderator"; break;
+ case MUCOccupant::NoRole: result = "none"; break;
+ case MUCOccupant::Participant: result = "participant"; break;
+ case MUCOccupant::Visitor: result = "visitor"; break;
+ default: assert(false);
+ }
+ return result;
+
+}
+
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h
new file mode 100644
index 0000000..bad21c9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/MUCUserPayload.h"
+
+namespace Swift {
+ class MUCUserPayloadSerializer : public GenericPayloadSerializer<MUCUserPayload> {
+ public:
+ MUCUserPayloadSerializer();
+ String affiliationToString(MUCOccupant::Affiliation affiliation) const;
+ String roleToString(MUCOccupant::Role role) const;
+
+ virtual String serializePayload(boost::shared_ptr<MUCUserPayload> version) const;
+ };
+}
+