diff options
| author | Kevin Smith <git@kismith.co.uk> | 2010-05-22 20:45:53 (GMT) | 
|---|---|---|
| committer | Kevin Smith <git@kismith.co.uk> | 2010-05-23 08:15:20 (GMT) | 
| commit | a68530cbbfeb17e01fd684b1ef41b960bc173f66 (patch) | |
| tree | 98b5cd195f04d781164cf1394cee9092bb7b2497 | |
| parent | c9659b556b932e2f887cf1d8ab6c5a0bead835eb (diff) | |
| download | swift-a68530cbbfeb17e01fd684b1ef41b960bc173f66.zip swift-a68530cbbfeb17e01fd684b1ef41b960bc173f66.tar.bz2 | |
Implement XEP-0045 joining, and appropriate error handling.
Resolves: #211
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; +	}; +} + | 
 Swift
 Swift