diff --git a/CMakeLists.txt b/CMakeLists.txt index 90cd3d67ec867da92b006a02810533a4b566d34a..a9bdeec145756c54a6b673ef9cf010b40c3b1478 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 1813046925ecd873a078b8eb7404282f592d7076..361099edc4d1413cdf897bf04f73236e9d891c52 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 01e3bad403e1ae57ff6310122a0371e2094af245..5bc8b9c85918ddaf06185daa3e683727a37f27b9 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 ffbadd91aa2c00db58518196b342f78e2fd22afc..e544316bd05e973900538fde2c5e571205f7ec14 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -141,37 +141,42 @@ <file>qml/SelfVerificationCheck.qml</file> <file>qml/TypingIndicator.qml</file> <file>qml/NotificationWarning.qml</file> - <file>qml/emoji/EmojiPicker.qml</file> - <file>qml/emoji/StickerPicker.qml</file> - <file>qml/delegates/MessageDelegate.qml</file> + <file>qml/components/AdaptiveLayout.qml</file> + <file>qml/components/AdaptiveLayoutElement.qml</file> + <file>qml/components/AvatarListTile.qml</file> + <file>qml/components/FlatButton.qml</file> <file>qml/delegates/Encrypted.qml</file> <file>qml/delegates/FileMessage.qml</file> <file>qml/delegates/ImageMessage.qml</file> + <file>qml/delegates/MessageDelegate.qml</file> <file>qml/delegates/NoticeMessage.qml</file> <file>qml/delegates/Pill.qml</file> <file>qml/delegates/Placeholder.qml</file> <file>qml/delegates/PlayableMediaMessage.qml</file> <file>qml/delegates/Reply.qml</file> <file>qml/delegates/TextMessage.qml</file> - <file>qml/device-verification/Waiting.qml</file> <file>qml/device-verification/DeviceVerification.qml</file> <file>qml/device-verification/DigitVerification.qml</file> <file>qml/device-verification/EmojiVerification.qml</file> - <file>qml/device-verification/NewVerificationRequest.qml</file> <file>qml/device-verification/Failed.qml</file> - <file>qml/device-verification/Success.qml</file> - <file>qml/dialogs/ImagePackSettingsDialog.qml</file> + <file>qml/device-verification/NewVerificationRequest.qml</file> + <file>qml/device-verification/Success.qml</file> + <file>qml/device-verification/Waiting.qml</file> <file>qml/dialogs/ImagePackEditorDialog.qml</file> - <file>qml/dialogs/InputDialog.qml</file> - <file>qml/dialogs/InviteDialog.qml</file> - <file>qml/dialogs/JoinRoomDialog.qml</file> - <file>qml/dialogs/LogoutDialog.qml</file> - <file>qml/dialogs/RawMessageDialog.qml</file> - <file>qml/dialogs/ReadReceipts.qml</file> - <file>qml/dialogs/RoomDirectory.qml</file> - <file>qml/dialogs/RoomMembers.qml</file> - <file>qml/dialogs/RoomSettings.qml</file> - <file>qml/dialogs/UserProfile.qml</file> + <file>qml/dialogs/ImagePackSettingsDialog.qml</file> + <file>qml/dialogs/InputDialog.qml</file> + <file>qml/dialogs/InviteDialog.qml</file> + <file>qml/dialogs/JoinRoomDialog.qml</file> + <file>qml/dialogs/LeaveRoomDialog.qml</file> + <file>qml/dialogs/LogoutDialog.qml</file> + <file>qml/dialogs/RawMessageDialog.qml</file> + <file>qml/dialogs/ReadReceipts.qml</file> + <file>qml/dialogs/RoomDirectory.qml</file> + <file>qml/dialogs/RoomMembers.qml</file> + <file>qml/dialogs/RoomSettings.qml</file> + <file>qml/dialogs/UserProfile.qml</file> + <file>qml/emoji/EmojiPicker.qml</file> + <file>qml/emoji/StickerPicker.qml</file> <file>qml/ui/Ripple.qml</file> <file>qml/ui/Spinner.qml</file> <file>qml/ui/animations/BlinkAnimation.qml</file> @@ -183,18 +188,6 @@ <file>qml/voip/PlaceCall.qml</file> <file>qml/voip/ScreenShare.qml</file> <file>qml/voip/VideoCall.qml</file> - <file>qml/components/AdaptiveLayout.qml</file> - <file>qml/components/AdaptiveLayoutElement.qml</file> - <file>qml/components/AvatarListTile.qml</file> - <file>qml/components/FlatButton.qml</file> - <file>qml/dialogs/InviteDialog.qml</file> - <file>qml/dialogs/LeaveRoomDialog.qml</file> - <file>qml/dialogs/RawMessageDialog.qml</file> - <file>qml/dialogs/ReadReceipts.qml</file> - <file>qml/dialogs/RoomDirectory.qml</file> - <file>qml/dialogs/RoomMembers.qml</file> - <file>qml/dialogs/RoomSettings.qml</file> - <file>qml/dialogs/UserProfile.qml</file> </qresource> <qresource prefix="/media"> <file>media/ring.ogg</file> diff --git a/src/encryption/VerificationManager.cpp b/src/encryption/VerificationManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9b51d35659788264a7a727afd90070794254ff8 --- /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<mtx::events::msg::KeyVerificationRequest> &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 0000000000000000000000000000000000000000..e00ddc10ff141fbe8f6d7f1fe8eb39fb5eaf424b --- /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 <QHash> +#include <QObject> +#include <QSharedPointer> + +#include <mtx/events.hpp> +#include <mtx/events/encrypted.hpp> + +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<mtx::events::msg::KeyVerificationRequest> &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<QString, QSharedPointer<DeviceVerificationFlow>> dvList; + bool isInitialSync_ = false; + RoomlistModel *rooms_; +}; + diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 86f59c52b67203f73e6fe4bb3448dfaa7b5a8ea3..94e6a0d71ebee0b3d4ce49a4ef823a1ab63ce24f 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<mtx::events::msg::KeyVerificationAccept>(); qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>(); @@ -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<SelfVerificationStatus>( "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<ChatPage *>(parent), &ChatPage::receivedRoomDeviceVerificationRequest, - this, - [this](const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &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<ChatPage *>(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<ChatPage *>(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<QString> &event_ids) diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 723282d615d7df5114dc3129ef4c788e2fee1529..6696b1c4dfb756a87500f6f5864771a51ec34808 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -9,7 +9,6 @@ #include <QQuickTextDocument> #include <QQuickView> #include <QQuickWidget> -#include <QSharedPointer> #include <QWidget> #include <mtx/common.hpp> @@ -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<QString, QColor> userColors; + // don't move this above the rooms_ + CallManager *callManager_ = nullptr; + VerificationManager *verificationManager_ = nullptr; - QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList; + QHash<QString, QColor> 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 d62e3248b13705a45ed0fa22189461d4776e2d65..0e3fd39f2ca756c945d729075d1037377f4d464a 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_); } }