From 41854ebf8c3376d6ee430efe3a9fdd25610bb2f5 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Tue, 27 Sep 2011 17:13:56 +0100
Subject: Allow room configuration.

Resolves: #989

diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 79d376c..67bd74f 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -70,6 +70,7 @@ namespace Swift {
 			virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const = 0;
 			virtual void dayTicked() {};
 			virtual void handleBareJIDCapsChanged(const JID& jid) = 0;
+			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>);
 
 		private:
 			IDGenerator idGenerator_;
@@ -78,7 +79,6 @@ namespace Swift {
 			void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage);
 			void handleAllMessagesRead();
 			void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);
-			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>);
 			void handleDayChangeTick();
 
 		protected:
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 178f4b6..56cc639 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -72,13 +72,15 @@ MUCController::MUCController (
 	chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1));
 	chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));
 	chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
-	chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this));
+	chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
 	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));
 	muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
+	muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
+	muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));
 	if (timerFactory) {
 		loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
 		loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
@@ -577,8 +579,23 @@ void MUCController::handleChangeSubjectRequest(const std::string& subject) {
 	muc_->changeSubject(subject);
 }
 
-void MUCController::handleConfigureRequest() {
-	muc_->requestConfigurationForm();
+void MUCController::handleConfigureRequest(Form::ref form) {
+	if (form) {
+		muc_->configureRoom(form);
+	}
+	else {
+		muc_->requestConfigurationForm();
+	}
+}
+
+void MUCController::handleConfigurationFailed(ErrorPayload::ref error) {
+	std::string errorMessage = getErrorMessage(error);
+	errorMessage = str(format(QT_TRANSLATE_NOOP("", "Room configuration failed: %1%.")) % errorMessage);
+	chatWindow_->addErrorMessage(errorMessage);
+}
+
+void MUCController::handleConfigurationFormReceived(Form::ref form) {
+	chatWindow_->showRoomConfigurationForm(form);
 }
 
 }
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 08a3fc3..4be1488 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -88,7 +88,9 @@ namespace Swift {
 			void dayTicked() {lastWasPresence_ = false;}
 			void processUserPart();
 			void handleBareJIDCapsChanged(const JID& jid);
-			void handleConfigureRequest();
+			void handleConfigureRequest(Form::ref);
+			void handleConfigurationFailed(ErrorPayload::ref);
+			void handleConfigurationFormReceived(Form::ref);
 
 		private:
 			MUC::ref muc_;
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 7004324..75c92d3 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -14,8 +14,10 @@
 #include <vector>
 
 #include <string>
-#include "Swiften/Elements/SecurityLabelsCatalog.h"
-#include "Swiften/Elements/ChatState.h"
+#include <Swiften/Elements/SecurityLabelsCatalog.h>
+#include <Swiften/Elements/ChatState.h>
+#include <Swiften/Elements/Form.h>
+
 
 namespace Swift {
 	class AvatarManager;
@@ -87,6 +89,8 @@ namespace Swift {
 			 * Actions that can be performed on the selected occupant.
 			 */
 			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0;
+
+			virtual void showRoomConfigurationForm(Form::ref) = 0;
 			boost::signal<void ()> onClosed;
 			boost::signal<void ()> onAllMessagesRead;
 			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;
@@ -97,7 +101,7 @@ namespace Swift {
 			boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged;
 			boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;
 			boost::signal<void (const std::string&)> onChangeSubjectRequest;
-			boost::signal<void ()> onConfigureRequest;
+			boost::signal<void (Form::ref)> onConfigureRequest;
 			
 			// File transfer related
 			boost::signal<void (std::string /* id */)> onFileTransferCancel;
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 143e281..ad8856b 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -47,6 +47,7 @@ namespace Swift {
 			virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
 			void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
 			void setSubject(const std::string& /*subject*/) {}
+			virtual void showRoomConfigurationForm(Form::ref) {}
 
 			boost::signal<void ()> onClosed;
 			boost::signal<void ()> onAllMessagesRead;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 2e3a225..1576880 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -162,6 +162,9 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 
 QtChatWindow::~QtChatWindow() {
 	delete fileTransferJS;
+	if (mucConfigurationWindow) {
+		delete mucConfigurationWindow.data();
+	}
 }
 
 
@@ -364,7 +367,9 @@ void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
 
 void QtChatWindow::setInputEnabled(bool enabled) {
 	inputEnabled_ = enabled;
-//	input_->setEnabled(enabled);
+	if (!enabled && mucConfigurationWindow) {
+		delete mucConfigurationWindow.data();
+	}
 }
 
 void QtChatWindow::showEvent(QShowEvent* event) {
@@ -694,7 +699,7 @@ void QtChatWindow::setSubject(const std::string& subject) {
 void QtChatWindow::handleActionButtonClicked() {
 	QMenu contextMenu;
 	QAction* changeSubject = contextMenu.addAction(tr("Change subject"));
-	//QAction* configure = contextMenu.addAction(tr("Configure room"));
+	QAction* configure = contextMenu.addAction(tr("Configure room"));
 	QAction* result = contextMenu.exec(QCursor::pos());
 	if (result == changeSubject) {
 		bool ok;
@@ -703,9 +708,17 @@ void QtChatWindow::handleActionButtonClicked() {
 			onChangeSubjectRequest(Q2PSTRING(subject));
 		}
 	}
-	//else if (result == configure) {
-	//	onConfigureRequest();
-	//}
+	else if (result == configure) {
+		onConfigureRequest(Form::ref());
+	}
+}
+
+void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
+	if (mucConfigurationWindow) {
+		delete mucConfigurationWindow.data();
+	}
+	mucConfigurationWindow = new QtMUCConfigurationWindow(form);
+	mucConfigurationWindow->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1));
 }
 
 }
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index b8fa478..b011427 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -6,15 +6,17 @@
 
 #pragma once
 
-#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/QtUI/QtMUCConfigurationWindow.h>
 
-#include "QtTabbable.h"
+#include <QtTabbable.h>
 
-#include "SwifTools/LastLineTracker.h"
+#include <SwifTools/LastLineTracker.h>
 
-#include "Swiften/Base/IDGenerator.h"
+#include <Swiften/Base/IDGenerator.h>
 
 #include <map>
+#include <QPointer>
 
 class QTextEdit;
 class QLineEdit;
@@ -69,6 +71,7 @@ namespace Swift {
 			QByteArray getSplitterState();
 			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
 			void setSubject(const std::string& subject);
+			void showRoomConfigurationForm(Form::ref);
 
 		public slots:
 			void handleChangeSplitterState(QByteArray state);
@@ -147,8 +150,8 @@ namespace Swift {
 			QSplitter *logRosterSplitter_;
 			Tristate correctionEnabled_;
 			QString alertStyleSheet_;
-			
 			std::map<QString, QString> descriptions;
 			QtFileTransferJSBridge* fileTransferJS;
+			QPointer<QtMUCConfigurationWindow> mucConfigurationWindow;
 	};
 }
diff --git a/Swift/QtUI/QtMUCConfigurationWindow.cpp b/Swift/QtUI/QtMUCConfigurationWindow.cpp
new file mode 100644
index 0000000..e6be9b7
--- /dev/null
+++ b/Swift/QtUI/QtMUCConfigurationWindow.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/QtUI/QtMUCConfigurationWindow.h>
+
+#include <boost/bind.hpp>
+#include <QBoxLayout>
+#include <Swift/QtUI/QtFormWidget.h>
+
+namespace Swift {
+QtMUCConfigurationWindow::QtMUCConfigurationWindow(Form::ref form) {
+
+	setAttribute(Qt::WA_DeleteOnClose);
+
+	QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+	layout->setContentsMargins(0,0,0,0);
+	layout->setSpacing(2);
+	QLabel* label = new QLabel(this);
+	label->setText(tr("Room configuration"));
+	layout->addWidget(label);
+
+	formWidget_ = NULL;
+	formWidget_ = new QtFormWidget(form, this);
+	layout->addWidget(formWidget_);
+
+	QWidget* buttonsWidget = new QWidget(this);
+	layout->addWidget(buttonsWidget);
+
+	QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget);
+	cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget);
+	buttonsLayout->addWidget(cancelButton_);
+	connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked()));
+	okButton_ = new QPushButton(tr("OK"), buttonsWidget);
+	buttonsLayout->addWidget(okButton_);
+	connect(okButton_, SIGNAL(clicked()), this, SLOT(handleOKClicked()));
+	show();
+}
+
+QtMUCConfigurationWindow::~QtMUCConfigurationWindow() {
+
+}
+
+void QtMUCConfigurationWindow::handleCancelClicked() {
+	close();
+}
+
+void QtMUCConfigurationWindow::handleOKClicked() {
+	onFormComplete(formWidget_->getCompletedForm());
+	close();
+}
+
+
+}
diff --git a/Swift/QtUI/QtMUCConfigurationWindow.h b/Swift/QtUI/QtMUCConfigurationWindow.h
new file mode 100644
index 0000000..2be126b
--- /dev/null
+++ b/Swift/QtUI/QtMUCConfigurationWindow.h
@@ -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.
+ */
+
+#pragma once
+
+#include <QWidget>
+#include <QPushButton>
+#include <QLabel>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Elements/Form.h>
+
+class QBoxLayout;
+
+namespace Swift {
+	class QtFormWidget;
+	class QtMUCConfigurationWindow : public QWidget {
+		Q_OBJECT
+		public:
+			QtMUCConfigurationWindow(Form::ref form);
+			virtual ~QtMUCConfigurationWindow();
+			boost::signal<void (Form::ref)> onFormComplete;
+		private slots:
+			void handleCancelClicked();
+			void handleOKClicked();
+		private:
+			QtFormWidget* formWidget_;
+			QPushButton* okButton_;
+			QPushButton* cancelButton_;
+	};
+}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 1d7ab78..82e4490 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -136,6 +136,7 @@ sources = [
     "qrc_DefaultTheme.cc",
     "qrc_Swift.cc",
     "QtFileTransferJSBridge.cpp",
+    "QtMUCConfigurationWindow.cpp",
   ]
 
 myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
