From 3e972565e51f5b2fa7e9768d5d84a7d4b6dfc09b Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Wed, 5 Oct 2011 08:34:05 +0100
Subject: Allow joining passworded MUCs.

Resolves: #991

diff --git a/.gitignore b/.gitignore
index 6500850..928b15b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ cppcheck.log
 /.settings/
 /nbproject/private/
 3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpcstrings.h
+*.sublime-workspace
\ No newline at end of file
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index d3060b8..e6441e1 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -199,7 +199,7 @@ void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) {
 	std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom());
 	if (it == mucControllers_.end() && bookmark.getAutojoin()) {
 		//FIXME: need vcard stuff here to get a nick
-		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getNick(), false, false);
+		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false);
 	}
 	chatListWindow_->addMUCBookmark(bookmark);
 }
@@ -321,7 +321,7 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 		mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark());
 	}
 	else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
-		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew());
+		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew());
 		mucControllers_[joinEvent->getJID()]->activateChatWindow();
 	}
 	else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
@@ -481,7 +481,7 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
 	chatControllers_[to]->setToJID(to);
 }
 
-void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew) {
+void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& password, const boost::optional<std::string>& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew) {
 	if (addAutoJoin) {
 		MUCBookmark bookmark(mucJID, mucJID.getNode());
 		bookmark.setAutojoin(true);
@@ -500,7 +500,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional
 		if (createAsReservedIfNew) {
 			muc->setCreateAsReservedIfNew();
 		}
-		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_);
+		MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_);
 		mucControllers_[mucJID] = controller;
 		controller->setAvailableServerFeatures(serverDiscoInfo_);
 		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
@@ -549,7 +549,7 @@ void ChatsManager::handleMUCSelectedAfterSearch(const JID& muc) {
 }
 
 void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) {
-	uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getNick()));
+	uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getPassword(), mucBookmark.getNick()));
 }
 
 void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) {
@@ -560,7 +560,8 @@ void ChatsManager::handleNewFileTransferController(FileTransferController* ftc)
 
 void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
 	if (chat.isMUC) {
-		uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(chat.jid, chat.nick));
+		/* FIXME: This means that recents requiring passwords will just flat-out not work */
+		uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(chat.jid, boost::optional<std::string>(), chat.nick));
 	}
 	else {
 		uiEventStream_->send(boost::make_shared<RequestChatUIEvent>(chat.jid));
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index a82492c..8e94d9a 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -58,7 +58,7 @@ namespace Swift {
 		private:
 			ChatListWindow::Chat createChatListChatItem(const JID& jid, const std::string& activity);
 			void handleChatRequest(const std::string& contact);
-			void handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& nick, bool addAutoJoin, bool createAsReservedIfNew);
+			void handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& password, const boost::optional<std::string>& nick, bool addAutoJoin, bool createAsReservedIfNew);
 			void handleSearchMUCRequest();
 			void handleMUCSelectedAfterSearch(const JID&);
 			void rebindControllerJID(const JID& from, const JID& to);
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index d72c0f7..2b8a8b7 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -44,6 +44,7 @@ namespace Swift {
 MUCController::MUCController (
 		const JID& self,
 		MUC::ref muc,
+		const boost::optional<std::string>& password,
 		const std::string &nick, 
 		StanzaChannel* stanzaChannel, 
 		IQRouter* iqRouter, 
@@ -55,7 +56,7 @@ MUCController::MUCController (
 		TimerFactory* timerFactory,
 		EventController* eventController,
 		EntityCapsProvider* entityCapsProvider) :
-			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick) {
+			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) {
 	parting_ = true;
 	joined_ = false;
 	lastWasPresence_ = false;
@@ -146,6 +147,9 @@ void MUCController::rejoin() {
 	if (parting_) {
 		joined_ = false;
 		parting_ = false;
+		if (password_) {
+			muc_->setPassword(*password_);
+		}
 		//FIXME: check for received activity
 		if (lastActivity_ == boost::posix_time::not_a_date_time) {
 			muc_->joinAs(nick_);
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 17dbba4..d22d2ca 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -42,7 +42,7 @@ namespace Swift {
 
 	class MUCController : public ChatControllerBase {
 		public:
-			MUCController(const JID& self, MUC::ref muc, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider);
+			MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider);
 			~MUCController();
 			boost::signal<void ()> onUserLeft;
 			boost::signal<void ()> onUserJoined;
@@ -112,6 +112,7 @@ namespace Swift {
 			std::set<std::string> currentOccupants_;
 			std::vector<NickJoinPart> joinParts_;
 			boost::posix_time::ptime lastActivity_;
+			boost::optional<std::string> password_;
 	};
 }
 
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index ad5ceac..16ad999 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -60,7 +60,7 @@ public:
 		entityCapsProvider_ = new DummyEntityCapsProvider();
 		muc_ = MUC::ref(new MUC(stanzaChannel_, iqRouter_, directedPresenceSender_, JID("teaparty@rooms.wonderland.lit"), mucRegistry_));
 		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_);
-		controller_ = new MUCController (self_, muc_, nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_);
+		controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_);
 	};
 
 	void tearDown() {
diff --git a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
index e1d65ad..b3ff8c7 100644
--- a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
+++ b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
@@ -12,21 +12,23 @@
 
 #include <Swiften/JID/JID.h>
 
-#include "Swift/Controllers/UIEvents/UIEvent.h"
+#include <Swift/Controllers/UIEvents/UIEvent.h>
 
 namespace Swift {
 	class JoinMUCUIEvent : public UIEvent {
 		public:
 			typedef boost::shared_ptr<JoinMUCUIEvent> ref;
-			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew) {};
-			boost::optional<std::string> getNick() {return nick_;};
-			JID getJID() {return jid_;};
-			bool getShouldJoinAutomatically() {return joinAutomatically_;}
-			bool getCreateAsReservedRoomIfNew() {return createAsReservedRoomIfNew_;}
+			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& password = boost::optional<std::string>(), const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password) {};
+			const boost::optional<std::string>& getNick() const {return nick_;}
+			const JID& getJID() const {return jid_;}
+			bool getShouldJoinAutomatically() const {return joinAutomatically_;}
+			bool getCreateAsReservedRoomIfNew() const {return createAsReservedRoomIfNew_;}
+			const boost::optional<std::string>& getPassword() const {return password_;}
 		private:
 			JID jid_;
 			boost::optional<std::string> nick_;
 			bool joinAutomatically_;
 			bool createAsReservedRoomIfNew_;
+			boost::optional<std::string> password_;
 	};
 }
diff --git a/Swift/QtUI/QtJoinMUCWindow.cpp b/Swift/QtUI/QtJoinMUCWindow.cpp
index fec3c4d..14777bd 100644
--- a/Swift/QtUI/QtJoinMUCWindow.cpp
+++ b/Swift/QtUI/QtJoinMUCWindow.cpp
@@ -38,8 +38,9 @@ void QtJoinMUCWindow::handleJoin() {
 	}
 
 	lastSetNick = Q2PSTRING(ui.nickName->text());
+	std::string password = Q2PSTRING(ui.password->text());
 	JID room(Q2PSTRING(ui.room->text()));
-	uiEventStream->send(boost::make_shared<JoinMUCUIEvent>(room, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked()));
+	uiEventStream->send(boost::make_shared<JoinMUCUIEvent>(room, password, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked()));
 	hide();
 }
 
