diff options
-rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 55 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 4 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp | 170 | ||||
-rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 8 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 11 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 44 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.h | 7 | ||||
-rw-r--r-- | Swiften/Elements/Form.h | 6 |
9 files changed, 305 insertions, 8 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index ff8efa2..9e12a66 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -1,55 +1,56 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/Controllers/Chat/MUCController.h> #include <algorithm> #include <cassert> #include <memory> #include <boost/bind.hpp> #include <boost/regex.hpp> #include <boost/algorithm/string.hpp> #include <boost/range/adaptor/reversed.hpp> #include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/format.h> #include <Swiften/Base/Tristate.h> #include <Swiften/Client/BlockList.h> #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> #include <Swiften/MUC/MUCBookmark.h> #include <Swiften/MUC/MUCBookmarkManager.h> #include <Swiften/Network/Timer.h> #include <Swiften/Network/TimerFactory.h> #include <Swiften/Roster/XMPPRoster.h> #include <SwifTools/TabComplete.h> #include <Swift/Controllers/Chat/ChatMessageParser.h> #include <Swift/Controllers/Highlighting/Highlighter.h> #include <Swift/Controllers/Intl.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> #include <Swift/Controllers/Roster/GroupRosterItem.h> #include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h> #include <Swift/Controllers/Roster/ItemOperations/SetMUC.h> #include <Swift/Controllers/Roster/ItemOperations/SetPresence.h> #include <Swift/Controllers/Roster/Roster.h> #include <Swift/Controllers/Roster/RosterVCardProvider.h> #include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h> #include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> #include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> #include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h> #include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> @@ -246,60 +247,63 @@ void MUCController::handleBareJIDCapsChanged(const JID& /*jid*/) { for (const auto& nick : currentOccupants_) { DiscoInfo::ref disco = entityCapsProvider_->getCapsCached(toJID_.toBare().toString() + "/" + nick); if (disco && disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) { any = true; } else { support = Maybe; } } if (!any) { support = No; } chatWindow_->setCorrectionEnabled(support); } /** * Join the MUC if not already in it. */ void MUCController::rejoin() { if (parting_) { joined_ = false; parting_ = false; if (password_) { muc_->setPassword(*password_); } //FIXME: check for received activity #ifdef SWIFT_EXPERIMENTAL_HISTORY if (lastActivity_ == boost::posix_time::not_a_date_time && historyController_) { lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_); } #endif + + requestSecurityMarking(); + if (lastActivity_ == boost::posix_time::not_a_date_time) { muc_->joinAs(nick_); } else { muc_->joinWithContextSince(nick_, lastActivity_); } } } bool MUCController::isJoined() { return joined_; } const std::string& MUCController::getNick() { return nick_; } const boost::optional<std::string> MUCController::getPassword() const { return password_; } bool MUCController::isImpromptu() const { return isImpromptu_; } std::map<std::string, JID> MUCController::getParticipantJIDs() const { std::map<std::string, JID> participants; std::map<std::string, MUCOccupant> occupants = muc_->getOccupants(); for (const auto& occupant : occupants) { if (occupant.first != nick_) { @@ -1216,31 +1220,80 @@ void MUCController::updateChatWindowBookmarkStatus(const boost::optional<MUCBook else { chatWindow_->setBookmarkState(ChatWindow::RoomBookmarked); } } else { chatWindow_->setBookmarkState(ChatWindow::RoomNotBookmarked); } } void MUCController::displaySubjectIfChanged(const std::string& subject) { if (subject_ != subject) { if (!subject.empty()) { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % subject)), ChatWindow::DefaultDirection); } else { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject has been removed")))), ChatWindow::DefaultDirection); } subject_ = subject; } } void MUCController::addChatSystemMessage() { lastJoinMessageUID_ = chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(lastStartMessage_), ChatWindow::DefaultDirection); } void MUCController::setChatWindowTitle(const std::string& title) { chatWindowTitle_ = 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 == "") { + chatWindow_->removeChatSecurityMarking(); + return; + } + auto markingForegroundColor = roomInfoForm->getField("x-isode#roominfo_marking_fg_color"); + auto markingBackgroundColor = roomInfoForm->getField("x-isode#roominfo_marking_bg_color"); + std::string markingForegroundColorValue = "Black"; + std::string markingBackgroundColorValue = "White"; + if (markingForegroundColor) { + markingForegroundColorValue = markingForegroundColor->getTextSingleValue(); + } + if (markingBackgroundColor) { + markingBackgroundColorValue = markingBackgroundColor->getTextSingleValue(); + } + chatWindow_->setChatSecurityMarking(markingValue, markingForegroundColorValue, markingBackgroundColorValue); + } + ); + discoInfoRequest->send(); +} + } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 4b39f54..949f530 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -1,32 +1,32 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <map> #include <memory> #include <set> #include <string> #include <boost/signals2.hpp> #include <boost/signals2/connection.hpp> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/MUCOccupant.h> #include <Swiften/Elements/Message.h> #include <Swiften/JID/JID.h> #include <Swiften/MUC/MUC.h> #include <Swiften/Network/Timer.h> #include <Swift/Controllers/Chat/ChatControllerBase.h> #include <Swift/Controllers/Roster/RosterItem.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> namespace Swift { class StanzaChannel; class IQRouter; class ChatWindowFactory; class Roster; @@ -121,60 +121,62 @@ namespace Swift { void handleConfigureRequest(Form::ref); void handleConfigurationFailed(ErrorPayload::ref); void handleConfigurationFormReceived(Form::ref); void handleDestroyRoomRequest(); void handleInvitePersonToThisMUCRequest(const std::vector<JID>& jidsToInvite); void handleConfigurationCancelled(); void handleOccupantRoleChangeFailed(ErrorPayload::ref, const JID&, MUCOccupant::Role); void handleGetAffiliationsRequest(); void handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids); void handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes); void handleInviteToMUCWindowDismissed(); void handleInviteToMUCWindowCompleted(); void handleUIEvent(std::shared_ptr<UIEvent> event); void addRecentLogs(); void checkDuplicates(std::shared_ptr<Message> newMessage); void setNick(const std::string& nick); void handleRoomUnlocked(); void configureAsImpromptuRoom(Form::ref form); Form::ref buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm); void handleUnblockUserRequest(); void handleBlockingStateChanged(); void handleMUCBookmarkAdded(const MUCBookmark& bookmark); void handleMUCBookmarkRemoved(const MUCBookmark& bookmark); void updateChatWindowBookmarkStatus(const boost::optional<MUCBookmark>& bookmark); void displaySubjectIfChanged(const std::string& sucject); void addChatSystemMessage(); + void requestSecurityMarking(); + private: MUC::ref muc_; std::string nick_; std::string desiredNick_; TabComplete* completer_; bool parting_; bool joined_; bool shouldJoinOnReconnect_; bool doneGettingHistory_; boost::signals2::scoped_connection avatarChangedConnection_; std::shared_ptr<Timer> loginCheckTimer_; std::set<std::string> currentOccupants_; std::vector<NickJoinPart> joinParts_; boost::posix_time::ptime lastActivity_; boost::optional<std::string> password_; XMPPRoster* xmppRoster_; std::unique_ptr<Roster> roster_; std::vector<HistoryMessage> joinContext_; size_t renameCounter_; bool isImpromptu_; bool isImpromptuAlreadyConfigured_; RosterVCardProvider* rosterVCardProvider_; std::string lastJoinMessageUID_; std::string lastStartMessage_; ClientBlockListManager* clientBlockListManager_; boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 8f6c3a8..e06a3c7 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -749,61 +749,61 @@ public: presence->setShow(StatusShow::None); presence->setType(Presence::Unavailable); stanzaChannel_->onPresenceReceived(presence); CPPUNIT_ASSERT_EQUAL(std::string("participantA has gone offline."), MockChatWindow::bodyFromMessage(window->lastReplacedLastMessage_)); } void testChatControllerMucPmUnavailableErrorHandling() { auto mucJID = JID("test@rooms.test.com"); auto participantA = mucJID.withResource("participantA"); auto participantB = mucJID.withResource("participantB"); auto mucWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(mucWindow); uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(mucJID, participantB.getResource())); CPPUNIT_ASSERT_EQUAL(true, mucWindow->mucType_.is_initialized()); auto window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(participantA, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(participantA)); CPPUNIT_ASSERT_EQUAL(false, window->mucType_.is_initialized()); Presence::ref presence = Presence::create(); presence->setFrom(participantA); presence->setShow(StatusShow::Online); stanzaChannel_->onPresenceReceived(presence); CPPUNIT_ASSERT_EQUAL(std::string("participantA has become available."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_)); // 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 auto messageErrorReply = std::make_shared<Message>(); messageErrorReply->setID(stanzaChannel_->getNewIQID()); messageErrorReply->setType(Message::Error); messageErrorReply->setFrom(participantA); messageErrorReply->setTo(jid_); messageErrorReply->addPayload(std::make_shared<ErrorPayload>(ErrorPayload::ItemNotFound, ErrorPayload::Cancel, "Recipient not in room")); auto lastMUCWindowErrorMessageBeforeError = MockChatWindow::bodyFromMessage(mucWindow->lastAddedErrorMessage_); manager_->handleIncomingMessage(messageErrorReply); // assert that error is not routed to MUC window CPPUNIT_ASSERT_EQUAL(lastMUCWindowErrorMessageBeforeError, MockChatWindow::bodyFromMessage(mucWindow->lastAddedErrorMessage_)); // assert that error is routed to PM CPPUNIT_ASSERT_EQUAL(std::string("This user could not be found in the room."), MockChatWindow::bodyFromMessage(window->lastAddedErrorMessage_)); } void testLocalMUCServiceDiscoveryResetOnDisconnect() { JID ownJID("test@test.com/resource"); JID sender("foo@test.com"); manager_->setOnline(true); // Open chat window to a sender. MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(sender, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(sender)); @@ -1510,61 +1510,65 @@ public: mucMirrored->addPayload(std::make_shared<Replace>("fooBlaID_2")); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("Some correctly spelled message."), window->bodyFromMessage(window->lastReplacedMessage_)); } void impromptuChatSetup(MockChatWindow* window) { stanzaChannel_->uniqueIDs_ = true; JID mucJID("795B7BBE-9099-4A0D-81BA-C816F78E275C@test.com"); manager_->setOnline(true); std::shared_ptr<IQ> infoRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[1]); CPPUNIT_ASSERT(infoRequest); std::shared_ptr<IQ> infoResponse = IQ::createResult(infoRequest->getFrom(), infoRequest->getTo(), infoRequest->getID()); DiscoInfo info; info.addIdentity(DiscoInfo::Identity("Shakespearean Chat Service", "conference", "text")); info.addFeature("http://jabber.org/protocol/muc"); infoResponse->addPayload(std::make_shared<DiscoInfo>(info)); stanzaChannel_->onIQReceived(infoResponse); std::vector<JID> jids; jids.emplace_back("foo@test.com"); jids.emplace_back("bar@test.com"); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); 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 auto mucResponse = Presence::create(); mucResponse->setTo(jid_); mucResponse->setFrom(mucJoinPresence->getTo()); mucResponse->addPayload([]() { auto mucUser = std::make_shared<MUCUserPayload>(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); mucUser->addStatusCode(MUCUserPayload::StatusCode(110)); mucUser->addStatusCode(MUCUserPayload::StatusCode(210)); return mucUser; }()); stanzaChannel_->onPresenceReceived(mucResponse); // Before people join the impromptu room, the title is based on names coming from Roster CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); auto mucParticipantJoined = [&](const JID& jid) { auto participantJoinedPresence = Presence::create(); participantJoinedPresence->setTo(jid_); participantJoinedPresence->setFrom(mucJID.withResource(jid.toString())); auto mucUser = std::make_shared<MUCUserPayload>(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); participantJoinedPresence->addPayload(mucUser); return participantJoinedPresence; }; for (const auto& participantJID : jids) { stanzaChannel_->onPresenceReceived(mucParticipantJoined(participantJID)); diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp index 1f69f4f..06486d3 100644 --- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp @@ -41,60 +41,68 @@ #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h> #include <Swift/Controllers/UnitTest/MockChatWindow.h> #include <Swift/Controllers/XMPPEvents/EventController.h> using namespace Swift; class MUCControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MUCControllerTest); CPPUNIT_TEST(testJoinPartStringContructionSimple); CPPUNIT_TEST(testJoinPartStringContructionMixed); CPPUNIT_TEST(testAppendToJoinParts); CPPUNIT_TEST(testAddressedToSelf); CPPUNIT_TEST(testNotAddressedToSelf); CPPUNIT_TEST(testAddressedToSelfBySelf); CPPUNIT_TEST(testMessageWithEmptyLabelItem); CPPUNIT_TEST(testMessageWithLabelItem); CPPUNIT_TEST(testCorrectMessageWithLabelItem); CPPUNIT_TEST(testRoleAffiliationStates); CPPUNIT_TEST(testSubjectChangeCorrect); CPPUNIT_TEST(testSubjectChangeIncorrectA); CPPUNIT_TEST(testSubjectChangeIncorrectB); CPPUNIT_TEST(testSubjectChangeIncorrectC); CPPUNIT_TEST(testHandleOccupantNicknameChanged); CPPUNIT_TEST(testHandleOccupantNicknameChangedRoster); CPPUNIT_TEST(testHandleChangeSubjectRequest); CPPUNIT_TEST(testNonImpromptuMUCWindowTitle); + CPPUNIT_TEST(testSecurityMarkingRequestCompleteMarking); + CPPUNIT_TEST(testSecurityMarkingRequestCompleteMarkingWithExtraForm); + CPPUNIT_TEST(testSecurityMarkingRequestEmptyMarking); + CPPUNIT_TEST(testSecurityMarkingRequestWithMarkingNoFormType); + CPPUNIT_TEST(testSecurityMarkingRequestNoMarking); + CPPUNIT_TEST(testSecurityMarkingRequestNoForm); + CPPUNIT_TEST(testSecurityMarkingRequestError); + CPPUNIT_TEST_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_); @@ -566,60 +574,222 @@ public: } void testRoleAffiliationStatesVerify(const std::map<std::string, MUCOccupant> &occupants) { /* verify that the roster is in sync */ GroupRosterItem* group = window_->getRosterModel()->getRoot(); for (auto rosterItem : group->getChildren()) { GroupRosterItem* child = dynamic_cast<GroupRosterItem*>(rosterItem); CPPUNIT_ASSERT(child); for (auto childItem : child->getChildren()) { ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem); CPPUNIT_ASSERT(item); std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource()); CPPUNIT_ASSERT(occupant != occupants.end()); CPPUNIT_ASSERT(item->getMUCRole() == occupant->second.getRole()); CPPUNIT_ASSERT(item->getMUCAffiliation() == occupant->second.getAffiliation()); } } } void testHandleChangeSubjectRequest() { std::string testStr("New Subject"); CPPUNIT_ASSERT_EQUAL(std::string(""), muc_->newSubjectSet_); window_->onChangeSubjectRequest(testStr); CPPUNIT_ASSERT_EQUAL(testStr, muc_->newSubjectSet_); } void testNonImpromptuMUCWindowTitle() { CPPUNIT_ASSERT_EQUAL(muc_->getJID().getNode(), window_->name_); } + void testSecurityMarkingRequestCompleteMarking() { + auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); + formTypeField->setName("FORM_TYPE"); + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + auto form = std::make_shared<Form>(Form::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Red"), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestCompleteMarkingWithExtraForm() { + auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); + formTypeField->setName("FORM_TYPE"); + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + auto extraForm = std::make_shared<Form>(Form::Type::ResultType); + auto form = std::make_shared<Form>(Form::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(extraForm); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Red"), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestNoColorsInMarking() { + auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); + formTypeField->setName("FORM_TYPE"); + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + auto form = std::make_shared<Form>(Form::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string("Test | Highest Possible Security"), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string("Black"), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string("White"), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestEmptyMarking() { + auto formTypeField = std::make_shared<FormField>(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, ""); + formTypeField->setName("FORM_TYPE"); + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + auto form = std::make_shared<Form>(Form::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestWithMarkingNoFormType() { + auto markingField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared<FormField>(FormField::Type::TextSingleType, "Red"); + markingField->setName("x-isode#roominfo_marking"); + markingForegroundColorField->setName("x-isode#roominfo_marking_fg_color"); + markingBackgroundColorField->setName("x-isode#roominfo_marking_bg_color"); + + auto form = std::make_shared<Form>(Form::Type::ResultType); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestNoMarking() { + auto form = std::make_shared<Form>(Form::Type::ResultType); + + auto discoInfoRef = std::make_shared<DiscoInfo>(); + discoInfoRef->addExtension(form); + + auto infoResponse = IQ::createResult(self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestNoForm() { + auto discoInfoRef = std::make_shared<DiscoInfo>(); + + auto infoResponse = IQ::createResult( self_, mucJID_, "test-id", discoInfoRef); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); + } + + void testSecurityMarkingRequestError() { + auto errorPayload = std::make_shared<ErrorPayload>(ErrorPayload::Condition::NotAuthorized, ErrorPayload::Type::Auth); + + auto infoResponse = IQ::createResult( self_, mucJID_, "test-id", errorPayload); + iqChannel_->onIQReceived(infoResponse); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingForegroundColorValue_); + CPPUNIT_ASSERT_EQUAL(std::string(""), window_->markingBackgroundColorValue_); + } + 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_; diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index 8273802..13cbb7d 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -1,65 +1,67 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <memory> #include <string> #include <vector> #include <boost/algorithm/string.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/optional.hpp> #include <boost/signals2.hpp> #include <Swiften/Base/Tristate.h> #include <Swiften/Elements/ChatState.h> #include <Swiften/Elements/Form.h> #include <Swiften/Elements/MUCOccupant.h> #include <Swiften/Elements/SecurityLabelsCatalog.h> #include <Swiften/MUC/MUCBookmark.h> #include <Swift/Controllers/Highlighting/HighlightManager.h> namespace Swift { class AvatarManager; class TreeWidget; class Roster; class TabComplete; class RosterItem; class ContactRosterItem; class FileTransferController; class UserSearchWindow; + class DiscoInfo; + class ErrorPayload; class ChatWindow { public: class ChatMessagePart { public: virtual ~ChatMessagePart() {} }; class ChatMessage { public: ChatMessage() {} ChatMessage(const std::string& text) { append(std::make_shared<ChatTextMessagePart>(text)); } void append(const std::shared_ptr<ChatMessagePart>& part) { parts_.push_back(part); } const std::vector<std::shared_ptr<ChatMessagePart> >& getParts() const { return parts_; } void setParts(const std::vector<std::shared_ptr<ChatMessagePart> >& parts) { parts_ = parts; } void setHighlightActionSender(const HighlightAction& action) { @@ -192,60 +194,63 @@ namespace Swift { virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0; virtual void setContactChatState(ChatState::ChatStateType state) = 0; virtual void setName(const std::string& name) = 0; virtual void show() = 0; virtual bool isVisible() const = 0; virtual void activate() = 0; virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) = 0; virtual void setSecurityLabelsEnabled(bool enabled) = 0; virtual void setCorrectionEnabled(Tristate enabled) = 0; virtual void setFileTransferEnabled(Tristate enabled) = 0; virtual void setUnreadMessageCount(int count) = 0; virtual void convertToMUC(MUCType mucType) = 0; // virtual TreeWidget *getTreeWidget() = 0; virtual void setSecurityLabelsError() = 0; virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() = 0; virtual void setOnline(bool online) = 0; virtual void setRosterModel(Roster* model) = 0; virtual void setTabComplete(TabComplete* completer) = 0; virtual void replaceLastMessage(const ChatMessage& message, const TimestampBehaviour timestampBehaviour) = 0; virtual void setAckState(const std::string& id, AckState state) = 0; virtual void flash() = 0; virtual void setSubject(const std::string& subject) = 0; virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) = 0; virtual void setAvailableRoomActions(const std::vector<RoomAction> &actions) = 0; virtual void setBlockingState(BlockingState state) = 0; virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) = 0; virtual void showBookmarkWindow(const MUCBookmark& bookmark) = 0; virtual void setBookmarkState(RoomBookmarkState bookmarkState) = 0; + virtual void setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue) = 0; + virtual void removeChatSecurityMarking() = 0; + /** * A handle that uniquely identities an alert message. */ typedef int AlertID; /** * Set an alert on the window. * @param alertText Description of alert (required). * @return A handle to the alert message. */ virtual AlertID addAlert(const std::string& alertText) = 0; /** * Removes an alert. * @param id An alert ID previously returned from setAlert */ virtual void removeAlert(const AlertID id) = 0; /** * Actions that can be performed on the selected occupant. */ virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0; /** * A room configuration has been requested, show the form. * If the form is cancelled, must emit onConfigurationFormCancelled(). */ virtual void showRoomConfigurationForm(Form::ref) = 0; boost::signals2::signal<void ()> onClosed; boost::signals2::signal<void ()> onAllMessagesRead; boost::signals2::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest; @@ -255,31 +260,30 @@ namespace Swift { boost::signals2::signal<void ()> onAlertButtonClicked; boost::signals2::signal<void (ContactRosterItem*)> onOccupantSelectionChanged; boost::signals2::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected; boost::signals2::signal<void (const std::string&)> onChangeSubjectRequest; boost::signals2::signal<void ()> onBookmarkRequest; boost::signals2::signal<void (Form::ref)> onConfigureRequest; boost::signals2::signal<void ()> onDestroyRequest; boost::signals2::signal<void (const std::vector<JID>&)> onInviteToChat; boost::signals2::signal<void ()> onConfigurationFormCancelled; boost::signals2::signal<void ()> onGetAffiliationsRequest; boost::signals2::signal<void (MUCOccupant::Affiliation, const JID&)> onSetAffiliationRequest; boost::signals2::signal<void (const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes)> onChangeAffiliationsRequest; boost::signals2::signal<void ()> onContinuationsBroken; // File transfer related boost::signals2::signal<void (std::string /* id */)> onFileTransferCancel; boost::signals2::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart; boost::signals2::signal<void (std::string /* id */, std::string /* path */)> onFileTransferAccept; boost::signals2::signal<void (std::string /* path */)> onSendFileRequest; //Whiteboard related boost::signals2::signal<void ()> onWhiteboardSessionAccept; boost::signals2::signal<void ()> onWhiteboardSessionCancel; boost::signals2::signal<void ()> onWhiteboardWindowShow; // Blocking Command related boost::signals2::signal<void ()> onBlockUserRequest; boost::signals2::signal<void ()> onUnblockUserRequest; }; } - diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 7682781..56f118d 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -106,54 +106,65 @@ namespace Swift { virtual void setBlockingState(BlockingState) {} virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) { impromptuMUCSupported_ = supportsImpromptu; } virtual void showBookmarkWindow(const MUCBookmark& /*bookmark*/) {} virtual void setBookmarkState(RoomBookmarkState) {} static std::string bodyFromMessage(const ChatMessage& message) { std::string body; std::shared_ptr<ChatTextMessagePart> text; std::shared_ptr<ChatHighlightingMessagePart> highlight; for (auto &&part : message.getParts()) { if ((text = std::dynamic_pointer_cast<ChatTextMessagePart>(part))) { body += text->text; } else if ((highlight = std::dynamic_pointer_cast<ChatHighlightingMessagePart>(part))) { body += highlight->text; } } return body; } void resetLastMessages() { lastAddedMessage_ = lastAddedAction_ = lastAddedPresence_ = lastReplacedLastMessage_ = lastAddedSystemMessage_ = lastReplacedSystemMessage_ = ChatMessage(); lastAddedMessageSenderName_ = lastAddedActionSenderName_ = ""; lastAddedMessageSenderIsSelf_ = lastAddedActionSenderIsSelf_ = false; } + void setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue) { + markingValue_ = markingValue; + markingForegroundColorValue_ = markingForegroundColorValue; + markingBackgroundColorValue_ = markingBackgroundColorValue; + } + + void removeChatSecurityMarking() {} + std::string name_; ChatMessage lastAddedMessage_; std::string lastAddedMessageSenderName_; bool lastAddedMessageSenderIsSelf_ = false; ChatMessage lastAddedAction_; std::string lastAddedActionSenderName_; bool lastAddedActionSenderIsSelf_ = false; ChatMessage lastAddedPresence_; ChatMessage lastReplacedMessage_; ChatMessage lastReplacedLastMessage_; ChatMessage lastAddedSystemMessage_; ChatMessage lastReplacedSystemMessage_; ChatMessage lastAddedErrorMessage_; JID lastMUCInvitationJID_; std::vector<SecurityLabelsCatalog::Item> labels_; bool labelsEnabled_ = false; bool impromptuMUCSupported_ = false; SecurityLabelsCatalog::Item label_; Roster* roster_ = nullptr; std::vector<std::pair<std::string, ReceiptState>> receiptChanges_; boost::optional<MUCType> mucType_; + std::string markingValue_; + std::string markingForegroundColorValue_; + std::string markingBackgroundColorValue_; }; } diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index e750caa..17e3328 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,32 +1,32 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/QtUI/QtChatWindow.h> #include <map> #include <memory> #include <string> #include <boost/cstdint.hpp> #include <boost/lexical_cast.hpp> #include <QApplication> #include <QBoxLayout> #include <QCloseEvent> #include <QComboBox> #include <QCursor> #include <QDebug> #include <QFileDialog> #include <QFileInfo> #include <QFontMetrics> #include <QInputDialog> #include <QLabel> #include <QLineEdit> #include <QMenu> #include <QMessageBox> #include <QMimeData> #include <QPoint> #include <QPushButton> @@ -957,31 +957,73 @@ void QtChatWindow::setFileTransferProgress(std::string id, const int percentageD void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { messageLog_->setFileTransferStatus(id, state, msg); } std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { handleAppendedToLog(); return messageLog_->addWhiteboardRequest(contact_, senderIsSelf); } void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) { messageLog_->setWhiteboardSessionStatus(id, state); } void QtChatWindow::replaceLastMessage(const ChatMessage& message, const TimestampBehaviour timestampBehaviour) { messageLog_->replaceLastMessage(message, timestampBehaviour); } void QtChatWindow::setAckState(const std::string& id, AckState state) { messageLog_->setAckState(id, state); } void QtChatWindow::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { messageLog_->setMessageReceiptState(id, state); } void QtChatWindow::setBookmarkState(RoomBookmarkState bookmarkState) { roomBookmarkState_ = bookmarkState; } +void QtChatWindow::setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue) { + auto layout = static_cast<QBoxLayout*>(this->layout()); + + if (securityMarkingLayout_) { + layout->removeItem(securityMarkingLayout_); + securityMarkingLayout_->removeWidget(securityMarkingDisplay_); + delete securityMarkingLayout_; + } + delete securityMarkingDisplay_; + + securityMarkingLayout_ = new QHBoxLayout(); + securityMarkingDisplay_ = new QLabel(P2QSTRING(markingValue)); + + securityMarkingLayout_->addWidget(securityMarkingDisplay_); + layout->insertLayout(1, securityMarkingLayout_); + + auto palette = securityMarkingDisplay_->palette(); + palette.setColor(securityMarkingDisplay_->foregroundRole(), P2QSTRING(markingForegroundColorValue)); + palette.setColor(securityMarkingDisplay_->backgroundRole(), P2QSTRING(markingBackgroundColorValue)); + + securityMarkingDisplay_->setPalette(palette); + securityMarkingDisplay_->setContentsMargins(4,4,4,4); + securityMarkingDisplay_->setAutoFillBackground(true); + securityMarkingDisplay_->setAlignment(Qt::AlignCenter); +} + +void QtChatWindow::removeChatSecurityMarking() { + if (!securityMarkingLayout_) { + return; + } + + auto layout = static_cast<QBoxLayout*>(this->layout()); + + layout->removeItem(securityMarkingLayout_); + securityMarkingLayout_->removeWidget(securityMarkingDisplay_); + + delete securityMarkingDisplay_; + delete securityMarkingLayout_; + securityMarkingDisplay_ = nullptr; + securityMarkingLayout_ = nullptr; +} + } diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 361d7c6..4e10053 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,32 +1,32 @@ /* - * Copyright (c) 2010-2017 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <map> #include <string> #include <QMap> #include <QMenu> #include <QPointer> #include <QString> #include <QTextCursor> #include <QVBoxLayout> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <SwifTools/EmojiMapper.h> #include <SwifTools/LastLineTracker.h> #include <Swift/QtUI/ChatSnippet.h> #include <Swift/QtUI/QtAffiliationEditor.h> #include <Swift/QtUI/QtEmojisSelector.h> #include <Swift/QtUI/QtMUCConfigurationWindow.h> #include <Swift/QtUI/QtSwiftUtil.h> #include <Swift/QtUI/QtTabbable.h> class QTextEdit; class QLineEdit; @@ -167,79 +167,84 @@ namespace Swift { private slots: void handleLogCleared(); void returnPressed(); void handleInputChanged(); void handleCursorPositionChanged(); void handleKeyPressEvent(QKeyEvent* event); void handleSplitterMoved(int pos, int index); void handleAlertButtonClicked(); void handleActionButtonClicked(); void handleAffiliationEditorAccepted(); void handleCurrentLabelChanged(int); void handleEmojisButtonClicked(); void handleTextInputReceivedFocus(); void handleTextInputLostFocus(); private: void updateTitleWithUnreadCount(); void tabComplete(); void beginCorrection(); void cancelCorrection(); void handleSettingChanged(const std::string& setting); void handleOccupantSelectionChanged(RosterItem* item); void handleAppendedToLog(); static std::vector<JID> jidListFromQByteArray(const QByteArray& dataBytes); void resetDayChangeTimer(); + void setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue); + void removeChatSecurityMarking(); + private: int unreadCount_; bool contactIsTyping_; LastLineTracker lastLineTracker_; std::string id_; QString contact_; QString lastSentMessage_; QTextCursor tabCompleteCursor_; QtChatView* messageLog_; QtChatTheme* theme_; QtTextEdit* input_; QWidget* midBar_; QBoxLayout* subjectLayout_; QComboBox* labelsWidget_; QtOccupantListWidget* treeWidget_; QLabel* correctingLabel_; boost::optional<AlertID> correctingAlert_; QVBoxLayout* alertLayout_; std::map<AlertID, QWidget*> alertWidgets_; AlertID nextAlertId_; TabComplete* completer_; QLineEdit* subject_; bool isCorrection_; bool inputClearing_; bool tabCompletion_; UIEventStream* eventStream_; bool isOnline_; QSplitter* logRosterSplitter_; Tristate correctionEnabled_; Tristate fileTransferEnabled_; QString alertStyleSheet_; QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_; QPointer<QtAffiliationEditor> affiliationEditor_; SettingsProvider* settings_ = nullptr; QtSettingsProvider* qtOnlySettings_ = nullptr; std::vector<ChatWindow::RoomAction> availableRoomActions_; QPalette defaultLabelsPalette_; LabelModel* labelModel_; BlockingState blockingState_; bool impromptu_; bool isMUC_; bool supportsImpromptuChat_; RoomBookmarkState roomBookmarkState_; std::unique_ptr<QMenu> emojisMenu_; QPointer<QtEmojisSelector> emojisGrid_; std::map<std::string, std::string> emoticonsMap_; QTimer* dayChangeTimer = nullptr; + QHBoxLayout* securityMarkingLayout_ = nullptr; + QLabel* securityMarkingDisplay_ = nullptr; }; } diff --git a/Swiften/Elements/Form.h b/Swiften/Elements/Form.h index 899fb93..827e497 100644 --- a/Swiften/Elements/Form.h +++ b/Swiften/Elements/Form.h @@ -62,63 +62,69 @@ namespace Swift { const std::vector<std::shared_ptr<FormText> >& getTextElements() const { return textElements_; } void addReportedRef(std::shared_ptr<FormReportedRef> reportedRef) { assert(reportedRef); reportedRefs_.push_back(reportedRef); } const std::vector<std::shared_ptr<FormReportedRef> >& getReportedRefs() const { return reportedRefs_; } void setTitle(const std::string& title) { title_ = title; } const std::string& getTitle() const { return title_; } void setInstructions(const std::string& instructions) { instructions_ = instructions; } const std::string& getInstructions() const { return instructions_; } + /** Returns the Form::Type enum (ie. ResultType, CancelType etc.). + * NOT to be confused with Form::getFormType(). + */ Type getType() const { return type_; } void setType(Type type) { type_ = type; } + /** Returns the value of the field FORM_TYPE + * NOT to be confused with Form::getType(). + */ std::string getFormType() const; FormField::ref getField(const std::string& name) const; void addItem(const FormItem& item); const std::vector<FormItem>& getItems() const; void clearItems() { items_.clear(); } void clearEmptyTextFields(); void addReportedField(FormField::ref field); const std::vector<FormField::ref> & getReportedFields() const; void clearReportedFields() { reportedFields_.clear(); } private: std::vector<std::shared_ptr<FormReportedRef> >reportedRefs_; std::vector<std::shared_ptr<FormText> > textElements_; std::vector<std::shared_ptr<FormPage> > pages_; std::vector<std::shared_ptr<FormField> > fields_; std::vector<std::shared_ptr<FormField> > reportedFields_; std::vector<FormItem> items_; std::shared_ptr<FormReportedRef> reportedRef_; std::string title_; std::string instructions_; Type type_; }; } |