From c9659b556b932e2f887cf1d8ab6c5a0bead835eb Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Fri, 21 May 2010 20:45:42 +0000
Subject: Warnings about MUC joins that never complete.


diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 69184ad..1ee501a 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -28,7 +28,8 @@ namespace Swift {
 typedef std::pair<JID, ChatController*> JIDChatControllerPair;
 typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
 
-ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency) : jid_(jid), useDelayForLatency_(useDelayForLatency) {
+ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory) : jid_(jid), useDelayForLatency_(useDelayForLatency) {
+	timerFactory_ = timerFactory;
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
 	iqRouter_ = iqRouter;
@@ -220,7 +221,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &muc, const boost::optional<St
 		//FIXME: What's correct behaviour here?
 	} else {
 		String nick = nickMaybe ? nickMaybe.get() : "Swift user";
-		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false);
+		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_);
 		mucControllers_[muc] = controller;
 		controller->setAvailableServerFeatures(serverDiscoInfo_);
 		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 1f0f203..9957891 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -33,10 +33,11 @@ namespace Swift {
 	class MUCBookmarkManager;
 	class ChatListWindow;
 	class ChatListWindowFactory;
+	class TimerFactory;
 
 	class ChatsManager : public MUCRegistry {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory);
 			~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setEnabled(bool enabled);
@@ -74,5 +75,6 @@ namespace Swift {
 			ChatListWindow* chatListWindow_;
 			boost::bsignals::scoped_connection uiEventConnection_;
 			bool useDelayForLatency_;
+			TimerFactory* timerFactory_;
 	};
 }
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 7e9a9ea..a8a6747 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -8,6 +8,8 @@
 
 #include <boost/bind.hpp>
 
+#include "Swiften/Network/Timer.h"
+#include "Swiften/Network/TimerFactory.h"
 #include "Swiften/Base/foreach.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindow.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
@@ -20,6 +22,8 @@
 #include "Swiften/Roster/SetAvatar.h"
 #include "Swiften/Roster/SetPresence.h"
 
