diff options
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 6 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 5 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 30 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 6 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 19 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 5 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp | 325 |
8 files changed, 323 insertions, 81 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index fe8e870..7fb9c59 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -1,86 +1,86 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/ChatController.h> #include <memory> #include <boost/bind.hpp> #include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Algorithm.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/Path.h> #include <Swiften/Base/format.h> #include <Swiften/Chat/ChatStateNotifier.h> #include <Swiften/Chat/ChatStateTracker.h> #include <Swiften/Client/ClientBlockListManager.h> #include <Swiften/Client/NickResolver.h> #include <Swiften/Client/StanzaChannel.h> #include <Swiften/Disco/EntityCapsProvider.h> #include <Swiften/Disco/FeatureOracle.h> #include <Swiften/Elements/DeliveryReceipt.h> #include <Swiften/Elements/DeliveryReceiptRequest.h> #include <Swiften/Elements/Idle.h> #include <Swiften/FileTransfer/FileTransferManager.h> #include <Swiften/Network/TimerFactory.h> #include <Swift/Controllers/Chat/ChatMessageParser.h> #include <Swift/Controllers/FileTransfer/FileTransferController.h> #include <Swift/Controllers/Highlighting/Highlighter.h> #include <Swift/Controllers/Intl.h> #include <Swift/Controllers/SettingConstants.h> #include <Swift/Controllers/StatusUtil.h> #include <Swift/Controllers/Translator.h> #include <Swift/Controllers/UIEvents/AcceptWhiteboardSessionUIEvent.h> #include <Swift/Controllers/UIEvents/CancelWhiteboardSessionUIEvent.h> #include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h> #include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> #include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h> #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/XMPPEvents/EventController.h> #include <Swift/Controllers/XMPPEvents/IncomingFileTransferEvent.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, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider) - : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, nickResolver, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) { +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, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, SettingsProvider* settings) + : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, nickResolver, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider, settings), userWantsReceipts_(userWantsReceipts), clientBlockListManager_(clientBlockListManager) { isInMUC_ = isInMUC; lastWasPresence_ = false; chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider, timerFactory, 20000); chatStateTracker_ = new ChatStateTracker(); nickResolver_ = nickResolver; presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1)); chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1)); stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1)); nickResolver_->onNickChanged.connect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2)); std::string nick = nickResolver_->jidToNick(toJID_); chatWindow_->setName(nick); std::string startMessage; Presence::ref theirPresence; if (isInMUC) { startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% in chatroom %2%")) % nick % contact.toBare().toString()); theirPresence = presenceOracle->getLastPresence(contact); } else { startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString()); theirPresence = contact.isBare() ? presenceOracle->getAccountPresence(contact) : presenceOracle->getLastPresence(contact); } Idle::ref idle; if (theirPresence && (idle = theirPresence->getPayload<Idle>())) { startMessage += str(format(QT_TRANSLATE_NOOP("", ", who has been idle since %1%")) % Swift::Translator::getInstance()->ptimeToHumanReadableString(idle->getSince())); } startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None); if (theirPresence && !theirPresence->getStatus().empty()) { startMessage += " (" + theirPresence->getStatus() + ")"; } lastShownStatus_ = theirPresence ? theirPresence->getShow() : StatusShow::None; chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available); diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index d290d05..c65eb9c 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -1,63 +1,63 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <map> #include <string> #include <Swiften/Base/Tristate.h> #include <Swift/Controllers/Chat/ChatControllerBase.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> namespace Swift { class AvatarManager; class ChatStateNotifier; class ChatStateTracker; class ClientBlockListManager; class EntityCapsProvider; class FileTransferController; class HighlightManager; class HistoryController; class NickResolver; class SettingsProvider; class TimerFactory; 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, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider); + 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, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, SettingsProvider* settings); virtual ~ChatController() override; virtual void setToJID(const JID& jid) override; virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) override; virtual void setOnline(bool online) override; virtual void handleNewFileTransferController(FileTransferController* ftc); virtual void handleWhiteboardSessionRequest(bool senderIsSelf); virtual void handleWhiteboardStateChange(const ChatWindow::WhiteboardSessionState state); virtual void setContactIsReceivingPresence(bool /*isReceivingPresence*/) override; virtual ChatWindow* detachChatWindow() override; virtual void handleIncomingOwnMessage(std::shared_ptr<Message> message) override; protected: virtual void cancelReplaces() override; virtual JID getBaseJID() override; virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) override; virtual bool shouldIgnoreMessage(std::shared_ptr<Message> message) override; virtual JID messageCorrectionJID(const JID& fromJID) override; private: void handlePresenceChange(std::shared_ptr<Presence> newPresence); std::string getStatusChangeString(std::shared_ptr<Presence> presence); virtual bool isIncomingMessageFromMe(std::shared_ptr<Message> message) override; virtual void postSendMessage(const std::string &body, std::shared_ptr<Stanza> sentStanza) override; virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) override; virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) override; virtual void handleIncomingReplaceMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, const std::string& idToReplace, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) override; virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) override; virtual void preSendMessageRequest(std::shared_ptr<Message>) override; virtual std::string senderHighlightNameFromMessage(const JID& from) override; virtual std::string senderDisplayNameFromMessage(const JID& from) override; @@ -77,44 +77,43 @@ namespace Swift { void handleWhiteboardWindowShow(); void handleSettingChanged(const std::string& settingPath); void checkForDisplayingDisplayReceiptsAlert(); void handleBlockingStateChanged(); void handleBlockUserRequest(); void handleUnblockUserRequest(); void handleInviteToChat(const std::vector<JID>& droppedJIDs); void handleWindowClosed(); void handleUIEvent(std::shared_ptr<UIEvent> event); private: NickResolver* nickResolver_; ChatStateNotifier* chatStateNotifier_; ChatStateTracker* chatStateTracker_; std::string myLastMessageUIID_; bool isInMUC_; std::string lastStatusChangeString_; std::map<std::shared_ptr<Stanza>, std::string> unackedStanzas_; std::map<std::string, std::string> requestedReceipts_; StatusShow::Type lastShownStatus_; Tristate contactSupportsReceipts_; bool receivingPresenceFromUs_ = false; bool userWantsReceipts_; std::map<std::string, FileTransferController*> ftControllers; - SettingsProvider* settings_; std::string lastWbID_; std::string lastHandledMessageID_; ClientBlockListManager* clientBlockListManager_; boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; boost::optional<ChatWindow::AlertID> deliveryReceiptAlert_; boost::optional<ChatWindow::AlertID> blockedContactAlert_; }; } diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 0fc735a..2ef91fa 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -1,71 +1,71 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/ChatControllerBase.h> #include <map> #include <memory> #include <sstream> #include <boost/algorithm/string.hpp> #include <boost/bind.hpp> #include <boost/numeric/conversion/cast.hpp> #include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Path.h> #include <Swiften/Base/format.h> #include <Swiften/Client/StanzaChannel.h> #include <Swiften/Disco/EntityCapsProvider.h> #include <Swiften/Elements/Delay.h> #include <Swiften/Elements/MUCInvitationPayload.h> #include <Swiften/Elements/MUCUserPayload.h> #include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h> #include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h> #include <Swift/Controllers/Chat/ChatMessageParser.h> #include <Swift/Controllers/Highlighting/HighlightManager.h> #include <Swift/Controllers/Highlighting/Highlighter.h> #include <Swift/Controllers/Intl.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/XMPPEvents/EventController.h> #include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> namespace Swift { -ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream) { +ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, SettingsProvider* settings) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream), roomSecurityMarking_(""), previousMessageSecurityMarking_(""), settings_(settings) { chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); chatWindow_->onContinuationsBroken.connect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this)); entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); highlighter_ = highlightManager->createHighlighter(nickResolver); ChatControllerBase::setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable()); } ChatControllerBase::~ChatControllerBase() { delete highlighter_; delete chatWindow_; } void ChatControllerBase::handleContinuationsBroken() { cancelReplaces(); } ChatWindow* ChatControllerBase::detachChatWindow() { ChatWindow* chatWindow = chatWindow_; chatWindow_ = nullptr; return chatWindow; } void ChatControllerBase::handleCapsChanged(const JID& jid) { if (jid.compare(toJID_, JID::WithoutResource) == 0) { handleBareJIDCapsChanged(jid); } } @@ -163,65 +163,87 @@ void ChatControllerBase::handleSecurityLabelsCatalogResponse(std::shared_ptr<Sec } else { labelsEnabled_ = false; chatWindow_->setSecurityLabelsError(); } } void ChatControllerBase::showChatWindow() { chatWindow_->show(); } void ChatControllerBase::activateChatWindow() { chatWindow_->activate(); } bool ChatControllerBase::hasOpenWindow() const { return chatWindow_ && chatWindow_->isVisible(); } ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std::string& message, const std::string& senderName, bool senderIsSelf) { ChatWindow::ChatMessage chatMessage; chatMessage = chatMessageParser_->parseMessageBody(message, senderName, senderIsSelf); return chatMessage; } void ChatControllerBase::updateMessageCount() { chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size())); onUnreadCountChanged(); } std::string ChatControllerBase::addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, const std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time) { + auto displayedLabel = label; + + if (settings_->getSetting(SettingConstants::MUC_MARKING_ELISION)) { + if (roomSecurityMarking_ != "") { + + if (label && label->getDisplayMarking() == roomSecurityMarking_) { + if (label->getDisplayMarking() == previousMessageSecurityMarking_) { + displayedLabel = std::make_shared<SecurityLabel>(); + } + previousMessageSecurityMarking_ = label->getDisplayMarking(); + } + else if (!label || label->getDisplayMarking().empty()) { + displayedLabel = std::make_shared<SecurityLabel>(); + displayedLabel->setDisplayMarking("Unmarked"); + previousMessageSecurityMarking_ = "Unmarked"; + } + else { + previousMessageSecurityMarking_ = displayedLabel->getDisplayMarking(); + } + } + } + if (chatMessage.isMeCommand()) { - return chatWindow_->addAction(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time); + return chatWindow_->addAction(chatMessage, senderName, senderIsSelf, displayedLabel, pathToString(avatarPath), time); } else { - return chatWindow_->addMessage(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time); + return chatWindow_->addMessage(chatMessage, senderName, senderIsSelf, displayedLabel, pathToString(avatarPath), time); } } void ChatControllerBase::replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time) { if (chatMessage.isMeCommand()) { chatWindow_->replaceWithAction(chatMessage, id, time); } else { chatWindow_->replaceMessage(chatMessage, id, time); } } bool ChatControllerBase::isFromContact(const JID& from) { return from.toBare() == toJID_.toBare(); } void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) { std::shared_ptr<Message> message = messageEvent->getStanza(); if (shouldIgnoreMessage(message)) { return; } preHandleIncomingMessage(messageEvent); if (messageEvent->isReadable() && !messageEvent->getConcluded()) { unreadMessages_.push_back(messageEvent); if (messageEvent->targetsMe()) { targetedUnreadMessages_.push_back(messageEvent); } } diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 88cb95c..f635a46 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -1,112 +1,113 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <map> #include <memory> #include <string> #include <vector> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem/path.hpp> #include <boost/optional.hpp> #include <boost/signals2.hpp> #include <Swiften/Base/IDGenerator.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/ErrorPayload.h> #include <Swiften/Elements/SecurityLabelsCatalog.h> #include <Swiften/Elements/Stanza.h> #include <Swiften/JID/JID.h> #include <Swiften/MUC/MUCRegistry.h> #include <Swiften/Presence/PresenceOracle.h> #include <Swiften/Queries/IQRouter.h> #include <Swift/Controllers/Highlighting/HighlightManager.h> #include <Swift/Controllers/HistoryController.h> +#include <Swift/Controllers/Settings/SettingsProvider.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> #include <Swift/Controllers/XMPPEvents/MessageEvent.h> namespace Swift { class AutoAcceptMUCInviteDecider; class AvatarManager; class ChatMessageParser; class ChatWindowFactory; class EntityCapsProvider; class EventController; class HighlightManager; class Highlighter; class IQRouter; class NickResolver; class StanzaChannel; class UIEventStream; class ChatControllerBase : public boost::signals2::trackable { public: class StreamWindowMessageIDPair { public: std::string idInStream; std::string idInWindow; }; public: virtual ~ChatControllerBase(); void showChatWindow(); void activateChatWindow(); bool hasOpenWindow() const; virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info); virtual void handleIncomingOwnMessage(std::shared_ptr<Message> /*message*/) {} void handleIncomingMessage(std::shared_ptr<MessageEvent> message); std::string addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time); void replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time); virtual void setOnline(bool online); void setEnabled(bool enabled); virtual void setToJID(const JID& jid) {toJID_ = jid;} /** Used for determining when something is recent.*/ boost::signals2::signal<void (const std::string& /*activity*/)> onActivity; boost::signals2::signal<void ()> onUnreadCountChanged; boost::signals2::signal<void ()> onWindowClosed; int getUnreadCount(); const JID& getToJID() {return toJID_;} void handleCapsChanged(const JID& jid); void setCanStartImpromptuChats(bool supportsImpromptu); virtual ChatWindow* detachChatWindow(); boost::signals2::signal<void(ChatWindow* /*window to reuse*/, const std::vector<JID>& /*invite people*/, const std::string& /*reason*/)> onConvertToMUC; protected: - ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider); + ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, SettingsProvider* settings); /** * Pass the Message appended, and the stanza used to send it. */ virtual void postSendMessage(const std::string&, std::shared_ptr<Stanza>) {} virtual std::string senderDisplayNameFromMessage(const JID& from) = 0; virtual std::string senderHighlightNameFromMessage(const JID& from) = 0; virtual bool isIncomingMessageFromMe(std::shared_ptr<Message>) = 0; virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) {} virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) = 0; virtual void handleIncomingReplaceMessage(const JID& from, const ChatWindow::ChatMessage& chatMessage, const std::string& messageID, const std::string& idToReplace, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) = 0; virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage&) {} virtual void preSendMessageRequest(std::shared_ptr<Message>) {} virtual bool isFromContact(const JID& from); virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message>) const = 0; virtual void dayTicked() {} virtual void handleBareJIDCapsChanged(const JID& jid) = 0; std::string getErrorMessage(std::shared_ptr<ErrorPayload>); virtual void setContactIsReceivingPresence(bool /* isReceivingPresence */) {} virtual void cancelReplaces() = 0; /** JID any iq for account should go to - bare except for PMs */ virtual JID getBaseJID(); virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) = 0; ChatWindow::ChatMessage buildChatWindowChatMessage(const std::string& message, const std::string& senderName, bool senderIsSelf); void updateMessageCount(); virtual bool shouldIgnoreMessage(std::shared_ptr<Message> /* message */) { return false; } /** @@ -123,32 +124,35 @@ namespace Swift { void handleSecurityLabelsCatalogResponse(std::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error); void handleMUCInvitation(Message::ref message); void handleMediatedMUCInvitation(Message::ref message); void handleGeneralMUCInvitation(MUCInviteEvent::ref event); void handleContinuationsBroken(); protected: JID selfJID_; std::vector<std::shared_ptr<StanzaEvent> > unreadMessages_; std::vector<std::shared_ptr<StanzaEvent> > targetedUnreadMessages_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ChatWindowFactory* chatWindowFactory_; ChatWindow* chatWindow_; JID toJID_; bool labelsEnabled_; std::map<JID, StreamWindowMessageIDPair> lastMessagesIDs_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; bool useDelayForLatency_; EventController* eventController_; EntityCapsProvider* entityCapsProvider_; SecurityLabelsCatalog::Item lastLabel_; HistoryController* historyController_; MUCRegistry* mucRegistry_; Highlighter* highlighter_; std::shared_ptr<ChatMessageParser> chatMessageParser_; AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider_; UIEventStream* eventStream_; bool lastWasPresence_ = false; + std::string roomSecurityMarking_; + std::string previousMessageSecurityMarking_; + SettingsProvider* settings_; }; } diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index b7087fd..dd74f6a 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,32 +1,32 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/ChatsManager.h> #include <memory> #include <boost/algorithm/string.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/archive/text_oarchive.hpp> #include <boost/bind.hpp> #include <boost/serialization/map.hpp> #include <boost/serialization/optional.hpp> #include <boost/serialization/split_free.hpp> #include <boost/serialization/string.hpp> #include <boost/serialization/vector.hpp> #include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Log.h> #include <Swiften/Client/ClientBlockListManager.h> #include <Swiften/Client/NickResolver.h> #include <Swiften/Client/StanzaChannel.h> #include <Swiften/Disco/DiscoServiceWalker.h> #include <Swiften/Disco/FeatureOracle.h> #include <Swiften/Elements/CarbonsReceived.h> #include <Swiften/Elements/CarbonsSent.h> #include <Swiften/Elements/ChatState.h> #include <Swiften/Elements/DeliveryReceipt.h> #include <Swiften/Elements/DeliveryReceiptRequest.h> @@ -744,61 +744,61 @@ void ChatsManager::setOnline(bool enabled) { localMUCServiceFinderWalker_->onWalkComplete.connect(boost::bind(&ChatsManager::handleLocalServiceWalkFinished, this)); localMUCServiceFinderWalker_->beginWalk(); } if (chatListWindow_) { chatListWindow_->setBookmarksEnabled(enabled); } } void ChatsManager::handleChatRequest(const std::string &contact) { ChatController* controller = getChatControllerOrFindAnother(JID(contact)); controller->activateChatWindow(); } ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact) { ChatController* controller = getChatControllerIfExists(contact); if (!controller && !mucRegistry_->isMUC(contact.toBare())) { for (JIDChatControllerPair pair : chatControllers_) { if (pair.first.toBare() == contact.toBare()) { controller = pair.second; break; } } } return controller ? controller : createNewChatController(contact); } ChatController* ChatsManager::createNewChatController(const JID& contact) { assert(chatControllers_.find(contact) == chatControllers_.end()); std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getConfiguration(), ChatMessageParser::Mode::Chat); /* a message parser that knows this is a chat (not a room/MUC) */ - ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, timerFactory_, eventController_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, autoAcceptMUCInviteDecider_); + auto controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, timerFactory_, eventController_, entityCapsProvider_, userWantsReceipts_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, autoAcceptMUCInviteDecider_, settings_); chatControllers_[contact] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); controller->onWindowClosed.connect(boost::bind(&ChatsManager::handleChatClosed, this, contact)); controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); controller->onConvertToMUC.connect(boost::bind(&ChatsManager::handleTransformChatToMUC, this, controller, _1, _2, _3)); updatePresenceReceivingStateOnChatController(contact); controller->setCanStartImpromptuChats(!localMUCServiceJID_.toString().empty()); return controller; } ChatController* ChatsManager::getChatControllerOrCreate(const JID &contact) { ChatController* controller = getChatControllerIfExists(contact); return controller ? controller : createNewChatController(contact); } ChatController* ChatsManager::getChatControllerIfExists(const JID &contact, bool rebindIfNeeded) { if (chatControllers_.find(contact) == chatControllers_.end()) { if (mucRegistry_->isMUC(contact.toBare())) { return nullptr; } //Need to look for an unbound window to bind first JID bare(contact.toBare()); if (chatControllers_.find(bare) != chatControllers_.end()) { if (rebindIfNeeded) { rebindControllerJID(bare, contact); } else { return chatControllers_[bare]; } @@ -833,61 +833,61 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti if (nickMaybe) { bookmark.setNick(*nickMaybe); } if (password) { bookmark.setPassword(*password); } mucBookmarkManager_->addBookmark(bookmark); } std::map<JID, MUCController*>::iterator it = mucControllers_.find(mucJID); if (it != mucControllers_.end()) { if (stanzaChannel_->isAvailable()) { it->second->rejoin(); } } else { std::string nick = (nickMaybe && !(*nickMaybe).empty()) ? nickMaybe.get() : nickResolver_->jidToNick(jid_); muc = mucManager->createMUC(mucJID); if (createAsReservedIfNew) { muc->setCreateAsReservedIfNew(); } if (isImpromptu) { muc->setCreateAsReservedIfNew(); } MUCController* controller = nullptr; SingleChatWindowFactoryAdapter* chatWindowFactoryAdapter = nullptr; if (reuseChatwindow) { chatWindowFactoryAdapter = new SingleChatWindowFactoryAdapter(reuseChatwindow); } std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat); /* a message parser that knows this is a room/MUC (not a chat) */ - controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, reuseChatwindow ? chatWindowFactoryAdapter : chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, isImpromptu, autoAcceptMUCInviteDecider_, vcardManager_, mucBookmarkManager_); + controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, reuseChatwindow ? chatWindowFactoryAdapter : chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, isImpromptu, autoAcceptMUCInviteDecider_, vcardManager_, mucBookmarkManager_, settings_); if (chatWindowFactoryAdapter) { /* The adapters are only passed to chat windows, which are deleted in their * controllers' dtor, which are deleted in ChatManager's dtor. The adapters * are also deleted there.*/ chatWindowFactoryAdapters_[controller] = chatWindowFactoryAdapter; } mucControllers_[mucJID] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true)); controller->onUserNicknameChanged.connect(boost::bind(&ChatsManager::handleUserNicknameChanged, this, controller, _1, _2)); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true)); controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); if (!stanzaChannel_->isAvailable()) { /* When online, the MUC is added to the registry in MUCImpl::internalJoin. This method is not * called when Swift is offline, so we add it here as only MUCs in the registry are rejoined * when going back online. */ mucRegistry_->addMUC(mucJID.toBare()); } handleChatActivity(mucJID.toBare(), "", true); } auto chatListWindowIter = std::find_if(recentChats_.begin(), recentChats_.end(), [&](const ChatListWindow::Chat& chatListWindow) { return mucJID == (chatListWindow.jid); }); if (chatListWindowIter != recentChats_.end() && (mucControllers_[mucJID]->isImpromptu() || !chatListWindowIter->impromptuJIDs.empty())) { mucControllers_[mucJID]->setChatWindowTitle(chatListWindowIter->getTitle()); } mucControllers_[mucJID]->showChatWindow(); diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 9e12a66..4c3f524 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -72,62 +72,63 @@ class MUCBookmarkPredicate { }; /** * The controller does not gain ownership of the stanzaChannel, nor the factory. */ MUCController::MUCController ( const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* uiEventStream, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, - MUCBookmarkManager* mucBookmarkManager) : - ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), nickResolver, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) { + MUCBookmarkManager* mucBookmarkManager, + SettingsProvider* settings) : + ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), nickResolver, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider, settings), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) { assert(avatarManager_); parting_ = true; joined_ = false; lastWasPresence_ = false; shouldJoinOnReconnect_ = true; doneGettingHistory_ = false; xmppRoster_ = xmppRoster; subject_ = ""; isInitialJoin_ = true; chatWindowTitle_ = ""; roster_ = std::make_unique<Roster>(false, true); rosterVCardProvider_ = new RosterVCardProvider(roster_.get(), vcardManager, JID::WithResource); completer_ = new TabComplete(); chatWindow_->setRosterModel(roster_.get()); chatWindow_->setTabComplete(completer_); chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1)); chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2)); chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1)); chatWindow_->onBookmarkRequest.connect(boost::bind(&MUCController::handleBookmarkRequest, this)); chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1)); chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this)); chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this)); chatWindow_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1)); chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this)); chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1)); chatWindow_->onUnblockUserRequest.connect(boost::bind(&MUCController::handleUnblockUserRequest, this)); chatWindow_->onContinuationsBroken.connect(boost::bind(&MUCController::addChatSystemMessage, this)); @@ -1250,50 +1251,60 @@ void MUCController::setChatWindowTitle(const std::string& title) { void MUCController::requestSecurityMarking() { auto discoInfoRequest = GetDiscoInfoRequest::create(muc_->getJID(), iqRouter_); discoInfoRequest->onResponse.connect( [this](std::shared_ptr<DiscoInfo> discoInfoRef, ErrorPayload::ref errorPayloadRef) { if (!discoInfoRef || errorPayloadRef) { return; } const std::vector<Form::ref>& extensionsList = discoInfoRef->getExtensions(); if (extensionsList.empty()) { return; } // Get the correct form if it exists Form::ref roomInfoForm; for (const auto& form : extensionsList) { if (form->getFormType() == "http://jabber.org/protocol/muc#roominfo") { roomInfoForm = form; break; } } if (!roomInfoForm) { return; } // It exists, now examine the security marking data auto marking = roomInfoForm->getField("x-isode#roominfo_marking"); if (!marking) { return; } // Now we know the marking is valid auto markingValue = marking->getTextSingleValue(); if (markingValue == "") { - chatWindow_->removeChatSecurityMarking(); + setMUCSecurityMarkingDefault(); return; } auto markingForegroundColor = roomInfoForm->getField("x-isode#roominfo_marking_fg_color"); auto markingBackgroundColor = roomInfoForm->getField("x-isode#roominfo_marking_bg_color"); std::string markingForegroundColorValue = "Black"; std::string markingBackgroundColorValue = "White"; if (markingForegroundColor) { markingForegroundColorValue = markingForegroundColor->getTextSingleValue(); } if (markingBackgroundColor) { markingBackgroundColorValue = markingBackgroundColor->getTextSingleValue(); } - chatWindow_->setChatSecurityMarking(markingValue, markingForegroundColorValue, markingBackgroundColorValue); + setMUCSecurityMarking(markingValue, markingForegroundColorValue, markingBackgroundColorValue); } ); discoInfoRequest->send(); } +void MUCController::setMUCSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue) { + roomSecurityMarking_ = markingValue; + chatWindow_->setChatSecurityMarking(markingValue, markingForegroundColorValue, markingBackgroundColorValue); +} + +void MUCController::setMUCSecurityMarkingDefault() { + roomSecurityMarking_ = ""; + chatWindow_->removeChatSecurityMarking(); +} + } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 949f530..afc524f 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -26,61 +26,61 @@ #include <Swift/Controllers/UIInterfaces/ChatWindow.h> namespace Swift { class StanzaChannel; class IQRouter; class ChatWindowFactory; class Roster; class AvatarManager; class UIEventStream; class TimerFactory; class TabComplete; class XMPPRoster; class HighlightManager; class UIEvent; class VCardManager; class RosterVCardProvider; class ClientBlockListManager; class MUCBookmarkManager; class MUCBookmark; enum JoinPart {Join, Part, JoinThenPart, PartThenJoin}; struct NickJoinPart { NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {} std::string nick; JoinPart type; }; class MUCController : public ChatControllerBase { public: - MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager); + MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager, SettingsProvider* settings); virtual ~MUCController() override; boost::signals2::signal<void ()> onUserLeft; boost::signals2::signal<void ()> onUserJoined; boost::signals2::signal<void ()> onImpromptuConfigCompleted; boost::signals2::signal<void (const std::string&, const std::string& )> onUserNicknameChanged; virtual void setOnline(bool online) override; virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) override; void rejoin(); static void appendToJoinParts(std::vector<NickJoinPart>& joinParts, const NickJoinPart& newEvent); static std::string generateJoinPartString(const std::vector<NickJoinPart>& joinParts, bool isImpromptu); static std::string concatenateListOfNames(const std::vector<NickJoinPart>& joinParts); static std::string generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname); bool isJoined(); const std::string& getNick(); const boost::optional<std::string> getPassword() const; bool isImpromptu() const; std::map<std::string, JID> getParticipantJIDs() const; void sendInvites(const std::vector<JID>& jids, const std::string& reason) const; void setChatWindowTitle(const std::string& title); protected: virtual void preSendMessageRequest(std::shared_ptr<Message> message) override; virtual bool isIncomingMessageFromMe(std::shared_ptr<Message> message) override; virtual std::string senderHighlightNameFromMessage(const JID& from) override; virtual std::string senderDisplayNameFromMessage(const JID& from) override; virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message> message) const override; virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) override; virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) override; virtual void handleIncomingReplaceMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, const std::string& idToReplace, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) override; virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage& chatMessage) override; @@ -122,73 +122,74 @@ namespace Swift { void handleConfigurationFailed(ErrorPayload::ref); void handleConfigurationFormReceived(Form::ref); void handleDestroyRoomRequest(); void handleInvitePersonToThisMUCRequest(const std::vector<JID>& jidsToInvite); void handleConfigurationCancelled(); void handleOccupantRoleChangeFailed(ErrorPayload::ref, const JID&, MUCOccupant::Role); void handleGetAffiliationsRequest(); void handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids); void handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes); void handleInviteToMUCWindowDismissed(); void handleInviteToMUCWindowCompleted(); void handleUIEvent(std::shared_ptr<UIEvent> event); void addRecentLogs(); void checkDuplicates(std::shared_ptr<Message> newMessage); void setNick(const std::string& nick); void handleRoomUnlocked(); void configureAsImpromptuRoom(Form::ref form); Form::ref buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm); void handleUnblockUserRequest(); void handleBlockingStateChanged(); void handleMUCBookmarkAdded(const MUCBookmark& bookmark); void handleMUCBookmarkRemoved(const MUCBookmark& bookmark); void updateChatWindowBookmarkStatus(const boost::optional<MUCBookmark>& bookmark); void displaySubjectIfChanged(const std::string& sucject); void addChatSystemMessage(); void requestSecurityMarking(); + void setMUCSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue); + void setMUCSecurityMarkingDefault(); private: MUC::ref muc_; std::string nick_; std::string desiredNick_; TabComplete* completer_; bool parting_; bool joined_; bool shouldJoinOnReconnect_; bool doneGettingHistory_; boost::signals2::scoped_connection avatarChangedConnection_; std::shared_ptr<Timer> loginCheckTimer_; std::set<std::string> currentOccupants_; std::vector<NickJoinPart> joinParts_; boost::posix_time::ptime lastActivity_; boost::optional<std::string> password_; XMPPRoster* xmppRoster_; std::unique_ptr<Roster> roster_; std::vector<HistoryMessage> joinContext_; size_t renameCounter_; bool isImpromptu_; bool isImpromptuAlreadyConfigured_; RosterVCardProvider* rosterVCardProvider_; std::string lastJoinMessageUID_; std::string lastStartMessage_; ClientBlockListManager* clientBlockListManager_; boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; boost::optional<ChatWindow::AlertID> blockedContactAlert_; MUCBookmarkManager* mucBookmarkManager_; boost::signals2::scoped_connection mucBookmarkManagerBookmarkAddedConnection_; boost::signals2::scoped_connection mucBookmarkManagerBookmarkRemovedConnection_; std::string subject_; bool isInitialJoin_; std::string chatWindowTitle_; }; } - diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp index 06486d3..9b45794 100644 --- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp @@ -9,195 +9,245 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <hippomocks.h> #include <Swiften/Avatars/NullAvatarManager.h> #include <Swiften/Client/ClientBlockListManager.h> #include <Swiften/Client/DummyStanzaChannel.h> #include <Swiften/Client/NickResolver.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> #include <Swiften/Disco/DummyEntityCapsProvider.h> #include <Swiften/Elements/MUCUserPayload.h> #include <Swiften/Elements/Thread.h> #include <Swiften/MUC/MUCBookmarkManager.h> #include <Swiften/MUC/UnitTest/MockMUC.h> #include <Swiften/Network/TimerFactory.h> #include <Swiften/Presence/DirectedPresenceSender.h> #include <Swiften/Presence/PresenceOracle.h> #include <Swiften/Presence/StanzaChannelPresenceSender.h> #include <Swiften/Queries/DummyIQChannel.h> #include <Swiften/Roster/XMPPRoster.h> #include <Swiften/Roster/XMPPRosterImpl.h> #include <Swiften/VCards/VCardManager.h> #include <Swiften/VCards/VCardMemoryStorage.h> #include <Swift/Controllers/Chat/ChatMessageParser.h> #include <Swift/Controllers/Chat/MUCController.h> #include <Swift/Controllers/Chat/UserSearchController.h> #include <Swift/Controllers/Roster/GroupRosterItem.h> #include <Swift/Controllers/Roster/Roster.h> +#include <Swift/Controllers/SettingConstants.h> #include <Swift/Controllers/Settings/DummySettingsProvider.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h> #include <Swift/Controllers/UnitTest/MockChatWindow.h> #include <Swift/Controllers/XMPPEvents/EventController.h> using namespace Swift; class MUCControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MUCControllerTest); CPPUNIT_TEST(testJoinPartStringContructionSimple); CPPUNIT_TEST(testJoinPartStringContructionMixed); CPPUNIT_TEST(testAppendToJoinParts); CPPUNIT_TEST(testAddressedToSelf); CPPUNIT_TEST(testNotAddressedToSelf); CPPUNIT_TEST(testAddressedToSelfBySelf); CPPUNIT_TEST(testMessageWithEmptyLabelItem); CPPUNIT_TEST(testMessageWithLabelItem); CPPUNIT_TEST(testCorrectMessageWithLabelItem); CPPUNIT_TEST(testRoleAffiliationStates); CPPUNIT_TEST(testSubjectChangeCorrect); CPPUNIT_TEST(testSubjectChangeIncorrectA); CPPUNIT_TEST(testSubjectChangeIncorrectB); CPPUNIT_TEST(testSubjectChangeIncorrectC); CPPUNIT_TEST(testHandleOccupantNicknameChanged); CPPUNIT_TEST(testHandleOccupantNicknameChangedRoster); CPPUNIT_TEST(testHandleChangeSubjectRequest); CPPUNIT_TEST(testNonImpromptuMUCWindowTitle); CPPUNIT_TEST(testSecurityMarkingRequestCompleteMarking); CPPUNIT_TEST(testSecurityMarkingRequestCompleteMarkingWithExtraForm); CPPUNIT_TEST(testSecurityMarkingRequestEmptyMarking); CPPUNIT_TEST(testSecurityMarkingRequestWithMarkingNoFormType); CPPUNIT_TEST(testSecurityMarkingRequestNoMarking); CPPUNIT_TEST(testSecurityMarkingRequestNoForm); CPPUNIT_TEST(testSecurityMarkingRequestError); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingA); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingB); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingA); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingB); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingC); + + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_NoElision_NoRoomMarkingA); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_NoElision_NoRoomMarkingB); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_NoElision_WithRoomMarkingA); + CPPUNIT_TEST(testSecurityMarkingAddedToMessage_NoElision_WithRoomMarkingB); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { crypto_ = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); self_ = JID("girl@wonderland.lit/rabbithole"); nick_ = "aLiCe"; mucJID_ = JID("teaparty@rooms.wonderland.lit"); mocks_ = new MockRepository(); stanzaChannel_ = new DummyStanzaChannel(); iqChannel_ = new DummyIQChannel(); iqRouter_ = new IQRouter(iqChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); userSearchWindowFactory_ = mocks_->InterfaceMock<UserSearchWindowFactory>(); xmppRoster_ = new XMPPRosterImpl(); presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); uiEventStream_ = new UIEventStream(); avatarManager_ = new NullAvatarManager(); TimerFactory* timerFactory = nullptr; window_ = new MockChatWindow(); mucRegistry_ = new MUCRegistry(); entityCapsProvider_ = new DummyEntityCapsProvider(); settings_ = new DummySettingsProvider(); highlightManager_ = new HighlightManager(settings_); highlightManager_->resetToDefaultConfiguration(); muc_ = std::make_shared<MockMUC>(mucJID_); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_); chatMessageParser_ = std::make_shared<ChatMessageParser>(std::map<std::string, std::string>(), highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat); vcardStorage_ = new VCardMemoryStorage(crypto_.get()); vcardManager_ = new VCardManager(self_, iqRouter_, vcardStorage_); nickResolver_ = new NickResolver(self_, xmppRoster_, vcardManager_, mucRegistry_); clientBlockListManager_ = new ClientBlockListManager(iqRouter_); mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_); - controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, nullptr, nullptr, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser_, false, nullptr, vcardManager_, mucBookmarkManager_); + controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, nullptr, nullptr, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser_, false, nullptr, vcardManager_, mucBookmarkManager_, settings_); } void tearDown() { delete controller_; delete mucBookmarkManager_; delete clientBlockListManager_; delete nickResolver_; delete vcardManager_; delete vcardStorage_; delete highlightManager_; delete settings_; delete entityCapsProvider_; delete eventController_; delete presenceOracle_; delete xmppRoster_; delete mocks_; 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_->getJID().toString() + "/" + nick_)); MUCUserPayload::ref status(new MUCUserPayload()); MUCUserPayload::StatusCode code; code.code = 110; status->addStatusCode(code); presence->addPayload(status); stanzaChannel_->onPresenceReceived(presence); } void joinCompleted() { std::string messageBody("test message"); window_->onSendMessageRequest(messageBody, false); std::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1]; Message::ref message = std::dynamic_pointer_cast<Message>(rawStanza); CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */ CPPUNIT_ASSERT(message); CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get_value_or("")); { Message::ref message = std::make_shared<Message>(); message->setType(Message::Groupchat); message->setTo(self_); message->setFrom(mucJID_.withResource("SomeNickname")); message->setID(iqChannel_->getNewIQID()); message->setSubject("Initial"); controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message)); } } + void setMUCSecurityMarking(const std::string& markingValue, const std::string & markingForegroundColorValue, const std::string& markingBackgroundColorValue, const bool includeFormTypeField = true) { + auto form = std::make_shared<Form>(Form::Type::ResultType); + + if (includeFormTypeField) { + std::shared_ptr<FormField> formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + formTypeField->setName("FORM_TYPE"); + form->addField(formTypeField); + } + + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, markingValue); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, markingForegroundColorValue); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, markingBackgroundColorValue); + + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + } + + Message::ref createTestMessageWithoutSecurityLabel() { + auto message = std::make_shared<Message>(); + message->setType(Message::Type::Groupchat); + message->setID("test-id"); + message->setTo(self_); + message->setFrom(mucJID_.withResource("TestNickname")); + message->setBody("Do Not Read This Message"); + return message; + } + void testAddressedToSelf() { finishJoin(); Message::ref message(new Message()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/otherperson")); message->setBody("basic " + nick_ + " test."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)1, eventController_->getEvents().size()); message = Message::ref(new Message()); 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)2, eventController_->getEvents().size()); 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)3, eventController_->getEvents().size()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/other2")); message->setBody("Hi " + boost::to_lower_copy(nick_) + "."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); @@ -575,221 +625,376 @@ public: void testRoleAffiliationStatesVerify(const std::map<std::string, MUCOccupant> &occupants) { /* verify that the roster is in sync */ GroupRosterItem* group = window_->getRosterModel()->getRoot(); for (auto rosterItem : group->getChildren()) { GroupRosterItem* child = dynamic_cast<GroupRosterItem*>(rosterItem); CPPUNIT_ASSERT(child); for (auto childItem : child->getChildren()) { ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem); CPPUNIT_ASSERT(item); std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource()); CPPUNIT_ASSERT(occupant != occupants.end()); CPPUNIT_ASSERT(item->getMUCRole() == occupant->second.getRole()); CPPUNIT_ASSERT(item->getMUCAffiliation() == occupant->second.getAffiliation()); } } } void testHandleChangeSubjectRequest() { std::string testStr("New Subject"); CPPUNIT_ASSERT_EQUAL(std::string(""), muc_->newSubjectSet_); window_->onChangeSubjectRequest(testStr); CPPUNIT_ASSERT_EQUAL(testStr, muc_->newSubjectSet_); } void testNonImpromptuMUCWindowTitle() { CPPUNIT_ASSERT_EQUAL(muc_->getJID().getNode(), window_->name_); } void testSecurityMarkingRequestCompleteMarking() { - auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); - auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); - auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); - auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); - formTypeField->setName("FORM_TYPE"); - markingField->setName("x-isode#roominfo_marking"); - markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); - markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); - - auto form = std::make_shared<Form>(Form::Type::ResultType); - form->addField(formTypeField); - form->addField(markingField); - form->addField(markingForegroundColorField); - form->addField(markingBackgroundColorField); + setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red", true); - auto discoInfoRef = std::make_shared<DiscoInfo>(); - discoInfoRef->addExtension(form); - - auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); - iqChannel_->onIQReceived(infoResponse); - CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string("Red"), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestCompleteMarkingWithExtraForm() { auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); - auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test|Highest Possible Security"); auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); formTypeField->setName("FORM_TYPE"); markingField->setName("x-isode#roominfo_marking"); markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); auto extraForm = std::make_shared<Form>(Form::Type::ResultType); auto form = std::make_shared<Form>(Form::Type::ResultType); form->addField(formTypeField); form->addField(markingField); form->addField(markingForegroundColorField); form->addField(markingBackgroundColorField); auto discoInfoRef = std::make_shared<DiscoInfo>(); discoInfoRef->addExtension(extraForm); discoInfoRef->addExtension(form); auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); iqChannel_->onIQReceived(infoResponse); - CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string("Red"), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestNoColorsInMarking() { auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); - auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test|Highest Possible Security"); auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); formTypeField->setName("FORM_TYPE"); markingField->setName("x-isode#roominfo_marking"); markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); auto form = std::make_shared<Form>(Form::Type::ResultType); form->addField(formTypeField); form->addField(markingField); form->addField(markingForegroundColorField); form->addField(markingBackgroundColorField); auto discoInfoRef = std::make_shared<DiscoInfo>(); discoInfoRef->addExtension(form); auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); iqChannel_->onIQReceived(infoResponse); - CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string("White"), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestEmptyMarking() { - auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); - auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); - auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); - auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); - formTypeField->setName("FORM_TYPE"); - markingField->setName("x-isode#roominfo_marking"); - markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); - markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + setMUCSecurityMarking("", "", "", true); - auto form = std::make_shared<Form>(Form::Type::ResultType); - form->addField(formTypeField); - form->addField(markingField); - form->addField(markingForegroundColorField); - form->addField(markingBackgroundColorField); - - auto discoInfoRef = std::make_shared<DiscoInfo>(); - discoInfoRef->addExtension(form); - - auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); - iqChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestWithMarkingNoFormType() { - auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); - auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); - auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); - markingField->setName("x-isode#roominfo_marking"); - markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); - markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red", false); - auto form = std::make_shared<Form>(Form::Type::ResultType); - form->addField(markingField); - form->addField(markingForegroundColorField); - form->addField(markingBackgroundColorField); - - auto discoInfoRef = std::make_shared<DiscoInfo>(); - discoInfoRef->addExtension(form); - - auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); - iqChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestNoMarking() { auto form = std::make_shared<Form>(Form::Type::ResultType); auto discoInfoRef = std::make_shared<DiscoInfo>(); discoInfoRef->addExtension(form); auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); iqChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestNoForm() { auto discoInfoRef = std::make_shared<DiscoInfo>(); auto infoResponse = IQ::createResult( self_, mucJID_, "test-id", discoInfoRef); iqChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); } void testSecurityMarkingRequestError() { auto errorPayload = std::make_shared<ErrorPayload>(ErrorPayload::Condition::NotAuthorized, ErrorPayload::Type::Auth); auto infoResponse = IQ::createResult( self_, mucJID_, "test-id", errorPayload); iqChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); } + void testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingA() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true); + setMUCSecurityMarking("", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking("Test|Highest Possible Security"); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingB() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true); + setMUCSecurityMarking("", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking(""); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string(""), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingA() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true); + setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking("Test|Highest Possible Security"); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + // Test the first message matching MUC marking. This message SHOULD have a marking + + auto sentMessageEvent1 = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent1); + + auto storedSecurityLabel1 = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel1 == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel1->getDisplayMarking()); + + // Test a consecutive message matching MUC marking. This message SHOULD NOT have a marking + + auto sentMessageEvent2 = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent2); + + auto storedSecurityLabel2 = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel2 == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string(""), storedSecurityLabel2->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingB() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true); + setMUCSecurityMarking("Test|Lower Security Marking", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking("Test|Highest Possible Security"); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingC() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true); + setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking(""); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Unmarked"), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_NoElision_NoRoomMarkingA() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, false); + setMUCSecurityMarking("", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking("Test|Highest Possible Security"); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_NoElision_NoRoomMarkingB() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, false); + setMUCSecurityMarking("", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking(""); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string(""), storedSecurityLabel->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_NoElision_WithRoomMarkingA() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, false); + setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking("Test|Highest Possible Security"); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + // Test the first message matching MUC marking. This message SHOULD have a marking + + auto sentMessageEvent1 = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent1); + + auto storedSecurityLabel1 = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel1 == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel1->getDisplayMarking()); + + // Test a consecutive message matching MUC marking. This message SHOULD ALSO have a marking + + auto sentMessageEvent2 = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent2); + + auto storedSecurityLabel2 = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel2 == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string("Test|Highest Possible Security"), storedSecurityLabel2->getDisplayMarking()); + } + + void testSecurityMarkingAddedToMessage_NoElision_WithRoomMarkingB() { + settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, false); + setMUCSecurityMarking("", "Black", "Red"); + + auto messageLabel = std::make_shared<SecurityLabel>(); + messageLabel->setDisplayMarking(""); + + auto sentMessage = createTestMessageWithoutSecurityLabel(); + sentMessage->addPayload(messageLabel); + + auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage); + controller_->handleIncomingMessage(sentMessageEvent); + + auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_; + + CPPUNIT_ASSERT_EQUAL(false, storedSecurityLabel == nullptr); + // This is the potentially altered security label that is displayed on the screen + CPPUNIT_ASSERT_EQUAL(std::string(""), storedSecurityLabel->getDisplayMarking()); + } + private: JID self_; JID mucJID_; MockMUC::ref muc_; std::string nick_; DummyStanzaChannel* stanzaChannel_; DummyIQChannel* iqChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; UserSearchWindowFactory* userSearchWindowFactory_; MUCController* controller_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; StanzaChannelPresenceSender* presenceSender_; DirectedPresenceSender* directedPresenceSender_; MockRepository* mocks_; UIEventStream* uiEventStream_; MockChatWindow* window_; MUCRegistry* mucRegistry_; DummyEntityCapsProvider* entityCapsProvider_; DummySettingsProvider* settings_; HighlightManager* highlightManager_; std::shared_ptr<ChatMessageParser> chatMessageParser_; std::shared_ptr<CryptoProvider> crypto_; VCardManager* vcardManager_; VCardMemoryStorage* vcardStorage_; ClientBlockListManager* clientBlockListManager_; MUCBookmarkManager* mucBookmarkManager_; |