diff --git a/CMakeLists.txt b/CMakeLists.txt index 90cd3d67..a9bdeec1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,7 @@ set(SRC_FILES src/encryption/DeviceVerificationFlow.cpp src/encryption/Olm.cpp src/encryption/SelfVerificationStatus.cpp + src/encryption/VerificationManager.cpp # Generic notification stuff src/notifications/Manager.cpp @@ -548,9 +549,10 @@ qt5_wrap_cpp(MOC_HEADERS src/voip/CallManager.h src/voip/WebRTCSession.h - src/encryption/SelfVerificationStatus.h src/encryption/DeviceVerificationFlow.h src/encryption/Olm.h + src/encryption/SelfVerificationStatus.h + src/encryption/VerificationManager.h src/notifications/Manager.h diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 18130469..361099ed 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -178,6 +178,10 @@ Page { dialog.show(); } + target: VerificationManager + } + + Connections { function onOpenProfile(profile) { var userProfile = userProfileComponent.createObject(timelineRoot, { "profile": profile diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 01e3bad4..5bc8b9c8 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -12,7 +12,7 @@ ApplicationWindow { property var flow - onClosing: TimelineManager.removeVerificationFlow(flow) + onClosing: VerificationManager.removeVerificationFlow(flow) title: stack.currentItem.title modality: Qt.NonModal palette: Nheko.colors diff --git a/resources/res.qrc b/resources/res.qrc index ffbadd91..e544316b 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -141,37 +141,42 @@ qml/SelfVerificationCheck.qml qml/TypingIndicator.qml qml/NotificationWarning.qml - qml/emoji/EmojiPicker.qml - qml/emoji/StickerPicker.qml - qml/delegates/MessageDelegate.qml + qml/components/AdaptiveLayout.qml + qml/components/AdaptiveLayoutElement.qml + qml/components/AvatarListTile.qml + qml/components/FlatButton.qml qml/delegates/Encrypted.qml qml/delegates/FileMessage.qml qml/delegates/ImageMessage.qml + qml/delegates/MessageDelegate.qml qml/delegates/NoticeMessage.qml qml/delegates/Pill.qml qml/delegates/Placeholder.qml qml/delegates/PlayableMediaMessage.qml qml/delegates/Reply.qml qml/delegates/TextMessage.qml - qml/device-verification/Waiting.qml qml/device-verification/DeviceVerification.qml qml/device-verification/DigitVerification.qml qml/device-verification/EmojiVerification.qml - qml/device-verification/NewVerificationRequest.qml qml/device-verification/Failed.qml - qml/device-verification/Success.qml - qml/dialogs/ImagePackSettingsDialog.qml + qml/device-verification/NewVerificationRequest.qml + qml/device-verification/Success.qml + qml/device-verification/Waiting.qml qml/dialogs/ImagePackEditorDialog.qml - qml/dialogs/InputDialog.qml - qml/dialogs/InviteDialog.qml - qml/dialogs/JoinRoomDialog.qml - qml/dialogs/LogoutDialog.qml - qml/dialogs/RawMessageDialog.qml - qml/dialogs/ReadReceipts.qml - qml/dialogs/RoomDirectory.qml - qml/dialogs/RoomMembers.qml - qml/dialogs/RoomSettings.qml - qml/dialogs/UserProfile.qml + qml/dialogs/ImagePackSettingsDialog.qml + qml/dialogs/InputDialog.qml + qml/dialogs/InviteDialog.qml + qml/dialogs/JoinRoomDialog.qml + qml/dialogs/LeaveRoomDialog.qml + qml/dialogs/LogoutDialog.qml + qml/dialogs/RawMessageDialog.qml + qml/dialogs/ReadReceipts.qml + qml/dialogs/RoomDirectory.qml + qml/dialogs/RoomMembers.qml + qml/dialogs/RoomSettings.qml + qml/dialogs/UserProfile.qml + qml/emoji/EmojiPicker.qml + qml/emoji/StickerPicker.qml qml/ui/Ripple.qml qml/ui/Spinner.qml qml/ui/animations/BlinkAnimation.qml @@ -183,18 +188,6 @@ qml/voip/PlaceCall.qml qml/voip/ScreenShare.qml qml/voip/VideoCall.qml - qml/components/AdaptiveLayout.qml - qml/components/AdaptiveLayoutElement.qml - qml/components/AvatarListTile.qml - qml/components/FlatButton.qml - qml/dialogs/InviteDialog.qml - qml/dialogs/LeaveRoomDialog.qml - qml/dialogs/RawMessageDialog.qml - qml/dialogs/ReadReceipts.qml - qml/dialogs/RoomDirectory.qml - qml/dialogs/RoomMembers.qml - qml/dialogs/RoomSettings.qml - qml/dialogs/UserProfile.qml media/ring.ogg diff --git a/src/encryption/VerificationManager.cpp b/src/encryption/VerificationManager.cpp new file mode 100644 index 00000000..b9b51d35 --- /dev/null +++ b/src/encryption/VerificationManager.cpp @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "VerificationManager.h" +#include "Cache.h" +#include "ChatPage.h" +#include "DeviceVerificationFlow.h" +#include "timeline/TimelineViewManager.h" + +VerificationManager::VerificationManager(TimelineViewManager *o) + : QObject(o) + , rooms_(o->rooms()) +{} + +void +VerificationManager::receivedRoomDeviceVerificationRequest( + const mtx::events::RoomEvent &message, + TimelineModel *model) +{ + if (this->isInitialSync_) + return; + + auto event_id = QString::fromStdString(message.event_id); + if (!this->dvList.contains(event_id)) { + if (auto flow = DeviceVerificationFlow::NewInRoomVerification( + this, model, message.content, QString::fromStdString(message.sender), event_id)) { + dvList[event_id] = flow; + emit newDeviceVerificationRequest(flow.data()); + } + } +} + +void +VerificationManager::receivedDeviceVerificationRequest( + const mtx::events::msg::KeyVerificationRequest &msg, + std::string sender) +{ + if (this->isInitialSync_) + return; + + if (!msg.transaction_id) + return; + + auto txnid = QString::fromStdString(msg.transaction_id.value()); + if (!this->dvList.contains(txnid)) { + if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( + this, msg, QString::fromStdString(sender), txnid)) { + dvList[txnid] = flow; + emit newDeviceVerificationRequest(flow.data()); + } + } +} + +void +VerificationManager::receivedDeviceVerificationStart( + const mtx::events::msg::KeyVerificationStart &msg, + std::string sender) +{ + if (this->isInitialSync_) + return; + + if (!msg.transaction_id) + return; + + auto txnid = QString::fromStdString(msg.transaction_id.value()); + if (!this->dvList.contains(txnid)) { + if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( + this, msg, QString::fromStdString(sender), txnid)) { + dvList[txnid] = flow; + emit newDeviceVerificationRequest(flow.data()); + } + } +} + +void +VerificationManager::verifyUser(QString userid) +{ + auto joined_rooms = cache::joinedRooms(); + auto room_infos = cache::getRoomInfo(joined_rooms); + + for (std::string room_id : joined_rooms) { + if ((room_infos[QString::fromStdString(room_id)].member_count == 2) && + cache::isRoomEncrypted(room_id)) { + auto room_members = cache::roomMembers(room_id); + if (std::find(room_members.begin(), room_members.end(), (userid).toStdString()) != + room_members.end()) { + if (auto model = rooms_->getRoomById(QString::fromStdString(room_id))) { + auto flow = + DeviceVerificationFlow::InitiateUserVerification(this, model.data(), userid); + connect(model.data(), + &TimelineModel::updateFlowEventId, + this, + [this, flow](std::string eventId) { + dvList[QString::fromStdString(eventId)] = flow; + }); + emit newDeviceVerificationRequest(flow.data()); + return; + } + } + } + } + + emit ChatPage::instance()->showNotification( + tr("No encrypted private chat found with this user. Create an " + "encrypted private chat with this user and try again.")); +} + +void +VerificationManager::removeVerificationFlow(DeviceVerificationFlow *flow) +{ + for (auto it = dvList.keyValueBegin(); it != dvList.keyValueEnd(); ++it) { + if ((*it).second == flow) { + dvList.remove((*it).first); + return; + } + } +} + +void +VerificationManager::verifyDevice(QString userid, QString deviceid) +{ + auto flow = DeviceVerificationFlow::InitiateDeviceVerification(this, userid, deviceid); + this->dvList[flow->transactionId()] = flow; + emit newDeviceVerificationRequest(flow.data()); +} diff --git a/src/encryption/VerificationManager.h b/src/encryption/VerificationManager.h new file mode 100644 index 00000000..e00ddc10 --- /dev/null +++ b/src/encryption/VerificationManager.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include +#include + +class DeviceVerificationFlow; +class TimelineModel; +class TimelineModel; +class TimelineViewManager; +class RoomlistModel; + +class VerificationManager : public QObject +{ + Q_OBJECT + +public: + VerificationManager(TimelineViewManager *o = nullptr); + + Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow); + void verifyUser(QString userid); + void verifyDevice(QString userid, QString deviceid); + +signals: + void newDeviceVerificationRequest(DeviceVerificationFlow *flow); + +public slots: + void receivedRoomDeviceVerificationRequest( + const mtx::events::RoomEvent &message, + TimelineModel *model); + void receivedDeviceVerificationRequest(const mtx::events::msg::KeyVerificationRequest &msg, + std::string sender); + void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &msg, + std::string sender); + +private: + QHash> dvList; + bool isInitialSync_ = false; + RoomlistModel *rooms_; +}; + diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 86f59c52..94e6a0d7 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -143,9 +143,10 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par , colorImgProvider(new ColorImageProvider()) , blurhashProvider(new BlurhashProvider()) , jdenticonProvider(new JdenticonProvider()) - , callManager_(callManager) , rooms_(new RoomlistModel(this)) , communities_(new CommunitiesModel(this)) + , callManager_(callManager) + , verificationManager_(new VerificationManager(this)) { qRegisterMetaType(); qRegisterMetaType(); @@ -244,6 +245,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par "im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Nheko(); }); + qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_); qmlRegisterSingletonType( "im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * { return new SelfVerificationStatus(); @@ -285,63 +287,16 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette); connect(dynamic_cast(parent), &ChatPage::receivedRoomDeviceVerificationRequest, - this, - [this](const mtx::events::RoomEvent &message, - TimelineModel *model) { - if (this->isInitialSync_) - return; - - auto event_id = QString::fromStdString(message.event_id); - if (!this->dvList.contains(event_id)) { - if (auto flow = DeviceVerificationFlow::NewInRoomVerification( - this, - model, - message.content, - QString::fromStdString(message.sender), - event_id)) { - dvList[event_id] = flow; - emit newDeviceVerificationRequest(flow.data()); - } - } - }); + verificationManager_, + &VerificationManager::receivedRoomDeviceVerificationRequest); connect(dynamic_cast(parent), &ChatPage::receivedDeviceVerificationRequest, - this, - [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { - if (this->isInitialSync_) - return; - - if (!msg.transaction_id) - return; - - auto txnid = QString::fromStdString(msg.transaction_id.value()); - if (!this->dvList.contains(txnid)) { - if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( - this, msg, QString::fromStdString(sender), txnid)) { - dvList[txnid] = flow; - emit newDeviceVerificationRequest(flow.data()); - } - } - }); + verificationManager_, + &VerificationManager::receivedDeviceVerificationRequest); connect(dynamic_cast(parent), &ChatPage::receivedDeviceVerificationStart, - this, - [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { - if (this->isInitialSync_) - return; - - if (!msg.transaction_id) - return; - - auto txnid = QString::fromStdString(msg.transaction_id.value()); - if (!this->dvList.contains(txnid)) { - if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( - this, msg, QString::fromStdString(sender), txnid)) { - dvList[txnid] = flow; - emit newDeviceVerificationRequest(flow.data()); - } - } - }); + verificationManager_, + &VerificationManager::receivedDeviceVerificationStart); connect(parent, &ChatPage::loggedOut, this, [this]() { isInitialSync_ = true; emit initialSyncChanged(true); @@ -475,58 +430,6 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img) }); } -void -TimelineViewManager::verifyUser(QString userid) -{ - auto joined_rooms = cache::joinedRooms(); - auto room_infos = cache::getRoomInfo(joined_rooms); - - for (std::string room_id : joined_rooms) { - if ((room_infos[QString::fromStdString(room_id)].member_count == 2) && - cache::isRoomEncrypted(room_id)) { - auto room_members = cache::roomMembers(room_id); - if (std::find(room_members.begin(), room_members.end(), (userid).toStdString()) != - room_members.end()) { - if (auto model = rooms_->getRoomById(QString::fromStdString(room_id))) { - auto flow = - DeviceVerificationFlow::InitiateUserVerification(this, model.data(), userid); - connect(model.data(), - &TimelineModel::updateFlowEventId, - this, - [this, flow](std::string eventId) { - dvList[QString::fromStdString(eventId)] = flow; - }); - emit newDeviceVerificationRequest(flow.data()); - return; - } - } - } - } - - emit ChatPage::instance()->showNotification( - tr("No encrypted private chat found with this user. Create an " - "encrypted private chat with this user and try again.")); -} - -void -TimelineViewManager::removeVerificationFlow(DeviceVerificationFlow *flow) -{ - for (auto it = dvList.keyValueBegin(); it != dvList.keyValueEnd(); ++it) { - if ((*it).second == flow) { - dvList.remove((*it).first); - return; - } - } -} - -void -TimelineViewManager::verifyDevice(QString userid, QString deviceid) -{ - auto flow = DeviceVerificationFlow::InitiateDeviceVerification(this, userid, deviceid); - this->dvList[flow->transactionId()] = flow; - emit newDeviceVerificationRequest(flow.data()); -} - void TimelineViewManager::updateReadReceipts(const QString &room_id, const std::vector &event_ids) diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 723282d6..6696b1c4 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -23,6 +22,7 @@ #include "Utils.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" +#include "encryption/VerificationManager.h" #include "timeline/CommunitiesModel.h" #include "timeline/RoomlistModel.h" #include "voip/CallManager.h" @@ -33,7 +33,6 @@ class BlurhashProvider; class ColorImageProvider; class UserSettings; class ChatPage; -class DeviceVerificationFlow; class ImagePackListModel; class TimelineViewManager : public QObject @@ -53,6 +52,7 @@ public: MxcImageProvider *imageProvider() { return imgProvider; } CallManager *callManager() { return callManager_; } + VerificationManager *verificationManager() { return verificationManager_; } void clearAll() { rooms_->clear(); } @@ -73,19 +73,14 @@ public: Q_INVOKABLE void openGlobalUserProfile(QString userId); Q_INVOKABLE void focusMessageInput(); - Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow); Q_INVOKABLE void fixImageRendering(QQuickTextDocument *t, QQuickItem *i); - void verifyUser(QString userid); - void verifyDevice(QString userid, QString deviceid); - signals: void activeTimelineChanged(TimelineModel *timeline); void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void newDeviceVerificationRequest(DeviceVerificationFlow *flow); void inviteUsers(QString roomId, QStringList users); void showRoomList(); void narrowViewChanged(); @@ -142,17 +137,17 @@ private: BlurhashProvider *blurhashProvider; JdenticonProvider *jdenticonProvider; - CallManager *callManager_ = nullptr; - bool isInitialSync_ = true; bool isWindowFocused_ = false; RoomlistModel *rooms_ = nullptr; CommunitiesModel *communities_ = nullptr; - QHash userColors; + // don't move this above the rooms_ + CallManager *callManager_ = nullptr; + VerificationManager *verificationManager_ = nullptr; - QHash> dvList; + QHash userColors; }; Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept) Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel) diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index d62e3248..0e3fd39f 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -338,9 +338,9 @@ void UserProfile::verify(QString device) { if (!device.isEmpty()) - manager->verifyDevice(userid_, device); + manager->verificationManager()->verifyDevice(userid_, device); else { - manager->verifyUser(userid_); + manager->verificationManager()->verifyUser(userid_); } }