From d509598b0f0edf5e103caedbab8662edc834445e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 31 Oct 2010 19:51:01 +0100
Subject: Refactoring Presence & MUC handling.


diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 4a9e164..5a32ced 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -18,6 +18,7 @@
 #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
 #include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/MUC/MUCManager.h"
 #include "Swiften/Elements/ChatState.h"
 #include "Swiften/MUC/MUCBookmarkManager.h"
 
@@ -26,7 +27,7 @@ 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, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsManager_(entityCapsManager) {
+ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager, MUCManager* mucManager) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsManager_(entityCapsManager), mucManager(mucManager) {
 	timerFactory_ = timerFactory;
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
@@ -83,7 +84,6 @@ void ChatsManager::handleUserLeftMUC(MUCController* mucController) {
 	std::map<JID, MUCController*>::iterator it;
 	for (it = mucControllers_.begin(); it != mucControllers_.end(); it++) {
 		if ((*it).second == mucController) {
-			mucRegistry_->removeMUC(it->first);
 			mucControllers_.erase(it);
 			delete mucController;
 			return;
@@ -214,19 +214,19 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
 	chatControllers_[to]->setToJID(to);
 }
 
-void ChatsManager::handleJoinMUCRequest(const JID &muc, const boost::optional<String>& nickMaybe) {
-	std::map<JID, MUCController*>::iterator it = mucControllers_.find(muc);
+void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<String>& nickMaybe) {
+	std::map<JID, MUCController*>::iterator it = mucControllers_.find(mucJID);
 	if (it != mucControllers_.end()) {
 		it->second->rejoin();
 	} else {
 		String nick = nickMaybe ? nickMaybe.get() : jid_.getNode();
-		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_);
-		mucControllers_[muc] = controller;
+		MUC::ref muc = mucManager->createMUC(mucJID);
+		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_);
+		mucControllers_[mucJID] = controller;
 		controller->setAvailableServerFeatures(serverDiscoInfo_);
 		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
-		mucRegistry_->addMUC(muc);
 	}
-	mucControllers_[muc]->activateChatWindow();
+	mucControllers_[mucJID]->activateChatWindow();
 }
 
 void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 724701c..0880f80 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -23,6 +23,7 @@ namespace Swift {
 	class EventController;
 	class ChatController;
 	class MUCController;
+	class MUCManager;
 	class ChatWindowFactory;
 	class NickResolver;
 	class PresenceOracle;
@@ -35,10 +36,11 @@ namespace Swift {
 	class ChatListWindowFactory;
 	class TimerFactory;
 	class EntityCapsManager;
+	class DirectedPresenceSender;
 
 	class ChatsManager {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager, MUCManager* mucManager);
 			virtual ~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setOnline(bool enabled);
@@ -78,5 +80,6 @@ namespace Swift {
 			TimerFactory* timerFactory_;
 			MUCRegistry* mucRegistry_;
 			EntityCapsManager* entityCapsManager_;
+			MUCManager* mucManager;
 	};
 }
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index ff102e7..259b715 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -36,10 +36,9 @@ namespace Swift {
  */
 MUCController::MUCController (
 		const JID& self,
-		const JID &muc, 
+		MUC::ref muc,
 		const String &nick, 
 		StanzaChannel* stanzaChannel, 
-		PresenceSender* presenceSender,
 		IQRouter* iqRouter, 
 		ChatWindowFactory* chatWindowFactory, 
 		PresenceOracle* presenceOracle,
@@ -48,7 +47,7 @@ MUCController::MUCController (
 		bool useDelayForLatency,
 		TimerFactory* timerFactory,
 		EventController* eventController) :
-			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory), muc_(new MUC(stanzaChannel, iqRouter, presenceSender, muc)), nick_(nick) {
+			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory), muc_(muc), nick_(nick) {
 	parting_ = true;
 	joined_ = false;
 	lastWasPresence_ = false;
@@ -59,7 +58,7 @@ MUCController::MUCController (
 	completer_ = new TabComplete();
 	chatWindow_->setRosterModel(roster_);
 	chatWindow_->setTabComplete(completer_);
-	chatWindow_->setName(muc.getNode());
+	chatWindow_->setName(muc->getJID().getNode());
 	chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
 	muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
 	muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
@@ -80,7 +79,6 @@ MUCController::MUCController (
 }
 
 MUCController::~MUCController() {
-	delete muc_;
 	chatWindow_->setRosterModel(NULL);
 	delete roster_;
 	if (loginCheckTimer_) {
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 2a6536c..1af9c58 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -41,7 +41,7 @@ namespace Swift {
 
 	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, TimerFactory* timerFactory, EventController* eventController);
+			MUCController(const JID& self, MUC::ref muc, const String &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController);
 			~MUCController();
 			boost::signal<void ()> onUserLeft;
 			virtual void setOnline(bool online);
@@ -80,7 +80,7 @@ namespace Swift {
 			void processUserPart();
 
 		private:
-			MUC* muc_;
+			MUC::ref muc_;
 			UIEventStream* events_;
 			String nick_;
 			Roster* roster_;
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 44fbcfe..08106e6 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -16,15 +16,17 @@
 #include "Swiften/Client/Client.h"
 #include "Swiften/Disco/EntityCapsManager.h"
 #include "Swiften/Disco/CapsProvider.h"
+#include "Swiften/MUC/MUCManager.h"
 #include "Swift/Controllers/Chat/ChatController.h"
 #include "Swift/Controllers/XMPPEvents/EventController.h"
 #include "Swift/Controllers/Chat/MUCController.h"
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/StanzaChannelPresenceSender.h"
 #include "Swiften/Avatars/NullAvatarManager.h"
 #include "Swiften/Avatars/AvatarMemoryStorage.h"
 #include "Swiften/VCards/VCardManager.h"
 #include "Swiften/VCards/VCardMemoryStorage.h"
 #include "Swiften/Client/NickResolver.h"
+#include "Swiften/Presence/DirectedPresenceSender.h"
 #include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swift/Controllers/UnitTest/MockChatWindow.h"
 #include "Swiften/Client/DummyStanzaChannel.h"
@@ -71,12 +73,14 @@ public:
 		nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_);
 		presenceOracle_ = new PresenceOracle(stanzaChannel_);
 		serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo());
-		presenceSender_ = new PresenceSender(stanzaChannel_);
+		presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_);
+		directedPresenceSender_ = new DirectedPresenceSender(presenceSender_);
+		mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_);
 		uiEventStream_ = new UIEventStream();
 		entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
 		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
 		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createWindow).With(uiEventStream_).Return(NULL);
-		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_);
+		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_);
 
 		avatarManager_ = new NullAvatarManager();
 		manager_->setAvatarManager(avatarManager_);
@@ -87,6 +91,7 @@ public:
 		delete mocks_;
 		delete avatarManager_;
 		delete manager_;
+		delete directedPresenceSender_;
 		delete presenceSender_;
 		delete presenceOracle_;
 		delete nickResolver_;
@@ -96,6 +101,7 @@ public:
 		delete iqChannel_;
 		delete iqRouter_;
 		delete uiEventStream_;
+		delete mucManager_;
 		delete xmppRoster_;
 		delete entityCapsManager_;
 		delete capsProvider_;
@@ -329,8 +335,10 @@ private:
 	UIEventStream* uiEventStream_;
 	ChatListWindowFactory* chatListWindowFactory_;
 	MUCRegistry* mucRegistry_;
+	DirectedPresenceSender* directedPresenceSender_;
 	EntityCapsManager* entityCapsManager_;
 	CapsProvider* capsProvider_;
+	MUCManager* mucManager_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index ea5a705..fb3a0ee 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -9,7 +9,8 @@
 #include "3rdParty/hippomocks.h"
 
 #include "Swift/Controllers/XMPPEvents/EventController.h"
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/DirectedPresenceSender.h"
+#include "Swiften/Presence/StanzaChannelPresenceSender.h"
 #include "Swiften/Avatars/NullAvatarManager.h"
 #include "Swift/Controllers/Chat/MUCController.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindow.h"
@@ -42,7 +43,6 @@ public:
 
 	void setUp() {
 		self_ = JID("girl@wonderland.lit/rabbithole");
-		muc_ = JID("teaparty@rooms.wonderland.lit");
 		nick_ = "aLiCe";
 		mocks_ = new MockRepository();
 		stanzaChannel_ = new DummyStanzaChannel();
@@ -51,14 +51,16 @@ public:
 		eventController_ = new EventController();
 		chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
 		presenceOracle_ = new PresenceOracle(stanzaChannel_);
-		presenceSender_ = new PresenceSender(stanzaChannel_);
+		presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_);
+		directedPresenceSender_ = new DirectedPresenceSender(presenceSender_);
 		uiEventStream_ = new UIEventStream();
 		avatarManager_ = new NullAvatarManager();
 		TimerFactory* timerFactory = NULL;
 		window_ = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>();
-		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_, uiEventStream_).Return(window_);
-		controller_ = new MUCController (self_, muc_, nick_, stanzaChannel_, presenceSender_,
-				iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_);
+		mucRegistry_ = new MUCRegistry();
+		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_);
 	};
 
 	void tearDown() {
@@ -69,14 +71,16 @@ public:
 		delete uiEventStream_;
 		delete stanzaChannel_;
 		delete presenceSender_;
+		delete directedPresenceSender_;
 		delete iqRouter_;
 		delete iqChannel_;
+		delete mucRegistry_;
 		delete avatarManager_;
 	}
 
 	void finishJoin() {
 		Presence::ref presence(new Presence());
-		presence->setFrom(JID(muc_.toString() + "/" + nick_));
+		presence->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
 		MUCUserPayload::ref status(new MUCUserPayload());
 		MUCUserPayload::StatusCode code;
 		code.code = 110;
@@ -90,20 +94,20 @@ public:
 		Message::ref message(new Message());
 
 		message = Message::ref(new Message());
-		message->setFrom(JID(muc_.toString() + "/otherperson"));
+		message->setFrom(JID(muc_->getJID().toString() + "/otherperson"));
 		message->setBody(nick_ + ": hi there");
 		message->setType(Message::Groupchat);
 		controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
 		CPPUNIT_ASSERT_EQUAL((size_t)1, eventController_->getEvents().size());
 
-		message->setFrom(JID(muc_.toString() + "/other"));
+		message->setFrom(JID(muc_->getJID().toString() + "/other"));
 		message->setBody("Hi there " + nick_);
 		message->setType(Message::Groupchat);
 		controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
 		CPPUNIT_ASSERT_EQUAL((size_t)2, eventController_->getEvents().size());
 
 		message = Message::ref(new Message());
-		message->setFrom(JID(muc_.toString() + "/other2"));
+		message->setFrom(JID(muc_->getJID().toString() + "/other2"));
 		message->setBody("Hi " + nick_.getLowerCase());
 		message->setType(Message::Groupchat);
 		controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
@@ -113,7 +117,7 @@ public:
 	void testNotAddressedToSelf() {
 		finishJoin();
 		Message::ref message(new Message());
-		message->setFrom(JID(muc_.toString() + "/other3"));
+		message->setFrom(JID(muc_->getJID().toString() + "/other3"));
 		message->setBody("Hi there Hatter");
 		message->setType(Message::Groupchat);
 		controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
@@ -123,7 +127,7 @@ public:
 	void testAddressedToSelfBySelf() {
 		finishJoin();
 		Message::ref message(new Message());
-		message->setFrom(JID(muc_.toString() + "/" + nick_));
+		message->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
 		message->setBody("Hi there " + nick_);
 		message->setType(Message::Groupchat);
 		controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
@@ -200,7 +204,7 @@ public:
 
 private:
 	JID self_;
-	JID muc_;
+	MUC::ref muc_;
 	String nick_;
 	StanzaChannel* stanzaChannel_;
 	IQChannel* iqChannel_;
@@ -211,10 +215,12 @@ private:
 //	NickResolver* nickResolver_;
 	PresenceOracle* presenceOracle_;
 	AvatarManager* avatarManager_;
-	PresenceSender* presenceSender_;
+	StanzaChannelPresenceSender* presenceSender_;
+	DirectedPresenceSender* directedPresenceSender_;
 	MockRepository* mocks_;
 	UIEventStream* uiEventStream_;
 	MockChatWindow* window_;
+	MUCRegistry* mucRegistry_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest);
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 4501f2c..f3b9977 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -231,11 +231,11 @@ void MainController::handleConnected() {
 	bool freshLogin = rosterController_ == NULL;
 	myStatusLooksOnline_ = true;
 	if (freshLogin) {
-		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), mainWindowFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), eventController_, uiEventStream_, client_->getIQRouter(), settings_);
+		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), mainWindowFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_);
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 
-		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, chatWindowFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, client_->getMUCRegistry(), client_->getEntityCapsManager());
+		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, chatWindowFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, client_->getMUCRegistry(), client_->getEntityCapsManager(), client_->getMUCManager());
 		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
 		chatsManager_->setAvatarManager(client_->getAvatarManager());
 
diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp
index 1ce3266..242f974 100644
--- a/Swift/Controllers/RosterController.cpp
+++ b/Swift/Controllers/RosterController.cpp
@@ -17,7 +17,7 @@
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swiften/Presence/PresenceOracle.h"
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/SubscriptionManager.h"
 #include "Swift/Controllers/XMPPEvents/EventController.h"
 #include "Swiften/Queries/IQRouter.h"
 #include "Swiften/Roster/Roster.h"
@@ -37,11 +37,11 @@ namespace Swift {
 /**
  * The controller does not gain ownership of these parameters.
  */
-RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings)
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings)
  : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()) {
 	iqRouter_ = iqRouter;
 	presenceOracle_ = presenceOracle;
-	presenceSender_ = presenceSender;
+	subscriptionManager_ = subscriptionManager;
 	eventController_ = eventController;
 	expandiness_ = new RosterGroupExpandinessPersister(roster_, settings);
 	roster_->addFilter(offlineFilter_);
@@ -54,7 +54,7 @@ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, Avata
 	xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3));
 	xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1));
 	xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this));
-	presenceOracle_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
+	subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
 	presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1));
 	uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1));
 	avatarManager_ = avatarManager;
@@ -167,7 +167,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 		SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
 		request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 		request->send();
