diff options
Diffstat (limited to 'Swift/Controllers/Chat')
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 66 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 13 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 2 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 57 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.h | 11 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 107 | 
6 files changed, 245 insertions, 11 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index a3d9fb5..9a56300 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -16,7 +16,6 @@  #include <Swiften/Chat/ChatStateNotifier.h>  #include <Swiften/Chat/ChatStateTracker.h>  #include <Swiften/Client/StanzaChannel.h> -#include <Swift/Controllers/UIInterfaces/ChatWindow.h>  #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>  #include <Swiften/Client/NickResolver.h>  #include <Swift/Controllers/XMPPEvents/EventController.h> @@ -26,15 +25,19 @@  #include <Swiften/Base/foreach.h>  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> +#include <Swiften/Elements/DeliveryReceipt.h> +#include <Swiften/Elements/DeliveryReceiptRequest.h> +#include <Swift/Controllers/UIEvents/ToggleRequestDeliveryReceiptsUIEvent.h> +#include <Swiften/Base/Log.h>  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, EntityCapsProvider* entityCapsProvider) -	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream) { +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, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts) +	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts) {  	isInMUC_ = isInMUC;  	lastWasPresence_ = false;  	chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider); @@ -70,7 +73,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ  	chatWindow_->onFileTransferCancel.connect(boost::bind(&ChatController::handleFileTransferCancel, this, _1));  	chatWindow_->onSendFileRequest.connect(boost::bind(&ChatController::handleSendFileRequest, this, _1));  	handleBareJIDCapsChanged(toJID_); - +	eventStream_->onUIEvent.connect(boost::bind(&ChatController::handleUIEvent, this, _1));  }  void ChatController::handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/) { @@ -80,6 +83,7 @@ void ChatController::handleContactNickChanged(const JID& jid, const std::string&  }  ChatController::~ChatController() { +	eventStream_->onUIEvent.disconnect(boost::bind(&ChatController::handleUIEvent, this, _1));  	nickResolver_->onNickChanged.disconnect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2));  	delete chatStateNotifier_;  	delete chatStateTracker_; @@ -93,9 +97,17 @@ void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) {  		} else {  			chatWindow_->setCorrectionEnabled(ChatWindow::No);  		} +		if (disco->hasFeature(DiscoInfo::MessageDeliveryReceiptsFeature)) { +			contactSupportsReceipts_ = ChatWindow::Yes; +		} else { +			contactSupportsReceipts_ = ChatWindow::No; +		}  	} else { +		SWIFT_LOG(debug) << "No disco info :(" << std::endl;  		chatWindow_->setCorrectionEnabled(ChatWindow::Maybe); +		contactSupportsReceipts_ = ChatWindow::Maybe;  	} +	checkForDisplayingDisplayReceiptsAlert();  }  void ChatController::setToJID(const JID& jid) { @@ -129,6 +141,21 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  	}  	chatStateTracker_->handleMessageReceived(message);  	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>()); + +	if (boost::shared_ptr<DeliveryReceipt> receipt = message->getPayload<DeliveryReceipt>()) { +		SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl; +		if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) { +			chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived); +			requestedReceipts_.erase(receipt->getReceivedID()); +		} +	} else if (message->getPayload<DeliveryReceiptRequest>()) { +		if (receivingPresenceFromUs_) { +			boost::shared_ptr<Message> receiptMessage = boost::make_shared<Message>(); +			receiptMessage->setTo(toJID_); +			receiptMessage->addPayload(boost::make_shared<DeliveryReceipt>(message->getID())); +			stanzaChannel_->sendMessage(receiptMessage); +		} +	}  }  void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { @@ -138,6 +165,30 @@ void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> m  void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {  	chatStateNotifier_->addChatStateRequest(message); +	if (userWantsReceipts_ && (contactSupportsReceipts_ != ChatWindow::No) && message) { +		message->addPayload(boost::make_shared<DeliveryReceiptRequest>()); +	} +} + +void ChatController::setContactIsReceivingPresence(bool isReceivingPresence) { +	receivingPresenceFromUs_ = isReceivingPresence; +} + +void ChatController::handleUIEvent(boost::shared_ptr<UIEvent> event) { +	if (boost::shared_ptr<ToggleRequestDeliveryReceiptsUIEvent> toggleAllowReceipts = boost::dynamic_pointer_cast<ToggleRequestDeliveryReceiptsUIEvent>(event)) { +		userWantsReceipts_ = toggleAllowReceipts->getEnabled(); +		checkForDisplayingDisplayReceiptsAlert(); +	} +} + +void ChatController::checkForDisplayingDisplayReceiptsAlert() { +	if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::No)) { +		chatWindow_->setAlert("This chat doesn't support delivery receipts."); +	} else if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::Maybe)) { +		chatWindow_->setAlert("This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent."); +	} else { +		chatWindow_->cancelAlert(); +	}  }  void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) { @@ -148,10 +199,17 @@ void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<  	} else {  		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time());  	} +  	if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) {  		chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending);  		unackedStanzas_[sentStanza] = myLastMessageUIID_;  	} + +	if (sentStanza->getPayload<DeliveryReceiptRequest>()) { +		requestedReceipts_[sentStanza->getID()] = myLastMessageUIID_; +		chatWindow_->setMessageReceiptState(myLastMessageUIID_, ChatWindow::ReceiptRequested); +	} +  	lastWasPresence_ = false;  	chatStateNotifier_->userSentMessage();  } diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 2531adb..9c01923 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -11,6 +11,8 @@  #include <map>  #include <string> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> +  namespace Swift {  	class AvatarManager;  	class ChatStateNotifier; @@ -18,14 +20,16 @@ namespace Swift {  	class NickResolver;  	class EntityCapsProvider;  	class FileTransferController; +	class UIEvent;  	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, EntityCapsProvider* entityCapsProvider); +			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, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts);  			virtual ~ChatController();  			virtual void setToJID(const JID& jid);  			virtual void setOnline(bool online);  			virtual void handleNewFileTransferController(FileTransferController* ftc); +			virtual void setContactIsReceivingPresence(bool /*isReceivingPresence*/);  		private:  			void handlePresenceChange(boost::shared_ptr<Presence> newPresence); @@ -47,6 +51,9 @@ namespace Swift {  			void handleFileTransferAccept(std::string /* id */, std::string /* filename */);  			void handleSendFileRequest(std::string filename); +			void handleUIEvent(boost::shared_ptr<UIEvent> event); +			void checkForDisplayingDisplayReceiptsAlert(); +  		private:  			NickResolver* nickResolver_;  			ChatStateNotifier* chatStateNotifier_; @@ -56,9 +63,13 @@ namespace Swift {  			bool lastWasPresence_;  			std::string lastStatusChangeString_;  			std::map<boost::shared_ptr<Stanza>, std::string> unackedStanzas_; +			std::map<std::string, std::string> requestedReceipts_;  			StatusShow::Type lastShownStatus_;  			UIEventStream* eventStream_; +			ChatWindow::Tristate contactSupportsReceipts_; +			bool receivingPresenceFromUs_; +			bool userWantsReceipts_;  			std::map<std::string, FileTransferController*> ftControllers;  	};  } diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index a857f3d..f1ecfe8 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -54,6 +54,7 @@ namespace Swift {  			int getUnreadCount();  			const JID& getToJID() {return toJID_;}  			void handleCapsChanged(const JID& jid); +  		protected:  			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider); @@ -71,6 +72,7 @@ namespace Swift {  			virtual void dayTicked() {};  			virtual void handleBareJIDCapsChanged(const JID& jid) = 0;  			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>); +			virtual void setContactIsReceivingPresence(bool /* isReceivingPresence */) {}  		private:  			IDGenerator idGenerator_; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index e648f20..8111c2b 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -21,6 +21,7 @@  #include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h>  #include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h>  #include <Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h> +#include <Swift/Controllers/UIEvents/ToggleRequestDeliveryReceiptsUIEvent.h>  #include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>  #include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h> @@ -28,12 +29,15 @@  #include <Swiften/Client/NickResolver.h>  #include <Swiften/MUC/MUCManager.h>  #include <Swiften/Elements/ChatState.h> +#include <Swiften/Elements/DeliveryReceipt.h> +#include <Swiften/Elements/DeliveryReceiptRequest.h>  #include <Swiften/MUC/MUCBookmarkManager.h>  #include <Swift/Controllers/FileTransfer/FileTransferController.h>  #include <Swift/Controllers/FileTransfer/FileTransferOverview.h>  #include <Swift/Controllers/ProfileSettingsProvider.h>  #include <Swiften/Avatars/AvatarManager.h>  #include <Swiften/Elements/MUCInvitationPayload.h> +#include <Swiften/Roster/XMPPRoster.h>  namespace Swift { @@ -61,6 +65,7 @@ ChatsManager::ChatsManager(  		MUCSearchWindowFactory* mucSearchWindowFactory,  		ProfileSettingsProvider* settings,  		FileTransferOverview* ftOverview, +		XMPPRoster* roster,  		bool eagleMode) :  			jid_(jid),   			joinMUCWindowFactory_(joinMUCWindowFactory),  @@ -69,6 +74,7 @@ ChatsManager::ChatsManager(  			entityCapsProvider_(entityCapsProvider),   			mucManager(mucManager),  			ftOverview_(ftOverview), +			roster_(roster),  			eagleMode_(eagleMode) {  	timerFactory_ = timerFactory;  	eventController_ = eventController; @@ -95,11 +101,19 @@ ChatsManager::ChatsManager(  	mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);  	mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));  	ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1)); +	roster_->onJIDAdded.connect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1)); +	roster_->onJIDRemoved.connect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1)); +	roster_->onJIDUpdated.connect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1)); +	roster_->onRosterCleared.connect(boost::bind(&ChatsManager::handleRosterCleared, this));  	setupBookmarks();  	loadRecents();  }  ChatsManager::~ChatsManager() { +	roster_->onJIDAdded.disconnect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1)); +	roster_->onJIDRemoved.disconnect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1)); +	roster_->onJIDUpdated.disconnect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1)); +	roster_->onRosterCleared.disconnect(boost::bind(&ChatsManager::handleRosterCleared, this));  	delete joinMUCWindow_;  	foreach (JIDChatControllerPair controllerPair, chatControllers_) {  		delete controllerPair.second; @@ -136,6 +150,39 @@ void ChatsManager::handleClearRecentsRequested() {  	handleUnreadCountChanged(NULL);  } +void ChatsManager::handleJIDAddedToRoster(const JID &jid) { +	updatePresenceReceivingStateOnChatController(jid); +} + +void ChatsManager::handleJIDRemovedFromRoster(const JID &jid) { +	updatePresenceReceivingStateOnChatController(jid); +} + +void ChatsManager::handleJIDUpdatedInRoster(const JID &jid) { +	updatePresenceReceivingStateOnChatController(jid); +} + +void ChatsManager::handleRosterCleared() { +	/*	Setting that all chat controllers aren't receiving presence anymore; +		including MUC 1-to-1 chats due to the assumtion that this handler +		is only called on log out. */ +	foreach(JIDChatControllerPair pair, chatControllers_) { +		pair.second->setContactIsReceivingPresence(false); +	} +} + +void ChatsManager::updatePresenceReceivingStateOnChatController(const JID &jid) { +	ChatController* controller = getChatControllerIfExists(jid); +	if (controller) { +		if (!mucRegistry_->isMUC(jid.toBare())) { +			RosterItemPayload::Subscription subscription = roster_->getSubscriptionStateForJID(jid); +			controller->setContactIsReceivingPresence(subscription == RosterItemPayload::From || subscription == RosterItemPayload::Both); +		} else { +			controller->setContactIsReceivingPresence(true); +		} +	} +} +  void ChatsManager::loadRecents() {  	std::string recentsString(profileSettings_->getStringSetting(RECENT_CHATS));  	std::vector<std::string> recents; @@ -316,6 +363,11 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {  		mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark());  		return;  	} +	boost::shared_ptr<ToggleRequestDeliveryReceiptsUIEvent> toggleRequestDeliveryReceipsEvent = boost::dynamic_pointer_cast<ToggleRequestDeliveryReceiptsUIEvent>(event); +	if (toggleRequestDeliveryReceipsEvent) { +		userWantsReceipts_ = toggleRequestDeliveryReceipsEvent->getEnabled(); +		return; +	}  	boost::shared_ptr<EditMUCBookmarkUIEvent> editMUCBookmarkEvent = boost::dynamic_pointer_cast<EditMUCBookmarkUIEvent>(event);  	if (editMUCBookmarkEvent) { @@ -436,11 +488,12 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact)  ChatController* ChatsManager::createNewChatController(const JID& contact) {  	assert(chatControllers_.find(contact) == chatControllers_.end()); -	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_); +	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_);  	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false));  	controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); +	updatePresenceReceivingStateOnChatController(contact);  	return controller;  } @@ -522,7 +575,7 @@ void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {  	JID jid = message->getFrom();  	boost::shared_ptr<MessageEvent> event(new MessageEvent(message));  	bool isInvite = message->getPayload<MUCInvitationPayload>(); -	if (!event->isReadable() && !message->getPayload<ChatState>() && !isInvite && !message->hasSubject()) { +	if (!event->isReadable() && !message->getPayload<ChatState>() && !message->getPayload<DeliveryReceipt>() && !message->getPayload<DeliveryReceiptRequest>() && !isInvite && !message->hasSubject()) {  		return;  	} diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 5d8d555..0c7f492 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -45,10 +45,11 @@ namespace Swift {  	class MUCSearchController;  	class FileTransferOverview;  	class FileTransferController; +	class XMPPRoster;  	class ChatsManager {  		public: -			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings, FileTransferOverview* ftOverview, bool eagleMode); +			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setOnline(bool enabled); @@ -81,6 +82,12 @@ namespace Swift {  			void handleUnreadCountChanged(ChatControllerBase* controller);  			void handleAvatarChanged(const JID& jid);  			void handleClearRecentsRequested(); +			void handleJIDAddedToRoster(const JID&); +			void handleJIDRemovedFromRoster(const JID&); +			void handleJIDUpdatedInRoster(const JID&); +			void handleRosterCleared(); + +			void updatePresenceReceivingStateOnChatController(const JID&);  			ChatController* getChatControllerOrFindAnother(const JID &contact);  			ChatController* createNewChatController(const JID &contact); @@ -115,6 +122,8 @@ namespace Swift {  			std::list<ChatListWindow::Chat> recentChats_;  			ProfileSettingsProvider* profileSettings_;  			FileTransferOverview* ftOverview_; +			XMPPRoster* roster_;  			bool eagleMode_; +			bool userWantsReceipts_;  	};  } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index df519e8..edb431a 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -43,8 +43,11 @@  #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"  #include "Swift/Controllers/UIEvents/UIEventStream.h" +#include "Swift/Controllers/UIEvents/ToggleRequestDeliveryReceiptsUIEvent.h"  #include <Swift/Controllers/ProfileSettingsProvider.h>  #include "Swift/Controllers/FileTransfer/FileTransferOverview.h" +#include "Swiften/Elements/DeliveryReceiptRequest.h" +#include "Swiften/Elements/DeliveryReceipt.h"  #include <Swiften/Base/Algorithm.h>  using namespace Swift; @@ -63,6 +66,10 @@ class ChatsManagerTest : public CppUnit::TestFixture {  	CPPUNIT_TEST(testUnbindRebind);  	CPPUNIT_TEST(testNoDuplicateUnbind);  	CPPUNIT_TEST(testThreeMUCWindows); +	CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnRemoveFromRoster); +	CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnAddToRoster); +	CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth); +	CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom);  	CPPUNIT_TEST_SUITE_END();  public: @@ -93,8 +100,9 @@ public:  		chatListWindow_ = new MockChatListWindow();  		ftManager_ = new DummyFileTransferManager();  		ftOverview_ = new FileTransferOverview(ftManager_); +  		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); -		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, false); +		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false);  		avatarManager_ = new NullAvatarManager();  		manager_->setAvatarManager(avatarManager_); @@ -335,11 +343,104 @@ public:  		manager_->handleIncomingMessage(message2b);  		CPPUNIT_ASSERT_EQUAL(body2b, window1->lastMessageBody_);  	} -	 + +	/** +	 *	Test that ChatController doesn't send receipts anymore after removal of the contact from the roster. +	 */ +	void testChatControllerPresenceAccessUpdatedOnRemoveFromRoster() { +		JID messageJID("testling@test.com/resource1"); +		xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), RosterItemPayload::Both); + +		MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); +		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); +		uiEventStream_->send(boost::shared_ptr<UIEvent>(new ToggleRequestDeliveryReceiptsUIEvent(true))); + +		boost::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1"); +		manager_->handleIncomingMessage(message); +		Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(0); +		CPPUNIT_ASSERT_EQUAL((size_t)1, stanzaChannel_->sentStanzas.size()); +		CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != 0); + +		xmppRoster_->removeContact(messageJID); + +		message->setID("2"); +		manager_->handleIncomingMessage(message); +		CPPUNIT_ASSERT_EQUAL((size_t)1, stanzaChannel_->sentStanzas.size()); +	} + +	/** +	 *	Test that ChatController sends receipts after the contact has been added to the roster. +	 */ +	void testChatControllerPresenceAccessUpdatedOnAddToRoster() { +		JID messageJID("testling@test.com/resource1"); + +		MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); +		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); +		uiEventStream_->send(boost::shared_ptr<UIEvent>(new ToggleRequestDeliveryReceiptsUIEvent(true))); + +		boost::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1"); +		manager_->handleIncomingMessage(message); + +		CPPUNIT_ASSERT_EQUAL((size_t)0, stanzaChannel_->sentStanzas.size()); + +		xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), RosterItemPayload::Both); +		message->setID("2"); +		manager_->handleIncomingMessage(message); + +		CPPUNIT_ASSERT_EQUAL((size_t)1, stanzaChannel_->sentStanzas.size()); +		Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(0); +		CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != 0); +	} + +	/** +	 *	Test that ChatController sends receipts if requested after change from subscription state To to subscription state Both. +	 */ +	void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth() { +		testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::Both); +	} + +	/** +	 *	Test that ChatController sends receipts if requested after change from subscription state To to subscription state From. +	 */ +	void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom() { +		testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::From); +	} + +	void testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::Subscription from, RosterItemPayload::Subscription to) { +		JID messageJID("testling@test.com/resource1"); +		xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), from); + +		MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); +		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); +		uiEventStream_->send(boost::shared_ptr<UIEvent>(new ToggleRequestDeliveryReceiptsUIEvent(true))); + +		boost::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1"); +		manager_->handleIncomingMessage(message); + +		CPPUNIT_ASSERT_EQUAL((size_t)0, stanzaChannel_->sentStanzas.size()); + +		xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), to); +		message->setID("2"); +		manager_->handleIncomingMessage(message); + +		CPPUNIT_ASSERT_EQUAL((size_t)1, stanzaChannel_->sentStanzas.size()); +		Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(0); +		CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != 0); +	} + +private: +	boost::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { +		boost::shared_ptr<Message> message = boost::make_shared<Message>(); +		message->setFrom(from); +		message->setID(id); +		message->addPayload(boost::make_shared<DeliveryReceiptRequest>()); +		return message; +	} +  private:  	JID jid_;  	ChatsManager* manager_; -	StanzaChannel* stanzaChannel_; +	DummyStanzaChannel* stanzaChannel_;  	IQChannel* iqChannel_;  	IQRouter* iqRouter_;  	EventController* eventController_;  | 
 Swift