From 9b1e36b4fe08f32896e92abdb6fc7e3dad501160 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Tue, 24 Nov 2009 22:56:19 +0100
Subject: Added PresenceSender object.


diff --git a/Swift/Controllers/MUCController.cpp b/Swift/Controllers/MUCController.cpp
index 848f540..da7a9e7 100644
--- a/Swift/Controllers/MUCController.cpp
+++ b/Swift/Controllers/MUCController.cpp
@@ -21,13 +21,14 @@ MUCController::MUCController (
 		const JID &muc, 
 		const String &nick, 
 		StanzaChannel* stanzaChannel, 
+		PresenceSender* presenceSender,
 		IQRouter* iqRouter, 
 		ChatWindowFactory* chatWindowFactory, 
 		TreeWidgetFactory *treeWidgetFactory,
 		PresenceOracle* presenceOracle,
 		AvatarManager* avatarManager) : 
 			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager),
-			muc_(new MUC(stanzaChannel, muc)), 
+			muc_(new MUC(stanzaChannel, presenceSender, muc)), 
 			nick_(nick), 
 			treeWidgetFactory_(treeWidgetFactory) { 
 	roster_ = new Roster(chatWindow_->getTreeWidget(), treeWidgetFactory_);
diff --git a/Swift/Controllers/MUCController.h b/Swift/Controllers/MUCController.h
index af4a23a..7ee6e81 100644
--- a/Swift/Controllers/MUCController.h
+++ b/Swift/Controllers/MUCController.h
@@ -22,7 +22,7 @@ namespace Swift {
 
 	class MUCController : public ChatControllerBase {
 		public:
-			MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager);
+			MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager);
 			~MUCController();
 		
 		protected:
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index f54e0a2..22d71e5 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -26,6 +26,7 @@
 #include "Swiften/Base/foreach.h"
 #include "Swiften/Base/String.h"
 #include "Swiften/Client/Client.h"
+#include "Swiften/Presence/PresenceSender.h"
 #include "Swiften/Elements/Presence.h"
 #include "Swiften/Elements/VCardUpdate.h"
 #include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
@@ -60,7 +61,7 @@ typedef std::pair<JID, ChatController*> JIDChatControllerPair;
 typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
 
 MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory *treeWidgetFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer)
