From 4ea336d9d124f1c761b22943eaf6771e16a61e58 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Sun, 2 Oct 2011 18:38:56 +0100
Subject: Simple MUC invites.

Resolves: #152

diff --git a/.project b/.project
index 60b9ebf..fcdfcdc 100644
--- a/.project
+++ b/.project
@@ -18,7 +18,7 @@
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
-					<value>Swift</value>
+					<value></value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildArguments</key>
@@ -29,10 +29,6 @@
 					<value>python</value>
 				</dictionary>
 				<dictionary>
-					<key>org.eclipse.cdt.make.core.buildLocation</key>
-					<value></value>
-				</dictionary>
-				<dictionary>
 					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
 					<value>-c</value>
 				</dictionary>
@@ -54,7 +50,7 @@
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
-					<value>Swift</value>
+					<value></value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.stopOnError</key>
diff --git a/COPYING b/COPYING
index 8b204f6..6244d87 100644
--- a/COPYING
+++ b/COPYING
@@ -825,11 +825,8 @@ Swift contains some code contributed under the BSD License, under the following
 --- START OF BSD LICENSE
 Copyright (c) 2011, Arnt Gulbrandsen
 Copyright (c) 2011, Thilo Cestonaro
-<<<<<<< HEAD
 Copyright (c) 2011, Vlad Voicu
-=======
 Copyright (c) 2011, Jan Kaluza
->>>>>>> swift-1.x
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index df59c2f..4bcb4c7 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -16,16 +16,17 @@
 
 #include <Swift/Controllers/Intl.h>
 #include <Swiften/Base/format.h>
-#include "Swiften/Base/String.h"
-#include "Swiften/Client/StanzaChannel.h"
-#include "Swiften/Elements/Delay.h"
-#include "Swiften/Base/foreach.h"
-#include "Swift/Controllers/XMPPEvents/EventController.h"
-#include "Swiften/Disco/EntityCapsProvider.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
-#include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h"
-#include "Swiften/Avatars/AvatarManager.h"
+#include <Swiften/Base/String.h>
+#include <Swiften/Client/StanzaChannel.h>
+#include <Swiften/Elements/Delay.h>
+#include <Swiften/Elements/MUCInvitationPayload.h>
+#include <Swiften/Base/foreach.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h>
+#include <Swiften/Avatars/AvatarManager.h>
 
 namespace Swift {
 
@@ -176,6 +177,10 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m
 		std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>()));
 		chatWindow_->addErrorMessage(errorMessage);
 	}
+	else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) {
+		handleMUCInvitation(messageEvent->getStanza());
+		return;
+	}
 	else {
 		if (!messageEvent->isReadable()) {
 			return;
@@ -256,4 +261,9 @@ std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload>
 	return defaultMessage;
 }
 
+void ChatControllerBase::handleMUCInvitation(Message::ref message) {
+	MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>();
+	chatWindow_->addMUCInvitation(invite->getJID(), invite->getReason(), invite->getPassword());
+}
+
 }
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 67bd74f..a857f3d 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -80,6 +80,7 @@ namespace Swift {
 			void handleAllMessagesRead();
 			void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);
 			void handleDayChangeTick();
+			void handleMUCInvitation(Message::ref message);
 
 		protected:
 			JID selfJID_;
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 47a65a8..c0b2a7d 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -33,6 +33,7 @@
 #include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
 #include <Swift/Controllers/ProfileSettingsProvider.h>
 #include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Elements/MUCInvitationPayload.h>
 
 namespace Swift {
 
@@ -516,12 +517,13 @@ void ChatsManager::handleSearchMUCRequest() {
 void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
 	JID jid = message->getFrom();
 	boost::shared_ptr<MessageEvent> event(new MessageEvent(message));
-	if (!event->isReadable() && !message->getPayload<ChatState>() && !message->hasSubject()) {
+	bool isInvite = message->getPayload<MUCInvitationPayload>();
+	if (!event->isReadable() && !message->getPayload<ChatState>() && !isInvite && !message->hasSubject()) {
 		return;
 	}
 
 	// Try to deliver it to a MUC
-	if (message->getType() == Message::Groupchat || message->getType() == Message::Error) {
+	if (message->getType() == Message::Groupchat || message->getType() == Message::Error || (isInvite && message->getType() == Message::Normal)) {
 		std::map<JID, MUCController*>::iterator i = mucControllers_.find(jid.toBare());
 		if (i != mucControllers_.end()) {
 			i->second->handleIncomingMessage(event);
@@ -562,5 +564,4 @@ void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
 	}
 }
 
-
 }
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index e1d02ae..87d5a16 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -74,6 +74,7 @@ MUCController::MUCController (
 	chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
 	chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
 	chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
+	chatWindow_->onInvitePersonToThisMUCRequest.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, 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));
@@ -603,4 +604,8 @@ void MUCController::handleDestroyRoomRequest() {
 	muc_->destroyRoom();
 }
 
+void MUCController::handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason) {
+	muc_->invitePerson(jid, reason);
+}
+
 }
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 7a7461b..f83e2af 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -92,6 +92,7 @@ namespace Swift {
 			void handleConfigurationFailed(ErrorPayload::ref);
 			void handleConfigurationFormReceived(Form::ref);
 			void handleDestroyRoomRequest();
+			void handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason);
 
 		private:
 			MUC::ref muc_;
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index df57d80..7977940 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -54,6 +54,7 @@ namespace Swift {
 			virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
 			virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
 			virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
+			virtual void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password) = 0;
 
 			virtual void setContactChatState(ChatState::ChatStateType state) = 0;
 			virtual void setName(const std::string& name) = 0;