-		presenceSender_->requestSubscription(addContactEvent->getJID());
+		subscriptionManager_->requestSubscription(addContactEvent->getJID());
 		return;
 	}
 	boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event);
@@ -240,7 +240,7 @@ void RosterController::handleIncomingPresence(Presence::ref newPresence) {
 
 void RosterController::handleSubscriptionRequest(const JID& jid, const String& message) {
 	if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) {
-		presenceSender_->confirmSubscription(jid);
+		subscriptionManager_->confirmSubscription(jid);
 		return;
 	}
 	SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message);
@@ -251,14 +251,14 @@ void RosterController::handleSubscriptionRequest(const JID& jid, const String& m
 }
 
 void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) {
-	presenceSender_->confirmSubscription(event->getJID());
+	subscriptionManager_->confirmSubscription(event->getJID());
 	if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) {
-		presenceSender_->requestSubscription(event->getJID());
+		subscriptionManager_->requestSubscription(event->getJID());
 	}
 }
 
 void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) {
-	presenceSender_->cancelSubscription(event->getJID());
+	subscriptionManager_->cancelSubscription(event->getJID());
 }
 
 void RosterController::handleAvatarChanged(const JID& jid) {
diff --git a/Swift/Controllers/RosterController.h b/Swift/Controllers/RosterController.h
index 3cb3812..cc51d23 100644
--- a/Swift/Controllers/RosterController.h
+++ b/Swift/Controllers/RosterController.h
@@ -4,8 +4,7 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#ifndef SWIFTEN_RosterController_H
-#define SWIFTEN_RosterController_H
+#pragma once
 
 #include "Swiften/JID/JID.h"
 #include "Swiften/Base/String.h"
@@ -28,7 +27,7 @@ namespace Swift {
 	class OfflineRosterFilter;
 	class NickResolver;
 	class PresenceOracle;
-	class PresenceSender;
+	class SubscriptionManager;
 	class EventController;
 	class SubscriptionRequestEvent;
 	class UIEventStream;
@@ -37,7 +36,7 @@ namespace Swift {
 
 	class RosterController {
 		public:
-			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings);
+			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings);
 			~RosterController();
 			void showRosterWindow();
 			MainWindow* getWindow() {return mainWindow_;};
@@ -70,7 +69,7 @@ namespace Swift {
 			AvatarManager* avatarManager_;
 			NickResolver* nickResolver_;
 			PresenceOracle* presenceOracle_;
-			PresenceSender* presenceSender_;
+			SubscriptionManager* subscriptionManager_;
 			EventController* eventController_;
 			RosterGroupExpandinessPersister* expandiness_;
 			IQRouter* iqRouter_;
@@ -80,5 +79,3 @@ namespace Swift {
 			boost::bsignals::scoped_connection uiEventConnection_;
 	};
 }
-#endif
-
diff --git a/Swift/Controllers/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
index c0c0319..50419f9 100644
--- a/Swift/Controllers/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
@@ -24,7 +24,7 @@
 #include "Swiften/Avatars/NullAvatarManager.h"
 #include "Swift/Controllers/XMPPEvents/EventController.h"
 #include "Swiften/Presence/PresenceOracle.h"
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/SubscriptionManager.h"
 #include "Swiften/Client/NickResolver.h"
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h"
@@ -63,11 +63,11 @@ class RosterControllerTest : public CppUnit::TestFixture
 			router_ = new IQRouter(channel_);
 			stanzaChannel_ = new DummyStanzaChannel();
 			presenceOracle_ = new PresenceOracle(stanzaChannel_);
-			presenceSender_ = new PresenceSender(stanzaChannel_);
+			subscriptionManager_ = new SubscriptionManager(stanzaChannel_);
 			eventController_ = new EventController();
 			uiEventStream_ = new UIEventStream();
 			settings_ = new DummySettingsProvider();
-			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, presenceSender_, eventController_, uiEventStream_, router_, settings_);
+			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_);
 			mainWindow_ = mainWindowFactory_->last;
 		};
 
@@ -80,7 +80,7 @@ class RosterControllerTest : public CppUnit::TestFixture
 			delete channel_;
 			delete router_;
 			delete eventController_;
-			delete presenceSender_;
+			delete subscriptionManager_;
 			delete presenceOracle_;
 			delete stanzaChannel_;
 			delete uiEventStream_;
@@ -310,7 +310,7 @@ class RosterControllerTest : public CppUnit::TestFixture
 		DummyStanzaChannel* stanzaChannel_;	
 		IQRouter* router_;
 		PresenceOracle* presenceOracle_;
