From 85a144fe80d0fe89b5fed852013b6986b44978d4 Mon Sep 17 00:00:00 2001 From: Peter Burgess Date: Fri, 19 Jan 2018 14:01:53 +0000 Subject: Request and display security markings for MUC chat windows Disco#info requested and handled by MUCController on rejoin(). UI display of disco#info implemented for QtChatWindow. Test-Information: Tests written for new MUCController features, and all tests passed. Swift runs with changes and security markings show as and when expected in local isode MUC windows. Change-Id: Ibef4a31f6f8c4cff5f518a66106266a7f961d103 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,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 #include #include +#include #include #include #include @@ -273,6 +274,9 @@ void MUCController::rejoin() { lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_); } #endif + + requestSecurityMarking(); + if (lastActivity_ == boost::posix_time::not_a_date_time) { muc_->joinAs(nick_); } @@ -1243,4 +1247,53 @@ 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 discoInfoRef, ErrorPayload::ref errorPayloadRef) { + if (!discoInfoRef || errorPayloadRef) { + return; + } + const std::vector& 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,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. */ @@ -148,6 +148,8 @@ namespace Swift { void displaySubjectIfChanged(const std::string& sucject); void addChatSystemMessage(); + void requestSecurityMarking(); + private: MUC::ref muc_; std::string nick_; 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 @@ -776,7 +776,7 @@ public: // send message to participantA auto messageBody = std::string("message body to send"); window->onSendMessageRequest(messageBody, false); - auto sendMessageStanza = stanzaChannel_->getStanzaAtIndex(2); + auto sendMessageStanza = stanzaChannel_->getStanzaAtIndex(3); CPPUNIT_ASSERT_EQUAL(messageBody, *sendMessageStanza->getBody()); // receive reply with error @@ -1537,7 +1537,11 @@ public: uiEventStream_->send(std::make_shared(jids, mucJID, "")); CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); - auto mucJoinPresence = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[2]); + // Check the MUC security marking request + auto mucInfoRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[2]); + CPPUNIT_ASSERT(mucInfoRequest); + + auto mucJoinPresence = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[3]); CPPUNIT_ASSERT(mucJoinPresence); // MUC presence reply 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 @@ -68,6 +68,14 @@ class MUCControllerTest : public CppUnit::TestFixture { 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: @@ -593,6 +601,168 @@ public: CPPUNIT_ASSERT_EQUAL(muc_->getJID().getNode(), window_->name_); } + void testSecurityMarkingRequestCompleteMarking() { + auto formTypeField = std::make_shared(FormField::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared(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::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared(); + 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::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared(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::Type::ResultType); + auto form = std::make_shared(Form::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared(); + 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::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared(FormField::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared(FormField::Type::TextSingleType, ""); + auto markingBackgroundColorField = std::make_shared(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::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared(); + 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::Type::HiddenType, "http://jabber.org/protocol/muc#roominfo"); + auto markingField = std::make_shared(FormField::Type::TextSingleType, ""); + auto markingForegroundColorField = std::make_shared(FormField::Type::TextSingleType, ""); + auto markingBackgroundColorField = std::make_shared(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::Type::ResultType); + form->addField(formTypeField); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared(); + 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::Type::TextSingleType, "Test | Highest Possible Security"); + auto markingForegroundColorField = std::make_shared(FormField::Type::TextSingleType, "Black"); + auto markingBackgroundColorField = std::make_shared(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::Type::ResultType); + form->addField(markingField); + form->addField(markingForegroundColorField); + form->addField(markingBackgroundColorField); + + auto discoInfoRef = std::make_shared(); + 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::Type::ResultType); + + auto discoInfoRef = std::make_shared(); + 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(); + + 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::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_; 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,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. */ @@ -33,6 +33,8 @@ namespace Swift { class ContactRosterItem; class FileTransferController; class UserSearchWindow; + class DiscoInfo; + class ErrorPayload; class ChatWindow { @@ -219,6 +221,9 @@ namespace Swift { 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. */ @@ -282,4 +287,3 @@ namespace Swift { boost::signals2::signal 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 @@ -133,6 +133,14 @@ namespace Swift { 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_; @@ -154,6 +162,9 @@ namespace Swift { Roster* roster_ = nullptr; std::vector> receiptChanges_; boost::optional 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,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. */ @@ -984,4 +984,46 @@ 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(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(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,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. */ @@ -194,6 +194,9 @@ namespace Swift { void resetDayChangeTimer(); + void setChatSecurityMarking(const std::string& markingValue, const std::string& markingForegroundColorValue, const std::string& markingBackgroundColorValue); + void removeChatSecurityMarking(); + private: int unreadCount_; bool contactIsTyping_; @@ -241,5 +244,7 @@ namespace Swift { QPointer emojisGrid_; std::map 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 @@ -89,6 +89,9 @@ namespace Swift { return instructions_; } + /** Returns the Form::Type enum (ie. ResultType, CancelType etc.). + * NOT to be confused with Form::getFormType(). + */ Type getType() const { return type_; } @@ -97,6 +100,9 @@ namespace Swift { 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); -- cgit v0.10.2-6-g49f6