@@ -59,6 +60,7 @@ void QtJoinMUCWindow::setMUC(const std::string& nick) {
 void QtJoinMUCWindow::show() {
 	QWidget::show();
 	QWidget::activateWindow();
+	ui.password->setText("");
 }
 
 }
diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui
index 74fe513..4c4935a 100644
--- a/Swift/QtUI/QtJoinMUCWindow.ui
+++ b/Swift/QtUI/QtJoinMUCWindow.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>410</width>
-    <height>212</height>
+    <height>224</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -53,6 +53,16 @@
        </property>
       </widget>
      </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Password:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="password"/>
+     </item>
     </layout>
    </item>
    <item>
diff --git a/Swiften/Elements/MUCPayload.h b/Swiften/Elements/MUCPayload.h
index 3b99111..29cab8d 100644
--- a/Swiften/Elements/MUCPayload.h
+++ b/Swiften/Elements/MUCPayload.h
@@ -40,6 +40,10 @@ namespace Swift {
 				since_ = since;
 			}
 
+			void setPassword(const std::string& password) {
+				password_ = password;
+			}
+
 			int getMaxChars() const{
 				return maxChars_;
 			}
@@ -52,6 +56,10 @@ namespace Swift {
 				return seconds_;
 			}
 
+			const boost::optional<std::string>& getPassword() const {
+				return password_;
+			}
+
 			const boost::posix_time::ptime& getSince() const {
 				return since_;
 			}
@@ -61,5 +69,6 @@ namespace Swift {
 			int maxStanzas_;
 			int seconds_;
 			boost::posix_time::ptime since_;
+			boost::optional<std::string> password_;
 	};
 }
diff --git a/Swiften/Elements/Storage.h b/Swiften/Elements/Storage.h
index 8118b3b..03c958c 100644
--- a/Swiften/Elements/Storage.h
+++ b/Swiften/Elements/Storage.h
@@ -8,9 +8,12 @@
 
 #include <vector>
 
+#include <boost/optional.hpp>
+
 #include <Swiften/Elements/Payload.h>
 #include <string>
 #include <Swiften/JID/JID.h>
