diff options
41 files changed, 824 insertions, 199 deletions
| diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index aa0a1e7..19d7b4d 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -23,6 +23,7 @@  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>  #include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swift/Controllers/Roster/ContactRosterItem.h>  #include <Swiften/Avatars/AvatarManager.h>  #include <Swiften/Elements/Delay.h>  #include <Swiften/MUC/MUC.h> @@ -68,6 +69,8 @@ MUCController::MUCController (  	chatWindow_->setTabComplete(completer_);  	chatWindow_->setName(muc->getJID().getNode());  	chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); +	chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1)); +	chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));  	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)); @@ -97,6 +100,21 @@ MUCController::~MUCController() {  	delete completer_;  } +void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item) { +	std::vector<ChatWindow::OccupantAction> actions; +	//FIXME +	if (item) { +		actions.push_back(ChatWindow::Kick); +	} +	chatWindow_->setAvailableOccupantActions(actions); +} + +void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction action, ContactRosterItem* item) { +	switch (action) { +		case ChatWindow::Kick: muc_->kickUser(item->getJID());break; +	} +} +  void MUCController::handleBareJIDCapsChanged(const JID& /*jid*/) {  	ChatWindow::Tristate support = ChatWindow::Yes;  	bool any = false; diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 3a79920..39e5fa4 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -19,11 +19,12 @@  #include <Swiften/JID/JID.h>  #include <Swiften/MUC/MUC.h>  #include <Swiften/Elements/MUCOccupant.h> +#include <Swift/Controllers/Roster/RosterItem.h> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h>  namespace Swift {  	class StanzaChannel;  	class IQRouter; -	class ChatWindow;  	class ChatWindowFactory;  	class Roster;  	class AvatarManager; @@ -64,6 +65,8 @@ namespace Swift {  		private:  			void clearPresenceQueue();  			void addPresenceMessage(const std::string& message); +			void handleWindowOccupantSelectionChanged(ContactRosterItem* item); +			void handleActionRequestedOnOccupant(ChatWindow::OccupantAction, ContactRosterItem* item);  			void handleWindowClosed();  			void handleAvatarChanged(const JID& jid);  			void handleOccupantJoined(const MUCOccupant& occupant); diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index e84116d..faef5c8 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -22,11 +22,14 @@ namespace Swift {  	class TreeWidget;  	class Roster;  	class TabComplete; +	class RosterItem; +	class ContactRosterItem;  	class ChatWindow {  		public:  			enum AckState {Pending, Received, Failed};  			enum Tristate {Yes, No, Maybe}; +			enum OccupantAction {Kick};  			ChatWindow() {}  			virtual ~ChatWindow() {}; @@ -72,6 +75,10 @@ namespace Swift {  			 */  			virtual void cancelAlert() = 0; +			/** +			 * Actions that can be performed on the selected occupant. +			 */ +			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0;  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead;  			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest; @@ -79,6 +86,8 @@ namespace Swift {  			boost::signal<void ()> onUserTyping;  			boost::signal<void ()> onUserCancelsTyping;  			boost::signal<void ()> onAlertButtonClicked; +			boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged; +			boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;  	};  }  #endif diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 7d6828f..574248f 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -40,6 +40,7 @@ namespace Swift {  			virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {};  			virtual void cancelAlert() {};  			virtual void setCorrectionEnabled(Tristate /*enabled*/) {} +			void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {}  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index a52d2de..a36bc32 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -7,7 +7,9 @@  #include "QtChatWindow.h"  #include "QtSwiftUtil.h"  #include "Swift/Controllers/Roster/Roster.h" -#include "Roster/QtTreeWidget.h" +#include "Swift/Controllers/Roster/RosterItem.h" +#include "Swift/Controllers/Roster/ContactRosterItem.h" +#include "Roster/QtOccupantListWidget.h"  #include "SwifTools/Linkify.h"  #include "QtChatView.h"  #include "MessageSnippet.h" @@ -70,7 +72,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	messageLog_ = new QtChatView(theme, this);  	logRosterSplitter_->addWidget(messageLog_); -	treeWidget_ = new QtTreeWidget(eventStream_); +	treeWidget_ = new QtOccupantListWidget(eventStream_, this);  	treeWidget_->hide();  	logRosterSplitter_->addWidget(treeWidget_);  	logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -115,7 +117,8 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));  	resize(400,300);  	connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int))); - +	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1)); +	treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));  } @@ -123,6 +126,9 @@ QtChatWindow::~QtChatWindow() {  } +void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) { +	onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item)); +}  void QtChatWindow::handleFontResized(int fontSizeSteps) {  	messageLog_->resizeFont(fontSizeSteps); @@ -530,4 +536,8 @@ void QtChatWindow::replaceLastMessage(const std::string& message) {  	messageLog_->replaceLastMessage(P2QSTRING(Linkify::linkify(message)));  } +void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) { +	treeWidget_->setAvailableOccupantActions(actions); +} +  } diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index bc1045d..d38e9c6 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -23,8 +23,7 @@ class QPushButton;  namespace Swift {  	class QtChatView; -	class QtTreeWidget; -	class QtTreeWidgetFactory; +	class QtOccupantListWidget;  	class QtChatTheme;  	class TreeWidget;  	class QtTextEdit; @@ -60,6 +59,7 @@ namespace Swift {  			void setAckState(const std::string& id, AckState state);  			void flash();  			QByteArray getSplitterState(); +			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);  		public slots:  			void handleChangeSplitterState(QByteArray state); @@ -88,13 +88,13 @@ namespace Swift {  			void handleKeyPressEvent(QKeyEvent* event);  			void handleSplitterMoved(int pos, int index);  			void handleAlertButtonClicked(); -  		private:  			void updateTitleWithUnreadCount();  			void tabComplete();  			void beginCorrection();  			void cancelCorrection();  			std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time); +			void handleOccupantSelectionChanged(RosterItem* item);  			int unreadCount_;  			bool contactIsTyping_; @@ -105,7 +105,7 @@ namespace Swift {  			QtChatTheme* theme_;  			QtTextEdit* input_;  			QComboBox* labelsWidget_; -			QtTreeWidget* treeWidget_; +			QtOccupantListWidget* treeWidget_;  			QLabel* correctingLabel_;  			QLabel* alertLabel_;  			QWidget* alertWidget_; diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 7c84773..9d35435 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -62,7 +62,7 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	contactTabLayout->setSpacing(0);  	contactTabLayout->setContentsMargins(0, 0, 0, 0); -	treeWidget_ = new QtRosterWidget(uiEventStream_); +	treeWidget_ = new QtRosterWidget(uiEventStream_, this);  	contactTabLayout->addWidget(treeWidget_);  	tabs_->addTab(contactsTabWidget_, tr("&Contacts")); @@ -115,7 +115,7 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction()));  	actionsMenu->addAction(signOutAction); -	connect(treeWidget_, SIGNAL(onSomethingSelectedChanged(bool)), editUserAction_, SLOT(setEnabled(bool))); +	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QAction::setEnabled, editUserAction_, _1));  	setAvailableAdHocCommands(std::vector<DiscoItems::Item>());  	QAction* adHocAction = new QAction(tr("Collecting commands..."), this); diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp index 2f992bf..cbda0f1 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp +++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp @@ -9,6 +9,7 @@  #include <QContextMenuEvent>  #include <QMenu> +#include <QAction>  #include <QInputDialog>  #include "Swift/Controllers/Roster/ContactRosterItem.h" @@ -26,33 +27,33 @@ QtOccupantListWidget::~QtOccupantListWidget() {  } +void QtOccupantListWidget::setAvailableOccupantActions(const std::vector<ChatWindow::OccupantAction>& actions) { +	availableOccupantActions_ = actions; +} +  void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) { -//	QModelIndex index = indexAt(event->pos()); -//	if (!index.isValid()) { -//		return; -//	} -//	RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); -//	QMenu contextMenu; -//	if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { -//		QAction* editContact = contextMenu.addAction(tr("Edit")); -//		QAction* removeContact = contextMenu.addAction(tr("Remove")); -//		QAction* result = contextMenu.exec(event->globalPos()); -//		if (result == editContact) { -//			eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID())); -//		} -//		else if (result == removeContact) { -//			if (QtContactEditWindow::confirmContactDeletion(contact->getJID())) { -//				eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID())); -//			} -//		} -//	} -//	else if (GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item)) { -//		QAction* renameGroupAction = contextMenu.addAction(tr("Rename")); -//		QAction* result = contextMenu.exec(event->globalPos()); -//		if (result == renameGroupAction) { -//			renameGroup(group); -//		} -//	} +	QModelIndex index = indexAt(event->pos()); +	if (!index.isValid()) { +		return; +	} +	RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); +	ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); +	if (contact) { +		QMenu contextMenu; +		std::map<QAction*, ChatWindow::OccupantAction> actions; +		foreach (ChatWindow::OccupantAction availableAction, availableOccupantActions_) { +			QString text = "Error: missing string"; +			switch (availableAction) { +				case ChatWindow::Kick: text = tr("Kick user"); break; +			} +			QAction* action = contextMenu.addAction(text); +			actions[action] = availableAction; +		} +		QAction* result = contextMenu.exec(event->globalPos()); +		if (result) { +			onOccupantActionSelected(actions[result], contact); +		} +	}  }  } diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.h b/Swift/QtUI/Roster/QtOccupantListWidget.h index 5444210..ef29c00 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.h +++ b/Swift/QtUI/Roster/QtOccupantListWidget.h @@ -8,6 +8,9 @@  #include "Swift/QtUI/Roster/QtTreeWidget.h" +#include "Swiften/Base/boost_bsignals.h" +#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +  namespace Swift {  class QtOccupantListWidget : public QtTreeWidget { @@ -15,8 +18,12 @@ class QtOccupantListWidget : public QtTreeWidget {  	public:  		QtOccupantListWidget(UIEventStream* eventStream, QWidget* parent = 0);  		virtual ~QtOccupantListWidget(); +		void setAvailableOccupantActions(const std::vector<ChatWindow::OccupantAction>& actions); +		boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;  	protected:  		void contextMenuEvent(QContextMenuEvent* event); +	private: +		std::vector<ChatWindow::OccupantAction> availableOccupantActions_;  };  } diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp index 79d7f67..923f977 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.cpp +++ b/Swift/QtUI/Roster/QtRosterWidget.cpp @@ -81,20 +81,4 @@ void QtRosterWidget::renameGroup(GroupRosterItem* group) {  	}  } -void QtRosterWidget::currentChanged(const QModelIndex& current, const QModelIndex& previous) { -	bool valid = false; -	QModelIndexList selectedIndexList = getSelectedIndexes(); -	if (selectedIndexList.empty() || !selectedIndexList[0].isValid()) { -		/* I didn't quite understand why using current didn't seem to work here.*/ -	} -	else if (current.isValid()) { -		RosterItem* item = static_cast<RosterItem*>(current.internalPointer()); -		if (dynamic_cast<ContactRosterItem*>(item)) { -			valid = true; -		} -	} -	onSomethingSelectedChanged(valid); -	QTreeView::currentChanged(current, previous); -} -  } diff --git a/Swift/QtUI/Roster/QtRosterWidget.h b/Swift/QtUI/Roster/QtRosterWidget.h index 7781e07..f870237 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.h +++ b/Swift/QtUI/Roster/QtRosterWidget.h @@ -9,7 +9,6 @@  #include "Swift/QtUI/Roster/QtTreeWidget.h"  namespace Swift { -  class QtRosterWidget : public QtTreeWidget {  	Q_OBJECT  	public: @@ -17,12 +16,8 @@ class QtRosterWidget : public QtTreeWidget {  		virtual ~QtRosterWidget();  	public slots:  		void handleEditUserActionTriggered(bool checked); -	signals: -		void onSomethingSelectedChanged(bool);  	protected:  		void contextMenuEvent(QContextMenuEvent* event); -	protected slots: -		virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);  	private:  		void renameGroup(GroupRosterItem* group);  }; diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp index 7de4028..96a078b 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.cpp +++ b/Swift/QtUI/Roster/QtTreeWidget.cpp @@ -81,6 +81,21 @@ QModelIndexList QtTreeWidget::getSelectedIndexes() const {  	return selectedIndexList;  } +void QtTreeWidget::currentChanged(const QModelIndex& current, const QModelIndex& previous) { +	RosterItem* item = NULL; +	QModelIndexList selectedIndexList = getSelectedIndexes(); +	if (selectedIndexList.empty() || !selectedIndexList[0].isValid()) { +		/* I didn't quite understand why using current didn't seem to work here.*/ +	} +	else if (current.isValid()) { +		item = static_cast<RosterItem*>(current.internalPointer()); +		item = dynamic_cast<ContactRosterItem*>(item); +	} +	onSomethingSelectedChanged(item); +	QTreeView::currentChanged(current, previous); +} + +  void QtTreeWidget::handleItemActivated(const QModelIndex& index) {  	RosterItem* item = static_cast<RosterItem*>(index.internalPointer());  	ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h index c45a1cd..1ad56d6 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.h +++ b/Swift/QtUI/Roster/QtTreeWidget.h @@ -23,6 +23,7 @@ class QtTreeWidget : public QTreeView{  		QtTreeWidgetItem* getRoot();  		void setRosterModel(Roster* roster);  		Roster* getRoster() {return roster_;} +		boost::signal<void (RosterItem*)> onSomethingSelectedChanged;  	private slots:  		void handleItemActivated(const QModelIndex&); @@ -35,7 +36,8 @@ class QtTreeWidget : public QTreeView{  		QModelIndexList getSelectedIndexes() const;  	private:  		void drawBranches(QPainter*, const QRect&, const QModelIndex&) const; -	 +	protected slots: +		virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);  	protected:  		UIEventStream* eventStream_; diff --git a/Swiften/Elements/MUCAdminPayload.h b/Swiften/Elements/MUCAdminPayload.h new file mode 100644 index 0000000..7b3da20 --- /dev/null +++ b/Swiften/Elements/MUCAdminPayload.h @@ -0,0 +1,35 @@ +/* + * 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 <boost/shared_ptr.hpp> +#include <string> +#include <vector> + +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/Payload.h> +#include <Swiften/Elements/MUCOccupant.h> +#include <Swiften/Elements/MUCItem.h> + +namespace Swift { +	class MUCAdminPayload : public Payload { +		public: +			typedef boost::shared_ptr<MUCAdminPayload> ref; + + +			MUCAdminPayload() { +			} + +			void addItem(MUCItem item) {items_.push_back(item);} + +			const std::vector<MUCItem>& getItems() const {return items_;} + +		private: +			std::vector<MUCItem> items_; +	}; +} diff --git a/Swiften/Elements/MUCItem.h b/Swiften/Elements/MUCItem.h new file mode 100644 index 0000000..86217ec --- /dev/null +++ b/Swiften/Elements/MUCItem.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/MUCOccupant.h> +#include <Swiften/JID/JID.h> +namespace Swift { +struct MUCItem { +	MUCItem() {} +	boost::optional<JID> realJID; +	boost::optional<std::string> nick; +	boost::optional<MUCOccupant::Affiliation> affiliation; +	boost::optional<MUCOccupant::Role> role; +	boost::optional<JID> actor; +	boost::optional<std::string> reason; +}; +} diff --git a/Swiften/Elements/MUCUserPayload.h b/Swiften/Elements/MUCUserPayload.h index 0276964..de9f1e5 100644 --- a/Swiften/Elements/MUCUserPayload.h +++ b/Swiften/Elements/MUCUserPayload.h @@ -14,20 +14,13 @@  #include <Swiften/JID/JID.h>  #include <Swiften/Elements/Payload.h>  #include <Swiften/Elements/MUCOccupant.h> +#include <Swiften/Elements/MUCItem.h>  namespace Swift {  	class MUCUserPayload : public Payload {  		public:  			typedef boost::shared_ptr<MUCUserPayload> ref; -			struct Item { -				Item(MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation, MUCOccupant::Role role = MUCOccupant::NoRole) : affiliation(affiliation), role(role) {} -				boost::optional<JID> realJID; -				boost::optional<std::string> nick; -				MUCOccupant::Affiliation affiliation; -				MUCOccupant::Role role; -			}; -  			struct StatusCode {  				StatusCode() : code(0) {}  				int code; @@ -48,16 +41,16 @@ namespace Swift {  			MUCUserPayload() {  			} -			void addItem(Item item) {items_.push_back(item);} +			void addItem(MUCItem item) {items_.push_back(item);}  			void addStatusCode(StatusCode code) {statusCodes_.push_back(code);} -			const std::vector<Item>& getItems() const {return items_;} +			const std::vector<MUCItem>& getItems() const {return items_;}  			const std::vector<StatusCode>& getStatusCodes() const {return statusCodes_;}  		private: -			std::vector<Item> items_; +			std::vector<MUCItem> items_;  			std::vector<StatusCode> statusCodes_;  	};  } diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp index 1ade7e3..4b3960f 100644 --- a/Swiften/MUC/MUC.cpp +++ b/Swiften/MUC/MUC.cpp @@ -17,6 +17,7 @@  #include <Swiften/Elements/Form.h>  #include <Swiften/Elements/IQ.h>  #include <Swiften/Elements/MUCUserPayload.h> +#include <Swiften/Elements/MUCAdminPayload.h>  #include <Swiften/Elements/MUCPayload.h>  #include <Swiften/MUC/MUCRegistry.h>  #include <Swiften/Queries/GenericRequest.h> @@ -121,8 +122,8 @@ void MUC::handleIncomingPresence(Presence::ref presence) {  	MUCOccupant::Affiliation affiliation(MUCOccupant::NoAffiliation);  	boost::optional<JID> realJID;  	if (mucPayload && mucPayload->getItems().size() > 0) { -		role = mucPayload->getItems()[0].role; -		affiliation = mucPayload->getItems()[0].affiliation; +		role = mucPayload->getItems()[0].role ? mucPayload->getItems()[0].role.get() : MUCOccupant::NoRole; +		affiliation = mucPayload->getItems()[0].affiliation ? mucPayload->getItems()[0].affiliation.get() : MUCOccupant::NoAffiliation;  		realJID = mucPayload->getItems()[0].realJID;  	} @@ -217,6 +218,23 @@ MUCOccupant MUC::getOccupant(const std::string& nick) {  	return occupants.find(nick)->second;  } +void MUC::kickUser(const JID& jid) { +	MUCAdminPayload::ref mucPayload = boost::make_shared<MUCAdminPayload>(); +	MUCItem item; +	item.role = MUCOccupant::NoRole; +	item.nick = jid.getResource(); +	mucPayload->addItem(item); +	GenericRequest<MUCAdminPayload>* request = new GenericRequest<MUCAdminPayload>(IQ::Set, getJID(), mucPayload, iqRouter_); +	request->onResponse.connect(boost::bind(&MUC::handleKickResponse, this, _1, _2, jid)); +	request->send(); +} + +void MUC::handleKickResponse(MUCAdminPayload::ref /*unused*/, ErrorPayload::ref error, const JID& jid) { +	if (error) { +		onKickFailed(error, jid); +	} +} +  //FIXME: Recognise Topic changes  //TODO: Invites(direct/mediated) diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h index 828a36f..41dbc4a 100644 --- a/Swiften/MUC/MUC.h +++ b/Swiften/MUC/MUC.h @@ -13,6 +13,7 @@  #include <Swiften/Elements/MUCOccupant.h>  #include <Swiften/MUC/MUCRegistry.h>  #include <Swiften/Elements/MUCOwnerPayload.h> +#include <Swiften/Elements/MUCAdminPayload.h>  #include <boost/shared_ptr.hpp>  #include <Swiften/Base/boost_bsignals.h> @@ -54,9 +55,11 @@ namespace Swift {  			/** Get occupant information*/  			MUCOccupant getOccupant(const std::string& nick);  			bool hasOccupant(const std::string& nick); +			void kickUser(const JID& jid);  		public:  			boost::signal<void (const std::string& /*nick*/)> onJoinComplete;  			boost::signal<void (ErrorPayload::ref)> onJoinFailed; +			boost::signal<void (ErrorPayload::ref, const JID&)> onKickFailed;  			boost::signal<void (Presence::ref)> onOccupantPresenceChange;  			boost::signal<void (const std::string&, const MUCOccupant& /*now*/, const MUCOccupant::Role& /*old*/)> onOccupantRoleChanged;  			boost::signal<void (const std::string&, const MUCOccupant::Affiliation& /*new*/, const MUCOccupant::Affiliation& /*old*/)> onOccupantAffiliationChanged; @@ -79,6 +82,7 @@ namespace Swift {  			void handleIncomingPresence(Presence::ref presence);  			void internalJoin(const std::string& nick);  			void handleCreationConfigResponse(MUCOwnerPayload::ref, ErrorPayload::ref); +			void handleKickResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&);  		private:  			JID ownMUCJID; diff --git a/Swiften/Parser/GenericPayloadTreeParser.cpp b/Swiften/Parser/GenericPayloadTreeParser.cpp new file mode 100644 index 0000000..0e25cbc --- /dev/null +++ b/Swiften/Parser/GenericPayloadTreeParser.cpp @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/GenericPayloadTreeParser.h> + diff --git a/Swiften/Parser/GenericPayloadTreeParser.h b/Swiften/Parser/GenericPayloadTreeParser.h new file mode 100644 index 0000000..0030af7 --- /dev/null +++ b/Swiften/Parser/GenericPayloadTreeParser.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <deque> + +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Parser/GenericPayloadParser.h> +#include <Swiften/Parser/Tree/ParserElement.h> +#include <Swiften/Parser/Tree/NullParserElement.h> + +#include <iostream> + +namespace Swift { + + +	/** +	 * Generic parser offering something a bit like a DOM to work with. +	 */ +	template<typename PAYLOAD_TYPE> +	class GenericPayloadTreeParser : public GenericPayloadParser<PAYLOAD_TYPE> { +		public: +			virtual void handleStartElement(const std::string& element, const std::string& xmlns, const AttributeMap& attributes) { +				//std::cerr << element << ", " << xmlns << ", " << attributes.getEntries().size(); +				if (!root_) { +					root_ = boost::make_shared<ParserElement>(element, xmlns, attributes); +					elementStack_.push_back(root_); +				} else { +					ParserElement::ref current = *elementStack_.rbegin(); +					elementStack_.push_back(current->addChild(element, xmlns, attributes)); +				} +			} + +			virtual void handleEndElement(const std::string& /*element*/, const std::string&) { +				elementStack_.pop_back(); +				if (elementStack_.empty()) { +					handleTree(root_); +				} +			} + +			virtual void handleCharacterData(const std::string& data) { +				ParserElement::ref current = *elementStack_.rbegin(); +				current->appendCharacterData(data); +			} + +			virtual void handleTree(ParserElement::ref root) = 0; + +		private: +			std::deque<ParserElement::ref> elementStack_; +			ParserElement::ref root_; +	}; +} diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index e9c49ee..1b59f6f 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -44,6 +44,7 @@  #include <Swiften/Parser/PayloadParsers/PrivateStorageParserFactory.h>  #include <Swiften/Parser/PayloadParsers/DelayParser.h>  #include <Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h> +#include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h>  #include <Swiften/Parser/PayloadParsers/NicknameParserFactory.h>  #include <Swiften/Parser/PayloadParsers/ReplaceParser.h>  #include <Swiften/Parser/PayloadParsers/LastParser.h> @@ -88,6 +89,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 MUCUserPayloadParserFactory())); +	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCAdminPayloadParser>("query", "http://jabber.org/protocol/muc#admin")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new NicknameParserFactory()));  	foreach(shared_ptr<PayloadParserFactory> factory, factories_) {  		addFactory(factory.get()); diff --git a/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.cpp b/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.cpp new file mode 100644 index 0000000..137ead4 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h> + +#include <boost/lexical_cast.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Elements/MUCOccupant.h> + +namespace Swift { + +void MUCAdminPayloadParser::handleTree(ParserElement::ref root) { +	foreach (ParserElement::ref itemElement, root->getChildren("item", "http://jabber.org/protocol/muc#admin")) { +		MUCItem item = MUCItemParser::itemFromTree(itemElement); +		getPayloadInternal()->addItem(item); +	} +} + +} diff --git a/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h b/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h new file mode 100644 index 0000000..dfa26da --- /dev/null +++ b/Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 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/MUCAdminPayload.h> +#include <Swiften/Parser/GenericPayloadTreeParser.h> +#include <Swiften/Parser/PayloadParsers/MUCItemParser.h> + +namespace Swift { +	class MUCAdminPayloadParser : public GenericPayloadTreeParser<MUCAdminPayload> { +		public: +			virtual void handleTree(ParserElement::ref root); +	}; +} diff --git a/Swiften/Parser/PayloadParsers/MUCItemParser.cpp b/Swiften/Parser/PayloadParsers/MUCItemParser.cpp new file mode 100644 index 0000000..2eb9997 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/MUCItemParser.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/PayloadParsers/MUCItemParser.h> + +#include <boost/lexical_cast.hpp> + +#include <Swiften/Elements/MUCOccupant.h> + +#include <cassert> +#include <iostream> + +namespace Swift { + +MUCItem MUCItemParser::itemFromTree(ParserElement::ref root) { +	MUCItem item; +	std::string affiliation = root->getAttributes().getAttribute("affiliation"); +	std::string role = root->getAttributes().getAttribute("role"); +	std::string nick = root->getAttributes().getAttribute("nick"); +	std::string jid = root->getAttributes().getAttribute("jid"); +	item.affiliation = parseAffiliation(affiliation); +	item.role = parseRole(role); +	if (!jid.empty()) { +		item.realJID = JID(jid); +	} +	if (!nick.empty()) { +		item.nick = nick; +	} +	std::string xmlns = root->getNamespace(); +	std::string reason = root->getChild("reason", xmlns)->getText(); +	std::string actor = root->getChild("actor", xmlns)->getAttributes().getAttribute("jid"); +	if (!reason.empty()) { +		item.reason = reason; +	} +	if (!actor.empty()) { +		item.actor = JID(actor); +	} + +	return item; +} + +boost::optional<MUCOccupant::Role> MUCItemParser::parseRole(const std::string& roleString) { +	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 boost::optional<MUCOccupant::Role>(); +} + +boost::optional<MUCOccupant::Affiliation> MUCItemParser::parseAffiliation(const std::string& affiliationString) { +	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 boost::optional<MUCOccupant::Affiliation>(); +} + +} + + diff --git a/Swiften/Parser/PayloadParsers/MUCItemParser.h b/Swiften/Parser/PayloadParsers/MUCItemParser.h new file mode 100644 index 0000000..a0ea060 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/MUCItemParser.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/MUCItem.h> +#include <Swiften/Parser/GenericPayloadTreeParser.h> + +namespace Swift { +	class MUCItemParser  { +		public: +			static MUCItem itemFromTree(ParserElement::ref root); +		private: +			static boost::optional<MUCOccupant::Role> parseRole(const std::string& itemString); +			static boost::optional<MUCOccupant::Affiliation> parseAffiliation(const std::string& statusString); +	}; +} diff --git a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp index bd81b88..71ae571 100644 --- a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp +++ b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.cpp @@ -8,87 +8,24 @@  #include <boost/lexical_cast.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Elements/MUCOccupant.h> -#include <cassert> -#include <iostream> -  namespace Swift { -MUCUserPayloadParser::MUCUserPayloadParser() : level(TopLevel) { -} - -void MUCUserPayloadParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) { -	if (level == ItemLevel) { -		if (element == "item") { -			MUCUserPayload::Item item; -			std::string affiliation = attributes.getAttribute("affiliation"); -			std::string role = attributes.getAttribute("role"); -			std::string nick = attributes.getAttribute("nick"); -			std::string jid = attributes.getAttribute("jid"); -			item.affiliation = parseAffiliation(affiliation); -			item.role = parseRole(role); -			if (!jid.empty()) { -				item.realJID = JID(jid); -			} -			if (!nick.empty()) { -				item.nick = nick; -			} -			getPayloadInternal()->addItem(item); -		} else if (element == "status") { -			MUCUserPayload::StatusCode status; -			try { -				status.code = boost::lexical_cast<int>(attributes.getAttribute("code").c_str()); -				getPayloadInternal()->addStatusCode(status); -			} catch (boost::bad_lexical_cast&) { -			} +void MUCUserPayloadParser::handleTree(ParserElement::ref root) { +	foreach (ParserElement::ref itemElement, root->getChildren("item", "http://jabber.org/protocol/muc#user")) { +		MUCItem item = MUCItemParser::itemFromTree(itemElement); +		getPayloadInternal()->addItem(item); +	} +	foreach (ParserElement::ref statusElement, root->getChildren("item", "http://jabber.org/protocol/muc#user")) { +		MUCUserPayload::StatusCode status; +		try { +			status.code = boost::lexical_cast<int>(statusElement->getAttributes().getAttribute("code").c_str()); +			getPayloadInternal()->addStatusCode(status); +		} catch (boost::bad_lexical_cast&) {  		}  	} -	++level; -} - -MUCOccupant::Role MUCUserPayloadParser::parseRole(const std::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 std::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 std::string& /*element*/, const std::string&) { -	--level; -} - -void MUCUserPayloadParser::handleCharacterData(const std::string& /*data*/) { -  }  } diff --git a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h index b819905..e020127 100644 --- a/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h +++ b/Swiften/Parser/PayloadParsers/MUCUserPayloadParser.h @@ -9,23 +9,12 @@  #include <boost/optional.hpp>  #include <Swiften/Elements/MUCUserPayload.h> -#include <Swiften/Parser/GenericPayloadParser.h> +#include <Swiften/Parser/GenericPayloadTreeParser.h> +#include <Swiften/Parser/PayloadParsers/MUCItemParser.h>  namespace Swift { -	class MUCUserPayloadParser : public GenericPayloadParser<MUCUserPayload> { +	class MUCUserPayloadParser : public GenericPayloadTreeParser<MUCUserPayload> {  		public: -			MUCUserPayloadParser(); - -			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); -			virtual void handleEndElement(const std::string& element, const std::string&); -			virtual void handleCharacterData(const std::string& data); -			MUCOccupant::Role parseRole(const std::string& itemString) const; -			MUCOccupant::Affiliation parseAffiliation(const std::string& statusString) const; -		private: -			enum Level {  -				TopLevel = 0,  -				ItemLevel = 1 -			}; -			int level; +			virtual void handleTree(ParserElement::ref root);  	};  } diff --git a/Swiften/Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp new file mode 100644 index 0000000..0648f58 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h> +#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h> + +using namespace Swift; + +class MUCAdminPayloadParserTest : public CppUnit::TestFixture +{ +		CPPUNIT_TEST_SUITE(MUCAdminPayloadParserTest); +		CPPUNIT_TEST(testParse); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		MUCAdminPayloadParserTest() {} + +		void testParse() { +			PayloadsParserTester parser; + +			CPPUNIT_ASSERT(parser.parse("<query xmlns=\"http://jabber.org/protocol/muc#admin\"><item affiliation=\"owner\" role=\"visitor\"><actor jid=\"kev@tester.lit\"/><reason>malice</reason></item></query>")); + +			MUCAdminPayload::ref payload = boost::dynamic_pointer_cast<MUCAdminPayload>(parser.getPayload()); +			MUCItem item = payload->getItems()[0]; +			CPPUNIT_ASSERT_EQUAL(MUCOccupant::Owner, item.affiliation.get()); +			CPPUNIT_ASSERT_EQUAL(MUCOccupant::Visitor, item.role.get()); +			CPPUNIT_ASSERT_EQUAL(JID("kev@tester.lit"), item.actor.get()); +			CPPUNIT_ASSERT_EQUAL(std::string("malice"), item.reason.get()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MUCAdminPayloadParserTest); + + + diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index 17505f1..b9fcebb 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -14,6 +14,7 @@ sources = [  		"CompressParser.cpp",  		"ElementParser.cpp",  		"IQParser.cpp", +		"GenericPayloadTreeParser.cpp",  		"MessageParser.cpp",  		"PayloadParser.cpp",  		"StanzaAckParser.cpp", @@ -51,6 +52,8 @@ sources = [  		"PayloadParsers/VCardUpdateParser.cpp",  		"PayloadParsers/DelayParser.cpp",  		"PayloadParsers/MUCUserPayloadParser.cpp", +		"PayloadParsers/MUCAdminPayloadParser.cpp", +		"PayloadParsers/MUCItemParser.cpp",  		"PayloadParsers/NicknameParser.cpp",  		"PayloadParsers/ReplaceParser.cpp",  		"PayloadParsers/LastParser.cpp", @@ -63,6 +66,7 @@ sources = [  		"StreamManagementEnabledParser.cpp",  		"StreamResumeParser.cpp",  		"StreamResumedParser.cpp", +		"Tree/ParserElement.cpp",  		"XMLParser.cpp",  		"XMLParserClient.cpp",  		"XMLParserFactory.cpp", diff --git a/Swiften/Parser/Tree/NullParserElement.h b/Swiften/Parser/Tree/NullParserElement.h new file mode 100644 index 0000000..93c0662 --- /dev/null +++ b/Swiften/Parser/Tree/NullParserElement.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <Swiften/Parser/Tree/ParserElement.h> + +namespace Swift { + +	class NullParserElement : public ParserElement { +		public: +			NullParserElement() : ParserElement("", "", AttributeMap()) {} +			virtual operator bool() {return false;}; +	}; +} diff --git a/Swiften/Parser/Tree/ParserElement.cpp b/Swiften/Parser/Tree/ParserElement.cpp new file mode 100644 index 0000000..c851b41 --- /dev/null +++ b/Swiften/Parser/Tree/ParserElement.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + + +#include <Swiften/Parser/Tree/ParserElement.h> +#include <Swiften/Parser/Tree/NullParserElement.h> + +#include <iostream> + +namespace Swift{ + +ParserElement::ParserElement(const std::string& name, const std::string& xmlns, const AttributeMap& attributes) : name_(name), xmlns_(xmlns), attributes_(attributes) { + +} + +ParserElement::~ParserElement() { + +} + +ParserElement::operator bool() { +	return true; +} + +ParserElement::ref ParserElement::addChild(const std::string& name, const std::string& xmlns, const AttributeMap& attributes) { +	ParserElement::ref child = boost::make_shared<ParserElement>(name, xmlns, attributes); +	children_.push_back(child); +	return child; +} + +void ParserElement::appendCharacterData(const std::string& data) { +	text_ += data; +} + +std::string ParserElement::getText() { +	return text_; +} + +std::string ParserElement::getName() { +	return name_; +} + +std::string ParserElement::getNamespace() { +	return xmlns_; +} + +struct DoesntMatch { +	public: +		DoesntMatch(const std::string& tagName, const std::string& ns) : tagName(tagName), ns(ns) {} +		bool operator()(ParserElement::ref element) { return element->getName() != tagName || element->getNamespace() != ns; } +	private: +		std::string tagName; +		std::string ns; +}; + + +std::vector<ParserElement::ref> ParserElement::getChildren(const std::string& name, const std::string& xmlns) { +	std::vector<ParserElement::ref> result; +	std::remove_copy_if(children_.begin(), children_.end(), std::back_inserter(result), DoesntMatch(name, xmlns)); +	return result; +} + +ParserElement::ref ParserElement::getChild(const std::string& name, const std::string& xmlns) { +	std::vector<ParserElement::ref> results = getChildren(name, xmlns); +	boost::shared_ptr<NullParserElement> nullParser = boost::make_shared<NullParserElement>(); +	ParserElement::ref result = results.empty() ? boost::dynamic_pointer_cast<ParserElement>(nullParser) : results[0]; +	return result; +} + +} diff --git a/Swiften/Parser/Tree/ParserElement.h b/Swiften/Parser/Tree/ParserElement.h new file mode 100644 index 0000000..ddf67fa --- /dev/null +++ b/Swiften/Parser/Tree/ParserElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + + +#pragma once + +#include <string> +#include <vector> +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/Parser/AttributeMap.h> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +namespace Swift { +class ParserElement { +	public: +		typedef boost::shared_ptr<ParserElement> ref; +		ParserElement(const std::string& name, const std::string& xmlns, const AttributeMap& attributes); +		virtual ~ParserElement(); +		virtual operator bool(); +		ParserElement::ref addChild(const std::string& name, const std::string& xmlns, const AttributeMap& attributes); +		void appendCharacterData(const std::string& data); +		std::string getText(); +		std::string getName(); +		std::string getNamespace(); +		std::vector<ParserElement::ref> getChildren(const std::string& name, const std::string& xmlns); +		ParserElement::ref getChild(const std::string& name, const std::string& xmlns); +		const AttributeMap& getAttributes() const {return attributes_;} +	private: +		std::vector<ParserElement::ref> children_; +		std::string name_; +		std::string xmlns_; +		AttributeMap attributes_; +		std::string text_; + +}; + +} diff --git a/Swiften/Parser/UnitTest/GenericPayloadTreeParserTest.cpp b/Swiften/Parser/UnitTest/GenericPayloadTreeParserTest.cpp new file mode 100644 index 0000000..d095afc --- /dev/null +++ b/Swiften/Parser/UnitTest/GenericPayloadTreeParserTest.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Parser/GenericPayloadTreeParser.h> +#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h> +#include <Swiften/Elements/RawXMLPayload.h> + +using namespace Swift; + +class GenericPayloadTreeParserTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(GenericPayloadTreeParserTest); +		CPPUNIT_TEST(testTree); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testTree() { +			MyParser testling; + +			std::string data = "<topLevel xmlns='urn:test:top'><firstLevelInheritedEmpty/><firstLevelInherited><secondLevelMultiChildren num='1'/><secondLevelMultiChildren num='2'/></firstLevelInherited><firstLevelNS xmlns='urn:test:first'/></topLevel>"; + +			PayloadParserTester tester(&testling); +			tester.parse(data); + +			ParserElement::ref tree = testling.tree; + +			CPPUNIT_ASSERT_EQUAL(std::string("topLevel"), tree->getName()); +			CPPUNIT_ASSERT_EQUAL(std::string("urn:test:top"), tree->getNamespace()); +			CPPUNIT_ASSERT(tree->getChild("firstLevelInheritedEmpty", "urn:test:top")); +			CPPUNIT_ASSERT(!*tree->getChild("firstLevelInheritedEmpty", "")); +			CPPUNIT_ASSERT(tree->getChild("firstLevelInherited", "urn:test:top")); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), tree->getChild("firstLevelInherited", "urn:test:top")->getChildren("secondLevelMultiChildren", "urn:test:top").size()); +			CPPUNIT_ASSERT_EQUAL(std::string("1"), tree->getChild("firstLevelInherited", "urn:test:top")->getChildren("secondLevelMultiChildren", "urn:test:top")[0]->getAttributes().getAttribute("num")); +			CPPUNIT_ASSERT_EQUAL(std::string("2"), tree->getChild("firstLevelInherited", "urn:test:top")->getChildren("secondLevelMultiChildren", "urn:test:top")[1]->getAttributes().getAttribute("num")); +			CPPUNIT_ASSERT(tree->getChild("firstLevelNS", "urn:test:first")); +		} + +	private: + + +		class MyParser : public GenericPayloadTreeParser<RawXMLPayload> +		{ +			public: +				virtual ~MyParser() {} +				virtual void handleTree(ParserElement::ref root) { +					tree = root; +				} +				ParserElement::ref tree; +		}; + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(GenericPayloadTreeParserTest); diff --git a/Swiften/SConscript b/Swiften/SConscript index 0144ddb..e20e5e6 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -146,6 +146,7 @@ if env["SCONS_STAGE"] == "build" :  			"Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp",  			"Serializer/PayloadSerializers/MUCPayloadSerializer.cpp",  			"Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp", +			"Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp",  			"Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.cpp",  			"Serializer/PayloadSerializers/ResourceBindSerializer.cpp",  			"Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp", @@ -292,8 +293,10 @@ if env["SCONS_STAGE"] == "build" :  			File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/ReplaceTest.cpp"), +			File("Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp"),  			File("Parser/UnitTest/AttributeMapTest.cpp"),  			File("Parser/UnitTest/IQParserTest.cpp"), +			File("Parser/UnitTest/GenericPayloadTreeParserTest.cpp"),  			File("Parser/UnitTest/MessageParserTest.cpp"),  			File("Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp"),  			File("Parser/UnitTest/PresenceParserTest.cpp"), @@ -336,6 +339,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"), +			File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"),  			File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"), diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp index 0746c37..0ddd445 100644 --- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp +++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp @@ -21,6 +21,7 @@  #include <Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h>  #include <Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h>  #include <Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h> +#include <Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h>  #include <Swiften/Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.h>  #include <Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h>  #include <Swiften/Serializer/PayloadSerializers/StatusSerializer.h> @@ -61,6 +62,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {  	serializers_.push_back(new RosterItemExchangeSerializer());  	serializers_.push_back(new MUCPayloadSerializer());  	serializers_.push_back(new MUCUserPayloadSerializer()); +	serializers_.push_back(new MUCAdminPayloadSerializer());  	serializers_.push_back(new MUCOwnerPayloadSerializer(this));  	serializers_.push_back(new SoftwareVersionSerializer());  	serializers_.push_back(new StatusSerializer()); diff --git a/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp new file mode 100644 index 0000000..552f7f1 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h> + +#include <sstream> + +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Serializer/XML/XMLElement.h> +#include <Swiften/Serializer/XML/XMLTextNode.h> +#include <Swiften/Serializer/PayloadSerializers/MUCItemSerializer.h> + + +namespace Swift { + +MUCAdminPayloadSerializer::MUCAdminPayloadSerializer() : GenericPayloadSerializer<MUCAdminPayload>() { +} + +std::string MUCAdminPayloadSerializer::serializePayload(boost::shared_ptr<MUCAdminPayload> payload)  const { +	XMLElement mucElement("query", "http://jabber.org/protocol/muc#admin"); +	foreach (const MUCItem item, payload->getItems()) { +		mucElement.addNode(MUCItemSerializer::itemToElement(item)); +	} +	return mucElement.serialize(); +} + + +} diff --git a/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h new file mode 100644 index 0000000..e288cd7 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 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/MUCAdminPayload.h> + +namespace Swift { +	class MUCAdminPayloadSerializer : public GenericPayloadSerializer<MUCAdminPayload> { +		public: +			MUCAdminPayloadSerializer(); +			std::string affiliationToString(MUCOccupant::Affiliation affiliation) const; +			std::string roleToString(MUCOccupant::Role role) const; + +			virtual std::string serializePayload(boost::shared_ptr<MUCAdminPayload> version)  const; +	}; +} + diff --git a/Swiften/Serializer/PayloadSerializers/MUCItemSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCItemSerializer.h new file mode 100644 index 0000000..7cb662c --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/MUCItemSerializer.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 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/MUCItem.h> +#include <boost/smart_ptr/make_shared.hpp> + +namespace Swift { +	class MUCItemSerializer { +		public: +			static std::string affiliationToString(MUCOccupant::Affiliation affiliation) { +				std::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; +			} + +			static std::string roleToString(MUCOccupant::Role role) { +				std::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; + +			} + +			static boost::shared_ptr<XMLElement> itemToElement(const MUCItem& item) { +				boost::shared_ptr<XMLElement> itemElement(new XMLElement("item")); +				if (item.affiliation) { +					itemElement->setAttribute("affiliation", affiliationToString(item.affiliation.get())); +				} +				if (item.role) { +					itemElement->setAttribute("role", roleToString(item.role.get())); +				} +				if (item.realJID) { +					itemElement->setAttribute("jid", item.realJID.get()); +				} +				if (item.nick) { +					itemElement->setAttribute("nick", item.nick.get()); +				} +				if (item.actor) { +					boost::shared_ptr<XMLElement> actorElement(new XMLElement("actor")); +					actorElement->setAttribute("jid", item.actor->toString()); +					itemElement->addNode(actorElement); +				} +				if (item.reason) { +					boost::shared_ptr<XMLElement> reasonElement(new XMLElement("reason")); +					reasonElement->addNode(boost::make_shared<XMLTextNode>(*item.reason)); +					itemElement->addNode(reasonElement); +				} +				return itemElement; +			} +	}; +} diff --git a/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp index 44aa506..0a8153d 100644 --- a/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp @@ -13,7 +13,7 @@  #include <Swiften/Base/foreach.h>  #include <Swiften/Serializer/XML/XMLElement.h>  #include <Swiften/Serializer/XML/XMLTextNode.h> - +#include <Swiften/Serializer/PayloadSerializers/MUCItemSerializer.h>  namespace Swift { @@ -29,46 +29,13 @@ std::string MUCUserPayloadSerializer::serializePayload(boost::shared_ptr<MUCUser  		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); +	foreach (const MUCItem item, payload->getItems()) { +		mucElement.addNode(MUCItemSerializer::itemToElement(item));  	}  	return mucElement.serialize();  } -std::string MUCUserPayloadSerializer::affiliationToString(MUCOccupant::Affiliation affiliation) const { -	std::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; -} - -std::string MUCUserPayloadSerializer::roleToString(MUCOccupant::Role role) const { -	std::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 index 634ce22..84c4a96 100644 --- a/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h +++ b/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h @@ -13,8 +13,6 @@ namespace Swift {  	class MUCUserPayloadSerializer : public GenericPayloadSerializer<MUCUserPayload> {  		public:  			MUCUserPayloadSerializer(); -			std::string affiliationToString(MUCOccupant::Affiliation affiliation) const; -			std::string roleToString(MUCOccupant::Role role) const;  			virtual std::string serializePayload(boost::shared_ptr<MUCUserPayload> version)  const;  	}; diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp new file mode 100644 index 0000000..a8acf80 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h> + +using namespace Swift; + +class MUCAdminPayloadSerializerTest : public CppUnit::TestFixture +{ +		CPPUNIT_TEST_SUITE(MUCAdminPayloadSerializerTest); +		CPPUNIT_TEST(testSerialize); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		MUCAdminPayloadSerializerTest() {} + +		void testSerialize() { +			MUCAdminPayloadSerializer testling; +			boost::shared_ptr<MUCAdminPayload> admin = boost::make_shared<MUCAdminPayload>(); +			MUCItem item; +			item.affiliation = MUCOccupant::Owner; +			item.role = MUCOccupant::Visitor; +			item.reason = "malice"; +			item.actor = JID("kev@tester.lit"); +			admin->addItem(item); + +			CPPUNIT_ASSERT_EQUAL(std::string("<query xmlns=\"http://jabber.org/protocol/muc#admin\"><item affiliation=\"owner\" role=\"visitor\"><actor jid=\"kev@tester.lit\"/><reason>malice</reason></item></query>"), testling.serialize(admin)); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MUCAdminPayloadSerializerTest); | 
 Swift
 Swift