-		PresenceSender* presenceSender_;
+		SubscriptionManager* subscriptionManager_;
 		EventController* eventController_;
 		UIEventStream* uiEventStream_;
 		MockMainWindow* mainWindow_;
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index 45eeeff..7dbcb70 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -10,8 +10,10 @@
 #include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swiften/Roster/XMPPRosterController.h"
 #include "Swiften/Presence/PresenceOracle.h"
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/StanzaChannelPresenceSender.h"
+#include "Swiften/Presence/DirectedPresenceSender.h"
 #include "Swiften/MUC/MUCRegistry.h"
+#include "Swiften/MUC/MUCManager.h"
 #include "Swiften/Client/MemoryStorages.h"
 #include "Swiften/VCards/VCardManager.h"
 #include "Swiften/VCards/VCardManager.h"
@@ -19,6 +21,7 @@
 #include "Swiften/Disco/CapsManager.h"
 #include "Swiften/Disco/EntityCapsManager.h"
 #include "Swiften/Client/NickResolver.h"
+#include "Swiften/Presence/SubscriptionManager.h"
 
 namespace Swift {
 
@@ -31,13 +34,16 @@ Client::Client(EventLoop* eventLoop, const JID& jid, const String& password, Sto
 	roster = new XMPPRosterImpl();
 	rosterController = new XMPPRosterController(getIQRouter(), roster);
 
+	subscriptionManager = new SubscriptionManager(getStanzaChannel());
+
 	presenceOracle = new PresenceOracle(getStanzaChannel());
 	presenceOracle->onPresenceChange.connect(boost::ref(onPresenceChange));
-	presenceOracle->onPresenceSubscriptionRequest.connect(boost::ref(onPresenceSubscriptionRequest));
 
-	presenceSender = new PresenceSender(getStanzaChannel());
+	stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel());
+	directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
 
 	mucRegistry = new MUCRegistry();
+	mucManager = new MUCManager(getStanzaChannel(), getIQRouter(), directedPresenceSender, mucRegistry);
 
 	vcardManager = new VCardManager(jid, getIQRouter(), getStorages()->getVCardStorage());
 	avatarManager = new AvatarManagerImpl(vcardManager, getStanzaChannel(), getStorages()->getAvatarStorage(), mucRegistry);
@@ -55,11 +61,14 @@ Client::~Client() {
 	delete avatarManager;
 	delete vcardManager;
 
+	delete mucManager;
 	delete mucRegistry;
 
-	delete presenceSender;
+	delete directedPresenceSender;
+	delete stanzaChannelPresenceSender;
 
 	delete presenceOracle;
+	delete subscriptionManager;
 	delete rosterController;
 	delete roster;
 
@@ -81,7 +90,6 @@ void Client::requestRoster() {
 	rosterController->requestRoster();
 }
 
-
 Presence::ref Client::getLastPresence(const JID& jid) const {
 	return presenceOracle->getLastPresence(jid);
 }
@@ -97,5 +105,8 @@ Storages* Client::getStorages() const {
 	return memoryStorages;
 }
 
+PresenceSender* Client::getPresenceSender() const {
+	return directedPresenceSender;
+}
 
 }
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 7e11df9..adfd549 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -12,9 +12,12 @@ namespace Swift {
 	class SoftwareVersionResponder;
 	class XMPPRoster;
 	class XMPPRosterImpl;
+	class MUCManager;
 	class XMPPRosterController;
 	class PresenceOracle;
 	class PresenceSender;
+	class DirectedPresenceSender;
+	class StanzaChannelPresenceSender;
 	class MUCRegistry;
 	class Storages;
 	class MemoryStorages;
@@ -23,6 +26,7 @@ namespace Swift {
 	class CapsManager;
 	class EntityCapsManager;
 	class NickResolver;
+	class SubscriptionManager;
 
 	/**
 	 * Provides the core functionality for writing XMPP client software.
@@ -88,8 +92,10 @@ namespace Swift {
 				return presenceOracle;
 			}
 
-			PresenceSender* getPresenceSender() const {
-				return presenceSender;
+			PresenceSender* getPresenceSender() const;
+
+			MUCManager* getMUCManager() const {
+				return mucManager;
 			}
 
 			MUCRegistry* getMUCRegistry() const {
@@ -112,15 +118,17 @@ namespace Swift {
 				return nickResolver;
 			}
 
+			SubscriptionManager* getSubscriptionManager() const {
+				return subscriptionManager;
+			}
+
 		public:
 			/**
 			 * This signal is emitted when a JID changes presence.
 			 */
 			boost::signal<void (Presence::ref)> onPresenceChange;
 
-			/**
-			 * This signal is emitted when a presence subscription request is received.
-			 */
+
 			boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
 
 		private:
@@ -133,12 +141,15 @@ namespace Swift {
 			XMPPRosterImpl* roster;
 			XMPPRosterController* rosterController;
 			PresenceOracle* presenceOracle;
-			PresenceSender* presenceSender;
+			DirectedPresenceSender* directedPresenceSender;
+			StanzaChannelPresenceSender* stanzaChannelPresenceSender;
 			MUCRegistry* mucRegistry;
 			VCardManager* vcardManager;
 			AvatarManager* avatarManager;
 			CapsManager* capsManager;
 			EntityCapsManager* entityCapsManager;
 			NickResolver* nickResolver;
+			SubscriptionManager* subscriptionManager;
+			MUCManager* mucManager;
 	};
 }
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index cfd468c..9f7c500 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -9,7 +9,7 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Presence/DirectedPresenceSender.h"
 #include "Swiften/Client/StanzaChannel.h"
 #include "Swiften/Queries/IQRouter.h"
 #include "Swiften/Elements/Form.h"
@@ -17,13 +17,15 @@
 #include "Swiften/Elements/MUCUserPayload.h"
 #include "Swiften/Elements/MUCOwnerPayload.h"
 #include "Swiften/Elements/MUCPayload.h"
+#include "Swiften/MUC/MUCRegistry.h"
 
 namespace Swift {
 
 typedef std::pair<String, MUCOccupant> StringMUCOccupantPair;
 
-MUC::MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, PresenceSender* presenceSender, const JID &muc) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), muc_(muc) {
+MUC::MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry) {
 	scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
+	mucRegistry->addMUC(getJID());
 }
 
 //FIXME: discover reserved nickname
@@ -57,6 +59,7 @@ void MUC::joinWithContextSince(const String &nick) {
 
 void MUC::part() {
 	presenceSender->removeDirectedPresenceReceiver(ownMUCJID);
+	mucRegistry->removeMUC(getJID());
 }
 
 void MUC::handleUserLeft(LeavingType type) {
@@ -157,7 +160,7 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
 				/* Currently deal with this by making an instant room */
 				boost::shared_ptr<MUCOwnerPayload> mucPayload(new MUCOwnerPayload());
 				mucPayload->setPayload(boost::shared_ptr<Payload>(new Form(Form::SubmitType)));
-				boost::shared_ptr<IQ> iq(IQ::createRequest(IQ::Set, muc_, iqRouter_->getNewIQID(), mucPayload));
+				boost::shared_ptr<IQ> iq(IQ::createRequest(IQ::Set, getJID(), iqRouter_->getNewIQID(), mucPayload));
 				iqRouter_->sendIQ(iq);
 			}
 		}
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index ea20ac4..29dbbe1 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -11,6 +11,7 @@
 #include "Swiften/Elements/Message.h"
 #include "Swiften/Elements/Presence.h"
 #include "Swiften/MUC/MUCOccupant.h"
+#include "Swiften/MUC/MUCRegistry.h"
 
 #include <boost/shared_ptr.hpp>
 #include "Swiften/Base/boost_bsignals.h"
@@ -21,15 +22,24 @@
 namespace Swift {
 	class StanzaChannel;
 	class IQRouter;
-	class PresenceSender;
+	class DirectedPresenceSender;
 
 	class MUC {
 		public:
+			typedef boost::shared_ptr<MUC> ref;
+
 			enum JoinResult { JoinSucceeded, JoinFailed };
-			enum LeavingType { Part, Disconnect };			
+			enum LeavingType { Part, Disconnect };
 
 		public:
-			MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, PresenceSender* presenceSender, const JID &muc);
+			MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry);
+
+			/**
+			 * Returns the (bare) JID of the MUC.
+			 */
+			JID getJID() const {
+				return ownMUCJID.toBare();
+			}
 
 			void joinAs(const String &nick);
 			void joinWithContextSince(const String &nick);
@@ -70,8 +80,8 @@ namespace Swift {
 			JID ownMUCJID;
 			StanzaChannel* stanzaChannel;
 			IQRouter* iqRouter_;
-			PresenceSender* presenceSender;
-			JID muc_;
+			DirectedPresenceSender* presenceSender;
+			MUCRegistry* mucRegistry;
 			std::map<String, MUCOccupant> occupants;
 			bool joinComplete_;
 			boost::bsignals::scoped_connection scopedConnection_;
diff --git a/Swiften/MUC/MUCManager.cpp b/Swiften/MUC/MUCManager.cpp
new file mode 100644
index 0000000..8950029
--- /dev/null
+++ b/Swiften/MUC/MUCManager.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/MUC/MUCManager.h"
+
+namespace Swift {
+
+MUCManager::MUCManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, MUCRegistry* mucRegistry) : stanzaChannel(stanzaChannel), iqRouter(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry) {
+}
+
+MUC::ref MUCManager::createMUC(const JID& jid) {
+	return MUC::ref(new MUC(stanzaChannel, iqRouter, presenceSender, jid, mucRegistry));
+}
+
+}
diff --git a/Swiften/MUC/MUCManager.h b/Swiften/MUC/MUCManager.h
new file mode 100644
index 0000000..0efdf9a
--- /dev/null
+++ b/Swiften/MUC/MUCManager.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/MUC/MUC.h"
+
+namespace Swift {
+	class IQRouter;
+	class StanzaChannel;
+	class DirectedPresenceSender;
+	class MUCRegistry;
+
+	class MUCManager {
+		public:
+			MUCManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, MUCRegistry* mucRegistry);
+
+			MUC::ref createMUC(const JID&);
+
+		private:
+			StanzaChannel* stanzaChannel;
+			IQRouter* iqRouter;
+			DirectedPresenceSender* presenceSender;
+			MUCRegistry* mucRegistry;
+	};
+}
diff --git a/Swiften/Presence/DirectedPresenceSender.cpp b/Swiften/Presence/DirectedPresenceSender.cpp
new file mode 100644
index 0000000..aade6cf
--- /dev/null
+++ b/Swiften/Presence/DirectedPresenceSender.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Presence/DirectedPresenceSender.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+
+DirectedPresenceSender::DirectedPresenceSender(PresenceSender* sender) : sender(sender) {
+}
+
+void DirectedPresenceSender::sendPresence(boost::shared_ptr<Presence> presence) {
+	if (!sender->isAvailable()) {
+		return;
+	}
+
+	sender->sendPresence(presence);
+
+	if (!presence->getTo().isValid()) {
+		boost::shared_ptr<Presence> presenceCopy(new Presence(*presence));
+		foreach(const JID& jid, directedPresenceReceivers) {
+			presenceCopy->setTo(jid);
+			sender->sendPresence(presenceCopy);
+		}
+
+		lastSentUndirectedPresence = presence;
+	}
+}
+
+/**
+ * Gets either the last broadcast presence, or an empty stanza if none has been sent.
+ */
+boost::shared_ptr<Presence> DirectedPresenceSender::getLastSentUndirectedPresence() {
+	boost::shared_ptr<Presence> presenceCopy(lastSentUndirectedPresence ? new Presence(*lastSentUndirectedPresence) : new Presence());
+	return presenceCopy;
+}
+
+void DirectedPresenceSender::addDirectedPresenceReceiver(const JID& jid) {
+	directedPresenceReceivers.insert(jid);
+	if (sender->isAvailable()) {
+		if (lastSentUndirectedPresence && lastSentUndirectedPresence->getType() == Presence::Available) {
+			boost::shared_ptr<Presence> presenceCopy(new Presence(*lastSentUndirectedPresence));
+			presenceCopy->setTo(jid);
+			sender->sendPresence(presenceCopy);
+		}
+	}
+}
+
+void DirectedPresenceSender::removeDirectedPresenceReceiver(const JID& jid) {
+	directedPresenceReceivers.erase(jid);
+	if (sender->isAvailable()) {
+		boost::shared_ptr<Presence> presence(new Presence());
+		presence->setType(Presence::Unavailable);
+		presence->setTo(jid);
+		sender->sendPresence(presence);
+	}
+}
+
+bool DirectedPresenceSender::isAvailable() const {
+	return sender->isAvailable();
+}
+
+}
diff --git a/Swiften/Presence/DirectedPresenceSender.h b/Swiften/Presence/DirectedPresenceSender.h
new file mode 100644
index 0000000..b63a50e
--- /dev/null
+++ b/Swiften/Presence/DirectedPresenceSender.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <set>
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Presence/PresenceSender.h"
+
+namespace Swift {
+	class DirectedPresenceSender : public PresenceSender {
+		public:
+			DirectedPresenceSender(PresenceSender*);
+
+			void addDirectedPresenceReceiver(const JID&);
+			void removeDirectedPresenceReceiver(const JID&);
+
+			void sendPresence(Presence::ref);
+
+			Presence::ref getLastSentUndirectedPresence();
+
+			bool isAvailable() const;
+
+		private:
+			Presence::ref lastSentUndirectedPresence;
+			PresenceSender* sender;
+			std::set<JID> directedPresenceReceivers;
+	};
+}
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
index fb4be3f..387ad42 100644
--- a/Swiften/Presence/PresenceOracle.cpp
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -33,12 +33,11 @@ void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) {
 void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
 	JID bareJID(presence->getFrom().toBare());
 	if (presence->getType() == Presence::Subscribe) {
-		onPresenceSubscriptionRequest(bareJID, presence->getStatus());
-	} else {
+	}
+	else {
 		Presence::ref passedPresence = presence;
 		if (presence->getType() == Presence::Unsubscribe) {
 			/* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
-			onPresenceSubscriptionRevoked(bareJID, presence->getStatus());
 			passedPresence = Presence::ref(new Presence());
 			passedPresence->setType(Presence::Unavailable);
 			passedPresence->setFrom(bareJID);
diff --git a/Swiften/Presence/PresenceOracle.h b/Swiften/Presence/PresenceOracle.h
index f30d05d..e846984 100644
--- a/Swiften/Presence/PresenceOracle.h
+++ b/Swiften/Presence/PresenceOracle.h
@@ -25,8 +25,6 @@ class StanzaChannel;
 
 		public:
 			boost::signal<void (Presence::ref)> onPresenceChange;
-			boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
-			boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRevoked;
 
 		private:
 			void handleIncomingPresence(Presence::ref presence);
diff --git a/Swiften/Presence/PresenceSender.cpp b/Swiften/Presence/PresenceSender.cpp
index 6df02b8..50d75eb 100644
--- a/Swiften/Presence/PresenceSender.cpp
+++ b/Swiften/Presence/PresenceSender.cpp
@@ -5,81 +5,10 @@
  */
 
 #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;
-	}
-}
-
-/**
- * Gets either the last broadcast presence, or an empty stanza if none has been sent.
- */
-boost::shared_ptr<Presence> PresenceSender::getLastSentUndirectedPresence() {
-	boost::shared_ptr<Presence> presenceCopy(lastSentUndirectedPresence ? new Presence(*lastSentUndirectedPresence) : new Presence());
-	return presenceCopy;
-}
-
-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);
-	}
-}
-
-void PresenceSender::cancelSubscription(const JID& jid) {
-	boost::shared_ptr<Presence> stanza(new Presence());
-	stanza->setType(Presence::Unsubscribed);
-	stanza->setTo(jid);
-	channel->sendPresence(stanza);
-}
-
-void PresenceSender::confirmSubscription(const JID& jid) {
-	boost::shared_ptr<Presence> stanza(new Presence());
-	stanza->setType(Presence::Subscribed);
-	stanza->setTo(jid);
-	channel->sendPresence(stanza);
-}
-
-
-void PresenceSender::requestSubscription(const JID& jid) {
-	boost::shared_ptr<Presence> stanza(new Presence());
-	stanza->setType(Presence::Subscribe);
-	stanza->setTo(jid);
-	channel->sendPresence(stanza);
+PresenceSender::~PresenceSender() {
 }
 
 }