diff --git a/Swiften/Elements/MUCOwnerPayload.h b/Swiften/Elements/MUCOwnerPayload.h
index a3db05b..5ccc755 100644
--- a/Swiften/Elements/MUCOwnerPayload.h
+++ b/Swiften/Elements/MUCOwnerPayload.h
@@ -9,6 +9,7 @@
 #include <boost/optional.hpp>
 
 #include <Swiften/Elements/Payload.h>
+#include <Swiften/Elements/Form.h>
 
 namespace Swift {
 	class MUCOwnerPayload : public Payload {
@@ -26,6 +27,10 @@ namespace Swift {
 				payload = p;
 			}
 
+			Form::ref getForm() {
+				return boost::dynamic_pointer_cast<Form>(payload);
+			}
+
 		private:
 			boost::shared_ptr<Payload> payload;
 	};
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 43d3f36..d4c1287 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -245,10 +245,37 @@ void MUC::changeSubject(const std::string& subject) {
 }
 
 void MUC::requestConfigurationForm() {
+	MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
+	GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Get, getJID(), mucPayload, iqRouter_);
+	request->onResponse.connect(boost::bind(&MUC::handleConfigurationFormReceived, this, _1, _2));
+	request->send();
+}
 
+void MUC::handleConfigurationFormReceived(MUCOwnerPayload::ref payload, ErrorPayload::ref error) {
+	Form::ref form;
+	if (payload) {
+		form = payload->getForm();
+	}
+	if (error || !form) {
+		onConfigurationFailed(error);
+	} else {
+		onConfigurationFormReceived(form);
+	}
 }
 
