diff options
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 19 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 6 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 4 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.h | 4 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 14 | ||||
| -rw-r--r-- | Swift/Controllers/MainController.cpp | 4 | ||||
| -rw-r--r-- | Swiften/Chat/ChatStateNotifier.cpp | 21 | ||||
| -rw-r--r-- | Swiften/Chat/ChatStateNotifier.h | 2 | ||||
| -rw-r--r-- | Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp | 6 | ||||
| -rw-r--r-- | Swiften/Elements/ChatState.h | 2 | 
10 files changed, 62 insertions, 20 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 7fbf677..90ca7f8 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -7,12 +7,14 @@  #include "Swift/Controllers/Chat/ChatController.h"  #include <boost/bind.hpp> +#include <stdio.h>  #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"  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"  #include "Swift/Controllers/NickResolver.h" @@ -23,11 +25,14 @@ namespace Swift {  /**   * The controller does not gain ownership of the stanzaChannel, nor the factory.   */ -ChatController::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) +ChatController::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)  	: 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);  	chatStateTracker_ = new ChatStateTracker();  	nickResolver_ = nickResolver; @@ -62,9 +67,19 @@ ChatController::~ChatController() {  	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);  	ChatControllerBase::setToJID(jid); +	handleCapsChanged(jid);  }  bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) { @@ -77,7 +92,7 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  	JID from = message->getFrom();  	if (!from.equals(toJID_, JID::WithResource)) {  		if (toJID_.equals(from, JID::WithoutResource)  && toJID_.isBare()){ -			toJID_ = from; +			setToJID(from);  		}  	}  	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>()); diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 6cb1443..1e530ac 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -15,9 +15,10 @@ namespace Swift {  	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); +			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);  			virtual ~ChatController();  			virtual void setToJID(const JID& jid);  			virtual void setEnabled(bool enabled); @@ -33,13 +34,14 @@ 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_; -			JID contact_;  			ChatStateNotifier* chatStateNotifier_;  			ChatStateMessageSender* chatStateMessageSender_;  			ChatStateTracker* chatStateTracker_; +			EntityCapsManager* entityCapsManager_;  			bool isInMUC_;  			bool lastWasPresence_;  			String lastStatusChangeString_; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index bd4fcb8..8c93120 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -26,7 +26,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, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry) { +ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsManager_(entityCapsManager) {  	timerFactory_ = timerFactory;  	eventController_ = eventController;  	stanzaChannel_ = stanzaChannel; @@ -183,7 +183,7 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact)  }  ChatController* ChatsManager::createNewChatController(const JID& contact) { -	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_); +	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsManager_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_);  	return controller; diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 17a5d94..1e31458 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -34,10 +34,11 @@ namespace Swift {  	class ChatListWindow;  	class ChatListWindowFactory;  	class TimerFactory; +	class EntityCapsManager;  	class ChatsManager {  		public: -			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry); +			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setEnabled(bool enabled); @@ -76,5 +77,6 @@ namespace Swift {  			bool useDelayForLatency_;  			TimerFactory* timerFactory_;  			MUCRegistry* mucRegistry_; +			EntityCapsManager* entityCapsManager_;  	};  } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index a17575c..e770e88 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -14,6 +14,8 @@  #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"  #include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"  #include "Swiften/Client/Client.h" +#include "Swiften/Disco/EntityCapsManager.h" +#include "Swiften/Disco/CapsProvider.h"  #include "Swift/Controllers/Chat/ChatController.h"  #include "Swift/Controllers/XMPPEvents/EventController.h"  #include "Swift/Controllers/Chat/MUCController.h" @@ -35,6 +37,10 @@  using namespace Swift; +class DummyCapsProvider : public CapsProvider { +		DiscoInfo::ref getCaps(const String&) const {return DiscoInfo::ref(new DiscoInfo());} +}; +  class ChatsManagerTest : public CppUnit::TestFixture  {  	CPPUNIT_TEST_SUITE(ChatsManagerTest); @@ -57,6 +63,7 @@ public:  		stanzaChannel_ = new DummyStanzaChannel();  		iqChannel_ = new DummyIQChannel();  		iqRouter_ = new IQRouter(iqChannel_); +		capsProvider_ = new DummyCapsProvider();  		eventController_ = new EventController();  		chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();  		xmppRoster_ = new XMPPRoster(); @@ -66,9 +73,10 @@ public:  		serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo());  		presenceSender_ = new PresenceSender(stanzaChannel_);  		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_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_); +		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_);  		avatarManager_ = new NullAvatarManager();  		manager_->setAvatarManager(avatarManager_); @@ -89,6 +97,8 @@ public:  		delete iqRouter_;  		delete uiEventStream_;  		delete xmppRoster_; +		delete entityCapsManager_; +		delete capsProvider_;  	}  	void testFirstOpenWindowIncoming() { @@ -319,6 +329,8 @@ private:  	UIEventStream* uiEventStream_;  	ChatListWindowFactory* chatListWindowFactory_;  	MUCRegistry* mucRegistry_; +	EntityCapsManager* entityCapsManager_; +	CapsProvider* capsProvider_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 3cfa2a7..fdfab98 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -44,6 +44,7 @@  #include "Swiften/Base/String.h"  #include "Swiften/Client/Client.h"  #include "Swiften/Presence/PresenceSender.h" +#include "Swiften/Elements/ChatState.h"  #include "Swiften/Elements/Presence.h"  #include "Swiften/Elements/VCardUpdate.h"  #include "Swiften/Queries/Responders/SoftwareVersionResponder.h" @@ -257,7 +258,7 @@ void MainController::handleConnected() {  		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));  		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this)); -		chatsManager_ = new ChatsManager(jid_, client_, client_->getIQRouter(), eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, mucRegistry_); +		chatsManager_ = new ChatsManager(jid_, client_, client_->getIQRouter(), eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, mucRegistry_, entityCapsManager_);  		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));  		chatsManager_->setAvatarManager(avatarManager_); @@ -271,6 +272,7 @@ void MainController::handleConnected() {  		DiscoInfo discoInfo;  		discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));  		discoInfo.addFeature("urn:xmpp:sec-label:0"); +		discoInfo.addFeature(ChatState::getFeatureNamespace());  		capsInfo_ = boost::shared_ptr<CapsInfo>(new CapsInfo(CapsInfoGenerator(CLIENT_NODE).generateCapsInfo(discoInfo)));  		discoResponder_ = new DiscoInfoResponder(client_->getIQRouter()); diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp index 5024958..667c244 100644 --- a/Swiften/Chat/ChatStateNotifier.cpp +++ b/Swiften/Chat/ChatStateNotifier.cpp @@ -9,18 +9,22 @@  namespace Swift {  ChatStateNotifier::ChatStateNotifier() { -	contactHas85Caps_ = false; -	isInConversation_ = false; -	contactHasSentActive_ = false; -	userIsTyping_ = false; +	contactJIDHasChanged();  }  void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {  	contactHas85Caps_ = hasCaps;  } +void ChatStateNotifier::contactJIDHasChanged() { +	contactHasSentActive_ = false; +	contactHas85Caps_ = false; +	userIsTyping_ = false; +} +  void ChatStateNotifier::setUserIsTyping() { -	if (contactShouldReceiveStates() && !userIsTyping_) { +	bool should = contactShouldReceiveStates(); +	if (should && !userIsTyping_) {  		userIsTyping_ = true;  		onChatStateChanged(ChatState::Composing);  	} @@ -38,14 +42,15 @@ void ChatStateNotifier::userCancelledNewMessage() {  }  void ChatStateNotifier::receivedMessageFromContact(bool hasActiveElement) { -	isInConversation_ = true;  	contactHasSentActive_ = 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.*/ -	return contactHasSentActive_ || (contactHas85Caps_ && !isInConversation_);; +	   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 contactHasSentActive_ || contactHas85Caps_ ;  }  } diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h index 71febfa..8dcd5cd 100644 --- a/Swiften/Chat/ChatStateNotifier.h +++ b/Swiften/Chat/ChatStateNotifier.h @@ -21,11 +21,11 @@ namespace Swift {  			void userCancelledNewMessage();  			void receivedMessageFromContact(bool hasActiveElement);  			bool contactShouldReceiveStates(); +			void contactJIDHasChanged();  			boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;  		private:  			bool contactHas85Caps_; -			bool isInConversation_;  			bool contactHasSentActive_;  			bool userIsTyping_;  	}; diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp index 712ba10..2fa4c26 100644 --- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp +++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp @@ -114,7 +114,11 @@ public:  	void testContactShouldReceiveStates_ActiveOverrideOff() {  		notifier_->setContactHas85Caps(true);  		notifier_->receivedMessageFromContact(false); -		CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates()); +		/* 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());  	} diff --git a/Swiften/Elements/ChatState.h b/Swiften/Elements/ChatState.h index 8dcf77c..8a96bf1 100644 --- a/Swiften/Elements/ChatState.h +++ b/Swiften/Elements/ChatState.h @@ -19,7 +19,7 @@ namespace Swift {  			ChatStateType getChatState() { return state_; }  			void setChatState(ChatStateType state) {state_ = state;} - +			static String getFeatureNamespace() {return "http://jabber.org/protocol/chatstates";}  		private:  			ChatStateType state_;  	};  | 
 Swift