summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Burgess <pete.burgess@isode.com>2018-02-26 18:01:43 (GMT)
committerPeter Burgess <pete.burgess@isode.com>2018-02-27 12:15:55 (GMT)
commit3a540816280691e7ca3191867d3c73beba465674 (patch)
tree25508e9fc836b0350adab57cc4e3265ad370c36c /Swift/Controllers/Chat
parent85a144fe80d0fe89b5fed852013b6986b44978d4 (diff)
downloadswift-3a540816280691e7ca3191867d3c73beba465674.zip
swift-3a540816280691e7ca3191867d3c73beba465674.tar.bz2
Divide differently marked msgs and elide msgs matching room
If consecutive message from the same user have differing security markings, then mark them as non continuing to show clearer seperation of messages with different security markings. They are seperated in the same way as consecutive messages from different users are. Further to this, there is a new scheme for displaying message security markings. Messages with a security marking matching the room security marking will not be marked, aside from the first in a series of consecutive messages marked with the same security marking. Unmarked messages in a room with a security marking will be marked as such. This new marking display system can be turned on and off via an xml setting "mucMarkingElision". Test-Information: Unit tests written and passed in MUCControllerTest, runs as expected. Change-Id: Id2b66417f363c49c131d27e738ce786755d65203
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp6
-rw-r--r--Swift/Controllers/Chat/ChatController.h5
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp30
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h8
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp6
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp19
-rw-r--r--Swift/Controllers/Chat/MUCController.h5
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp325
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_;