-		: timerFactory_(&boostIOServiceThread_.getIOService()), idleDetector_(&idleQuerier_, &timerFactory_, 100), client_(NULL), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), treeWidgetFactory_(treeWidgetFactory), settings_(settings), xmppRosterController_(NULL), rosterController_(NULL), loginWindow_(NULL), clientVersionResponder_(NULL), nickResolver_(NULL), discoResponder_(NULL) {
+		: timerFactory_(&boostIOServiceThread_.getIOService()), idleDetector_(&idleQuerier_, &timerFactory_, 100), client_(NULL), presenceSender_(NULL), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), treeWidgetFactory_(treeWidgetFactory), settings_(settings), xmppRosterController_(NULL), rosterController_(NULL), loginWindow_(NULL), clientVersionResponder_(NULL), nickResolver_(NULL), discoResponder_(NULL) {
 	application_ = application;
 	presenceOracle_ = NULL;
 	avatarManager_ = NULL;
@@ -114,6 +115,8 @@ void MainController::resetClient() {
 	clientVersionResponder_ = NULL;
 	delete discoResponder_;
 	discoResponder_ = NULL;
+	delete presenceSender_;
+	presenceSender_ = NULL;
 	delete client_;
 	client_ = NULL;
 	
@@ -210,7 +213,7 @@ void MainController::sendPresence(boost::shared_ptr<Presence> presence) {
 	}
 	presence->addPayload(capsInfo_);
 	lastSentPresence_ = presence;
-	client_->sendPresence(presence);
+	presenceSender_->sendPresence(presence);
 	if (presence->getType() == Presence::Unavailable) {
 		logout();
 	}
@@ -258,6 +261,7 @@ void MainController::handleLoginRequest(const String &username, const String &pa
 void MainController::performLoginFromCachedCredentials() {
 	if (!client_) {
 		client_ = new Swift::Client(jid_, password_);
+		presenceSender_ = new PresenceSender(client_);
 		//client_->onDataRead.connect(&printIncomingData);
 		//client_->onDataWritten.connect(&printOutgoingData);
 		if (!certificateFile_.isEmpty()) {
@@ -368,7 +372,7 @@ void MainController::handleChatControllerJIDChanged(const JID& from, const JID&
 }
 
 void MainController::handleJoinMUCRequest(const JID &muc, const String &nick) {
-	mucControllers_[muc] = new MUCController(jid_, muc, nick, client_, client_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_);
+	mucControllers_[muc] = new MUCController(jid_, muc, nick, client_, presenceSender_, client_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_);
 	mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_);
 }
 
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 270b131..78e49a2 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -33,6 +33,7 @@ namespace Swift {
 	class NickResolver;
 	class RosterController;
 	class XMPPRosterController;
+	class PresenceSender;
 	class DiscoInfoResponder;
 	class AvatarManager;
 	class LoginWindow;
@@ -86,6 +87,7 @@ namespace Swift {
 			PlatformIdleQuerier idleQuerier_;
 			ActualIdleDetector idleDetector_;
 			Client* client_;
+			PresenceSender* presenceSender_;
 			ChatWindowFactory* chatWindowFactory_;
 			MainWindowFactory* mainWindowFactory_;
 			LoginWindowFactory* loginWindowFactory_;
diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h
index 052c470..d618167 100644
--- a/Swiften/Client/DummyStanzaChannel.h
+++ b/Swiften/Client/DummyStanzaChannel.h
@@ -10,19 +10,19 @@ namespace Swift {
 			DummyStanzaChannel() {}
 
 			virtual void sendStanza(boost::shared_ptr<Stanza> stanza) {
-				sentStanzas_.push_back(stanza);
+				sentStanzas.push_back(stanza);
 			}
 
 			virtual void sendIQ(boost::shared_ptr<IQ> iq) {
-				sentStanzas_.push_back(iq);
+				sentStanzas.push_back(iq);
 			}
 
 			virtual void sendMessage(boost::shared_ptr<Message> message) {
-				sentStanzas_.push_back(message);
+				sentStanzas.push_back(message);
 			}
 
 			virtual void sendPresence(boost::shared_ptr<Presence> presence) {
-				sentStanzas_.push_back(presence);
+				sentStanzas.push_back(presence);
 			}
 
 			virtual String getNewIQID() {
@@ -33,6 +33,6 @@ namespace Swift {
 				return true;
 			}
 
-			std::vector<boost::shared_ptr<Stanza> > sentStanzas_;
+			std::vector<boost::shared_ptr<Stanza> > sentStanzas;
 	};
 }
diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h
index 37c58c7..a0e291d 100644
--- a/Swiften/Client/StanzaChannel.h
+++ b/Swiften/Client/StanzaChannel.h
@@ -1,5 +1,4 @@
-#ifndef SWIFTEN_MessageChannel_H
-#define SWIFTEN_MessageChannel_H
+#pragma once
 
 #include <boost/signal.hpp>
 #include <boost/shared_ptr.hpp>
@@ -19,5 +18,3 @@ namespace Swift {
 			boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived;
 	};
 }
-
-#endif
diff --git a/Swiften/Elements/Presence.h b/Swiften/Elements/Presence.h
index 002ed0e..a1f15fa 100644
--- a/Swiften/Elements/Presence.h
+++ b/Swiften/Elements/Presence.h
@@ -1,5 +1,4 @@
-#ifndef SWIFTEN_Presence
-#define SWIFTEN_Presence
+#pragma once
 
 #include "Swiften/Elements/Stanza.h"
 #include "Swiften/Elements/Status.h"
@@ -57,5 +56,3 @@ namespace Swift {
 			Presence::Type type_;
 	};
 }
-
-#endif
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 2b8054f..a6fbcbd 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -3,63 +3,64 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
+#include "Swiften/Presence/PresenceSender.h"
 #include "Swiften/Client/StanzaChannel.h"
 #include "Swiften/Elements/IQ.h"
-#include "Swiften/Elements/Message.h"
 #include "Swiften/Elements/MUCPayload.h"
-#include "Swiften/Elements/Presence.h"
 
 namespace Swift {
 
 typedef std::pair<String, MUCOccupant> StringMUCOccupantPair;
 
-MUC::MUC(StanzaChannel* stanzaChannel, const JID &muc) : muc_(muc), stanzaChannel_(stanzaChannel) {
-	stanzaChannel_->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
-}
-
-MUC::~MUC() {
+MUC::MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc) : ownMUCJID(muc), stanzaChannel(stanzaChannel), presenceSender(presenceSender) {
+	stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
 }
 
 void MUC::joinAs(const String &nick) {
+	firstPresenceSeen = false;
+
+	ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
+
 	boost::shared_ptr<Presence> joinPresence(new Presence());
-	joinPresence->setTo(JID(muc_.getNode(), muc_.getDomain(), nick));
+	joinPresence->setTo(ownMUCJID);
 	joinPresence->addPayload(boost::shared_ptr<Payload>(new MUCPayload()));
-	stanzaChannel_->sendPresence(joinPresence);
-	myNick_ = nick;
+	presenceSender->sendPresence(joinPresence);
 }
 
 void MUC::part() {
-	boost::shared_ptr<Presence> partPresence(new Presence());
-	partPresence->setType(Presence::Unavailable);
-	partPresence->setTo(JID(muc_.getNode(), muc_.getDomain(), myNick_));
-	stanzaChannel_->sendPresence(partPresence);
+	presenceSender->removeDirectedPresenceReceiver(ownMUCJID);
 }
 
 void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
-	if (presence->getFrom().toBare() != muc_ || presence->getFrom().getResource() == "") {
+	if (!isFromMUC(presence->getFrom())) {
 		return;
 	}
+
+	if (!firstPresenceSeen) {
+		if (presence->getType() == Presence::Error) {
+			onJoinComplete(JoinFailed);
+			return;
+		}
+		firstPresenceSeen = true;
+		onJoinComplete(JoinSucceeded);
+		presenceSender->addDirectedPresenceReceiver(ownMUCJID);
+	}
+
 	String nick = presence->getFrom().getResource();
+	if (nick.isEmpty()) {
+		return;
+	}
 	if (presence->getType() == Presence::Unavailable) {
-		foreach (StringMUCOccupantPair occupantPair,  occupants_) {
-			if (occupantPair.first == nick) {
-				occupants_.erase(nick);
-				onOccupantLeft(occupantPair.second, Part, "");
-				break;
-			}
-		}
-	} else if (presence->getType() == Presence::Available) {
-		bool found = false;
-		foreach (StringMUCOccupantPair occupantPair,  occupants_) {
-			if (occupantPair.first == nick) {
-				found = true;
-				break;
-			}
+		std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
+		if (i != occupants.end()) {
+			onOccupantLeft(i->second, Part, "");
+			occupants.erase(i);
 		}
-		if (!found) {
-			MUCOccupant occupant(nick);
-			occupants_.insert(occupants_.end(), std::pair<String, MUCOccupant>(nick, occupant));
-			onOccupantJoined(occupant);
+	}
+	else if (presence->getType() == Presence::Available) {
+		std::pair<std::map<String,MUCOccupant>::iterator, bool> result = occupants.insert(std::make_pair(nick, MUCOccupant(nick)));
+		if (result.second) {
+			onOccupantJoined(result.first->second);
 		}
 		onOccupantPresenceChange(presence);
 	}
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index 45bcbd3..2a327f4 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -1,5 +1,4 @@
-#ifndef SWIFTEN_MUC_H
-#define SWIFTEN_MUC_H
+#pragma once
 
 #include "Swiften/JID/JID.h"
 #include "Swiften/Base/String.h"
@@ -14,6 +13,7 @@
 
 namespace Swift {
 	class StanzaChannel;
+	class PresenceSender;
 
 	class MUC {
 		public:
@@ -21,8 +21,7 @@ namespace Swift {
 			enum LeavingType { Part };
 
 		public:
-			MUC(StanzaChannel* stanzaChannel, const JID &muc);
-			~MUC();
+			MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc);
 
 			void joinAs(const String &nick);
 			String getCurrentNick();
@@ -38,12 +37,22 @@ namespace Swift {
 			boost::signal<void (const MUCOccupant&, LeavingType, const String&)> onOccupantLeft;
 
 		private:
+			bool isFromMUC(const JID& j) const {
+				return ownMUCJID.equals(j, JID::WithoutResource);
+			}
+
+			const String& getOwnNick() const {
+				return ownMUCJID.getResource();
+			}
+
+		private:
 			void handleIncomingPresence(boost::shared_ptr<Presence> presence);
-			JID muc_;
-			StanzaChannel *stanzaChannel_;
-			String myNick_;
-			std::map<String, MUCOccupant> occupants_;
+
+		private:
+			JID ownMUCJID;
+			StanzaChannel* stanzaChannel;
+			PresenceSender* presenceSender;
+			std::map<String, MUCOccupant> occupants;
+			bool firstPresenceSeen;
 	};
 }
-
-#endif
diff --git a/Swiften/Presence/PresenceSender.cpp b/Swiften/Presence/PresenceSender.cpp
new file mode 100644
index 0000000..8e7ef68
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.cpp
@@ -0,0 +1,49 @@
+#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+PresenceSender::PresenceSender(StanzaChannel* channel) : channel(channel) {
+}
+
+void PresenceSender::sendPresence(boost::shared_ptr<Presence> presence) {
+	if (!channel->isAvailable()) {
+		return;
+	}
+
+	channel->sendPresence(presence);
+
+	if (!presence->getTo().isValid()) {
+		boost::shared_ptr<Presence> presenceCopy(new Presence(*presence));
+		foreach(const JID& jid, directedPresenceReceivers) {
+			presenceCopy->setTo(jid);
+			channel->sendPresence(presenceCopy);
+		}
+
+		lastSentUndirectedPresence = presence;
+	}
+}
+
+void PresenceSender::addDirectedPresenceReceiver(const JID& jid) {
+	directedPresenceReceivers.insert(jid);
+	if (channel->isAvailable()) {
+		if (lastSentUndirectedPresence && lastSentUndirectedPresence->getType() == Presence::Available) {
+			boost::shared_ptr<Presence> presenceCopy(new Presence(*lastSentUndirectedPresence));
+			presenceCopy->setTo(jid);
+			channel->sendPresence(presenceCopy);
+		}
+	}
+}
+
+void PresenceSender::removeDirectedPresenceReceiver(const JID& jid) {
+	directedPresenceReceivers.erase(jid);
+	if (channel->isAvailable()) {
+		boost::shared_ptr<Presence> presence(new Presence());
+		presence->setType(Presence::Unavailable);
+		presence->setTo(jid);
+		channel->sendPresence(presence);
+	}
+}
+
+}
diff --git a/Swiften/Presence/PresenceSender.h b/Swiften/Presence/PresenceSender.h
new file mode 100644
index 0000000..ef69447
--- /dev/null
+++ b/Swiften/Presence/PresenceSender.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <set>
+
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+	class StanzaChannel;
+
+	class PresenceSender {
+		public:
+			PresenceSender(StanzaChannel*);
+
+			void addDirectedPresenceReceiver(const JID&);
+			void removeDirectedPresenceReceiver(const JID&);
+
+			void sendPresence(boost::shared_ptr<Presence>);
+
+		private:
+			boost::shared_ptr<Presence> lastSentUndirectedPresence;
+			StanzaChannel* channel;
+			std::set<JID> directedPresenceReceivers;
+	};
+}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 9742768..e7e1582 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -33,6 +33,7 @@ sources = [
 		"MUC/MUCRegistry.cpp",
 		"Notifier/Notifier.cpp",
 		"Presence/PresenceOracle.cpp",
+		"Presence/PresenceSender.cpp",
 		"Queries/IQChannel.cpp",
 		"Queries/IQHandler.cpp",
 		"Queries/IQRouter.cpp",
@@ -155,6 +156,7 @@ env.Append(UNITTEST_SOURCES = [
 		File("Parser/UnitTest/XMLParserTest.cpp"),
 		File("Parser/UnitTest/XMPPParserTest.cpp"),
 		File("Presence/UnitTest/PresenceOracleTest.cpp"),
+		File("Presence/UnitTest/PresenceSenderTest.cpp"),
 		File("Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp"),
 		File("Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp"),
 		File("Queries/UnitTest/IQRouterTest.cpp"),
-- 
cgit v0.10.2-6-g49f6