From bbd3bbf5747c28ec5925a89ea43fd4767d135f6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Wed, 27 Oct 2010 23:22:16 +0200
Subject: Make ChatStateNotifier use StanzaChannel directly.


diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index f465ef1..eb68abd 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -11,7 +11,6 @@
 
 #include "Swiften/Avatars/AvatarManager.h"
 #include "Swiften/Chat/ChatStateNotifier.h"
-#include "Swiften/Chat/ChatStateMessageSender.h"
 #include "Swiften/Chat/ChatStateTracker.h"
 #include "Swiften/Client/StanzaChannel.h"
 #include "Swiften/Disco/EntityCapsManager.h"
@@ -29,11 +28,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
 	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory) {
 	isInMUC_ = isInMUC;
 	lastWasPresence_ = false;
-	entityCapsManager_ = entityCapsManager;
-	chatStateNotifier_ = new ChatStateNotifier();
-	entityCapsManager_->onCapsChanged.connect(boost::bind(&ChatController::handleCapsChanged, this, _1));
-	handleCapsChanged(toJID_);
-	chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact);
+	chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsManager);
 	chatStateTracker_ = new ChatStateTracker();
 	nickResolver_ = nickResolver;
 	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1));
@@ -64,23 +59,12 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
 
 ChatController::~ChatController() {
 	delete chatStateNotifier_;
-	delete chatStateMessageSender_;
 	delete chatStateTracker_;
 }
 
-void ChatController::handleCapsChanged(const JID& jid) {
-	if (jid == toJID_) {
-		DiscoInfo::ref caps = entityCapsManager_->getCaps(toJID_);
-		bool hasCSN = caps && caps->hasFeature(ChatState::getFeatureNamespace());
-		chatStateNotifier_->setContactHas85Caps(hasCSN);
-	}
-}
-
 void ChatController::setToJID(const JID& jid) {
-	chatStateNotifier_->contactJIDHasChanged();
-	chatStateMessageSender_->setContact(jid);
+	chatStateNotifier_->setContact(jid);
 	ChatControllerBase::setToJID(jid);
-	handleCapsChanged(jid);
 	Presence::ref presence;
 	if (isInMUC_) {
 		presence = presenceOracle_->getLastPresence(jid);
@@ -112,9 +96,7 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me
 }
 
 void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {
-	if (chatStateNotifier_->contactShouldReceiveStates()) {
-		message->addPayload(boost::shared_ptr<Payload>(new ChatState(ChatState::Active)));
-	}
+	chatStateNotifier_->addChatStateRequest(message);
 }
 
 void ChatController::postSendMessage(const String& body, boost::shared_ptr<Stanza> sentStanza) {
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 26686ff..601ae1b 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -4,18 +4,17 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#ifndef SWIFTEN_ChatController_H
-#define SWIFTEN_ChatController_H
+#pragma once
 
 #include "Swift/Controllers/Chat/ChatControllerBase.h"
 
 namespace Swift {
 	class AvatarManager;
 	class ChatStateNotifier;
-	class ChatStateMessageSender;
 	class ChatStateTracker;
 	class NickResolver;
 	class EntityCapsManager;
+
 	class ChatController : public ChatControllerBase {
 		public:
 			ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsManager* entityCapsManager);
@@ -34,19 +33,15 @@ namespace Swift {
 			virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const;
 			void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);
 			void dayTicked() {lastWasPresence_ = false;}
-			void handleCapsChanged(const JID& jid);
 
 		private:
 			NickResolver* nickResolver_;
 			ChatStateNotifier* chatStateNotifier_;
-			ChatStateMessageSender* chatStateMessageSender_;
 			ChatStateTracker* chatStateTracker_;
-			EntityCapsManager* entityCapsManager_;
 			bool isInMUC_;
 			bool lastWasPresence_;
 			String lastStatusChangeString_;
 			std::map<boost::shared_ptr<Stanza>, String> unackedStanzas_;
 	};
 }
-#endif
 
diff --git a/Swiften/Chat/ChatStateMessageSender.cpp b/Swiften/Chat/ChatStateMessageSender.cpp
deleted file mode 100644
index e150d29..0000000
--- a/Swiften/Chat/ChatStateMessageSender.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2010 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#include "Swiften/Chat/ChatStateMessageSender.h"
-
-#include <boost/bind.hpp>
-
-#include "Swiften/Client/StanzaChannel.h"
-
-namespace Swift {
-
-ChatStateMessageSender::ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact) : contact_(contact) {
-	notifier_ = notifier;
-	stanzaChannel_ = stanzaChannel;
-	notifier_->onChatStateChanged.connect(boost::bind(&ChatStateMessageSender::handleChatStateChanged, this, _1));
-}
-
-void ChatStateMessageSender::handleChatStateChanged(ChatState::ChatStateType state) {
-	boost::shared_ptr<Message> message(new Message());
-	message->setTo(contact_);
-	message->addPayload(boost::shared_ptr<Payload>(new ChatState(state)));
-	stanzaChannel_->sendMessage(message);
-}
-
-}
diff --git a/Swiften/Chat/ChatStateMessageSender.h b/Swiften/Chat/ChatStateMessageSender.h
deleted file mode 100644
index d6ce40d..0000000
--- a/Swiften/Chat/ChatStateMessageSender.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2010 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#pragma once
-
-#include "Swiften/Chat/ChatStateNotifier.h"
-#include "Swiften/Elements/ChatState.h"
-#include "Swiften/JID/JID.h"
-
-namespace Swift {
-	class StanzaChannel;
-	class ChatStateMessageSender {
-		public:
-			ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact);
-			void setContact(const JID& contact) {contact_ = contact;};
-
-		private:
-			void handleChatStateChanged(ChatState::ChatStateType state);
-			ChatStateNotifier* notifier_;
-			StanzaChannel* stanzaChannel_;
-			JID contact_;
-	};
-}
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
index 1aee9cf..cb7fa9a 100644
--- a/Swiften/Chat/ChatStateNotifier.cpp
+++ b/Swiften/Chat/ChatStateNotifier.cpp
@@ -6,32 +6,41 @@
 
 #include "Swiften/Chat/ChatStateNotifier.h"
 