diff --git a/Swiften/Presence/PresenceSender.h b/Swiften/Presence/PresenceSender.h
index 3336523..5abf2f3 100644
--- a/Swiften/Presence/PresenceSender.h
+++ b/Swiften/Presence/PresenceSender.h
@@ -6,31 +6,15 @@
 
 #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>);
-
-			void cancelSubscription(const JID& jid);
-			void confirmSubscription(const JID& jid);
-			void requestSubscription(const JID& jid);
+			virtual ~PresenceSender();
 
-			boost::shared_ptr<Presence> getLastSentUndirectedPresence();
+			virtual void sendPresence(Presence::ref) = 0;
 
-		private:
-			boost::shared_ptr<Presence> lastSentUndirectedPresence;
-			StanzaChannel* channel;
-			std::set<JID> directedPresenceReceivers;
+			virtual bool isAvailable() const = 0;
 	};
 }
diff --git a/Swiften/Presence/StanzaChannelPresenceSender.cpp b/Swiften/Presence/StanzaChannelPresenceSender.cpp
new file mode 100644
index 0000000..654b7e7
--- /dev/null
+++ b/Swiften/Presence/StanzaChannelPresenceSender.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Presence/StanzaChannelPresenceSender.h"
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+StanzaChannelPresenceSender::StanzaChannelPresenceSender(StanzaChannel* channel) : channel(channel) {
+}
+
+void StanzaChannelPresenceSender::sendPresence(Presence::ref presence) {
+	channel->sendPresence(presence);
+}
+
+bool StanzaChannelPresenceSender::isAvailable() const {
+	return channel->isAvailable();
+}
+
+}
diff --git a/Swiften/Presence/StanzaChannelPresenceSender.h b/Swiften/Presence/StanzaChannelPresenceSender.h
new file mode 100644
index 0000000..23230ab
--- /dev/null
+++ b/Swiften/Presence/StanzaChannelPresenceSender.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swiften/Presence/PresenceSender.h"
+
+namespace Swift {
+	class StanzaChannel;
+
+	class StanzaChannelPresenceSender : public PresenceSender {
+		public:
+			StanzaChannelPresenceSender(StanzaChannel*);
+
+			void sendPresence(Presence::ref);
+
+			bool isAvailable() const;
+
+		private:
+			StanzaChannel* channel;
+	};
+}
diff --git a/Swiften/Presence/SubscriptionManager.cpp b/Swiften/Presence/SubscriptionManager.cpp
new file mode 100644
index 0000000..12534dc
--- /dev/null
+++ b/Swiften/Presence/SubscriptionManager.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Presence/SubscriptionManager.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+SubscriptionManager::SubscriptionManager(StanzaChannel* channel) : stanzaChannel(channel) {
+	stanzaChannel->onPresenceReceived.connect(boost::bind(&SubscriptionManager::handleIncomingPresence, this, _1));
+}
+
+SubscriptionManager::~SubscriptionManager() {
+	stanzaChannel->onPresenceReceived.disconnect(boost::bind(&SubscriptionManager::handleIncomingPresence, this, _1));
+}
+
+void SubscriptionManager::cancelSubscription(const JID& jid) {
+	Presence::ref stanza(new Presence());
+	stanza->setType(Presence::Unsubscribed);
+	stanza->setTo(jid);
+	stanzaChannel->sendPresence(stanza);
+}
+
+void SubscriptionManager::confirmSubscription(const JID& jid) {
+	Presence::ref stanza(new Presence());
+	stanza->setType(Presence::Subscribed);
+	stanza->setTo(jid);
+	stanzaChannel->sendPresence(stanza);
+}
+
+
+void SubscriptionManager::requestSubscription(const JID& jid) {
+	Presence::ref stanza(new Presence());
+	stanza->setType(Presence::Subscribe);
+	stanza->setTo(jid);
+	stanzaChannel->sendPresence(stanza);
+}
+
+void SubscriptionManager::handleIncomingPresence(Presence::ref presence) {
+	JID bareJID(presence->getFrom().toBare());
+	if (presence->getType() == Presence::Subscribe) {
+		onPresenceSubscriptionRequest(bareJID, presence->getStatus());
+	}
+	else if (presence->getType() == Presence::Unsubscribe) {
+		onPresenceSubscriptionRevoked(bareJID, presence->getStatus());
+	}
+}
+
+
+}
diff --git a/Swiften/Presence/SubscriptionManager.h b/Swiften/Presence/SubscriptionManager.h
new file mode 100644
index 0000000..477a2fd
--- /dev/null
+++ b/Swiften/Presence/SubscriptionManager.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <map>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/Elements/Presence.h"
+
+namespace Swift {
+	class StanzaChannel;
+
+	class SubscriptionManager {
+		public:
+			SubscriptionManager(StanzaChannel* stanzaChannel);
+			~SubscriptionManager();
+
+			void cancelSubscription(const JID& jid);
+			void confirmSubscription(const JID& jid);
+			void requestSubscription(const JID& jid);
+
+			/**
+			 * This signal is emitted when a presence subscription request is received.
+			 */
+			boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
+
+			boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRevoked;
+
+		private:
+			void handleIncomingPresence(Presence::ref presence);
+
+		private:
+			StanzaChannel* stanzaChannel;
+	};
+}
diff --git a/Swiften/Presence/UnitTest/DirectedPresenceSenderTest.cpp b/Swiften/Presence/UnitTest/DirectedPresenceSenderTest.cpp
new file mode 100644
index 0000000..a60c429
--- /dev/null
+++ b/Swiften/Presence/UnitTest/DirectedPresenceSenderTest.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/Presence/DirectedPresenceSender.h"
+#include "Swiften/Presence/StanzaChannelPresenceSender.h"
+
+using namespace Swift;
+
+class DirectedPresenceSenderTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(DirectedPresenceSenderTest);
+		CPPUNIT_TEST(testSendPresence);
+		CPPUNIT_TEST(testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers);
+		CPPUNIT_TEST(testSendPresence_DirectedPresenceWithDirectedPresenceReceivers);
+		CPPUNIT_TEST(testAddDirectedPresenceReceiver);
+		CPPUNIT_TEST(testAddDirectedPresenceReceiver_AfterSendingDirectedPresence);
+		CPPUNIT_TEST(testRemoveDirectedPresenceReceiver);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		void setUp() {
+			channel = new DummyStanzaChannel();
+			testPresence = boost::shared_ptr<Presence>(new Presence());
+			testPresence->setStatus("Foo");
+			secondTestPresence = boost::shared_ptr<Presence>(new Presence());
+			secondTestPresence->setStatus("Bar");
+			stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel);
+		}
+
+		void tearDown() {
+			delete stanzaChannelPresenceSender;
+			delete channel;
+		}
+
+		void testSendPresence() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->sendPresence(testPresence);
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+			CPPUNIT_ASSERT(testPresence == presence);
+		}
+
+		void testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+			testling->sendPresence(testPresence);
+
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+			CPPUNIT_ASSERT(testPresence == presence);
+			presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[1]);
+			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+		}
+
+		void testSendPresence_DirectedPresenceWithDirectedPresenceReceivers() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+			channel->sentStanzas.clear();
+
+			testPresence->setTo(JID("foo@bar.com"));
+			testling->sendPresence(testPresence);
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+			CPPUNIT_ASSERT(testPresence == presence);
+		}
+
+		void testAddDirectedPresenceReceiver() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->sendPresence(testPresence);
+			channel->sentStanzas.clear();
+
+			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+		}
+
+		void testAddDirectedPresenceReceiver_AfterSendingDirectedPresence() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->sendPresence(testPresence);
+			secondTestPresence->setTo(JID("foo@bar.com"));
+			testling->sendPresence(secondTestPresence);
+			channel->sentStanzas.clear();
+
+			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+
+			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
+			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
+			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
+			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
+		}
+
+		void testRemoveDirectedPresenceReceiver() {
+			std::auto_ptr<DirectedPresenceSender> testling(createPresenceSender());
+			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+			channel->sentStanzas.clear();
+
+			testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
+			testling->sendPresence(testPresence);
+
+			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
+			CPPUNIT_ASSERT_EQUAL(boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0])->getType(), Presence::Unavailable);
+			CPPUNIT_ASSERT(channel->sentStanzas[1] == testPresence);
+		}
+
+	private:
+		DirectedPresenceSender* createPresenceSender() {
+			return new DirectedPresenceSender(stanzaChannelPresenceSender);
+		}
+	
+	private:
+		DummyStanzaChannel* channel;
+		StanzaChannelPresenceSender* stanzaChannelPresenceSender;
+		boost::shared_ptr<Presence> testPresence;
+		boost::shared_ptr<Presence> secondTestPresence;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DirectedPresenceSenderTest);
diff --git a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
index d3b4b20..aa450a2 100644
--- a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
+++ b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
@@ -11,6 +11,7 @@
 
 #include "Swiften/Presence/PresenceOracle.h"
 #include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/Presence/SubscriptionManager.h"
 
 using namespace Swift;
 