-//FIXME: Recognise Topic changes
+void MUC::handleConfigurationResultReceived(MUCOwnerPayload::ref /*payload*/, ErrorPayload::ref error) {
+	if (error) {
+		onConfigurationFailed(error);
+	}
+}
+
+void MUC::configureRoom(Form::ref form) {
+	MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
+	mucPayload->setPayload(form);
+	GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
+	request->onResponse.connect(boost::bind(&MUC::handleConfigurationResultReceived, this, _1, _2));
+	request->send();
+}
 
 //TODO: Invites(direct/mediated)
 
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index e223de8..4017c97 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -14,6 +14,7 @@
 #include <Swiften/MUC/MUCRegistry.h>
 #include <Swiften/Elements/MUCOwnerPayload.h>
 #include <Swiften/Elements/MUCAdminPayload.h>
+#include <Swiften/Elements/Form.h>
 
 #include <boost/shared_ptr.hpp>
 #include <Swiften/Base/boost_bsignals.h>
@@ -58,15 +59,18 @@ namespace Swift {
 			void kickUser(const JID& jid);
 			void changeSubject(const std::string& subject);
 			void requestConfigurationForm();
+			void configureRoom(Form::ref);
 		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 (ErrorPayload::ref)> onConfigurationFailed;
 			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;
 			boost::signal<void (const MUCOccupant&)> onOccupantJoined;
 			boost::signal<void (const MUCOccupant&, LeavingType, const std::string& /*reason*/)> onOccupantLeft;
+			boost::signal<void (Form::ref)> onConfigurationFormReceived;
 			/* boost::signal<void (const MUCInfo&)> onInfoResult; */
 			/* boost::signal<void (const blah&)> onItemsResult; */
 			
@@ -85,6 +89,8 @@ namespace Swift {
 			void internalJoin(const std::string& nick);
 			void handleCreationConfigResponse(MUCOwnerPayload::ref, ErrorPayload::ref);
 			void handleKickResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&);
+			void handleConfigurationFormReceived(MUCOwnerPayload::ref, ErrorPayload::ref);
+			void handleConfigurationResultReceived(MUCOwnerPayload::ref, ErrorPayload::ref);
 
 		private:
 			JID ownMUCJID;
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 9eafcba..3d56f61 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -45,6 +45,8 @@
 #include <Swiften/Parser/PayloadParsers/DelayParser.h>
 #include <Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/MUCOwnerPayloadParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/NicknameParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/ReplaceParser.h>
 #include <Swiften/Parser/PayloadParsers/LastParser.h>
@@ -102,6 +104,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 MUCOwnerPayloadParserFactory(this)));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCAdminPayloadParser>("query", "http://jabber.org/protocol/muc#admin")));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new NicknameParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleParserFactory(this)));
diff --git a/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.cpp b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.cpp
new file mode 100644
index 0000000..d7ae789
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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/MUCOwnerPayloadParser.h>
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+
+namespace Swift {
+
+MUCOwnerPayloadParser::MUCOwnerPayloadParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+}
+
+void MUCOwnerPayloadParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+	if (level == 1) {
+		PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+		if (payloadParserFactory) {
+			currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+		}
+	}
+
+	if (level >= 1 && currentPayloadParser) {
+		currentPayloadParser->handleStartElement(element, ns, attributes);
+	}
+	++level;
+}
+
+void MUCOwnerPayloadParser::handleEndElement(const std::string& element, const std::string& ns) {
+	--level;
+	if (currentPayloadParser) {
+		if (level >= 1) {
+			currentPayloadParser->handleEndElement(element, ns);
+		}
+
+		if (level == 1) {
+			getPayloadInternal()->setPayload(currentPayloadParser->getPayload());
+		}
+	}
+}
+
+void MUCOwnerPayloadParser::handleCharacterData(const std::string& data) {
+	if (level > 1 && currentPayloadParser) {
+		currentPayloadParser->handleCharacterData(data);
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h
new file mode 100644
index 0000000..2761981
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h
@@ -0,0 +1,31 @@
+/*
+ * 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/MUCOwnerPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class PayloadParserFactoryCollection;
+
+	class MUCOwnerPayloadParser : public GenericPayloadParser<MUCOwnerPayload> {
+		public:
+		MUCOwnerPayloadParser(PayloadParserFactoryCollection* factories);
+
+		private:
+			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);
+
+		private:
+			PayloadParserFactoryCollection* factories;
+			int level;
+			boost::shared_ptr<PayloadParser> currentPayloadParser;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParserFactory.h
new file mode 100644
index 0000000..918c72c
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCOwnerPayloadParserFactory.h
@@ -0,0 +1,32 @@
+/*
+ * 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/Parser/PayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h>
+
+namespace Swift {
+	class PayloadParserFactoryCollection;
+
+	class MUCOwnerPayloadParserFactory : public PayloadParserFactory {
+		public:
+			MUCOwnerPayloadParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+			}
+
+			virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+				return element == "query" && ns == "http://jabber.org/protocol/muc#owner";
+			}
+
+			virtual PayloadParser* createPayloadParser() {
+				return new MUCOwnerPayloadParser(factories);
+			}
+
+		private:
+			PayloadParserFactoryCollection* factories;
+
+	};
+}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 3dbfbee..67f706d 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -61,6 +61,7 @@ sources = [
 		"PayloadParsers/DelayParser.cpp",
 		"PayloadParsers/MUCUserPayloadParser.cpp",
 		"PayloadParsers/MUCAdminPayloadParser.cpp",
+		"PayloadParsers/MUCOwnerPayloadParser.cpp",
 		"PayloadParsers/MUCItemParser.cpp",
 		"PayloadParsers/NicknameParser.cpp",
 		"PayloadParsers/ReplaceParser.cpp",
-- 
cgit v0.10.2-6-g49f6