@@ -74,6 +75,7 @@ namespace Swift {
 			virtual void setAckState(const std::string& id, AckState state) = 0;
 			virtual void flash() = 0;
 			virtual void setSubject(const std::string& subject) = 0;
+
 			/**
 			 * Set an alert on the window.
 			 * @param alertText Description of alert (required).
@@ -103,6 +105,7 @@ namespace Swift {
 			boost::signal<void (const std::string&)> onChangeSubjectRequest;
 			boost::signal<void (Form::ref)> onConfigureRequest;
 			boost::signal<void ()> onDestroyRequest;
+			boost::signal<void (const JID&, const std::string& /*reason*/)> onInvitePersonToThisMUCRequest;
 			
 			// 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 ad8856b..58e8698 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -48,6 +48,7 @@ namespace Swift {
 			void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
 			void setSubject(const std::string& /*subject*/) {}
 			virtual void showRoomConfigurationForm(Form::ref) {}
+			virtual void addMUCInvitation(const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/) {};
 
 			boost::signal<void ()> onClosed;
 			boost::signal<void ()> onAllMessagesRead;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 50fe984..df78767 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -21,6 +21,7 @@
 #include "SwifTools/TabComplete.h"
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
 #include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
 #include "QtFileTransferJSBridge.h"
 
 #include <boost/cstdint.hpp>
@@ -28,6 +29,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <QLabel>
+#include <QMessageBox>
 #include <QInputDialog>
 #include <QApplication>
 #include <QBoxLayout>
@@ -702,6 +704,7 @@ void QtChatWindow::handleActionButtonClicked() {
 	QAction* changeSubject = contextMenu.addAction(tr("Change subject"));
 	QAction* configure = contextMenu.addAction(tr("Configure room"));
 	QAction* destroy = contextMenu.addAction(tr("Destroy room"));
+	QAction* invite = contextMenu.addAction(tr("Invite person to this room"));
 	QAction* result = contextMenu.exec(QCursor::pos());
 	if (result == changeSubject) {
 		bool ok;
@@ -716,6 +719,13 @@ void QtChatWindow::handleActionButtonClicked() {
 	else if (result == destroy) {
 		onDestroyRequest();
 	}
+	else if (result == invite) {
+		bool ok;
+		QString jid = QInputDialog::getText(this, tr("Enter person's address"), tr("Address:"), QLineEdit::Normal, "", &ok);
+		if (ok) {
+			onInvitePersonToThisMUCRequest(JID(Q2PSTRING(jid)), "");
+		}
+	}
 }
 
 void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
@@ -726,4 +736,28 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
 	mucConfigurationWindow->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1));
 }
 
+void QtChatWindow::addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password) {
+	bool accepted = false;
+	QMessageBox msgBox;
+	msgBox.setText(QString("You have been invited to the room %1 by %2.").arg(P2QSTRING(jid.toString())).arg(contact_));
+	QString reasonString;
+	if (!reason.empty()) {
+		reasonString = QString("\"%1\"").arg(P2QSTRING(reason));
+	}
+	msgBox.setInformativeText(QString("Accept invitation from %1 to enter %2?\n%3").arg(contact_).arg(P2QSTRING(jid.toString())).arg(reasonString));
+	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+	msgBox.setDefaultButton(QMessageBox::Yes);
+	int ret = msgBox.exec();
+	switch (ret) {
+	   case QMessageBox::Yes:
+	       accepted = true;
+	       break;
+	   default:
+	       break;
+	 }
+	if (accepted) {
+		eventStream_->send(boost::make_shared<JoinMUCUIEvent>(jid));
+	}
+}
+
 }
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index b011427..8dfe767 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -72,6 +72,7 @@ namespace Swift {
 			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
 			void setSubject(const std::string& subject);
 			void showRoomConfigurationForm(Form::ref);
+			void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password);
 
 		public slots:
 			void handleChangeSplitterState(QByteArray state);
