summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp7
-rw-r--r--Swift/Controllers/Chat/ChatController.h57
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp69
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h19
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp166
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h78
-rw-r--r--Swift/Controllers/Chat/Chattables.cpp43
-rw-r--r--Swift/Controllers/Chat/Chattables.h47
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp95
-rw-r--r--Swift/Controllers/Chat/MUCController.h44
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatControllerTest.cpp162
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp206
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChattablesTest.cpp140
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp1382
-rw-r--r--Swift/Controllers/Chat/UnitTest/MockChatListWindow.h2
15 files changed, 1803 insertions, 714 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 40f6156..5f441f8 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -52,8 +52,8 @@ 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, Chattables& chattables)
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, nickResolver, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider, settings, chattables), userWantsReceipts_(userWantsReceipts), clientBlockListManager_(clientBlockListManager) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider, timerFactory, 20000);
@@ -359,6 +359,7 @@ void ChatController::setOnline(bool online) {
if (!online) {
for (auto& stanzaIdPair : unackedStanzas_) {
chatWindow_->setAckState(stanzaIdPair.second, ChatWindow::Failed);
+ failedStanzas_[stanzaIdPair.second] = stanzaIdPair.first;
}
unackedStanzas_.clear();
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index a9093d0..447e263 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -9,7 +9,6 @@
#include <map>
#include <string>
-#include <Swiften/Base/Override.h>
#include <Swiften/Base/Tristate.h>
#include <Swift/Controllers/Chat/ChatControllerBase.h>
@@ -31,42 +30,42 @@ namespace Swift {
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);
- virtual ~ChatController();
- virtual void setToJID(const JID& jid) SWIFTEN_OVERRIDE;
- virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) SWIFTEN_OVERRIDE;
- virtual void setOnline(bool online) SWIFTEN_OVERRIDE;
+ 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, Chattables& chattables);
+ 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*/) SWIFTEN_OVERRIDE;
- virtual ChatWindow* detachChatWindow() SWIFTEN_OVERRIDE;
- virtual void handleIncomingOwnMessage(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
+ virtual void setContactIsReceivingPresence(bool /*isReceivingPresence*/) override;
+ virtual ChatWindow* detachChatWindow() override;
+ virtual void handleIncomingOwnMessage(std::shared_ptr<Message> message) override;
protected:
- virtual void cancelReplaces() SWIFTEN_OVERRIDE;
- virtual JID getBaseJID() SWIFTEN_OVERRIDE;
- virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) SWIFTEN_OVERRIDE;
- virtual bool shouldIgnoreMessage(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
- virtual JID messageCorrectionJID(const JID& fromJID) SWIFTEN_OVERRIDE;
+ 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) SWIFTEN_OVERRIDE;
- virtual void postSendMessage(const std::string &body, std::shared_ptr<Stanza> sentStanza) SWIFTEN_OVERRIDE;
- virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) SWIFTEN_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) SWIFTEN_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) SWIFTEN_OVERRIDE;
- virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE;
- virtual void preSendMessageRequest(std::shared_ptr<Message>) SWIFTEN_OVERRIDE;
- virtual std::string senderHighlightNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
- virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
- virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message>) const SWIFTEN_OVERRIDE;
+ 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;
+ virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message>) const override;
void handleStanzaAcked(std::shared_ptr<Stanza> stanza);
- virtual void dayTicked() SWIFTEN_OVERRIDE { lastWasPresence_ = false; }
+ virtual void dayTicked() override { lastWasPresence_ = false; }
void handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/);
- virtual void handleBareJIDCapsChanged(const JID& jid) SWIFTEN_OVERRIDE;
+ virtual void handleBareJIDCapsChanged(const JID& jid) override;
void handleFileTransferCancel(std::string /* id */);
void handleFileTransferStart(std::string /* id */, std::string /* description */);
@@ -97,15 +96,12 @@ namespace Swift {
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_;
@@ -119,4 +115,3 @@ namespace Swift {
boost::optional<ChatWindow::AlertID> blockedContactAlert_;
};
}
-
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 19bbf8d..3805084 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -20,11 +20,14 @@
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Elements/Delay.h>
+#include <Swiften/Elements/DeliveryReceipt.h>
+#include <Swiften/Elements/DeliveryReceiptRequest.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/Chattables.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <Swift/Controllers/Highlighting/HighlightManager.h>
#include <Swift/Controllers/Highlighting/Highlighter.h>
@@ -38,11 +41,12 @@
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, Chattables& chattables) : 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), chattables_(chattables) {
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));
+ scopedConnectionResendMessage_ = chatWindow_->onResendMessageRequest.connect(boost::bind(&ChatControllerBase::handleResendMessageRequest, this, _1));
entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1));
highlighter_ = highlightManager->createHighlighter(nickResolver);
ChatControllerBase::setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());
@@ -61,6 +65,7 @@ ChatWindow* ChatControllerBase::detachChatWindow() {
chatWindow_->onContinuationsBroken.disconnect(boost::bind(&ChatControllerBase::handleContinuationsBroken, this));
chatWindow_->onSendMessageRequest.disconnect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));
chatWindow_->onAllMessagesRead.disconnect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));
+ scopedConnectionResendMessage_.disconnect();
ChatWindow* chatWindow = chatWindow_;
chatWindow_ = nullptr;
return chatWindow;
@@ -113,8 +118,8 @@ void ChatControllerBase::handleAllMessagesRead() {
}
}
-int ChatControllerBase::getUnreadCount() {
- return boost::numeric_cast<int>(targetedUnreadMessages_.size());
+size_t ChatControllerBase::getUnreadCount() {
+ return targetedUnreadMessages_.size();
}
void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) {
@@ -188,16 +193,44 @@ ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std
}
void ChatControllerBase::updateMessageCount() {
- chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size()));
+ auto baseJID = getBaseJID();
+ auto state = chattables_.getState(baseJID);
+ state.unreadCount = unreadMessages_.size();
+ chatWindow_->setUnreadMessageCount(state.unreadCount);
+ chattables_.setState(baseJID, state);
+#ifndef NOT_YET
onUnreadCountChanged();
+#endif
}
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);
}
}
@@ -287,7 +320,9 @@ void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> mes
addMessageHandleIncomingMessage(from, chatMessage, message->getID(), senderIsSelf, label, timeStamp);
}
+#ifdef SWIFT_EXPERIMENTAL_HISTORY
logMessage(body, from, selfJID_, timeStamp, true);
+#endif
}
chatWindow_->show();
updateMessageCount();
@@ -342,7 +377,7 @@ void ChatControllerBase::handleMUCInvitation(Message::ref message) {
MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>();
if (autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) {
- eventStream_->send(std::make_shared<JoinMUCUIEvent>(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true));
+ eventStream_->send(std::make_shared<JoinMUCUIEvent>(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, true));
} else {
MUCInviteEvent::ref inviteEvent = std::make_shared<MUCInviteEvent>(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true, invite->getIsImpromptu());
handleGeneralMUCInvitation(inviteEvent);
@@ -365,4 +400,22 @@ void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) {
handleGeneralMUCInvitation(inviteEvent);
}
+void ChatControllerBase::handleResendMessageRequest(const std::string& id) {
+ if (failedStanzas_.find(id) != failedStanzas_.end()) {
+ if (auto resendMsg = std::dynamic_pointer_cast<Message>(failedStanzas_[id])) {
+ stanzaChannel_->sendMessage(resendMsg);
+ if (stanzaChannel_->getStreamManagementEnabled()) {
+ chatWindow_->setAckState(id, ChatWindow::Pending);
+ unackedStanzas_[failedStanzas_[id]] = id;
+ }
+ if (resendMsg->getPayload<DeliveryReceiptRequest>()) {
+ requestedReceipts_[resendMsg->getID()] = id;
+ chatWindow_->setMessageReceiptState(id, ChatWindow::ReceiptRequested);
+ }
+ lastWasPresence_ = false;
+ failedStanzas_.erase(id);
+ }
+ }
+}
+
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 88cb95c..92c6175 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -28,6 +28,7 @@
#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>
@@ -36,11 +37,12 @@ namespace Swift {
class AutoAcceptMUCInviteDecider;
class AvatarManager;
class ChatMessageParser;
+ class Chattables;
class ChatWindowFactory;
class EntityCapsProvider;
class EventController;
- class HighlightManager;
class Highlighter;
+ class HighlightManager;
class IQRouter;
class NickResolver;
class StanzaChannel;
@@ -71,7 +73,7 @@ namespace Swift {
boost::signals2::signal<void (const std::string& /*activity*/)> onActivity;
boost::signals2::signal<void ()> onUnreadCountChanged;
boost::signals2::signal<void ()> onWindowClosed;
- int getUnreadCount();
+ size_t getUnreadCount();
const JID& getToJID() {return toJID_;}
void handleCapsChanged(const JID& jid);
void setCanStartImpromptuChats(bool supportsImpromptu);
@@ -79,7 +81,7 @@ namespace Swift {
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, Chattables& chattables);
/**
* Pass the Message appended, and the stanza used to send it.
@@ -113,6 +115,7 @@ namespace Swift {
* What JID should be used for last message correction (XEP-0308) tracking.
*/
virtual JID messageCorrectionJID(const JID& fromJID) = 0;
+ virtual void handleResendMessageRequest(const std::string& id);
private:
IDGenerator idGenerator_;
@@ -150,5 +153,13 @@ namespace Swift {
AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider_;
UIEventStream* eventStream_;
bool lastWasPresence_ = false;
+ std::string roomSecurityMarking_;
+ std::string previousMessageSecurityMarking_;
+ SettingsProvider* settings_;
+ boost::signals2::scoped_connection scopedConnectionResendMessage_;
+ std::map<std::string, std::string> requestedReceipts_;
+ std::map<std::shared_ptr<Stanza>, std::string> unackedStanzas_;
+ std::map<std::string, std::shared_ptr<Stanza>> failedStanzas_;
+ Chattables& chattables_;
};
}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 19400f9..6530a7e 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -34,6 +34,7 @@
#include <Swiften/VCards/VCardManager.h>
#include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h>
+#include <Swift/Controllers/Chat/Chattables.h>
#include <Swift/Controllers/Chat/ChatController.h>
#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swift/Controllers/Chat/ChatListWindowChatBoostSerialize.h>
@@ -66,7 +67,9 @@ namespace Swift {
typedef std::pair<JID, ChatController*> JIDChatControllerPair;
typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
+#ifndef NOT_YET
#define RECENT_CHATS "recent_chats"
+#endif
ChatsManager::ChatsManager(
JID jid, StanzaChannel* stanzaChannel,
@@ -78,7 +81,9 @@ ChatsManager::ChatsManager(
PresenceOracle* presenceOracle,
PresenceSender* presenceSender,
UIEventStream* uiEventStream,
+#ifndef NOT_YET
ChatListWindowFactory* chatListWindowFactory,
+#endif
bool useDelayForLatency,
TimerFactory* timerFactory,
MUCRegistry* mucRegistry,
@@ -95,7 +100,8 @@ ChatsManager::ChatsManager(
HighlightManager* highlightManager,
ClientBlockListManager* clientBlockListManager,
const std::map<std::string, std::string>& emoticons,
- VCardManager* vcardManager) :
+ VCardManager* vcardManager,
+ Chattables& chattables) :
jid_(jid),
joinMUCWindowFactory_(joinMUCWindowFactory),
useDelayForLatency_(useDelayForLatency),
@@ -111,7 +117,8 @@ ChatsManager::ChatsManager(
highlightManager_(highlightManager),
emoticons_(emoticons),
clientBlockListManager_(clientBlockListManager),
- vcardManager_(vcardManager) {
+ vcardManager_(vcardManager),
+ chattables_(chattables) {
timerFactory_ = timerFactory;
eventController_ = eventController;
stanzaChannel_ = stanzaChannel;
@@ -127,12 +134,12 @@ ChatsManager::ChatsManager(
profileSettings_ = profileSettings;
presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));
uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
-
+#ifndef NOT_YET
chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_);
chatListWindow_->onMUCBookmarkActivated.connect(boost::bind(&ChatsManager::handleMUCBookmarkActivated, this, _1));
chatListWindow_->onRecentActivated.connect(boost::bind(&ChatsManager::handleRecentActivated, this, _1));
chatListWindow_->onClearRecentsRequested.connect(boost::bind(&ChatsManager::handleClearRecentsRequested, this));
-
+#endif
joinMUCWindow_ = nullptr;
mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, profileSettings_);
mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));
@@ -146,18 +153,22 @@ ChatsManager::ChatsManager(
roster_->onJIDUpdated.connect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1));
roster_->onRosterCleared.connect(boost::bind(&ChatsManager::handleRosterCleared, this));
+ chattables_.onActivated.connect(boost::bind(&ChatsManager::handleChattableActivated, this, _1));
+
settings_->onSettingChanged.connect(boost::bind(&ChatsManager::handleSettingChanged, this, _1));
userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS);
setupBookmarks();
+#ifndef NOT_YET
loadRecents();
-
+#endif
autoAcceptMUCInviteDecider_ = new AutoAcceptMUCInviteDecider(jid.getDomain(), roster_, settings_);
}
ChatsManager::~ChatsManager() {
settings_->onSettingChanged.disconnect(boost::bind(&ChatsManager::handleSettingChanged, this, _1));
+ chattables_.onActivated.disconnect(boost::bind(&ChatsManager::handleChattableActivated, this, _1));
roster_->onJIDAdded.disconnect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1));
roster_->onJIDRemoved.disconnect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1));
roster_->onJIDUpdated.disconnect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1));
@@ -176,6 +187,7 @@ ChatsManager::~ChatsManager() {
delete autoAcceptMUCInviteDecider_;
}
+#ifndef NOT_YET
void ChatsManager::saveRecents() {
std::stringstream serializeStream;
boost::archive::text_oarchive oa(serializeStream);
@@ -208,6 +220,7 @@ void ChatsManager::handleClearRecentsRequested() {
saveRecents();
handleUnreadCountChanged(nullptr);
}
+#endif
void ChatsManager::handleJIDAddedToRoster(const JID &jid) {
updatePresenceReceivingStateOnChatController(jid);
@@ -242,6 +255,7 @@ void ChatsManager::updatePresenceReceivingStateOnChatController(const JID &jid)
}
}
+#ifndef NOT_YET
ChatListWindow::Chat ChatsManager::updateChatStatusAndAvatarHelper(const ChatListWindow::Chat& chat) const {
ChatListWindow::Chat fixedChat = chat;
if (fixedChat.isMUC) {
@@ -309,6 +323,7 @@ void ChatsManager::loadRecents() {
}
handleUnreadCountChanged(nullptr);
}
+#endif
void ChatsManager::setupBookmarks() {
if (!mucBookmarkManager_) {
@@ -317,35 +332,47 @@ void ChatsManager::setupBookmarks() {
mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&ChatsManager::handleMUCBookmarkAdded, this, _1));
mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&ChatsManager::handleMUCBookmarkRemoved, this, _1));
+#ifndef NOT_YET
if (chatListWindow_) {
chatListWindow_->setBookmarksEnabled(false);
chatListWindow_->clearBookmarks();
}
+#endif
}
}
void ChatsManager::handleBookmarksReady() {
+#ifndef NOT_YET
if (chatListWindow_) {
chatListWindow_->setBookmarksEnabled(true);
}
+#endif
}
void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) {
if (bookmark.getRoom().isBare() && !bookmark.getRoom().getNode().empty()) {
std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom());
if (it == mucControllers_.end() && bookmark.getAutojoin()) {
- handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false );
+ handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false);
}
+#ifndef NOT_YET
+ //Only one entry of the bookmark should exist
+ chatListWindow_->removeMUCBookmark(bookmark);
chatListWindow_->addMUCBookmark(bookmark);
+#endif
+ chattables_.addJID(bookmark.getRoom(), Chattables::State::Type::Room);
}
}
void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) {
+#ifndef NOT_YET
chatListWindow_->removeMUCBookmark(bookmark);
+#endif
}
+#ifndef NOT_YET
ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const std::string& activity, bool privateMessage) {
- int unreadCount = 0;
+ size_t unreadCount = 0;
if (mucRegistry_->isMUC(jid)) {
MUCController* controller = mucControllers_[jid.toBare()];
StatusShow::Type type = StatusShow::None;
@@ -388,8 +415,10 @@ ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const
return ChatListWindow::Chat(bareishJID, nickResolver_->jidToNick(bareishJID), activity, unreadCount, type, avatarPath, false, privateMessage);
}
}
+#endif
void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity, bool isMUC) {
+#ifndef NOT_YET
const bool privateMessage = mucRegistry_->isMUC(jid.toBare()) && !isMUC;
ChatListWindow::Chat chat = createChatListChatItem(jid, activity, privateMessage);
/* FIXME: handle nick changes */
@@ -405,15 +434,20 @@ void ChatsManager::handleChatActivity(const JID& jid, const std::string& activit
mucControllers_[jid]->setChatWindowTitle(chatListWindowIter->getTitle());
}
}
+#endif
}
void ChatsManager::handleChatClosed(const JID& /*jid*/) {
cleanupPrivateMessageRecents();
+#ifndef NOT_YET
chatListWindow_->setRecents(recentChats_);
+#endif
}
+#ifndef NOT_YET
+
void ChatsManager::handleUnreadCountChanged(ChatControllerBase* controller) {
- int unreadTotal = 0;
+ size_t unreadTotal = 0;
bool controllerIsMUC = dynamic_cast<MUCController*>(controller);
bool isPM = controller && !controllerIsMUC && mucRegistry_->isMUC(controller->getToJID().toBare());
for (ChatListWindow::Chat& chatItem : recentChats_) {
@@ -445,7 +479,9 @@ boost::optional<ChatListWindow::Chat> ChatsManager::removeExistingChat(const Cha
return boost::optional<ChatListWindow::Chat>();
}
}
+#endif
+#ifndef NOT_YET
void ChatsManager::cleanupPrivateMessageRecents() {
/* if we leave a MUC and close a PM, remove it's recent chat entry */
const std::list<ChatListWindow::Chat> chats = recentChats_;
@@ -483,23 +519,38 @@ void ChatsManager::prependRecent(const ChatListWindow::Chat& chat) {
}
recentChats_.push_back(mergedChat);
}
+#endif
void ChatsManager::handleUserLeftMUC(MUCController* mucController) {
std::map<JID, MUCController*>::iterator it;
for (it = mucControllers_.begin(); it != mucControllers_.end(); ++it) {
if ((*it).second == mucController) {
+#ifndef NOT_YET
for (ChatListWindow::Chat& chat : recentChats_) {
if (chat.isMUC && chat.jid == (*it).first) {
chat.statusType = StatusShow::None;
}
}
+#endif
+ const auto& jid = it->first;
+ auto state = chattables_.getState(jid);
+ state.status = StatusShow::None;
+ chattables_.setState(jid, state);
+ //If user deletes bookmark from chatListWindow_ and then decides to leave the room, or if the server doesn't support bookmarks, the bookmark will not exist.
+ if (auto bookmarkFound = mucBookmarkManager_->lookupBookmark(jid)) {
+ MUCBookmark newBookmark(bookmarkFound.get());
+ newBookmark.setAutojoin(false);
+ mucBookmarkManager_->replaceBookmark(bookmarkFound.get(), newBookmark);
+ }
mucControllers_.erase(it);
delete mucController;
break;
}
}
cleanupPrivateMessageRecents();
+#ifndef NOT_YET
chatListWindow_->setRecents(recentChats_);
+#endif
}
void ChatsManager::handleSettingChanged(const std::string& settingPath) {
@@ -576,7 +627,7 @@ void ChatsManager::handleUIEvent(std::shared_ptr<UIEvent> event) {
invitees_[roomJID].insert(jid);
}
// join muc
- MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional<std::string>(), nickResolver_->jidToNick(jid_), false, true, true);
+ MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional<std::string>(), nickResolver_->jidToNick(jid_), true, true);
mucControllers_[roomJID]->onImpromptuConfigCompleted.connect(boost::bind(&ChatsManager::finalizeImpromptuJoin, this, muc, createImpromptuMUCEvent->getJIDs(), createImpromptuMUCEvent->getReason(), boost::optional<JID>()));
mucControllers_[roomJID]->activateChatWindow();
}
@@ -586,7 +637,7 @@ void ChatsManager::handleUIEvent(std::shared_ptr<UIEvent> event) {
mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark());
}
else if (JoinMUCUIEvent::ref joinEvent = std::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
- handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew(), joinEvent->isImpromptu());
+ handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getCreateAsReservedRoomIfNew(), joinEvent->isImpromptu());
mucControllers_[joinEvent->getJID()]->activateChatWindow();
}
else if (std::shared_ptr<RequestJoinMUCUIEvent> joinEvent = std::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
@@ -600,6 +651,7 @@ void ChatsManager::handleUIEvent(std::shared_ptr<UIEvent> event) {
}
}
+#ifndef NOT_YET
void ChatsManager::markAllRecentsOffline() {
for (ChatListWindow::Chat& chat : recentChats_) {
chat.setStatusType(StatusShow::None);
@@ -607,6 +659,7 @@ void ChatsManager::markAllRecentsOffline() {
chatListWindow_->setRecents(recentChats_);
}
+#endif
void ChatsManager::handleTransformChatToMUC(ChatController* chatController, ChatWindow* chatWindow, const std::vector<JID>& jidsToInvite, const std::string& reason) {
JID reuseChatInvite = chatController->getToJID();
@@ -620,7 +673,7 @@ void ChatsManager::handleTransformChatToMUC(ChatController* chatController, Chat
JID roomJID = JID(idGenerator_.generateID(), localMUCServiceJID_);
// join muc
- MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional<std::string>(), nickResolver_->jidToNick(jid_), false, true, true, chatWindow);
+ MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional<std::string>(), nickResolver_->jidToNick(jid_), true, true, chatWindow);
mucControllers_[roomJID]->onImpromptuConfigCompleted.connect(boost::bind(&ChatsManager::finalizeImpromptuJoin, this, muc, jidsToInvite, reason, boost::optional<JID>(reuseChatInvite)));
}
@@ -629,7 +682,7 @@ void ChatsManager::handleTransformChatToMUC(ChatController* chatController, Chat
*/
void ChatsManager::handlePresenceChange(std::shared_ptr<Presence> newPresence) {
if (mucRegistry_->isMUC(newPresence->getFrom().toBare())) return;
-
+#ifndef NOT_YET
for (ChatListWindow::Chat& chat : recentChats_) {
if (newPresence->getFrom().toBare() == chat.jid.toBare() && !chat.isMUC) {
Presence::ref presence = presenceOracle_->getHighestPriorityPresence(chat.jid.toBare());
@@ -638,8 +691,9 @@ void ChatsManager::handlePresenceChange(std::shared_ptr<Presence> newPresence) {
break;
}
}
-
+#endif
//if (newPresence->getType() != Presence::Unavailable) return;
+
JID fullJID(newPresence->getFrom());
std::map<JID, ChatController*>::iterator it = chatControllers_.find(fullJID);
if (it == chatControllers_.end()) return;
@@ -654,21 +708,25 @@ void ChatsManager::setAvatarManager(AvatarManager* avatarManager) {
avatarManager_->onAvatarChanged.disconnect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1));
}
avatarManager_ = avatarManager;
+#ifndef NOT_YET
for (ChatListWindow::Chat& chat : recentChats_) {
if (!chat.isMUC) {
chat.setAvatarPath(avatarManager_->getAvatarPath(chat.jid));
}
}
+#endif
avatarManager_->onAvatarChanged.connect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1));
}
void ChatsManager::handleAvatarChanged(const JID& jid) {
+#ifndef NOT_YET
for (ChatListWindow::Chat& chat : recentChats_) {
if (!chat.isMUC && jid.toBare() == chat.jid.toBare()) {
chat.setAvatarPath(avatarManager_->getAvatarPath(jid));
break;
}
}
+#endif
}
void ChatsManager::setServerDiscoInfo(std::shared_ptr<DiscoInfo> info) {
@@ -705,10 +763,11 @@ void ChatsManager::setOnline(bool enabled) {
localMUCServiceFinderWalker_->onWalkComplete.connect(boost::bind(&ChatsManager::handleLocalServiceWalkFinished, this));
localMUCServiceFinderWalker_->beginWalk();
}
-
+#ifndef NOT_YET
if (chatListWindow_) {
chatListWindow_->setBookmarksEnabled(enabled);
}
+#endif
}
void ChatsManager::handleChatRequest(const std::string &contact) {
@@ -732,12 +791,14 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &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_, chattables_);
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));
+#ifndef NOT_YET
controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller));
+#endif
controller->onConvertToMUC.connect(boost::bind(&ChatsManager::handleTransformChatToMUC, this, controller, _1, _2, _3));
updatePresenceReceivingStateOnChatController(contact);
controller->setCanStartImpromptuChats(!localMUCServiceJID_.toString().empty());
@@ -786,20 +847,8 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
chatControllers_[to]->setToJID(to);
}
-MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& password, const boost::optional<std::string>& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow) {
+MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& password, const boost::optional<std::string>& nickMaybe, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow) {
MUC::ref muc;
- if (addAutoJoin) {
- MUCBookmark bookmark(mucJID, mucJID.getNode());
- bookmark.setAutojoin(true);
- 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()) {
@@ -821,7 +870,11 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
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_, chattables_);
+ chattables_.addJID(muc->getJID(), Chattables::State::Type::Room);
+ auto state = chattables_.getState(muc->getJID());
+ state.status = StatusShow::Online;
+ chattables_.setState(muc->getJID(), state);
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
@@ -835,7 +888,9 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
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));
+#ifndef NOT_YET
+ controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller));
+#endif
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
@@ -845,12 +900,30 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
}
handleChatActivity(mucJID.toBare(), "", true);
}
-
+#ifndef NOT_YET
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());
}
-
+#endif
+ if (auto existingBookmark = mucBookmarkManager_->lookupBookmark(mucJID)) {
+ if (!existingBookmark->getAutojoin()) {
+ MUCBookmark newbookmark(existingBookmark.get());
+ newbookmark.setAutojoin(true);
+ mucBookmarkManager_->replaceBookmark(*existingBookmark, newbookmark);
+ }
+ }
+ else {
+ MUCBookmark bookmark(mucJID, mucJID.getNode());
+ bookmark.setAutojoin(true);
+ if (nickMaybe) {
+ bookmark.setNick(*nickMaybe);
+ }
+ if (password) {
+ bookmark.setPassword(*password);
+ }
+ mucBookmarkManager_->addBookmark(bookmark);
+ }
mucControllers_[mucJID]->showChatWindow();
return muc;
}
@@ -960,11 +1033,11 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> incomingMessag
ChatWindow* window = controller->detachChatWindow();
chatControllers_.erase(fromJID);
delete controller;
- handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true, window);
+ handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, true, window);
return;
}
} else {
- handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true);
+ handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, true);
return;
}
}
@@ -1023,7 +1096,7 @@ void ChatsManager::handleWhiteboardStateChange(const JID& contact, const ChatWin
chatListWindow_->removeWhiteboardSession(contact.toBare());
}
}
-
+#ifndef NOT_YET
void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
if (chat.isMUC && !chat.impromptuJIDs.empty()) {
typedef std::pair<std::string, JID> StringJIDPair;
@@ -1036,13 +1109,30 @@ void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
else if (chat.isMUC) {
bool isImpromptu = (!chat.inviteesNames.empty() || !chat.impromptuJIDs.empty());
/* FIXME: This means that recents requiring passwords will just flat-out not work */
- uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(chat.jid, boost::optional<std::string>(), chat.nick, false, false, isImpromptu));
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(chat.jid, boost::optional<std::string>(), chat.nick, false, isImpromptu));
}
else {
uiEventStream_->send(std::make_shared<RequestChatUIEvent>(chat.jid));
}
}
+#endif
+
+void ChatsManager::handleChattableActivated(const JID& jid) {
+ auto state = chattables_.getState(jid);
+ if (state.type == Chattables::State::Type::Person) {
+ uiEventStream_->send(std::make_shared<RequestChatUIEvent>(jid));
+ }
+ else if (state.type == Chattables::State::Type::Room) {
+ if (auto foundBookmark = mucBookmarkManager_->lookupBookmark(jid)) {
+ handleMUCBookmarkActivated(foundBookmark.get());
+ }
+ else {
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(jid, boost::optional<std::string>(), boost::optional<std::string>())); // Just a quick hack to reuse already open MUCs
+ }
+ }
+}
+
void ChatsManager::handleLocalServiceFound(const JID& service, std::shared_ptr<DiscoInfo> info) {
for (DiscoInfo::Identity identity : info->getIdentities()) {
if ((identity.getCategory() == "directory"
@@ -1068,17 +1158,21 @@ void ChatsManager::handleLocalServiceWalkFinished() {
onImpromptuMUCServiceDiscovered(impromptuMUCSupported);
}
+#ifndef NOT_YET
std::vector<ChatListWindow::Chat> ChatsManager::getRecentChats() const {
return std::vector<ChatListWindow::Chat>(recentChats_.begin(), recentChats_.end());
}
+#endif
std::vector<Contact::ref> Swift::ChatsManager::getContacts(bool withMUCNicks) {
std::vector<Contact::ref> result;
+#ifndef NOT_YET
for (ChatListWindow::Chat chat : recentChats_) {
if (!chat.isMUC) {
result.push_back(std::make_shared<Contact>(chat.chatName.empty() ? chat.jid.toString() : chat.chatName, chat.jid, chat.statusType, chat.avatarPath));
}
}
+#endif
if (withMUCNicks) {
/* collect MUC nicks */
typedef std::map<JID, MUCController*>::value_type Item;
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 6004347..e120831 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -27,49 +27,52 @@
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
namespace Swift {
- class EventController;
+ class AutoAcceptMUCInviteDecider;
+ class AvatarManager;
class ChatController;
class ChatControllerBase;
- class MUCController;
- class MUCManager;
+ class ChatListWindowFactory;
+ class ChatMessageParser;
+ class Chattables;
+ class ClientBlockListManager;
+ class DirectedPresenceSender;
+ class DiscoServiceWalker;
+ class EntityCapsProvider;
+ class EventController;
+ class FileTransferController;
+ class FileTransferOverview;
+ class HighlightManager;
+ class HistoryController;
+ class IQRouter;
class JoinMUCWindow;
class JoinMUCWindowFactory;
+ class MUCBookmarkManager;
+ class MUCController;
+ class MUCManager;
+ class MUCSearchController;
+ class MUCSearchWindowFactory;
class NickResolver;
class PresenceOracle;
- class AvatarManager;
- class StanzaChannel;
- class IQRouter;
class PresenceSender;
- class MUCBookmarkManager;
- class ChatListWindowFactory;
- class TimerFactory;
- class EntityCapsProvider;
- class DirectedPresenceSender;
- class MUCSearchWindowFactory;
class ProfileSettingsProvider;
- class MUCSearchController;
- class FileTransferOverview;
- class FileTransferController;
- class XMPPRoster;
class SettingsProvider;
- class WhiteboardManager;
- class HistoryController;
- class HighlightManager;
- class ClientBlockListManager;
- class ChatMessageParser;
- class DiscoServiceWalker;
- class AutoAcceptMUCInviteDecider;
+ class StanzaChannel;
+ class TimerFactory;
class VCardManager;
+ class WhiteboardManager;
+ class XMPPRoster;
class ChatsManager : public ContactProvider {
public:
- ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, const std::map<std::string, std::string>& emoticons, VCardManager* vcardManager);
+ ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, const std::map<std::string, std::string>& emoticons, VCardManager* vcardManager, Chattables& chattables);
virtual ~ChatsManager();
void setAvatarManager(AvatarManager* avatarManager);
void setOnline(bool enabled);
void setServerDiscoInfo(std::shared_ptr<DiscoInfo> info);
void handleIncomingMessage(std::shared_ptr<Message> incomingMessage);
+#ifndef NOT_YET
std::vector<ChatListWindow::Chat> getRecentChats() const;
+#endif
virtual std::vector<Contact::ref> getContacts(bool withMUCNicks);
boost::signals2::signal<void (bool supportsImpromptu)> onImpromptuMUCServiceDiscovered;
@@ -86,10 +89,12 @@ namespace Swift {
};
private:
+#ifndef NOT_YET
ChatListWindow::Chat createChatListChatItem(const JID& jid, const std::string& activity, bool privateMessage);
+#endif
void handleChatRequest(const std::string& contact);
void finalizeImpromptuJoin(MUC::ref muc, const std::vector<JID>& jidsToInvite, const std::string& reason, const boost::optional<JID>& reuseChatJID = boost::optional<JID>());
- MUC::ref handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& password, const boost::optional<std::string>& nick, bool addAutoJoin, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow = nullptr);
+ MUC::ref handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& password, const boost::optional<std::string>& nick, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow = nullptr);
void handleSearchMUCRequest();
void handleMUCSelectedAfterSearch(const JID&);
void rebindControllerJID(const JID& from, const JID& to);
@@ -105,18 +110,25 @@ namespace Swift {
void handleNewFileTransferController(FileTransferController*);
void handleWhiteboardSessionRequest(const JID& contact, bool senderIsSelf);
void handleWhiteboardStateChange(const JID& contact, const ChatWindow::WhiteboardSessionState state);
+#ifndef NOT_YET
boost::optional<ChatListWindow::Chat> removeExistingChat(const ChatListWindow::Chat& chat);
+#endif
bool messageCausesSessionBinding(std::shared_ptr<Message> message);
void cleanupPrivateMessageRecents();
+#ifndef NOT_YET
void appendRecent(const ChatListWindow::Chat& chat);
void prependRecent(const ChatListWindow::Chat& chat);
+#endif
void setupBookmarks();
+#ifndef NOT_YET
void loadRecents();
void saveRecents();
void handleChatMadeRecent();
void handleMUCBookmarkActivated(const MUCBookmark&);
void handleRecentActivated(const ChatListWindow::Chat&);
void handleUnreadCountChanged(ChatControllerBase* controller);
+#endif
+ void handleChattableActivated(const JID& jid);
void handleAvatarChanged(const JID& jid);
void handleClearRecentsRequested();
void handleJIDAddedToRoster(const JID&);
@@ -126,12 +138,13 @@ namespace Swift {
void handleSettingChanged(const std::string& settingPath);
void markAllRecentsOffline();
void handleTransformChatToMUC(ChatController* chatController, ChatWindow* chatWindow, const std::vector<JID>& jidsToInvite, const std::string& reason);
-
void handleLocalServiceFound(const JID& service, std::shared_ptr<DiscoInfo> info);
void handleLocalServiceWalkFinished();
-
void updatePresenceReceivingStateOnChatController(const JID&);
+#ifndef NOT_YET
ChatListWindow::Chat updateChatStatusAndAvatarHelper(const ChatListWindow::Chat& chat) const;
+#endif
+
ChatController* getChatControllerOrFindAnother(const JID &contact);
@@ -139,6 +152,9 @@ namespace Swift {
ChatController* getChatControllerOrCreate(const JID &contact);
ChatController* getChatControllerIfExists(const JID &contact, bool rebindIfNeeded = true);
+ protected:
+ MUCBookmarkManager* mucBookmarkManager_;
+
private:
std::map<JID, MUCController*> mucControllers_;
std::map<JID, ChatController*> chatControllers_;
@@ -154,9 +170,10 @@ namespace Swift {
AvatarManager* avatarManager_;
PresenceSender* presenceSender_;
UIEventStream* uiEventStream_;
- MUCBookmarkManager* mucBookmarkManager_;
std::shared_ptr<DiscoInfo> serverDiscoInfo_;
+#ifndef NOT_YET
ChatListWindow* chatListWindow_;
+#endif
JoinMUCWindow* joinMUCWindow_;
boost::signals2::scoped_connection uiEventConnection_;
bool useDelayForLatency_;
@@ -165,7 +182,9 @@ namespace Swift {
EntityCapsProvider* entityCapsProvider_;
MUCManager* mucManager;
MUCSearchController* mucSearchController_;
+#ifndef NOT_YET
std::list<ChatListWindow::Chat> recentChats_;
+#endif
ProfileSettingsProvider* profileSettings_;
FileTransferOverview* ftOverview_;
XMPPRoster* roster_;
@@ -182,6 +201,7 @@ namespace Swift {
AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider_;
IDGenerator idGenerator_;
VCardManager* vcardManager_;
+ Chattables& chattables_;
std::map<JID, std::set<JID>> invitees_;
};
diff --git a/Swift/Controllers/Chat/Chattables.cpp b/Swift/Controllers/Chat/Chattables.cpp
new file mode 100644
index 0000000..707046f
--- /dev/null
+++ b/Swift/Controllers/Chat/Chattables.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Chat/Chattables.h>
+
+namespace Swift {
+
+const std::vector<JID>& Chattables::get() const {
+ return list_;
+}
+
+const Chattables::State& Chattables::getState(const JID& jid) const {
+ auto it = states_.find(jid);
+ return it != states_.end() ? it->second : unknown_;
+}
+
+void Chattables::addJID(const JID& jid, State::Type type) {
+ if (states_.find(jid) != states_.end()) {
+ return;
+ }
+ State state;
+ state.type = type;
+ state.jid = jid;
+ onBeginAdd(static_cast<int>(list_.size()));
+ list_.push_back(jid);
+ states_[jid] = state;
+ onAdded();
+}
+
+void Chattables::setState(const JID& jid, State state) {
+ auto stateIter = states_.find(jid);
+ if (stateIter == states_.end()) {
+ return;
+ }
+ stateIter->second = state;
+ auto listPos = static_cast<int>(std::distance(list_.begin(), std::find(list_.begin(), list_.end(), jid)));
+ onChanged(jid, listPos);
+}
+
+}
diff --git a/Swift/Controllers/Chat/Chattables.h b/Swift/Controllers/Chat/Chattables.h
new file mode 100644
index 0000000..3b5817a
--- /dev/null
+++ b/Swift/Controllers/Chat/Chattables.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include <boost/signals2.hpp>
+
+#include <Swiften/Elements/StatusShow.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+class Chattables {
+ public:
+ struct State {
+ enum class Type {Room, Person};
+ JID jid;
+ /// Empty for no name
+ std::string name;
+ size_t unreadCount = 0;
+ Type type;
+ StatusShow::Type status = StatusShow::None;
+ //avatar
+ //status
+ };
+ const std::vector<JID>& get() const;
+ const State& getState(const JID& jid) const;
+
+ void addJID(const JID& jid, State::Type type);
+ void setState(const JID& jid, State state);
+
+ boost::signals2::signal<void (int)> onBeginAdd;
+ boost::signals2::signal<void ()> onAdded;
+ boost::signals2::signal<void (const JID&, int)> onChanged;
+ /// The UI has activated a chattable item (e.g. clicked in the roster)
+ boost::signals2::signal<void (const JID&)> onActivated;
+ private:
+ std::vector<JID> list_;
+ std::map<JID, State> states_;
+ State unknown_;
+};
+}
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index d10e6d4..30d4933 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -23,6 +23,7 @@
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Disco/GetDiscoInfoRequest.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/Thread.h>
#include <Swiften/MUC/MUC.h>
@@ -35,6 +36,7 @@
#include <SwifTools/TabComplete.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <Swift/Controllers/Chat/Chattables.h>
#include <Swift/Controllers/Highlighting/Highlighter.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
@@ -59,17 +61,6 @@
namespace Swift {
-class MUCBookmarkPredicate {
- public:
- MUCBookmarkPredicate(const JID& mucJID) : roomJID_(mucJID) { }
- bool operator()(const MUCBookmark& operand) {
- return operand.getRoom() == roomJID_;
- }
-
- private:
- JID roomJID_;
-};
-
/**
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
@@ -98,8 +89,10 @@ MUCController::MUCController (
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,
+ Chattables& chattables) :
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), nickResolver, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider, settings, chattables), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) {
assert(avatarManager_);
parting_ = true;
@@ -112,7 +105,7 @@ MUCController::MUCController (
isInitialJoin_ = true;
chatWindowTitle_ = "";
- roster_ = std::unique_ptr<Roster>(new Roster(false, true));
+ roster_ = std::make_unique<Roster>(false, true);
rosterVCardProvider_ = new RosterVCardProvider(roster_.get(), vcardManager, JID::WithResource);
completer_ = new TabComplete();
chatWindow_->setRosterModel(roster_.get());
@@ -171,14 +164,7 @@ MUCController::MUCController (
mucBookmarkManagerBookmarkAddedConnection_ = (mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&MUCController::handleMUCBookmarkAdded, this, _1)));
mucBookmarkManagerBookmarkRemovedConnection_ = (mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&MUCController::handleMUCBookmarkRemoved, this, _1)));
- std::vector<MUCBookmark> mucBookmarks = mucBookmarkManager_->getBookmarks();
- std::vector<MUCBookmark>::iterator bookmarkIterator = std::find_if(mucBookmarks.begin(), mucBookmarks.end(), MUCBookmarkPredicate(muc->getJID()));
- if (bookmarkIterator != mucBookmarks.end()) {
- updateChatWindowBookmarkStatus(*bookmarkIterator);
- }
- else {
- updateChatWindowBookmarkStatus(boost::optional<MUCBookmark>());
- }
+ updateChatWindowBookmarkStatus(mucBookmarkManager_->lookupBookmark(muc->getJID()));
}
MUCController::~MUCController() {
@@ -273,6 +259,9 @@ void MUCController::rejoin() {
lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_);
}
#endif
+
+ requestSecurityMarking();
+
if (lastActivity_ == boost::posix_time::not_a_date_time) {
muc_->joinAs(nick_);
}
@@ -492,6 +481,7 @@ void MUCController::setAvailableRoomActions(const MUCOccupant::Affiliation& affi
if (role <= MUCOccupant::Visitor) {
actions.push_back(ChatWindow::Invite);
}
+ actions.push_back(ChatWindow::Leave);
chatWindow_->setAvailableRoomActions(actions);
}
@@ -1243,4 +1233,63 @@ void MUCController::setChatWindowTitle(const std::string& title) {
chatWindow_->setName(chatWindowTitle_);
}
+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 == "") {
+ 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 && markingForegroundColor->getTextSingleValue() != "") {
+ markingForegroundColorValue = markingForegroundColor->getTextSingleValue();
+ }
+ if (markingBackgroundColor && markingBackgroundColor->getTextSingleValue() != "") {
+ markingBackgroundColorValue = markingBackgroundColor->getTextSingleValue();
+ }
+ 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 258eaaf..bd1148f 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -14,7 +14,6 @@
#include <boost/signals2.hpp>
#include <boost/signals2/connection.hpp>
-#include <Swiften/Base/Override.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/MUCOccupant.h>
#include <Swiften/Elements/Message.h>
@@ -54,14 +53,14 @@ namespace Swift {
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);
- virtual ~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* 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, Chattables& chattables);
+ 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) SWIFTEN_OVERRIDE;
- virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) SWIFTEN_OVERRIDE;
+ 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);
@@ -76,18 +75,18 @@ namespace Swift {
void setChatWindowTitle(const std::string& title);
protected:
- virtual void preSendMessageRequest(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
- virtual bool isIncomingMessageFromMe(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
- virtual std::string senderHighlightNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
- virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
- virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message> message) const SWIFTEN_OVERRIDE;
- virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) SWIFTEN_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) SWIFTEN_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) SWIFTEN_OVERRIDE;
- virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE;
- virtual void cancelReplaces() SWIFTEN_OVERRIDE;
- virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) SWIFTEN_OVERRIDE;
- virtual JID messageCorrectionJID(const JID& fromJID) SWIFTEN_OVERRIDE;
+ 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;
+ virtual void cancelReplaces() override;
+ virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) override;
+ virtual JID messageCorrectionJID(const JID& fromJID) override;
private:
void setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role);
@@ -116,9 +115,9 @@ namespace Swift {
bool messageTargetsMe(std::shared_ptr<Message> message);
void updateJoinParts();
bool shouldUpdateJoinParts();
- virtual void dayTicked() SWIFTEN_OVERRIDE { clearPresenceQueue(); }
+ virtual void dayTicked() override { clearPresenceQueue(); }
void processUserPart();
- virtual void handleBareJIDCapsChanged(const JID& jid) SWIFTEN_OVERRIDE;
+ virtual void handleBareJIDCapsChanged(const JID& jid) override;
void handleConfigureRequest(Form::ref);
void handleConfigurationFailed(ErrorPayload::ref);
void handleConfigurationFormReceived(Form::ref);
@@ -149,6 +148,10 @@ namespace Swift {
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_;
@@ -190,4 +193,3 @@ namespace Swift {
std::string chatWindowTitle_;
};
}
-
diff --git a/Swift/Controllers/Chat/UnitTest/ChatControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatControllerTest.cpp
new file mode 100644
index 0000000..e010656
--- /dev/null
+++ b/Swift/Controllers/Chat/UnitTest/ChatControllerTest.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.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/Disco/DummyEntityCapsProvider.h>
+#include <Swiften/Network/DummyTimerFactory.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/AutoAcceptMUCInviteDecider.h>
+#include <Swift/Controllers/Chat/ChatController.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <Swift/Controllers/Chat/Chattables.h>
+#include <Swift/Controllers/Settings/DummySettingsProvider.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <Swift/Controllers/UnitTest/MockChatWindow.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+
+using namespace Swift;
+
+/**
+ * Most of the ChatController tests are in ChatsManagerTest.
+ * New tests related with ChatController should be added here,
+ * and old tests should be migrated when possible.
+ */
+
+class ExtendedChatController : public ChatController {
+public:
+ ExtendedChatController(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, Chattables& chattables) :
+ ChatController(self, stanzaChannel, iqRouter, chatWindowFactory, contact, nickResolver, presenceOracle, avatarManager, isInMUC, useDelayForLatency, eventStream, timerFactory, eventController, entityCapsProvider, userWantsReceipts, historyController, mucRegistry, highlightManager, clientBlockListManager, chatMessageParser, autoAcceptMUCInviteDecider, settings, chattables) {
+ }
+
+ std::map<std::shared_ptr<Stanza>, std::string> getUnackedStanzas() { return unackedStanzas_; }
+ std::map<std::string, std::shared_ptr<Stanza>> getFailedStanzas() { return failedStanzas_; }
+};
+
+class ChatControllerTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ self_ = JID("alice@wonderland.lit");
+ other_ = JID("whiterabbit@wonderland.lit");
+ stanzaChannel_ = new DummyStanzaChannel();
+ iqChannel_ = new DummyIQChannel();
+ iqRouter_ = new IQRouter(iqChannel_);
+ eventController_ = new EventController();
+ xmppRoster_ = new XMPPRosterImpl();
+ vCardManager_ = new VCardManager(self_, iqRouter_, vCardMemoryStorage_);
+ mucRegistry_ = new MUCRegistry();
+ nickResolver_ = new NickResolver(self_, xmppRoster_, vCardManager_, mucRegistry_);
+ presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_);
+ avatarManager_ = new NullAvatarManager();
+ uiEventStream_ = new UIEventStream();
+ timerFactory_ = new DummyTimerFactory();
+ entityCapsProvider_ = new DummyEntityCapsProvider();
+ settings_ = new DummySettingsProvider();
+ highlightManager_ = new HighlightManager(settings_);
+ highlightManager_->resetToDefaultConfiguration();
+ clientBlockListManager_ = new ClientBlockListManager(iqRouter_);
+ autoAcceptMUCInviteDecider_ = new AutoAcceptMUCInviteDecider(self_.getDomain(), xmppRoster_, settings_);
+ chatMessageParser_ = std::make_shared<ChatMessageParser>(std::map<std::string, std::string>(), highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat);
+ mocks_ = new MockRepository();
+ window_ = new MockChatWindow();
+ chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(other_, uiEventStream_).Return(window_);
+ chattables_ = std::make_unique<Chattables>();
+
+ controller_ = new ExtendedChatController(self_, stanzaChannel_, iqRouter_, chatWindowFactory_, other_, nickResolver_, presenceOracle_, avatarManager_, false, false, uiEventStream_, timerFactory_, eventController_, entityCapsProvider_, false, nullptr, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser_, nullptr, settings_, *chattables_);
+ }
+ virtual void TearDown() {
+ delete controller_;
+ chattables_.reset();
+ chatMessageParser_.reset();
+ delete autoAcceptMUCInviteDecider_;
+ delete clientBlockListManager_;
+ delete highlightManager_;
+ delete settings_;
+ delete entityCapsProvider_;
+ delete timerFactory_;
+ delete uiEventStream_;
+ delete avatarManager_;
+ delete presenceOracle_;
+ delete nickResolver_;
+ delete mucRegistry_;
+ delete vCardManager_;
+ delete xmppRoster_;
+ delete eventController_;
+ delete iqRouter_;
+ delete iqChannel_;
+ delete stanzaChannel_;
+ }
+
+ JID self_, other_;
+ AvatarManager* avatarManager_ = nullptr;
+ ExtendedChatController* controller_ = nullptr;
+ ChatWindowFactory* chatWindowFactory_;
+ ClientBlockListManager* clientBlockListManager_;
+ EventController* eventController_ = nullptr;
+ EntityCapsProvider* entityCapsProvider_ = nullptr;
+ IQChannel* iqChannel_ = nullptr;
+ IQRouter* iqRouter_ = nullptr;
+ MockRepository* mocks_;
+ MockChatWindow* window_;
+ MUCRegistry* mucRegistry_ = nullptr;
+ NickResolver* nickResolver_ = nullptr;
+ PresenceOracle* presenceOracle_ = nullptr;
+ DummyStanzaChannel* stanzaChannel_ = nullptr;
+ TimerFactory* timerFactory_;
+ XMPPRosterImpl* xmppRoster_ = nullptr;
+ UIEventStream* uiEventStream_;
+ VCardManager* vCardManager_ = nullptr;
+ VCardMemoryStorage* vCardMemoryStorage_ = nullptr;
+ DummySettingsProvider* settings_;
+ HighlightManager* highlightManager_;
+ std::shared_ptr<ChatMessageParser> chatMessageParser_;
+ AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider_;
+ std::unique_ptr<Chattables> chattables_;
+
+};
+
+TEST_F(ChatControllerTest, testResendMessage) {
+ std::string msgBody("TestMsg");
+ stanzaChannel_->setStreamManagementEnabled(true);
+ window_->onSendMessageRequest(msgBody, false);
+ {
+ auto failedStanzas = controller_->getFailedStanzas();
+ auto unackedStanzas = controller_->getUnackedStanzas();
+ ASSERT_EQ(failedStanzas.size(), 0);
+ ASSERT_EQ(unackedStanzas.size(), 1);
+ }
+ //Disconnecting to fail the stanza
+ controller_->setOnline(false);
+ controller_->setOnline(true);
+ {
+ auto failedStanzas = controller_->getFailedStanzas();
+ auto unackedStanzas = controller_->getUnackedStanzas();
+ ASSERT_EQ(failedStanzas.size(), 1);
+ ASSERT_EQ(unackedStanzas.size(), 0);
+ }
+ window_->onResendMessageRequest("id");
+ {
+ auto failedStanzas = controller_->getFailedStanzas();
+ auto unackedStanzas = controller_->getUnackedStanzas();
+ ASSERT_EQ(failedStanzas.size(), 0);
+ ASSERT_EQ(unackedStanzas.size(), 1);
+ }
+}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index ac62942..954dd2f 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -38,6 +38,7 @@
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/MUC/MUCManager.h>
#include <Swiften/MUC/UnitTest/MockMUC.h>
+#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/Network/DummyTimerFactory.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/Presence/PresenceOracle.h>
@@ -50,6 +51,7 @@
#include <Swift/Controllers/Chat/ChatController.h>
#include <Swift/Controllers/Chat/ChatsManager.h>
+#include <Swift/Controllers/Chat/Chattables.h>
#include <Swift/Controllers/Chat/MUCController.h>
#include <Swift/Controllers/Chat/UnitTest/MockChatListWindow.h>
#include <Swift/Controllers/EventNotifier.h>
@@ -57,6 +59,7 @@
#include <Swift/Controllers/ProfileSettingsProvider.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Settings/DummySettingsProvider.h>
+#include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h>
#include <Swift/Controllers/UIEvents/CreateImpromptuMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
@@ -106,6 +109,16 @@ class DummyNotifier : public Notifier {
std::vector<Notification> notifications;
};
+class ExtendedChatsManager : public ChatsManager {
+public:
+ ExtendedChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, const std::map<std::string, std::string>& emoticons, VCardManager* vcardManager, Chattables& chattables) :
+ ChatsManager(jid, stanzaChannel, iqRouter, eventController, chatWindowFactory, joinMUCWindowFactory, nickResolver, presenceOracle, presenceSender, uiEventStream, chatListWindowFactory, useDelayForLatency, timerFactory, mucRegistry, entityCapsProvider, mucManager, mucSearchWindowFactory, profileSettings, ftOverview, roster, eagleMode, settings, historyController_, whiteboardManager, highlightManager, clientBlockListManager, emoticons, vcardManager, chattables) {
+ }
+ MUCBookmarkManager* getBookmarkManager() {
+ return mucBookmarkManager_;
+ }
+};
+
class ChatsManagerTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(ChatsManagerTest);
@@ -161,13 +174,19 @@ class ChatsManagerTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testReceivingBookmarksWithDomainJID);
CPPUNIT_TEST(testReceivingBookmarksWithBareJID);
CPPUNIT_TEST(testReceivingBookmarksWithFullJID);
+ CPPUNIT_TEST(testAutoJoinBookmarksAndChattables);
+ CPPUNIT_TEST(testJoinNoAutojoinBookmark);
+ CPPUNIT_TEST(testJoinAndBookmarkMUC);
+ CPPUNIT_TEST(testReceivingNoBookmarks);
+ CPPUNIT_TEST(testReceivingNullBookmarks);
+ CPPUNIT_TEST(testReceivingBookmarksError);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
mocks_ = new MockRepository();
- notifier_ = std::unique_ptr<DummyNotifier>(new DummyNotifier());
+ notifier_ = std::make_unique<DummyNotifier>();
jid_ = JID("test@test.com/resource");
stanzaChannel_ = new DummyStanzaChannel();
iqRouter_ = new IQRouter(stanzaChannel_);
@@ -207,7 +226,8 @@ public:
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_);
clientBlockListManager_ = new ClientBlockListManager(iqRouter_);
timerFactory_ = new DummyTimerFactory();
- manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, timerFactory_, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_);
+ chattables_ = std::make_unique<Chattables>();
+ manager_ = new ExtendedChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, timerFactory_, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_, *chattables_);
manager_->setAvatarManager(avatarManager_);
}
@@ -783,7 +803,7 @@ public:
// send message to participantA
auto messageBody = std::string("message body to send");
window->onSendMessageRequest(messageBody, false);
- auto sendMessageStanza = stanzaChannel_->getStanzaAtIndex<Message>(2);
+ auto sendMessageStanza = stanzaChannel_->getStanzaAtIndex<Message>(3);
CPPUNIT_ASSERT_EQUAL(messageBody, *sendMessageStanza->getBody());
// receive reply with error
@@ -1565,7 +1585,11 @@ public:
uiEventStream_->send(std::make_shared<CreateImpromptuMUCUIEvent>(jids, mucJID, ""));
CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle());
- auto mucJoinPresence = std::dynamic_pointer_cast<Presence>(stanzaChannel_->sentStanzas[2]);
+ // Check the MUC security marking request
+ auto mucInfoRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[2]);
+ CPPUNIT_ASSERT(mucInfoRequest);
+
+ auto mucJoinPresence = std::dynamic_pointer_cast<Presence>(stanzaChannel_->sentStanzas[3]);
CPPUNIT_ASSERT(mucJoinPresence);
// MUC presence reply
@@ -1626,17 +1650,7 @@ public:
CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), window->name_);
}
- static std::shared_ptr<Storage> createBookmarkStorageWithJID(const JID& jid) {
- auto storage = std::make_shared<Storage>();
- auto room = Storage::Room();
- room.jid = jid;
- room.autoJoin = true;
- storage->addRoom(room);
- return storage;
- }
-
- void testReceivingBookmarksWithDomainJID() {
- auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ std::shared_ptr<Storage> createBookmarkStorageWithJID(std::shared_ptr<IQ> bookmarkRequest, const JID& jid, const bool autojoin) {
CPPUNIT_ASSERT(bookmarkRequest);
CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType());
@@ -1646,56 +1660,179 @@ public:
auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload());
CPPUNIT_ASSERT(storage);
+ auto roomsStorage = std::make_shared<Storage>();
+ if (jid.isValid()) {
+ auto room = Storage::Room();
+ room.jid = jid;
+ room.autoJoin = autojoin;
+ roomsStorage->addRoom(room);
+ }
+ return roomsStorage;
+ }
+
+ void testReceivingBookmarksWithDomainJID() {
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
auto response = IQ::createResult(
bookmarkRequest->getFrom(),
bookmarkRequest->getTo(),
bookmarkRequest->getID(),
- std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("montague.lit"))
+ std::make_shared<PrivateStorage>(createBookmarkStorageWithJID(bookmarkRequest, "montague.lit", true))
);
stanzaChannel_->onIQReceived(response);
}
void testReceivingBookmarksWithBareJID() {
auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
- CPPUNIT_ASSERT(bookmarkRequest);
- CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType());
-
- auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>();
- CPPUNIT_ASSERT(privateStorage);
-
- auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload());
- CPPUNIT_ASSERT(storage);
-
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window);
+ auto response = IQ::createResult(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ std::make_shared<PrivateStorage>(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", true))
+ );
+ stanzaChannel_->onIQReceived(response);
+ }
+ void testReceivingNoBookmarks() {
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
auto response = IQ::createResult(
bookmarkRequest->getFrom(),
bookmarkRequest->getTo(),
bookmarkRequest->getID(),
- std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit"))
+ std::make_shared<PrivateStorage>()
+ );
+ stanzaChannel_->onIQReceived(response);
+ }
+
+ void testReceivingNullBookmarks() {
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ auto response = IQ::createResult(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ nullptr
+ );
+ stanzaChannel_->onIQReceived(response);
+ }
+
+ void testReceivingBookmarksError() {
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ auto response = IQ::createError(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ ErrorPayload::Condition::ServiceUnavailable,
+ ErrorPayload::Type::Cancel,
+ nullptr
);
stanzaChannel_->onIQReceived(response);
}
void testReceivingBookmarksWithFullJID() {
auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
- CPPUNIT_ASSERT(bookmarkRequest);
- CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType());
+ auto response = IQ::createResult(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ std::make_shared<PrivateStorage>(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit/someresource", true))
+ );
+ stanzaChannel_->onIQReceived(response);
+ }
- auto privateStorage = bookmarkRequest->getPayload<PrivateStorage>();
- CPPUNIT_ASSERT(privateStorage);
+ void testAutoJoinBookmarksAndChattables() {
- auto storage = std::dynamic_pointer_cast<Storage>(privateStorage->getPayload());
- CPPUNIT_ASSERT(storage);
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "autojoin@bookmark.lit", true);
+ auto room = Storage::Room();
+ room.jid = "noAutojoin@bookmark.lit";
+ roomsStorage->addRoom(room);
+
+ //Only autojoin@bookmark.lit window should open.
+ MockChatWindow* autojoinBookmarkWindow = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("autojoin@bookmark.lit"), uiEventStream_).Return(autojoinBookmarkWindow);
auto response = IQ::createResult(
bookmarkRequest->getFrom(),
bookmarkRequest->getTo(),
bookmarkRequest->getID(),
- std::make_shared<PrivateStorage>(createBookmarkStorageWithJID("example@montague.lit/someresource"))
+ std::make_shared<PrivateStorage>(roomsStorage)
);
stanzaChannel_->onIQReceived(response);
+ //Both bookmarks should be added to the chattables.
+ CPPUNIT_ASSERT_EQUAL(size_t(2), chattables_->get().size());
+ auto autoJoinState = chattables_->getState("autojoin@bookmark.lit");
+ CPPUNIT_ASSERT(autoJoinState.type == Chattables::State::Type::Room);
+ CPPUNIT_ASSERT_EQUAL(autoJoinState.status, StatusShow::Online);
+ auto noAutoJoinState = chattables_->getState("noAutojoin@bookmark.lit");
+ CPPUNIT_ASSERT(noAutoJoinState.type == Chattables::State::Type::Room);
+ CPPUNIT_ASSERT_EQUAL(noAutoJoinState.status, StatusShow::None);
+ }
+
+ void testJoinNoAutojoinBookmark() {
+
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", false);
+
+ auto response = IQ::createResult(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ std::make_shared<PrivateStorage>(roomsStorage)
+ );
+ stanzaChannel_->onIQReceived(response);
+
+ //Join previous bookmarked room, expecting no increase in chattables and change of autojoin in bookmark to true
+ MockChatWindow* newExampleChatWindow = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(newExampleChatWindow);
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>("example@montague.lit", boost::optional<std::string>(), boost::optional<std::string>()));
+ CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size());
+ auto state = chattables_->getState("example@montague.lit");
+ CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room);
+ CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online);
+
+ auto bookmarks = manager_->getBookmarkManager()->getBookmarks();
+ CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1));
+ CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit"));
+ CPPUNIT_ASSERT(bookmarks[0].getAutojoin());
+ }
+
+ void testJoinAndBookmarkMUC() {
+ auto bookmarkRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[0]);
+ auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "", true);
+ auto response = IQ::createResult(
+ bookmarkRequest->getFrom(),
+ bookmarkRequest->getTo(),
+ bookmarkRequest->getID(),
+ std::make_shared<PrivateStorage>(roomsStorage)
+ );
+ stanzaChannel_->onIQReceived(response);
+
+ //Join non-bookmarked room expecting for the room to get bookmarked with autojoin to true
+ MockChatWindow* exampleChatWindow = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With("example@montague.lit", uiEventStream_).Return(exampleChatWindow);
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>("example@montague.lit", boost::optional<std::string>(), boost::optional<std::string>()));
+ {
+ CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size());
+ auto state = chattables_->getState("example@montague.lit");
+ CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room);
+ CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online);
+
+ auto bookmarks = manager_->getBookmarkManager()->getBookmarks();
+ CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1));
+ CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit"));
+ CPPUNIT_ASSERT(bookmarks[0].getAutojoin());
+
+ }
+ //Exiting room that is bookmarked, expecting chattable to stay but bookmark autojoin change to false.
+ exampleChatWindow->onClosed();
+ {
+ CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size());
+ auto bookmarks = manager_->getBookmarkManager()->getBookmarks();
+ CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1));
+ CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit"));
+ CPPUNIT_ASSERT(!bookmarks[0].getAutojoin());
+ }
}
private:
@@ -1722,7 +1859,7 @@ private:
private:
JID jid_;
std::unique_ptr<DummyNotifier> notifier_;
- ChatsManager* manager_;
+ ExtendedChatsManager* manager_;
DummyStanzaChannel* stanzaChannel_;
IQRouter* iqRouter_;
EventController* eventController_;
@@ -1760,6 +1897,7 @@ private:
int handledHighlightActions_;
std::set<std::string> soundsPlayed_;
DummyTimerFactory* timerFactory_;
+ std::unique_ptr<Chattables> chattables_;
};
diff --git a/Swift/Controllers/Chat/UnitTest/ChattablesTest.cpp b/Swift/Controllers/Chat/UnitTest/ChattablesTest.cpp
new file mode 100644
index 0000000..f30e3fd
--- /dev/null
+++ b/Swift/Controllers/Chat/UnitTest/ChattablesTest.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <gtest/gtest.h>
+#include <hippomocks.h>
+
+#include <Swift/Controllers/Chat/Chattables.h>
+
+// Clang wrongly things that tests for 0 are using 0 as null.
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+
+using namespace Swift;
+
+class ChattablesTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ beginListSize_ = 0;
+ endListSize_ = 0;
+ callsToBegin_ = 0;
+ callsToEnd_ = 0;
+ }
+
+ void TearDown() {
+ }
+
+ void updateBeginListSize(int listSize);
+ void updateEndListSize();
+
+ Chattables chattables_;
+ int beginListSize_;
+ int endListSize_;
+ int callsToBegin_;
+ int callsToEnd_;
+};
+
+void ChattablesTest::updateBeginListSize(int listSize) {
+ beginListSize_ = listSize;
+ callsToBegin_++;
+}
+
+void ChattablesTest::updateEndListSize() {
+ endListSize_ = chattables_.get().size();
+ callsToEnd_++;
+}
+
+TEST_F(ChattablesTest, testAddJID) {
+ chattables_.onBeginAdd.connect([this](int listSize){ updateBeginListSize(listSize); });
+ chattables_.onAdded.connect([this](){ updateEndListSize(); });
+ JID jid("user@swift.jid");
+
+ chattables_.addJID(jid, Chattables::State::Type::Person);
+ ASSERT_EQ(0, beginListSize_);
+ ASSERT_EQ(1, endListSize_);
+ ASSERT_EQ(1, callsToBegin_);
+ ASSERT_EQ(1, callsToEnd_);
+
+ ASSERT_EQ(jid, chattables_.get()[0]);
+ Chattables::State state = chattables_.getState(jid);
+ ASSERT_EQ(jid, state.jid);
+ ASSERT_EQ(Chattables::State::Type::Person, state.type);
+}
+
+TEST_F(ChattablesTest, testAddMultipleJIDs) {
+ chattables_.onBeginAdd.connect([this](int listSize){ updateBeginListSize(listSize); });
+ chattables_.onAdded.connect([this](){ updateEndListSize(); });
+ JID jid0("user0@swift.jid");
+ JID jid1("user1@swift.jid");
+ JID jid2("user2@swift.jid");
+
+ chattables_.addJID(jid0, Chattables::State::Type::Person);
+ ASSERT_EQ(0, beginListSize_);
+ ASSERT_EQ(1, endListSize_);
+ ASSERT_EQ(1, callsToBegin_);
+ ASSERT_EQ(1, callsToEnd_);
+ chattables_.addJID(jid1, Chattables::State::Type::Person);
+ ASSERT_EQ(1, beginListSize_);
+ ASSERT_EQ(2, endListSize_);
+ ASSERT_EQ(2, callsToBegin_);
+ ASSERT_EQ(2, callsToEnd_);
+ chattables_.addJID(jid2, Chattables::State::Type::Person);
+ ASSERT_EQ(2, beginListSize_);
+ ASSERT_EQ(3, endListSize_);
+ ASSERT_EQ(3, callsToBegin_);
+ ASSERT_EQ(3, callsToEnd_);
+}
+
+TEST_F(ChattablesTest, testAddRoomJID) {
+ chattables_.onBeginAdd.connect([this](int listSize){ updateBeginListSize(listSize); });
+ chattables_.onAdded.connect([this](){ updateEndListSize(); });
+ JID jid("room@swift.jid");
+
+ chattables_.addJID(jid, Chattables::State::Type::Room);
+ ASSERT_EQ(0, beginListSize_);
+ ASSERT_EQ(1, endListSize_);
+ ASSERT_EQ(1, callsToBegin_);
+ ASSERT_EQ(1, callsToEnd_);
+
+ ASSERT_EQ(jid, chattables_.get()[0]);
+ Chattables::State state = chattables_.getState(jid);
+ ASSERT_EQ(jid, state.jid);
+ ASSERT_EQ(Chattables::State::Type::Room, state.type);
+}
+
+TEST_F(ChattablesTest, testSetState) {
+ JID jid("user@swift.jid");
+ chattables_.addJID(jid, Chattables::State::Type::Person);
+ ASSERT_EQ(1, chattables_.get().size());
+ ASSERT_EQ(jid, chattables_.get()[0]);
+ ASSERT_EQ(Chattables::State::Type::Person, chattables_.getState(jid).type);
+ ASSERT_EQ(StatusShow::None, chattables_.getState(jid).status);
+
+ JID returnedJID;
+ int returnedIndex;
+ int callsToChanged = 0;
+ chattables_.onChanged.connect([&returnedJID, &returnedIndex, &callsToChanged](const JID& jid, int index){
+ returnedJID = jid;
+ returnedIndex = index;
+ callsToChanged++;
+ });
+
+ Chattables::State newState;
+ newState.jid = jid;
+ newState.type = Chattables::State::Type::Room;
+ newState.status = StatusShow::Online;
+ chattables_.setState(jid, newState);
+
+ auto storedState = chattables_.getState(jid);
+
+ ASSERT_EQ(1, chattables_.get().size());
+ ASSERT_EQ(jid, chattables_.get()[0]);
+ ASSERT_EQ(jid, returnedJID);
+ ASSERT_EQ(0, returnedIndex);
+ ASSERT_EQ(1, callsToChanged);
+ ASSERT_EQ(jid, storedState.jid);
+ ASSERT_EQ(Chattables::State::Type::Room, storedState.type);
+ ASSERT_EQ(StatusShow::Online, storedState.status);
+}
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index 1f69f4f..5339c7b 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -1,13 +1,14 @@
/*
- * Copyright (c) 2010-2017 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <boost/algorithm/string.hpp>
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <memory>
+
+#include <gtest/gtest.h>
#include <hippomocks.h>
#include <Swiften/Avatars/NullAvatarManager.h>
@@ -32,10 +33,12 @@
#include <Swiften/VCards/VCardMemoryStorage.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <Swift/Controllers/Chat/Chattables.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>
@@ -44,587 +47,918 @@
#include <Swift/Controllers/UnitTest/MockChatWindow.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
+// Clang wrongly things that tests for 0 are using 0 as null.
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+
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_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_);
- }
+class MUCControllerTest : public ::testing::Test {
+
+ protected:
+ 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_);
+ chattables_ = std::make_unique<Chattables>();
+ 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_, *chattables_);
+ }
- 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 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 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");
+ 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);
+ ASSERT_TRUE(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ ASSERT_TRUE(message);
+ ASSERT_EQ(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));
+ }
+ }
- controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
+ void checkEqual(const std::vector<NickJoinPart>& expected, const std::vector<NickJoinPart>& actual) {
+ ASSERT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ ASSERT_EQ(expected[i].nick, actual[i].nick);
+ ASSERT_EQ(expected[i].type, actual[i].type);
+ }
+ }
+
+ JID jidFromOccupant(const MUCOccupant& occupant) {
+ return JID(mucJID_.toString()+"/"+occupant.getNick());
}
- }
- void testAddressedToSelf() {
- finishJoin();
- Message::ref message(new Message());
+ 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);
+ ASSERT_TRUE(child);
+ for (auto childItem : child->getChildren()) {
+ ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem);
+ ASSERT_TRUE(item);
+ std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource());
+ ASSERT_TRUE(occupant != occupants.end());
+ ASSERT_TRUE(item->getMUCRole() == occupant->second.getRole());
+ ASSERT_TRUE(item->getMUCAffiliation() == occupant->second.getAffiliation());
+ }
+ }
+ }
- 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());
+ 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);
- 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());
+ 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);
+ }
- 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());
+ 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);
- 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)));
+ markingField->setName("x-isode#roominfo_marking");
+ markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color");
+ markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color");
- // The last message is ignored because self-mention highlights are matched case
- // sensitive against the nickname.
- CPPUNIT_ASSERT_EQUAL((size_t)3, eventController_->getEvents().size());
+ form->addField(markingField);
+ form->addField(markingForegroundColorField);
+ form->addField(markingBackgroundColorField);
- message = Message::ref(new Message());
- message->setFrom(JID(muc_->getJID().toString() + "/other3"));
- message->setBody("Hi bert.");
- message->setType(Message::Groupchat);
- controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
- CPPUNIT_ASSERT_EQUAL((size_t)3, eventController_->getEvents().size());
+ auto discoInfoRef = std::make_shared<DiscoInfo>();
+ discoInfoRef->addExtension(form);
- message = Message::ref(new Message());
- message->setFrom(JID(muc_->getJID().toString() + "/other2"));
- message->setBody("Hi " + boost::to_lower_copy(nick_) + "ie.");
- message->setType(Message::Groupchat);
- controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
- CPPUNIT_ASSERT_EQUAL((size_t)3, eventController_->getEvents().size());
+ 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;
+ }
+
+ 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_;
+ XMPPRoster* xmppRoster_;
+ std::unique_ptr<Chattables> chattables_;
+};
+
+TEST_F(MUCControllerTest, 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)));
+ ASSERT_EQ(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)));
+ ASSERT_EQ(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)));
+ ASSERT_EQ(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)));
+
+ // The last message is ignored because self-mention highlights are matched case
+ // sensitive against the nickname.
+ ASSERT_EQ(3, eventController_->getEvents().size());
+
+ message = Message::ref(new Message());
+ message->setFrom(JID(muc_->getJID().toString() + "/other3"));
+ message->setBody("Hi bert.");
+ message->setType(Message::Groupchat);
+ controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
+ ASSERT_EQ(3, eventController_->getEvents().size());
+
+ message = Message::ref(new Message());
+ message->setFrom(JID(muc_->getJID().toString() + "/other2"));
+ message->setBody("Hi " + boost::to_lower_copy(nick_) + "ie.");
+ message->setType(Message::Groupchat);
+ controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
+ ASSERT_EQ(3, eventController_->getEvents().size());
+}
+
+TEST_F(MUCControllerTest, testNotAddressedToSelf) {
+ finishJoin();
+ Message::ref message(new Message());
+ message->setFrom(JID(muc_->getJID().toString() + "/other3"));
+ message->setBody("Hi there Hatter");
+ message->setType(Message::Groupchat);
+ controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
+ ASSERT_EQ(0, eventController_->getEvents().size());
+}
+
+TEST_F(MUCControllerTest, testAddressedToSelfBySelf) {
+ finishJoin();
+ Message::ref message(new Message());
+ message->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
+ message->setBody("Hi there " + nick_);
+ message->setType(Message::Groupchat);
+ controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
+ ASSERT_EQ(0, eventController_->getEvents().size());
+}
+
+TEST_F(MUCControllerTest, testMessageWithEmptyLabelItem) {
+ SecurityLabelsCatalog::Item label;
+ label.setSelector("Bob");
+ window_->label_ = label;
+ std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
+ features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
+ controller_->setAvailableServerFeatures(features);
+ IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
+ SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
+ labelPayload->addItem(label);
+ IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
+ iqChannel_->onIQReceived(result);
+ std::string messageBody("agamemnon");
+ window_->onSendMessageRequest(messageBody, false);
+ std::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = std::dynamic_pointer_cast<Message>(rawStanza);
+ ASSERT_EQ(iq->getTo(), result->getFrom());
+ ASSERT_TRUE(window_->labelsEnabled_);
+ ASSERT_TRUE(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ ASSERT_TRUE(message);
+ ASSERT_EQ(messageBody, message->getBody().get());
+ ASSERT_FALSE(message->getPayload<SecurityLabel>());
+}
+
+TEST_F(MUCControllerTest, testMessageWithLabelItem) {
+ std::shared_ptr<SecurityLabel> label = std::make_shared<SecurityLabel>();
+ label->setLabel("a");
+ SecurityLabelsCatalog::Item labelItem;
+ labelItem.setSelector("Bob");
+ labelItem.setLabel(label);
+ window_->label_ = labelItem;
+ std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
+ features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
+ controller_->setAvailableServerFeatures(features);
+ IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
+ SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
+ labelPayload->addItem(labelItem);
+ IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
+ iqChannel_->onIQReceived(result);
+ std::string messageBody("agamemnon");
+ window_->onSendMessageRequest(messageBody, false);
+ std::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = std::dynamic_pointer_cast<Message>(rawStanza);
+ ASSERT_EQ(iq->getTo(), result->getFrom());
+ ASSERT_TRUE(window_->labelsEnabled_);
+ ASSERT_TRUE(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ ASSERT_TRUE(message);
+ ASSERT_EQ(messageBody, message->getBody().get());
+ ASSERT_EQ(label, message->getPayload<SecurityLabel>());
+}
+
+TEST_F(MUCControllerTest, testCorrectMessageWithLabelItem) {
+ std::shared_ptr<SecurityLabel> label = std::make_shared<SecurityLabel>();
+ label->setLabel("a");
+ SecurityLabelsCatalog::Item labelItem;
+ labelItem.setSelector("Bob");
+ labelItem.setLabel(label);
+ std::shared_ptr<SecurityLabel> label2 = std::make_shared<SecurityLabel>();
+ label->setLabel("b");
+ SecurityLabelsCatalog::Item labelItem2;
+ labelItem2.setSelector("Charlie");
+ labelItem2.setLabel(label2);
+ window_->label_ = labelItem;
+ std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
+ features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
+ controller_->setAvailableServerFeatures(features);
+ IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
+ SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
+ labelPayload->addItem(labelItem);
+ IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
+ iqChannel_->onIQReceived(result);
+ std::string messageBody("agamemnon");
+ window_->onSendMessageRequest(messageBody, false);
+ std::shared_ptr<Stanza> rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ Message::ref message = std::dynamic_pointer_cast<Message>(rawStanza);
+ ASSERT_EQ(iq->getTo(), result->getFrom());
+ ASSERT_TRUE(window_->labelsEnabled_);
+ ASSERT_TRUE(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
+ ASSERT_TRUE(message);
+ ASSERT_EQ(messageBody, message->getBody().get());
+ ASSERT_EQ(label, message->getPayload<SecurityLabel>());
+ window_->label_ = labelItem2;
+ window_->onSendMessageRequest(messageBody, true);
+ rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
+ message = std::dynamic_pointer_cast<Message>(rawStanza);
+ ASSERT_EQ(messageBody, message->getBody().get());
+ ASSERT_EQ(label, message->getPayload<SecurityLabel>());
+}
+
+TEST_F(MUCControllerTest, testAppendToJoinParts) {
+ std::vector<NickJoinPart> list;
+ std::vector<NickJoinPart> gold;
+ MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join));
+ gold.push_back(NickJoinPart("Kev", Join));
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Remko", Join));
+ gold.push_back(NickJoinPart("Remko", Join));
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Bert", Join));
+ gold.push_back(NickJoinPart("Bert", Join));
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Bert", Part));
+ gold[2].type = JoinThenPart;
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Kev", Part));
+ gold[0].type = JoinThenPart;
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Remko", Part));
+ gold[1].type = JoinThenPart;
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part));
+ gold.push_back(NickJoinPart("Ernie", Part));
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Join));
+ gold[3].type = PartThenJoin;
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join));
+ gold[0].type = Join;
+ checkEqual(gold, list);
+ MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part));
+ gold[3].type = Part;
+ checkEqual(gold, list);
+
+}
+
+TEST_F(MUCControllerTest, testJoinPartStringContructionSimple) {
+ std::vector<NickJoinPart> list;
+ list.push_back(NickJoinPart("Kev", Join));
+ ASSERT_EQ(std::string("Kev has entered the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Remko", Part));
+ ASSERT_EQ(std::string("Kev has entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Bert", Join));
+ ASSERT_EQ(std::string("Kev and Bert have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Ernie", Join));
+ ASSERT_EQ(std::string("Kev, Bert and Ernie have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
+}
+
+TEST_F(MUCControllerTest, testJoinPartStringContructionMixed) {
+ std::vector<NickJoinPart> list;
+ list.push_back(NickJoinPart("Kev", JoinThenPart));
+ ASSERT_EQ(std::string("Kev has entered then left the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Remko", Part));
+ ASSERT_EQ(std::string("Remko has left the room and Kev has entered then left the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Bert", PartThenJoin));
+ ASSERT_EQ(std::string("Remko has left the room, Kev has entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list, false));
+ list.push_back(NickJoinPart("Ernie", JoinThenPart));
+ ASSERT_EQ(std::string("Remko has left the room, Kev and Ernie have entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list, false));
+}
+
+TEST_F(MUCControllerTest, testRoleAffiliationStates) {
+
+ typedef std::map<std::string, MUCOccupant> occupant_map;
+ occupant_map occupants;
+ occupants.insert(occupant_map::value_type("Kev", MUCOccupant("Kev", MUCOccupant::Participant, MUCOccupant::Owner)));
+ occupants.insert(occupant_map::value_type("Remko", MUCOccupant("Remko", MUCOccupant::Participant, MUCOccupant::Owner)));
+ occupants.insert(occupant_map::value_type("Bert", MUCOccupant("Bert", MUCOccupant::Participant, MUCOccupant::Owner)));
+ occupants.insert(occupant_map::value_type("Ernie", MUCOccupant("Ernie", MUCOccupant::Participant, MUCOccupant::Owner)));
+
+ /* populate the MUC with fake users */
+ for (auto&& occupant : occupants) {
+ muc_->insertOccupant(occupant.second);
}
- void testNotAddressedToSelf() {
- finishJoin();
- Message::ref message(new Message());
- message->setFrom(JID(muc_->getJID().toString() + "/other3"));
- message->setBody("Hi there Hatter");
- message->setType(Message::Groupchat);
- controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
- CPPUNIT_ASSERT_EQUAL((size_t)0, eventController_->getEvents().size());
+ std::vector<MUCOccupant> alterations;
+ alterations.push_back(MUCOccupant("Kev", MUCOccupant::Visitor, MUCOccupant::Admin));
+ alterations.push_back(MUCOccupant("Remko", MUCOccupant::Moderator, MUCOccupant::Member));
+ alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::Outcast));
+ alterations.push_back(MUCOccupant("Ernie", MUCOccupant::NoRole, MUCOccupant::Member));
+ alterations.push_back(MUCOccupant("Bert", MUCOccupant::Moderator, MUCOccupant::Owner));
+ alterations.push_back(MUCOccupant("Kev", MUCOccupant::Participant, MUCOccupant::Outcast));
+ alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::NoAffiliation));
+ alterations.push_back(MUCOccupant("Remko", MUCOccupant::NoRole, MUCOccupant::NoAffiliation));
+ alterations.push_back(MUCOccupant("Ernie", MUCOccupant::Visitor, MUCOccupant::Outcast));
+
+ for (const auto& alteration : alterations) {
+ /* perform an alteration to a user's role and affiliation */
+ occupant_map::iterator occupant = occupants.find(alteration.getNick());
+ ASSERT_TRUE(occupant != occupants.end());
+ const JID jid = jidFromOccupant(occupant->second);
+ /* change the affiliation, leave the role in place */
+ muc_->changeAffiliation(jid, alteration.getAffiliation());
+ occupant->second = MUCOccupant(occupant->first, occupant->second.getRole(), alteration.getAffiliation());
+ testRoleAffiliationStatesVerify(occupants);
+ /* change the role, leave the affiliation in place */
+ muc_->changeOccupantRole(jid, alteration.getRole());
+ occupant->second = MUCOccupant(occupant->first, alteration.getRole(), occupant->second.getAffiliation());
+ testRoleAffiliationStatesVerify(occupants);
}
+}
+
+TEST_F(MUCControllerTest, testSubjectChangeCorrect) {
+ joinCompleted();
- void testAddressedToSelfBySelf() {
- finishJoin();
- Message::ref message(new Message());
- message->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
- message->setBody("Hi there " + nick_);
+ {
+ Message::ref message = std::make_shared<Message>();
message->setType(Message::Groupchat);
- controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message)));
- CPPUNIT_ASSERT_EQUAL((size_t)0, eventController_->getEvents().size());
- }
+ message->setTo(self_);
+ message->setFrom(mucJID_.withResource("SomeNickname"));
+ message->setID("3FB99C56-7C92-4755-91B0-9C0098BC7AE0");
+ message->setSubject("New Room Subject");
- void testMessageWithEmptyLabelItem() {
- SecurityLabelsCatalog::Item label;
- label.setSelector("Bob");
- window_->label_ = label;
- std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
- features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
- controller_->setAvailableServerFeatures(features);
- IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
- SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
- labelPayload->addItem(label);
- IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
- iqChannel_->onIQReceived(result);
- std::string messageBody("agamemnon");
- 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_EQUAL(iq->getTo(), result->getFrom());
- CPPUNIT_ASSERT(window_->labelsEnabled_);
- CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
- CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
- CPPUNIT_ASSERT(!message->getPayload<SecurityLabel>());
+ controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
+ ASSERT_EQ(std::string("The room subject is now: New Room Subject"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
}
+}
- void testMessageWithLabelItem() {
- std::shared_ptr<SecurityLabel> label = std::make_shared<SecurityLabel>();
- label->setLabel("a");
- SecurityLabelsCatalog::Item labelItem;
- labelItem.setSelector("Bob");
- labelItem.setLabel(label);
- window_->label_ = labelItem;
- std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
- features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
- controller_->setAvailableServerFeatures(features);
- IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
- SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
- labelPayload->addItem(labelItem);
- IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
- iqChannel_->onIQReceived(result);
- std::string messageBody("agamemnon");
- 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_EQUAL(iq->getTo(), result->getFrom());
- CPPUNIT_ASSERT(window_->labelsEnabled_);
- CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
- CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
- CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
- }
+/*
+ * Test that message stanzas with subject element and non-empty body element do not cause a subject change.
+ */
+TEST_F(MUCControllerTest, testSubjectChangeIncorrectA) {
+ joinCompleted();
- void testCorrectMessageWithLabelItem() {
- std::shared_ptr<SecurityLabel> label = std::make_shared<SecurityLabel>();
- label->setLabel("a");
- SecurityLabelsCatalog::Item labelItem;
- labelItem.setSelector("Bob");
- labelItem.setLabel(label);
- std::shared_ptr<SecurityLabel> label2 = std::make_shared<SecurityLabel>();
- label->setLabel("b");
- SecurityLabelsCatalog::Item labelItem2;
- labelItem2.setSelector("Charlie");
- labelItem2.setLabel(label2);
- window_->label_ = labelItem;
- std::shared_ptr<DiscoInfo> features = std::make_shared<DiscoInfo>();
- features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature);
- controller_->setAvailableServerFeatures(features);
- IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1];
- SecurityLabelsCatalog::ref labelPayload = std::make_shared<SecurityLabelsCatalog>();
- labelPayload->addItem(labelItem);
- IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload);
- iqChannel_->onIQReceived(result);
- std::string messageBody("agamemnon");
- 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_EQUAL(iq->getTo(), result->getFrom());
- CPPUNIT_ASSERT(window_->labelsEnabled_);
- CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */
- CPPUNIT_ASSERT(message);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
- CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
- window_->label_ = labelItem2;
- window_->onSendMessageRequest(messageBody, true);
- rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1];
- message = std::dynamic_pointer_cast<Message>(rawStanza);
- CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody().get());
- CPPUNIT_ASSERT_EQUAL(label, message->getPayload<SecurityLabel>());
+ {
+ 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("New Room Subject");
+ message->setBody("Some body text that prevents this stanza from being a subject change.");
+
+ controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
+ ASSERT_EQ(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
}
+}
- void checkEqual(const std::vector<NickJoinPart>& expected, const std::vector<NickJoinPart>& actual) {
- CPPUNIT_ASSERT_EQUAL(expected.size(), actual.size());
- for (size_t i = 0; i < expected.size(); i++) {
- CPPUNIT_ASSERT_EQUAL(expected[i].nick, actual[i].nick);
- CPPUNIT_ASSERT_EQUAL(expected[i].type, actual[i].type);
- }
+/*
+ * Test that message stanzas with subject element and thread element do not cause a subject change.
+ */
+TEST_F(MUCControllerTest, testSubjectChangeIncorrectB) {
+ joinCompleted();
+
+ {
+ 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("New Room Subject");
+ message->addPayload(std::make_shared<Thread>("Thread that prevents the subject change."));
+
+ controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
+ ASSERT_EQ(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
}
+}
- void testAppendToJoinParts() {
- std::vector<NickJoinPart> list;
- std::vector<NickJoinPart> gold;
- MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join));
- gold.push_back(NickJoinPart("Kev", Join));
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Remko", Join));
- gold.push_back(NickJoinPart("Remko", Join));
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Bert", Join));
- gold.push_back(NickJoinPart("Bert", Join));
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Bert", Part));
- gold[2].type = JoinThenPart;
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Kev", Part));
- gold[0].type = JoinThenPart;
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Remko", Part));
- gold[1].type = JoinThenPart;
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part));
- gold.push_back(NickJoinPart("Ernie", Part));
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Join));
- gold[3].type = PartThenJoin;
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join));
- gold[0].type = Join;
- checkEqual(gold, list);
- MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part));
- gold[3].type = Part;
- checkEqual(gold, list);
+/*
+ * Test that message stanzas with subject element and empty body element do not cause a subject change.
+ */
+TEST_F(MUCControllerTest, testSubjectChangeIncorrectC) {
+ joinCompleted();
+ {
+ 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("New Room Subject");
+ message->setBody("");
+
+ controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
+ ASSERT_EQ(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
}
+}
+
+TEST_F(MUCControllerTest, testHandleOccupantNicknameChanged) {
+ const auto occupantCount = [&](const std::string & nick) {
+ auto roster = window_->getRosterModel();
+ const auto currentOccupantsJIDs = roster->getJIDs();
+ int count = 0;
+ for (auto & p : currentOccupantsJIDs) {
+ if (p.getResource() == nick) {
+ ++count;
+ }
+ }
+ return count;
+ };
+
+ muc_->insertOccupant(MUCOccupant("TestUserOne", MUCOccupant::Participant, MUCOccupant::Owner));
+ muc_->insertOccupant(MUCOccupant("TestUserTwo", MUCOccupant::Participant, MUCOccupant::Owner));
+ muc_->insertOccupant(MUCOccupant("TestUserThree", MUCOccupant::Participant, MUCOccupant::Owner));
+
+ muc_->onOccupantNicknameChanged("TestUserOne", "TestUserTwo");
+
+ ASSERT_EQ(0, occupantCount("TestUserOne"));
+ ASSERT_EQ(1, occupantCount("TestUserTwo"));
+ ASSERT_EQ(1, occupantCount("TestUserThree"));
+}
+
+TEST_F(MUCControllerTest, testHandleOccupantNicknameChangedRoster) {
+ const auto occupantCount = [&](const std::string & nick) {
+ auto roster = window_->getRosterModel();
+ const auto participants = roster->getGroup("Participants");
+ const auto displayedParticipants = participants->getDisplayedChildren();
+ int count = 0;
+ for (auto & p : displayedParticipants) {
+ if (p->getDisplayName() == nick) {
+ ++count;
+ }
+ }
+ return count;
+ };
+
+ muc_->insertOccupant(MUCOccupant("TestUserOne", MUCOccupant::Participant, MUCOccupant::Owner));
+ muc_->insertOccupant(MUCOccupant("TestUserTwo", MUCOccupant::Participant, MUCOccupant::Owner));
+ muc_->insertOccupant(MUCOccupant("TestUserThree", MUCOccupant::Participant, MUCOccupant::Owner));
+ ASSERT_EQ(1, occupantCount("TestUserOne"));
+ ASSERT_EQ(1, occupantCount("TestUserTwo"));
+ ASSERT_EQ(1, occupantCount("TestUserThree"));
+
+ muc_->onOccupantNicknameChanged("TestUserOne", "TestUserTwo");
+
+ ASSERT_EQ(0, occupantCount("TestUserOne"));
+ ASSERT_EQ(1, occupantCount("TestUserTwo"));
+ ASSERT_EQ(1, occupantCount("TestUserThree"));
+}
+
+TEST_F(MUCControllerTest, testHandleChangeSubjectRequest) {
+ std::string testStr("New Subject");
+ ASSERT_EQ(std::string(""), muc_->newSubjectSet_);
+ window_->onChangeSubjectRequest(testStr);
+ ASSERT_EQ(testStr, muc_->newSubjectSet_);
+}
+
+TEST_F(MUCControllerTest, testNonImpromptuMUCWindowTitle) {
+ ASSERT_EQ(muc_->getJID().getNode(), window_->name_);
+}
+
+TEST_F(MUCControllerTest, testSecurityMarkingRequestCompleteMarking) {
+ setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red", true);
+
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), window_->markingValue_);
+ ASSERT_EQ(std::string("Black"), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string("Red"), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, 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 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);
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), window_->markingValue_);
+ ASSERT_EQ(std::string("Black"), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string("Red"), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, 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 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);
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), window_->markingValue_);
+ ASSERT_EQ(std::string("Black"), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string("White"), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, testSecurityMarkingRequestEmptyMarking) {
+ setMUCSecurityMarking("", "", "", true);
+
+ ASSERT_EQ(std::string(""), window_->markingValue_);
+ ASSERT_EQ(std::string(""), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string(""), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, testSecurityMarkingRequestWithMarkingNoFormType) {
+ setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red", false);
+
+ ASSERT_EQ(std::string(""), window_->markingValue_);
+ ASSERT_EQ(std::string(""), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string(""), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, 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);
+ ASSERT_EQ(std::string(""), window_->markingValue_);
+ ASSERT_EQ(std::string(""), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string(""), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, testSecurityMarkingRequestNoForm) {
+ auto discoInfoRef = std::make_shared<DiscoInfo>();
+
+ auto infoResponse = IQ::createResult( self_, mucJID_, "test-id", discoInfoRef);
+ iqChannel_->onIQReceived(infoResponse);
+ ASSERT_EQ(std::string(""), window_->markingValue_);
+ ASSERT_EQ(std::string(""), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string(""), window_->markingBackgroundColorValue_);
+}
+
+TEST_F(MUCControllerTest, 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);
+ ASSERT_EQ(std::string(""), window_->markingValue_);
+ ASSERT_EQ(std::string(""), window_->markingForegroundColorValue_);
+ ASSERT_EQ(std::string(""), window_->markingBackgroundColorValue_);
+}
- void testJoinPartStringContructionSimple() {
- std::vector<NickJoinPart> list;
- list.push_back(NickJoinPart("Kev", Join));
- CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Remko", Part));
- CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Bert", Join));
- CPPUNIT_ASSERT_EQUAL(std::string("Kev and Bert have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Ernie", Join));
- CPPUNIT_ASSERT_EQUAL(std::string("Kev, Bert and Ernie have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list, false));
- }
+TEST_F(MUCControllerTest, testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingA) {
+ settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true);
+ setMUCSecurityMarking("", "Black", "Red");
- void testJoinPartStringContructionMixed() {
- std::vector<NickJoinPart> list;
- list.push_back(NickJoinPart("Kev", JoinThenPart));
- CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered then left the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Remko", Part));
- CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room and Kev has entered then left the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Bert", PartThenJoin));
- CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room, Kev has entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list, false));
- list.push_back(NickJoinPart("Ernie", JoinThenPart));
- CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room, Kev and Ernie have entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list, false));
- }
+ auto messageLabel = std::make_shared<SecurityLabel>();
+ messageLabel->setDisplayMarking("Test|Highest Possible Security");
- JID jidFromOccupant(const MUCOccupant& occupant) {
- return JID(mucJID_.toString()+"/"+occupant.getNick());
- }
+ auto sentMessage = createTestMessageWithoutSecurityLabel();
+ sentMessage->addPayload(messageLabel);
- void testRoleAffiliationStates() {
+ auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage);
+ controller_->handleIncomingMessage(sentMessageEvent);
- typedef std::map<std::string, MUCOccupant> occupant_map;
- occupant_map occupants;
- occupants.insert(occupant_map::value_type("Kev", MUCOccupant("Kev", MUCOccupant::Participant, MUCOccupant::Owner)));
- occupants.insert(occupant_map::value_type("Remko", MUCOccupant("Remko", MUCOccupant::Participant, MUCOccupant::Owner)));
- occupants.insert(occupant_map::value_type("Bert", MUCOccupant("Bert", MUCOccupant::Participant, MUCOccupant::Owner)));
- occupants.insert(occupant_map::value_type("Ernie", MUCOccupant("Ernie", MUCOccupant::Participant, MUCOccupant::Owner)));
+ auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_;
- /* populate the MUC with fake users */
- for (auto&& occupant : occupants) {
- muc_->insertOccupant(occupant.second);
- }
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking());
+}
- std::vector<MUCOccupant> alterations;
- alterations.push_back(MUCOccupant("Kev", MUCOccupant::Visitor, MUCOccupant::Admin));
- alterations.push_back(MUCOccupant("Remko", MUCOccupant::Moderator, MUCOccupant::Member));
- alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::Outcast));
- alterations.push_back(MUCOccupant("Ernie", MUCOccupant::NoRole, MUCOccupant::Member));
- alterations.push_back(MUCOccupant("Bert", MUCOccupant::Moderator, MUCOccupant::Owner));
- alterations.push_back(MUCOccupant("Kev", MUCOccupant::Participant, MUCOccupant::Outcast));
- alterations.push_back(MUCOccupant("Bert", MUCOccupant::Visitor, MUCOccupant::NoAffiliation));
- alterations.push_back(MUCOccupant("Remko", MUCOccupant::NoRole, MUCOccupant::NoAffiliation));
- alterations.push_back(MUCOccupant("Ernie", MUCOccupant::Visitor, MUCOccupant::Outcast));
-
- for (const auto& alteration : alterations) {
- /* perform an alteration to a user's role and affiliation */
- occupant_map::iterator occupant = occupants.find(alteration.getNick());
- CPPUNIT_ASSERT(occupant != occupants.end());
- const JID jid = jidFromOccupant(occupant->second);
- /* change the affiliation, leave the role in place */
- muc_->changeAffiliation(jid, alteration.getAffiliation());
- occupant->second = MUCOccupant(occupant->first, occupant->second.getRole(), alteration.getAffiliation());
- testRoleAffiliationStatesVerify(occupants);
- /* change the role, leave the affiliation in place */
- muc_->changeOccupantRole(jid, alteration.getRole());
- occupant->second = MUCOccupant(occupant->first, alteration.getRole(), occupant->second.getAffiliation());
- testRoleAffiliationStatesVerify(occupants);
- }
- }
+TEST_F(MUCControllerTest, testSecurityMarkingAddedToMessage_Elision_NoRoomMarkingB) {
+ settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true);
+ setMUCSecurityMarking("", "Black", "Red");
- void testSubjectChangeCorrect() {
- joinCompleted();
+ auto messageLabel = std::make_shared<SecurityLabel>();
+ messageLabel->setDisplayMarking("");
- {
- Message::ref message = std::make_shared<Message>();
- message->setType(Message::Groupchat);
- message->setTo(self_);
- message->setFrom(mucJID_.withResource("SomeNickname"));
- message->setID("3FB99C56-7C92-4755-91B0-9C0098BC7AE0");
- message->setSubject("New Room Subject");
+ auto sentMessage = createTestMessageWithoutSecurityLabel();
+ sentMessage->addPayload(messageLabel);
- controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
- CPPUNIT_ASSERT_EQUAL(std::string("The room subject is now: New Room Subject"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
- }
- }
+ auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage);
+ controller_->handleIncomingMessage(sentMessageEvent);
- /*
- * Test that message stanzas with subject element and non-empty body element do not cause a subject change.
- */
- void testSubjectChangeIncorrectA() {
- joinCompleted();
+ auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_;
- {
- 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("New Room Subject");
- message->setBody("Some body text that prevents this stanza from being a subject change.");
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string(""), storedSecurityLabel->getDisplayMarking());
+}
- controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
- CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
- }
- }
+TEST_F(MUCControllerTest, testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingA) {
+ settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true);
+ setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red");
- /*
- * Test that message stanzas with subject element and thread element do not cause a subject change.
- */
- void testSubjectChangeIncorrectB() {
- joinCompleted();
+ auto messageLabel = std::make_shared<SecurityLabel>();
+ messageLabel->setDisplayMarking("Test|Highest Possible Security");
- {
- 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("New Room Subject");
- message->addPayload(std::make_shared<Thread>("Thread that prevents the subject change."));
+ auto sentMessage = createTestMessageWithoutSecurityLabel();
+ sentMessage->addPayload(messageLabel);
- controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
- CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
- }
- }
+ // Test the first message matching MUC marking. This message SHOULD have a marking
- /*
- * Test that message stanzas with subject element and empty body element do not cause a subject change.
- */
- void testSubjectChangeIncorrectC() {
- joinCompleted();
+ auto sentMessageEvent1 = std::make_shared<MessageEvent>(sentMessage);
+ controller_->handleIncomingMessage(sentMessageEvent1);
- {
- 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("New Room Subject");
- message->setBody("");
+ auto storedSecurityLabel1 = window_->lastAddedMessageSecurityLabel_;
- controller_->handleIncomingMessage(std::make_shared<MessageEvent>(message));
- CPPUNIT_ASSERT_EQUAL(std::string("Trying to enter room teaparty@rooms.wonderland.lit"), std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(window_->lastAddedSystemMessage_.getParts()[0])->text);
- }
- }
+ ASSERT_EQ(false, storedSecurityLabel1 == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), storedSecurityLabel1->getDisplayMarking());
- void testHandleOccupantNicknameChanged() {
- const auto occupantCount = [&](const std::string & nick) {
- auto roster = window_->getRosterModel();
- CPPUNIT_ASSERT(roster != nullptr);
- const auto currentOccupantsJIDs = roster->getJIDs();
- int count = 0;
- for (auto & p : currentOccupantsJIDs) {
- if (p.getResource() == nick) {
- ++count;
- }
- }
- return count;
- };
+ // Test a consecutive message matching MUC marking. This message SHOULD NOT have a marking
- muc_->insertOccupant(MUCOccupant("TestUserOne", MUCOccupant::Participant, MUCOccupant::Owner));
- muc_->insertOccupant(MUCOccupant("TestUserTwo", MUCOccupant::Participant, MUCOccupant::Owner));
- muc_->insertOccupant(MUCOccupant("TestUserThree", MUCOccupant::Participant, MUCOccupant::Owner));
+ auto sentMessageEvent2 = std::make_shared<MessageEvent>(sentMessage);
+ controller_->handleIncomingMessage(sentMessageEvent2);
- muc_->onOccupantNicknameChanged("TestUserOne", "TestUserTwo");
+ auto storedSecurityLabel2 = window_->lastAddedMessageSecurityLabel_;
- CPPUNIT_ASSERT_EQUAL(0, occupantCount("TestUserOne"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserTwo"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserThree"));
- }
+ ASSERT_EQ(false, storedSecurityLabel2 == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string(""), storedSecurityLabel2->getDisplayMarking());
+}
- void testHandleOccupantNicknameChangedRoster() {
- const auto occupantCount = [&](const std::string & nick) {
- auto roster = window_->getRosterModel();
- CPPUNIT_ASSERT(roster != nullptr);
- const auto participants = roster->getGroup("Participants");
- CPPUNIT_ASSERT(participants != nullptr);
- const auto displayedParticipants = participants->getDisplayedChildren();
- int count = 0;
- for (auto & p : displayedParticipants) {
- if (p->getDisplayName() == nick) {
- ++count;
- }
- }
- return count;
- };
+TEST_F(MUCControllerTest, testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingB) {
+ settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true);
+ setMUCSecurityMarking("Test|Lower Security Marking", "Black", "Red");
- muc_->insertOccupant(MUCOccupant("TestUserOne", MUCOccupant::Participant, MUCOccupant::Owner));
- muc_->insertOccupant(MUCOccupant("TestUserTwo", MUCOccupant::Participant, MUCOccupant::Owner));
- muc_->insertOccupant(MUCOccupant("TestUserThree", MUCOccupant::Participant, MUCOccupant::Owner));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserOne"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserTwo"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserThree"));
+ auto messageLabel = std::make_shared<SecurityLabel>();
+ messageLabel->setDisplayMarking("Test|Highest Possible Security");
- muc_->onOccupantNicknameChanged("TestUserOne", "TestUserTwo");
+ auto sentMessage = createTestMessageWithoutSecurityLabel();
+ sentMessage->addPayload(messageLabel);
- CPPUNIT_ASSERT_EQUAL(0, occupantCount("TestUserOne"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserTwo"));
- CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserThree"));
- }
+ auto sentMessageEvent = std::make_shared<MessageEvent>(sentMessage);
+ controller_->handleIncomingMessage(sentMessageEvent);
- 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());
- }
- }
- }
+ auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_;
- void testHandleChangeSubjectRequest() {
- std::string testStr("New Subject");
- CPPUNIT_ASSERT_EQUAL(std::string(""), muc_->newSubjectSet_);
- window_->onChangeSubjectRequest(testStr);
- CPPUNIT_ASSERT_EQUAL(testStr, muc_->newSubjectSet_);
- }
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking());
+}
- void testNonImpromptuMUCWindowTitle() {
- CPPUNIT_ASSERT_EQUAL(muc_->getJID().getNode(), window_->name_);
- }
+TEST_F(MUCControllerTest, testSecurityMarkingAddedToMessage_Elision_WithRoomMarkingC) {
+ settings_->storeSetting(SettingConstants::MUC_MARKING_ELISION, true);
+ setMUCSecurityMarking("Test|Highest Possible Security", "Black", "Red");
-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_;
- XMPPRoster* xmppRoster_;
-};
+ 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_;
+
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Unmarked"), storedSecurityLabel->getDisplayMarking());
+}
+
+TEST_F(MUCControllerTest, 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_;
+
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), storedSecurityLabel->getDisplayMarking());
+}
+
+TEST_F(MUCControllerTest, 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_;
+
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string(""), storedSecurityLabel->getDisplayMarking());
+}
+
+TEST_F(MUCControllerTest, 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_;
+
+ ASSERT_EQ(false, storedSecurityLabel1 == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(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_;
+
+ ASSERT_EQ(false, storedSecurityLabel2 == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string("Test|Highest Possible Security"), storedSecurityLabel2->getDisplayMarking());
+}
+
+TEST_F(MUCControllerTest, 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);
-CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest);
+ auto storedSecurityLabel = window_->lastAddedMessageSecurityLabel_;
+ ASSERT_EQ(false, storedSecurityLabel == nullptr);
+ // This is the potentially altered security label that is displayed on the screen
+ ASSERT_EQ(std::string(""), storedSecurityLabel->getDisplayMarking());
+}
diff --git a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h
index 395b050..1d980d3 100644
--- a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h
+++ b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h
@@ -20,7 +20,7 @@ namespace Swift {
void removeWhiteboardSession(const JID& /*jid*/) {}
void setBookmarksEnabled(bool /*enabled*/) {}
void setRecents(const std::list<ChatListWindow::Chat>& /*recents*/) {}
- void setUnreadCount(int /*unread*/) {}
+ void setUnreadCount(size_t /*unread*/) {}
void clearBookmarks() {}
void setOnline(bool /*isOnline*/) {}
};