-namespace Swift {
+#include <boost/bind.hpp>
 
-ChatStateNotifier::ChatStateNotifier() {
-	contactJIDHasChanged();
-}
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Disco/EntityCapsProvider.h"
 
-void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {
-	contactHas85Caps_ = hasCaps;
+namespace Swift {
+
+ChatStateNotifier::ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager) : stanzaChannel_(stanzaChannel), entityCapsManager_(entityCapsManager), contact_(contact) {
+	setContact(contact);
+	entityCapsManager_->onCapsChanged.connect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1));
 }
 
-void ChatStateNotifier::setContactIsOnline(bool online) {
-	contactIsOnline_ = online;
+ChatStateNotifier::~ChatStateNotifier() {
+	entityCapsManager_->onCapsChanged.disconnect(boost::bind(&ChatStateNotifier::handleCapsChanged, this, _1));
 }
 
-void ChatStateNotifier::contactJIDHasChanged() {
+void ChatStateNotifier::setContact(const JID& contact) {
 	contactHasSentActive_ = false;
-	contactHas85Caps_ = false;
 	userIsTyping_ = false;
 	contactIsOnline_ = false;
+	contact_ = contact;
+	handleCapsChanged(contact_);
+}
+
+void ChatStateNotifier::setContactIsOnline(bool online) {
+	contactIsOnline_ = online;
 }
 
 void ChatStateNotifier::setUserIsTyping() {
 	bool should = contactShouldReceiveStates();
 	if (should && !userIsTyping_) {
 		userIsTyping_ = true;
-		onChatStateChanged(ChatState::Composing);
+		changeState(ChatState::Composing);
 	}
 }
 
@@ -42,7 +51,7 @@ void ChatStateNotifier::userSentMessage() {
 void ChatStateNotifier::userCancelledNewMessage() {
 	if (userIsTyping_) {
 		userIsTyping_ = false;
-		onChatStateChanged(ChatState::Active);
+		changeState(ChatState::Active);
 	}
 }
 
@@ -52,10 +61,32 @@ void ChatStateNotifier::receivedMessageFromContact(bool hasActiveElement) {
 
 bool ChatStateNotifier::contactShouldReceiveStates() {
 	/* So, yes, the XEP says to look at caps, but it also says that once you've
-	   heard from the contact, the active state overrides this.
-	   *HOWEVER* it says that the MUST NOT send csn if you haven't received
-	   active is OPTIONAL behaviour for if you haven't got caps.*/
+		 heard from the contact, the active state overrides this.
+		 *HOWEVER* it says that the MUST NOT send csn if you haven't received
+		 active is OPTIONAL behaviour for if you haven't got caps.*/
 	return contactIsOnline_ && (contactHasSentActive_ || contactHas85Caps_);
 }
 
+void ChatStateNotifier::changeState(ChatState::ChatStateType state) {
+	boost::shared_ptr<Message> message(new Message());
+	message->setTo(contact_);
+	message->addPayload(boost::shared_ptr<Payload>(new ChatState(state)));
+	stanzaChannel_->sendMessage(message);
+}
+
+void ChatStateNotifier::addChatStateRequest(Message::ref message) {
+	if (contactShouldReceiveStates()) {
+		message->addPayload(boost::shared_ptr<Payload>(new ChatState(ChatState::Active)));
+	}
+}
+
+
+void ChatStateNotifier::handleCapsChanged(const JID& jid) {
+	if (jid == contact_) {
+		DiscoInfo::ref caps = entityCapsManager_->getCaps(contact_);
+		bool hasCSN = caps && caps->hasFeature(ChatState::getFeatureNamespace());
+		contactHas85Caps_ = hasCSN;
+	}
+}
+
 }
diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h
index 3889b9c..1185ed0 100644
--- a/Swiften/Chat/ChatStateNotifier.h
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -9,23 +9,39 @@
 #include "Swiften/Base/boost_bsignals.h"
 #include <boost/shared_ptr.hpp>
 
+#include "Swiften/Elements/Message.h"
 #include "Swiften/Elements/ChatState.h"
+#include "Swiften/JID/JID.h"
 
 namespace Swift {
+	class StanzaChannel;
+	class EntityCapsProvider;
+
 	class ChatStateNotifier {
 		public:
-			ChatStateNotifier();
-			void setContactHas85Caps(bool hasCaps);
+			ChatStateNotifier(StanzaChannel* stanzaChannel, const JID& contact, EntityCapsProvider* entityCapsManager);
+			~ChatStateNotifier();
+
+			void setContact(const JID& contact);
+
+			void addChatStateRequest(Message::ref message);
+
 			void setUserIsTyping();
-			void setContactIsOnline(bool online);
 			void userSentMessage();
 			void userCancelledNewMessage();
+
 			void receivedMessageFromContact(bool hasActiveElement);
+			void setContactIsOnline(bool online);
+
+		private:
 			bool contactShouldReceiveStates();
-			void contactJIDHasChanged();
+			void changeState(ChatState::ChatStateType type);
+			void handleCapsChanged(const JID& contact);
 
-			boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;
 		private:
+			StanzaChannel* stanzaChannel_;
+			EntityCapsProvider* entityCapsManager_;
+			JID contact_;
 			bool contactHas85Caps_;
 			bool contactHasSentActive_;
 			bool userIsTyping_;
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
index 045a245..8380330 100644
--- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
+++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
@@ -9,41 +9,11 @@
 #include <boost/bind.hpp>
 
 #include "Swiften/Chat/ChatStateNotifier.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/Disco/DummyEntityCapsProvider.h"
 
 using namespace Swift;
 
-
-class ChatStateMonitor {
-public:
-	ChatStateMonitor(ChatStateNotifier* notifier) {
-		notifier_ = notifier;
-		composingCallCount = 0;
-		activeCallCount = 0;
-		notifier->onChatStateChanged.connect(boost::bind(&ChatStateMonitor::handleChatStateChanged, this, _1));
-	};
-
-	int composingCallCount;
-	int activeCallCount;
-	ChatState::ChatStateType currentState;
-
-private:
-	void handleChatStateChanged(ChatState::ChatStateType newState) {
-		switch (newState) {
-			case ChatState::Composing:
-				composingCallCount++;
-				break;
-			case ChatState::Active:
-				activeCallCount++;
-				break;
-			default:
-				break;
-			}
-		currentState = newState;
-	};
-
-	ChatStateNotifier* notifier_;
-};
-
 class ChatStateNotifierTest : public CppUnit::TestFixture {
 	CPPUNIT_TEST_SUITE(ChatStateNotifierTest);
 	CPPUNIT_TEST(testStartTypingReply_CapsNotIncluded);
@@ -57,101 +27,140 @@ class ChatStateNotifierTest : public CppUnit::TestFixture {
 	CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOff);
 	CPPUNIT_TEST_SUITE_END();
 	
-private:
-	ChatStateNotifier* notifier_;
-	ChatStateMonitor* monitor_;
-	
 public:
 	void setUp() {
-		notifier_ = new ChatStateNotifier();
+		stanzaChannel = new DummyStanzaChannel();
+		stanzaChannel->setAvailable(true);
+		entityCapsProvider = new DummyEntityCapsProvider();
+		notifier_ = new ChatStateNotifier(stanzaChannel, JID("foo@bar.com/baz"), entityCapsProvider);
 		notifier_->setContactIsOnline(true);
-		monitor_ = new ChatStateMonitor(notifier_);
 	}
 	
 	void tearDown() {
 		delete notifier_;
-		delete monitor_;
+		delete stanzaChannel;
 	}
 	
 	void testStartTypingReply_CapsNotIncluded() {
-		notifier_->setContactHas85Caps(false);
 		notifier_->setUserIsTyping();
-		CPPUNIT_ASSERT_EQUAL(0, monitor_->composingCallCount);
+		CPPUNIT_ASSERT_EQUAL(0, getComposingCount());
 	}
 
 	void testSendTwoMessages() {
-		notifier_->setContactHas85Caps(true);
+		setContactHas85Caps();
 		notifier_->setUserIsTyping();
 		notifier_->userSentMessage();
 		notifier_->setUserIsTyping();
 		notifier_->userSentMessage();
-		CPPUNIT_ASSERT_EQUAL(2, monitor_->composingCallCount);
+		CPPUNIT_ASSERT_EQUAL(2, getComposingCount());
 	}
 
 	void testCancelledNewMessage() {
-		notifier_->setContactHas85Caps(true);
+		setContactHas85Caps();
 		notifier_->setUserIsTyping();
 		notifier_->userCancelledNewMessage();
-		CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
-		CPPUNIT_ASSERT_EQUAL(1, monitor_->activeCallCount);
-		CPPUNIT_ASSERT_EQUAL(ChatState::Active, monitor_->currentState);
+		CPPUNIT_ASSERT_EQUAL(1, getComposingCount());
+		CPPUNIT_ASSERT_EQUAL(1, getActiveCount());
+		CPPUNIT_ASSERT_EQUAL(ChatState::Active, stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getPayload<ChatState>()->getChatState());
 	}
 
 
 	void testContactShouldReceiveStates_CapsOnly() {
-		notifier_->setContactHas85Caps(true);
-		CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+		setContactHas85Caps();
+		boost::shared_ptr<Message> message(new Message());
+		notifier_->addChatStateRequest(message);
+		CPPUNIT_ASSERT(message->getPayload<ChatState>());
+		CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState());
 	}
 
 	void testContactShouldReceiveStates_CapsNorActive() {
-		CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+		boost::shared_ptr<Message> message(new Message());
+		notifier_->addChatStateRequest(message);
+		CPPUNIT_ASSERT(!message->getPayload<ChatState>());
 	}
 
 	void testContactShouldReceiveStates_ActiveOverrideOn() {
-		notifier_->setContactHas85Caps(false);
 		notifier_->receivedMessageFromContact(true);
-		CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+		boost::shared_ptr<Message> message(new Message());
+		notifier_->addChatStateRequest(message);
+		CPPUNIT_ASSERT(message->getPayload<ChatState>());
+		CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState());
 	}
 
 	void testContactShouldReceiveStates_ActiveOverrideOff() {
-		notifier_->setContactHas85Caps(true);
+		setContactHas85Caps();
 		notifier_->receivedMessageFromContact(false);
 		/* I originally read the MUST NOT send after receiving without Active and
 		 * thought this should check for false, but I later found it was OPTIONAL
 		 * (MAY) behaviour only for if you didn't receive caps.
 		 */
-		CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+		boost::shared_ptr<Message> message(new Message());
+		notifier_->addChatStateRequest(message);
+		CPPUNIT_ASSERT(message->getPayload<ChatState>());
+		CPPUNIT_ASSERT_EQUAL(ChatState::Active, message->getPayload<ChatState>()->getChatState());
 	}
 
 
 	void testStartTypingReply_CapsIncluded() {
-		notifier_->setContactHas85Caps(true);
+		setContactHas85Caps();
 		notifier_->setUserIsTyping();
-		CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+		CPPUNIT_ASSERT_EQUAL(1, getComposingCount());
 	}
 
 	void testContinueTypingReply_CapsIncluded() {
-		notifier_->setContactHas85Caps(true);
+		setContactHas85Caps();
 		notifier_->setUserIsTyping();
 		notifier_->setUserIsTyping();
 		notifier_->setUserIsTyping();
-		CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+		CPPUNIT_ASSERT_EQUAL(1, getComposingCount());
 		notifier_->userSentMessage();
 		notifier_->setUserIsTyping();
-		CPPUNIT_ASSERT_EQUAL(2, monitor_->composingCallCount);
+		CPPUNIT_ASSERT_EQUAL(2, getComposingCount());
 
 	}
 
 	void testTypeReplies_WentOffline() {
-			notifier_->setContactHas85Caps(true);
+			setContactHas85Caps();
 			notifier_->setUserIsTyping();
-			CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+			CPPUNIT_ASSERT_EQUAL(1, getComposingCount());
 			notifier_->setContactIsOnline(false);
 			notifier_->userSentMessage();
 			notifier_->setUserIsTyping();
-			CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+			CPPUNIT_ASSERT_EQUAL(1, getComposingCount());
 		}
-	
+
+	private:
+		void setContactHas85Caps() {
+			DiscoInfo::ref caps(new DiscoInfo());
+			caps->addFeature(ChatState::getFeatureNamespace());
+			entityCapsProvider->caps[JID("foo@bar.com/baz")] = caps;
+			entityCapsProvider->onCapsChanged(JID("foo@bar.com/baz"));
+		}
+
+		int getComposingCount() const {
+			int result = 0;
+			foreach(boost::shared_ptr<Stanza> stanza, stanzaChannel->sentStanzas) {
+				if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Composing) {
+					result++;
+				}
+			}
+			return result;
+		}
+
+		int getActiveCount() const {
+			int result = 0;
+			foreach(boost::shared_ptr<Stanza> stanza, stanzaChannel->sentStanzas) {
+				if (stanza->getPayload<ChatState>() && stanza->getPayload<ChatState>()->getChatState() == ChatState::Active) {
+					result++;
+				}
+			}
+			return result;
+		}
+
+	private:
+		DummyStanzaChannel* stanzaChannel;
+		DummyEntityCapsProvider* entityCapsProvider;
+		ChatStateNotifier* notifier_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ChatStateNotifierTest);
diff --git a/Swiften/Disco/DummyEntityCapsProvider.h b/Swiften/Disco/DummyEntityCapsProvider.h
new file mode 100644
index 0000000..68cef2f
--- /dev/null
+++ b/Swiften/Disco/DummyEntityCapsProvider.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 <map>
+#include <iostream>
+#include "Swiften/Disco/EntityCapsProvider.h"
+
+namespace Swift {
+	class DummyEntityCapsProvider : public EntityCapsProvider {
+		public:
+			DummyEntityCapsProvider() {
+			}
+
+			DiscoInfo::ref getCaps(const JID& jid) const {
+				std::map<JID, DiscoInfo::ref>::const_iterator i = caps.find(jid);
+				if (i != caps.end()) {
+					return i->second;
+				}
+				return DiscoInfo::ref();
+			}
+
+			std::map<JID, DiscoInfo::ref> caps;
+	};
+}
diff --git a/Swiften/Disco/EntityCapsManager.h b/Swiften/Disco/EntityCapsManager.h
index ecfcdea..f507a1d 100644
--- a/Swiften/Disco/EntityCapsManager.h
+++ b/Swiften/Disco/EntityCapsManager.h
@@ -12,6 +12,7 @@
 #include "Swiften/Elements/Presence.h"
 #include "Swiften/Elements/DiscoInfo.h"
 #include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/Disco/EntityCapsProvider.h"
 
 namespace Swift {
 	class StanzaChannel;
@@ -23,7 +24,7 @@ namespace Swift {
 	 * This information is provided in the form of service discovery
 	 * information.
 	 */
-	class EntityCapsManager : public boost::bsignals::trackable { 
+	class EntityCapsManager : public EntityCapsProvider, public boost::bsignals::trackable {
 		public:
 			EntityCapsManager(CapsProvider*, StanzaChannel*);
 
@@ -32,11 +33,6 @@ namespace Swift {
 			 */
 			DiscoInfo::ref getCaps(const JID&) const;
 
-			/**
-			 * Emitted when the capabilities of a JID changes.
-			 */
-			boost::signal<void (const JID&)> onCapsChanged;
-
 		private:
 			void handlePresenceReceived(boost::shared_ptr<Presence>);
 			void handleStanzaChannelAvailableChanged(bool);
diff --git a/Swiften/Disco/EntityCapsProvider.cpp b/Swiften/Disco/EntityCapsProvider.cpp
new file mode 100644
index 0000000..a337328
--- /dev/null
+++ b/Swiften/Disco/EntityCapsProvider.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Disco/EntityCapsProvider.h"
+
+namespace Swift {
+
+EntityCapsProvider::~EntityCapsProvider() {
+}
+
+}
diff --git a/Swiften/Disco/EntityCapsProvider.h b/Swiften/Disco/EntityCapsProvider.h
new file mode 100644
index 0000000..07fa452
--- /dev/null
+++ b/Swiften/Disco/EntityCapsProvider.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 "Swiften/Base/boost_bsignals.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+	/**
+	 * This class provides information about capabilities of entities on the network.
+	 * This information is provided in the form of service discovery
+	 * information.
+	 */
+	class EntityCapsProvider {
+		public:
+			virtual ~EntityCapsProvider();
+
+			/**
+			 * Returns the service discovery information of the given JID.
+			 */
+			virtual DiscoInfo::ref getCaps(const JID&) const = 0;
+
+			/**
+			 * Emitted when the capabilities of a JID changes.
+			 */
+			boost::signal<void (const JID&)> onCapsChanged;
+	};
+}
diff --git a/Swiften/Disco/SConscript b/Swiften/Disco/SConscript
index d07d490..3838d0e 100644
--- a/Swiften/Disco/SConscript
+++ b/Swiften/Disco/SConscript
@@ -4,6 +4,7 @@ objects = swiften_env.StaticObject([
 			"CapsInfoGenerator.cpp",
 			"CapsManager.cpp",
 			"EntityCapsManager.cpp",
+			"EntityCapsProvider.cpp",
 			"CapsStorage.cpp",
 			"CapsFileStorage.cpp",
 			"DiscoInfoResponder.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 90f8d98..886051f 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -28,7 +28,6 @@ if env["SCONS_STAGE"] == "build" :
 	sources = [
 			"Chat/ChatStateTracker.cpp",
 			"Chat/ChatStateNotifier.cpp",
-			"Chat/ChatStateMessageSender.cpp",
 			"Client/ClientSessionStanzaChannel.cpp",
 			"Client/CoreClient.cpp",
 			"Client/Client.cpp",
-- 
cgit v0.10.2-6-g49f6