diff --git a/Swiften/Elements/MUCInvitationPayload.h b/Swiften/Elements/MUCInvitationPayload.h
new file mode 100644
index 0000000..ebae61a
--- /dev/null
+++ b/Swiften/Elements/MUCInvitationPayload.h
@@ -0,0 +1,68 @@
+/*
+ * 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/Elements/Payload.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+	class MUCInvitationPayload : public Payload {
+		public:
+			typedef boost::shared_ptr<MUCInvitationPayload> ref;
+			MUCInvitationPayload() : continuation_(false) {
+			}
+
+			void setIsContinuation(bool b) {
+				continuation_ = b;
+			}
+
+			bool getIsContinuation() const {
+				return continuation_;
+			}
+
+			void setJID(const JID& jid) {
+				jid_ = jid;
+			}
+
+			const JID& getJID() const {
+				return jid_;
+			}
+
+			void setPassword(const std::string& password) {
+				password_ = password;
+			}
+
+			const std::string& getPassword() const {
+				return password_;
+			}
+
+			void setReason(const std::string& text) {
+				reason_ = text;
+			}
+
+			const std::string& getReason() const {
+				return reason_;
+			}
+
+			void setThread(const std::string& thread) {
+				thread_ = thread;
+			}
+
+			const std::string& getThread() const {
+				return thread_;
+			}
+
+		private:
+			bool continuation_;
+			JID jid_;
+			std::string password_;
+			std::string reason_;
+			std::string thread_;
+	};
+}
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 6fe8f19..4c9e537 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -21,6 +21,7 @@
 #include <Swiften/Elements/MUCAdminPayload.h>
 #include <Swiften/Elements/MUCPayload.h>
 #include <Swiften/Elements/MUCDestroyPayload.h>
+#include <Swiften/Elements/MUCInvitationPayload.h>
 #include <Swiften/MUC/MUCRegistry.h>
 #include <Swiften/Queries/GenericRequest.h>
 
@@ -286,6 +287,17 @@ void MUC::destroyRoom() {
 	request->send();
 }
 
+void MUC::invitePerson(const JID& person, const std::string& reason) {
+	Message::ref message = boost::make_shared<Message>();
+	message->setTo(person);
+	message->setType(Message::Normal);
+	MUCInvitationPayload::ref invite = boost::make_shared<MUCInvitationPayload>();
+	invite->setReason(reason);
+	invite->setJID(ownMUCJID.toBare());
+	message->addPayload(invite);
+	stanzaChannel->sendMessage(message);
+}
+
 //TODO: Invites(direct/mediated)
 
 //TODO: requesting membership
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index b99c4b4..45b8004 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -61,6 +61,8 @@ namespace Swift {
 			void requestConfigurationForm();
 			void configureRoom(Form::ref);
 			void destroyRoom();
+			/** Send an invite for the person to join the MUC */
+			void invitePerson(const JID& person, const std::string& reason = "");
 		public:
 			boost::signal<void (const std::string& /*nick*/)> onJoinComplete;
 			boost::signal<void (ErrorPayload::ref)> onJoinFailed;
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 9865a1e..86ea1c6 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -47,6 +47,7 @@
 #include <Swiften/Parser/PayloadParsers/MUCAdminPayloadParser.h>
 #include <Swiften/Parser/PayloadParsers/MUCOwnerPayloadParser.h>
 #include <Swiften/Parser/PayloadParsers/MUCDestroyPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.h>
 #include <Swiften/Parser/PayloadParsers/MUCOwnerPayloadParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/NicknameParser.h>
 #include <Swiften/Parser/PayloadParsers/ReplaceParser.h>