+#define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000
+
 namespace Swift {
 	
 /**
@@ -36,20 +40,29 @@ MUCController::MUCController (
 		PresenceOracle* presenceOracle,
 		AvatarManager* avatarManager,
 		UIEventStream* uiEventStream,
-		bool useDelayForLatency) :
+		bool useDelayForLatency,
+		TimerFactory* timerFactory) :
 	ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream),
 			muc_(new MUC(stanzaChannel, presenceSender, muc)), 
-			nick_(nick) { 
+	nick_(nick) {
+	loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
 	parting_ = false;
 	events_ = uiEventStream;
+	
 	roster_ = new Roster();
 	chatWindow_->setRosterModel(roster_);
 	chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
-	muc_->joinAs(nick);
+	muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
 	muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
 	muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
 	muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
+	loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
+	loginCheckTimer_->start();
+
+	muc_->joinAs(nick);
 	chatWindow_->convertToMUC();
+	chatWindow_->addSystemMessage("Trying to join room " + toJID_.toString());
+	joined_ = false;
 	if (avatarManager_ != NULL) {
 		avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1, _2)));
 	} 
@@ -59,6 +72,22 @@ MUCController::~MUCController() {
 	delete muc_;
 	chatWindow_->setRosterModel(NULL);
 	delete roster_;
+	loginCheckTimer_->stop();
+}
+
+void MUCController::handleJoinTimeoutTick() {
+	loginCheckTimer_->stop();
+	chatWindow_->addSystemMessage("Room " + toJID_.toString() + " is not responding. This operation may never complete");
+}
+
+void MUCController::handleJoinComplete(MUC::JoinResult result) {
+	loginCheckTimer_->stop();
+	if (result == MUC::JoinFailed) {
+		chatWindow_->addErrorMessage("Unable to join this room");
+	} 
+	joined_ = true;
+	String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick_;
+	chatWindow_->addSystemMessage(joinMessage);
 }
 
 void MUCController::handleAvatarChanged(const JID& jid, const String&) {
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 8e6e01b..9e79835 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -12,6 +12,7 @@
 #include <boost/signals/connection.hpp>
 
 #include "Swiften/Base/String.h"
+#include "Swiften/Network/Timer.h"
 #include "Swift/Controllers/Chat/ChatControllerBase.h"
 #include "Swiften/Elements/Message.h"
 #include "Swiften/Elements/DiscoInfo.h"
@@ -27,10 +28,11 @@ namespace Swift {
 	class Roster;
 	class AvatarManager;
 	class UIEventStream;
+	class TimerFactory;
 
 	class MUCController : public ChatControllerBase {
 		public:
-			MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency);
+			MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory);
 			~MUCController();
 			boost::signal<void ()> onUserLeft;
 		
@@ -45,6 +47,8 @@ namespace Swift {
 			void handleOccupantJoined(const MUCOccupant& occupant);
 			void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason);
 			void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
+			void handleJoinComplete(MUC::JoinResult result);
+			void handleJoinTimeoutTick();
 
 		private:
 			MUC* muc_;
@@ -52,7 +56,9 @@ namespace Swift {
 			String nick_;
 			Roster* roster_;
 			bool parting_;
+			bool joined_;
 			boost::bsignals::scoped_connection avatarChangedConnection_;
+			boost::shared_ptr<Timer> loginCheckTimer_;
 	};
 }
 #endif
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 13426eb..6c9c5dd 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -13,6 +13,8 @@
 
 #include "Swiften/Application/Application.h"
 #include "Swiften/Application/ApplicationMessageDisplay.h"
+#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/BoostTimerFactory.h"
 #include "Swift/Controllers/Chat/ChatController.h"
 #include "Swift/Controllers/Chat/MUCSearchController.h"
 //#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
@@ -183,7 +185,7 @@ void MainController::handleConnected() {
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 
-		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_);
+		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_);
 		nickResolver_->setMUCRegistry(chatsManager_);
 		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
 		chatsManager_->setAvatarManager(avatarManager_);
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 4390e9b..e52eae4 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -22,7 +22,11 @@ MUC::MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID
 	scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
 }
 
+//FIXME: discover reserved nickname
+
 void MUC::joinAs(const String &nick) {
+	//FIXME: password
+	//FIXME: history request
 	firstPresenceSeen = false;
 
 	ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
@@ -45,6 +49,14 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
 	if (!firstPresenceSeen) {
 		if (presence->getType() == Presence::Error) {
 			onJoinComplete(JoinFailed);
+			//FIXME: parse error element
+			//Wrong password
+			//Members-only
+			//Banned
+			//Nickname-conflict
+			//Max-users
+			//Locked-room
+			
 			return;
 		}
 		firstPresenceSeen = true;
@@ -56,9 +68,19 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
 	if (nick.isEmpty()) {
 		return;
 	}
+	//FIXME: occupant affiliation, role.
+	//FIXME: if status code='110', This is me, stop talking.
+	//FIXME: what's status 210?
+	//100 is non-anonymous
+	//FIXME: 100 may also be specified in a <message/>
+	//Once I've got my nick (110), everything new is a join
+	//170 is room logging to http
+	//FIXME: full JIDs
+	//FIXME: Nick changes
 	if (presence->getType() == Presence::Unavailable) {
 		std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
 		if (i != occupants.end()) {
+			//FIXME: part type
 			onOccupantLeft(i->second, Part, "");
 			occupants.erase(i);
 		}
@@ -72,5 +94,20 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
 	}
 }
 
+//FIXME: Recognise Topic changes
+
+//TODO: Invites(direct/mediated)
+
+//TODO: requesting membership
+
+//TODO: get member list
+
+//TODO: request voice
+
+//TODO: moderator use cases
+
+//TODO: Admin use cases
+
+//TODO: Owner use cases
 
 }
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index 7ee21a3..e306f11 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -26,11 +26,16 @@ namespace Swift {
 		public:
 			enum JoinResult { JoinSucceeded, JoinFailed };
 			enum LeavingType { Part };
+			enum Roles {Moderator, Participant, Visitor, NoRole};
+			enum Affiliations {Owner, Admin, Member, Outcast, NoAffiliation};
+			
 
 		public:
 			MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc);
 
 			void joinAs(const String &nick);
+			/*void queryRoomInfo(); */
+			/*void queryRoomItems(); */
 			String getCurrentNick();
 			void part();
 			void handleIncomingMessage(boost::shared_ptr<Message> message);
@@ -39,8 +44,10 @@ namespace Swift {
 			boost::signal<void (JoinResult)> onJoinComplete;
 			boost::signal<void (boost::shared_ptr<Presence>)> onOccupantPresenceChange;
 			boost::signal<void (const MUCOccupant&)> onOccupantJoined;
-			/**Occupant, type, and reason. */
-			boost::signal<void (const MUCOccupant&, LeavingType, const String&)> onOccupantLeft;
+			boost::signal<void (const MUCOccupant&, LeavingType, const String& /*reason*/)> onOccupantLeft;
+			/* boost::signal<void (const MUCInfo&)> onInfoResult; */
+			/* boost::signal<void (const blah&)> onItemsResult; */
+			
 
 		private:
 			bool isFromMUC(const JID& j) const {
-- 
cgit v0.10.2-6-g49f6