+#include <Swiften/Base/SafeString.h>
 
 namespace Swift {
 	class Storage : public Payload {
@@ -22,7 +25,7 @@ namespace Swift {
 				JID jid;
 				bool autoJoin;
 				std::string nick;
-				std::string password;
+				boost::optional<std::string> password;
 			};
 
 			struct URL {
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 08391b4..15355ad 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -44,6 +44,13 @@ void MUC::joinAs(const std::string &nick) {
 }
 
 /**
+ * Set the password used for entering the room.
+ */
+void MUC::setPassword(const boost::optional<std::string>& newPassword) {
+	password = newPassword;
+}
+
+/**
  * Join the MUC with context since date.
  */
 void MUC::joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since) {
@@ -68,6 +75,9 @@ void MUC::internalJoin(const std::string &nick) {
 	if (joinSince_ != boost::posix_time::not_a_date_time) {
 		mucPayload->setSince(joinSince_);
 	}
+	if (password) {
+		mucPayload->setPassword(*password);
+	}
 	joinPresence->addPayload(mucPayload);
 
 	presenceSender->sendPresence(joinPresence);
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index adc5707..a9b42b8 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -65,6 +65,7 @@ namespace Swift {
 			/** Send an invite for the person to join the MUC */
 			void invitePerson(const JID& person, const std::string& reason = "");
 			void setCreateAsReservedIfNew() {createAsReservedIfNew = true;}
+			void setPassword(const boost::optional<std::string>& password);
 		public:
 			boost::signal<void (const std::string& /*nick*/)> onJoinComplete;
 			boost::signal<void (ErrorPayload::ref)> onJoinFailed;
@@ -110,5 +111,6 @@ namespace Swift {
 			boost::posix_time::ptime joinSince_;
 			bool createAsReservedIfNew;
 			bool unlocking;
+			boost::optional<std::string> password;
 	};
 }
diff --git a/Swiften/MUC/MUCBookmark.h b/Swiften/MUC/MUCBookmark.h
index 3f612c4..3c28bdb 100644
--- a/Swiften/MUC/MUCBookmark.h
+++ b/Swiften/MUC/MUCBookmark.h
@@ -59,7 +59,8 @@ namespace Swift {
 			}
 
 			bool operator==(const MUCBookmark& rhs) const {
-				return rhs.room_ == room_ && rhs.name_ == name_ && rhs.nick_ == nick_ && rhs.password_ == password_ && rhs.autojoin_ == autojoin_;
+				/* FIXME: not checking passwords for equality - which might make sense, perhaps */
+				return rhs.room_ == room_ && rhs.name_ == name_ && rhs.nick_ == nick_ /*&& rhs.password_ == password_*/ && rhs.autojoin_ == autojoin_;
 			}
 
 			Storage::Room toStorage() const {
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
index 4fd8ae5..a378d0b 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StorageParserTest.cpp
@@ -42,7 +42,7 @@ class StorageParserTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(JID("council@conference.underhill.org"), rooms[0].jid);
 			CPPUNIT_ASSERT(rooms[0].autoJoin);
 			CPPUNIT_ASSERT_EQUAL(std::string("Puck"), rooms[0].nick);
-			CPPUNIT_ASSERT_EQUAL(std::string("MyPass"), rooms[0].password);
+			CPPUNIT_ASSERT_EQUAL(std::string("MyPass"), *rooms[0].password);
 		}
 
 		void testParse_MultipleRooms() {
diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
index 7080e14..58f78b8 100644
--- a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp
@@ -7,9 +7,11 @@
 #include <Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h>
 
 #include <boost/lexical_cast.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 #include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLTextNode.h>
 #include <Swiften/Base/String.h>
 
 namespace Swift {
@@ -40,6 +42,12 @@ std::string MUCPayloadSerializer::serializePayload(boost::shared_ptr<MUCPayload>
 		historyElement->setAttribute("since", sinceString);
 		history = true;
 	}
+	if (muc->getPassword()) {
+		std::string password = *muc->getPassword();
+		boost::shared_ptr<XMLElement> passwordElement(new XMLElement("password"));
+		passwordElement->addNode(boost::make_shared<XMLTextNode>(password));
+		mucElement.addNode(passwordElement);
+	}
 	if (history) {
 		mucElement.addNode(historyElement);
 	}
diff --git a/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp
index 30adf26..77fb3c0 100644
--- a/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/StorageSerializer.cpp
@@ -30,9 +30,9 @@ std::string StorageSerializer::serializePayload(boost::shared_ptr<Storage> stora
 			nickElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(room.nick)));
 			conferenceElement->addNode(nickElement);
 		}
-		if (!room.password.empty()) {
+		if (room.password) {
 			boost::shared_ptr<XMLElement> passwordElement(new XMLElement("password"));
-			passwordElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(room.password)));
+			passwordElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(*room.password)));
 			conferenceElement->addNode(passwordElement);
 		}
 		storageElement.addNode(conferenceElement);
-- 
cgit v0.10.2-6-g49f6