@@ -105,6 +106,10 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(boost::make_shared<ChatStateParserFactory>());
 	factories_.push_back(boost::make_shared<MUCUserPayloadParserFactory>(this));
 	factories_.push_back(boost::make_shared<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 GenericPayloadParserFactory<MUCInvitationPayloadParser>("x", "jabber:x:conference")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCDestroyPayloadParser>("destroy", "http://jabber.org/protocol/muc#user")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCDestroyPayloadParser>("destroy", "http://jabber.org/protocol/muc#owner")));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<MUCAdminPayloadParser> >("query", "http://jabber.org/protocol/muc#admin"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<MUCDestroyPayloadParser> >("destroy", "http://jabber.org/protocol/muc#user"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<MUCDestroyPayloadParser> >("destroy", "http://jabber.org/protocol/muc#owner"));
diff --git a/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.cpp b/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.cpp
new file mode 100644
index 0000000..fa95af7
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.cpp
@@ -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.
+ */
+
+#include <Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.h>
+
+
+
+namespace Swift {
+
+void MUCInvitationPayloadParser::handleTree(ParserElement::ref root) {
+	MUCInvitationPayload::ref invite = getPayloadInternal();
+	invite->setIsContinuation(root->getAttributes().getBoolAttribute("continue", false));
+	invite->setJID(JID(root->getAttributes().getAttribute("jid")));
+	invite->setPassword(root->getAttributes().getAttribute("password"));
+	invite->setReason(root->getAttributes().getAttribute("reason"));
+	invite->setThread(root->getAttributes().getAttribute("thread"));
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.h b/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.h
new file mode 100644
index 0000000..2cd3535
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/MUCInvitationPayloadParser.h
@@ -0,0 +1,17 @@
+/*
+ * 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/MUCInvitationPayload.h>
+#include <Swiften/Parser/GenericPayloadTreeParser.h>
+
+namespace Swift {
+	class MUCInvitationPayloadParser : public GenericPayloadTreeParser<MUCInvitationPayload> {
+		public:
+			virtual void handleTree(ParserElement::ref root);
+	};
+}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 1cef02c..e1ec1d8 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -64,6 +64,7 @@ sources = [
 		"PayloadParsers/MUCAdminPayloadParser.cpp",
 		"PayloadParsers/MUCOwnerPayloadParser.cpp",
 		"PayloadParsers/MUCDestroyPayloadParser.cpp",
+		"PayloadParsers/MUCInvitationPayloadParser.cpp",
 		"PayloadParsers/MUCItemParser.cpp",
 		"PayloadParsers/NicknameParser.cpp",
 		"PayloadParsers/ReplaceParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index e650e2a..6d85d84 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -151,6 +151,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp",
 			"Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.cpp",
 			"Serializer/PayloadSerializers/MUCDestroyPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp",
 			"Serializer/PayloadSerializers/ResourceBindSerializer.cpp",
 			"Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp",
 			"Serializer/PayloadSerializers/RosterSerializer.cpp",
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index f3e22d2..87dfbc3 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -24,6 +24,7 @@
 #include <Swiften/Serializer/PayloadSerializers/MUCAdminPayloadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/MUCDestroyPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/StatusSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h>
@@ -74,6 +75,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
 	serializers_.push_back(new MUCPayloadSerializer());
 	serializers_.push_back(new MUCDestroyPayloadSerializer());
 	serializers_.push_back(new MUCAdminPayloadSerializer());
+	serializers_.push_back(new MUCInvitationPayloadSerializer());
 	serializers_.push_back(new MUCOwnerPayloadSerializer(this));
 	serializers_.push_back(new MUCUserPayloadSerializer(this));
 	serializers_.push_back(new SoftwareVersionSerializer());
diff --git a/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp
new file mode 100644
index 0000000..24e30e6
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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/MUCInvitationPayloadSerializer.h>
+
+#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 {
+
+MUCInvitationPayloadSerializer::MUCInvitationPayloadSerializer() : GenericPayloadSerializer<MUCInvitationPayload>() {
+}
+
+std::string MUCInvitationPayloadSerializer::serializePayload(boost::shared_ptr<MUCInvitationPayload> payload)  const {
+	XMLElement mucElement("x", "jabber:x:conference");
+	if (payload->getIsContinuation()) {
+		mucElement.setAttribute("continue", "true");
+	}
+	if (payload->getJID().isValid()) {
+		mucElement.setAttribute("jid", payload->getJID().toString());
+	}
+	if (!payload->getPassword().empty()) {
+		mucElement.setAttribute("password", payload->getPassword());
+	}
+	if (!payload->getReason().empty()) {
+		mucElement.setAttribute("reason", payload->getReason());
+	}
+	if (!payload->getThread().empty()) {
+		mucElement.setAttribute("thread", payload->getThread());
+	}
+	return mucElement.serialize();
+}
+
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.h
new file mode 100644
index 0000000..e60d5ca
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.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/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/MUCInvitationPayload.h>
+
+namespace Swift {
+	class MUCInvitationPayloadSerializer : public GenericPayloadSerializer<MUCInvitationPayload> {
+		public:
+			MUCInvitationPayloadSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<MUCInvitationPayload> version)  const;
+	};
+}
+
-- 
cgit v0.10.2-6-g49f6