@@ -31,13 +32,15 @@ class PresenceOracleTest : public CppUnit::TestFixture {
 			stanzaChannel_ = new DummyStanzaChannel();
 			oracle_ = new PresenceOracle(stanzaChannel_);
 			oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1));
-			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2));
+			subscriptionManager_ = new SubscriptionManager(stanzaChannel_);
+			subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2));
 			user1 = JID("user1@foo.com/Foo");
 			user1alt = JID("user1@foo.com/Bar");
 			user2 = JID("user2@bar.com/Bar");
 		}
 
 		void tearDown() {
+			delete subscriptionManager_;
 			delete oracle_;
 			delete stanzaChannel_;
 		}
@@ -181,6 +184,7 @@ class PresenceOracleTest : public CppUnit::TestFixture {
 			String reason;
 		};
 		PresenceOracle* oracle_;
+		SubscriptionManager* subscriptionManager_;
 		DummyStanzaChannel* stanzaChannel_;
 		std::vector<Presence::ref> changes;
 		std::vector<SubscriptionRequestInfo> subscriptionRequests;
diff --git a/Swiften/Presence/UnitTest/PresenceSenderTest.cpp b/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
deleted file mode 100644
index 737f317..0000000
--- a/Swiften/Presence/UnitTest/PresenceSenderTest.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
-
-#include "Swiften/Client/DummyStanzaChannel.h"
-#include "Swiften/Presence/PresenceSender.h"
-
-using namespace Swift;
-
-class PresenceSenderTest : public CppUnit::TestFixture {
-		CPPUNIT_TEST_SUITE(PresenceSenderTest);
-		CPPUNIT_TEST(testSendPresence);
-		CPPUNIT_TEST(testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers);
-		CPPUNIT_TEST(testSendPresence_DirectedPresenceWithDirectedPresenceReceivers);
-		CPPUNIT_TEST(testAddDirectedPresenceReceiver);
-		CPPUNIT_TEST(testAddDirectedPresenceReceiver_AfterSendingDirectedPresence);
-		CPPUNIT_TEST(testRemoveDirectedPresenceReceiver);
-		CPPUNIT_TEST_SUITE_END();
-
-	public:
-		void setUp() {
-			channel = new DummyStanzaChannel();
-			testPresence = boost::shared_ptr<Presence>(new Presence());
-			testPresence->setStatus("Foo");
-			secondTestPresence = boost::shared_ptr<Presence>(new Presence());
-			secondTestPresence->setStatus("Bar");
-		}
-
-		void tearDown() {
-			delete channel;
-		}
-
-		void testSendPresence() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->sendPresence(testPresence);
-
-			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
-			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
-			CPPUNIT_ASSERT(testPresence == presence);
-		}
-
-		void testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-
-			testling->sendPresence(testPresence);
-
-			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
-			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
-			CPPUNIT_ASSERT(testPresence == presence);
-			presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[1]);
-			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
-			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
-		}
-
-		void testSendPresence_DirectedPresenceWithDirectedPresenceReceivers() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-			channel->sentStanzas.clear();
-
-			testPresence->setTo(JID("foo@bar.com"));
-			testling->sendPresence(testPresence);
-
-			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
-			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
-			CPPUNIT_ASSERT(testPresence == presence);
-		}
-
-		void testAddDirectedPresenceReceiver() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->sendPresence(testPresence);
-			channel->sentStanzas.clear();
-
-			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-
-			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
-			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
-			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
-			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
-		}
-
-		void testAddDirectedPresenceReceiver_AfterSendingDirectedPresence() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->sendPresence(testPresence);
-			secondTestPresence->setTo(JID("foo@bar.com"));
-			testling->sendPresence(secondTestPresence);
-			channel->sentStanzas.clear();
-
-			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-
-			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->sentStanzas.size()));
-			boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0]);
-			CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus());
-			CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo());
-		}
-
-		void testRemoveDirectedPresenceReceiver() {
-			std::auto_ptr<PresenceSender> testling(createPresenceSender());
-			testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-			channel->sentStanzas.clear();
-
-			testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"));
-			testling->sendPresence(testPresence);
-
-			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
-			CPPUNIT_ASSERT_EQUAL(boost::dynamic_pointer_cast<Presence>(channel->sentStanzas[0])->getType(), Presence::Unavailable);
-			CPPUNIT_ASSERT(channel->sentStanzas[1] == testPresence);
-		}
-
-	private:
-		PresenceSender* createPresenceSender() {
-			return new PresenceSender(channel);
-		}
-	
-	private:
-		DummyStanzaChannel* channel;
-		boost::shared_ptr<Presence> testPresence;
-		boost::shared_ptr<Presence> secondTestPresence;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION(PresenceSenderTest);
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 886051f..5790672 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -47,11 +47,15 @@ if env["SCONS_STAGE"] == "build" :
 			"Elements/Stanza.cpp",
 			"Elements/VCard.cpp",
 			"MUC/MUC.cpp",
+			"MUC/MUCManager.cpp",
 			"MUC/MUCOccupant.cpp",
 			"MUC/MUCRegistry.cpp",
 			"MUC/MUCBookmarkManager.cpp",
 			"Presence/PresenceOracle.cpp",
 			"Presence/PresenceSender.cpp",
+			"Presence/DirectedPresenceSender.cpp",
+			"Presence/StanzaChannelPresenceSender.cpp",
+			"Presence/SubscriptionManager.cpp",
 			"Queries/IQChannel.cpp",
 			"Queries/IQHandler.cpp",
 			"Queries/IQRouter.cpp",
@@ -218,7 +222,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Parser/UnitTest/XMLParserTest.cpp"),
 			File("Parser/UnitTest/XMPPParserTest.cpp"),
 			File("Presence/UnitTest/PresenceOracleTest.cpp"),
-			File("Presence/UnitTest/PresenceSenderTest.cpp"),
+			File("Presence/UnitTest/DirectedPresenceSenderTest.cpp"),
 			File("Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp"),
 			File("Disco/UnitTest/DiscoInfoResponderTest.cpp"),
 			File("Queries/UnitTest/IQRouterTest.cpp"),
-- 
cgit v0.10.2-6-g49f6