summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp55
-rw-r--r--Swift/Controllers/Chat/MUCController.h4
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp8
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp170
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h8
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h11
-rw-r--r--Swift/QtUI/QtChatWindow.cpp44
-rw-r--r--Swift/QtUI/QtChatWindow.h7
-rw-r--r--Swiften/Elements/Form.h6
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_;
};
}