From 64f204d984d4c4f520f8c5de613e17b01dd9a317 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 17 May 2020 19:04:47 +0530 Subject: Rewrite UserProfile in qml --- src/ui/UserProfile.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/ui/UserProfile.cpp (limited to 'src/ui/UserProfile.cpp') diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp new file mode 100644 index 00000000..ac35f1d4 --- /dev/null +++ b/src/ui/UserProfile.cpp @@ -0,0 +1,58 @@ +#include "UserProfile.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "Utils.h" + +UserProfile::UserProfile(QObject *parent) + : QObject(parent) +{} + +QMap +UserProfile::getDeviceList() +{ + return this->deviceList; +} + +void +UserProfile::fetchDeviceList(const QString &userId) +{ + auto localUser = utils::localUser(); + mtx::requests::QueryKeys req; + req.device_keys[userId.toStdString()] = {}; + + http::client()->query_keys( + req, + [user_id = userId.toStdString()](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + if (res.device_keys.empty() || + (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + auto devices = res.device_keys.at(user_id); + + std::vector deviceInfo; + for (const auto &d : devices) { + auto device = d.second; + + // TODO: Verify signatures and ignore those that don't pass. + deviceInfo.emplace_back(DeviceInfo{ + QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name)}); + } + + std::sort(deviceInfo.begin(), + deviceInfo.end(), + [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); + }); +} -- cgit 1.5.1 From a54a973ad6be4a1e71be6d8f993600fb02601574 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Fri, 22 May 2020 11:17:02 +0530 Subject: Adding DeviceList for userprofile --- resources/qml/UserProfile.qml | 18 ++++++++- .../qml/device-verification/DeviceVerification.qml | 8 ---- src/timeline/TimelineViewManager.cpp | 9 ++--- src/ui/UserProfile.cpp | 44 ++++++++++++++++------ src/ui/UserProfile.h | 32 +++++++++++++--- 5 files changed, 79 insertions(+), 32 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f019ee25..ae91abc4 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQuick.Window 2.3 +import im.nheko 1.0 + ApplicationWindow{ property var user_data property var colors: currentActivePalette @@ -18,7 +20,21 @@ ApplicationWindow{ userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") userProfileName.text = user_data.userName matrixUserID.text = user_data.userId - console.log("this is happening"); + userProfile.userId = user_data.userId + log_devices() + } + + function log_devices() + { + console.log(userProfile.deviceList); + userProfile.deviceList.forEach((item,index)=>{ + console.log(item.device_id) + console.log(item.display_name) + }) + } + + UserProfileContent{ + id: userProfile } background: Item{ diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 2c9486ae..dd637e59 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,11 +267,7 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" -<<<<<<< HEAD - implicitHeight: Qt.application.font.pixelSize * 3 -======= implicitHeight: Qt.application.font.pixelSize * 8 ->>>>>>> Add DeviceVerificationFlow dummy and verification test button implicitWidth: col.width ColumnLayout { id: col @@ -414,11 +410,7 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 -<<<<<<< HEAD - Label { -======= Text { ->>>>>>> Add DeviceVerificationFlow dummy and verification test button Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index afd1acb6..227b410f 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -15,6 +15,7 @@ #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" +#include "../ui/UserProfile.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) @@ -86,6 +87,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); + qmlRegisterType("im.nheko",1,0,"UserProfileContent"); + qRegisterMetaType(); qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); @@ -467,9 +470,3 @@ TimelineViewManager::startDummyVerification() { emit deviceVerificationRequest(new DeviceVerificationFlow(this)); } - -void -TimelineViewManager::startDummyVerification() -{ - emit deviceVerificationRequest(new DeviceVerificationFlow(this)); -} diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index ac35f1d4..30785699 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,28 +1,43 @@ #include "UserProfile.h" #include "Logging.h" -#include "MatrixClient.h" #include "Utils.h" +#include "mtx/responses/crypto.hpp" +#include UserProfile::UserProfile(QObject *parent) : QObject(parent) {} -QMap -UserProfile::getDeviceList() -{ +QVector +UserProfile::getDeviceList(){ + UserProfile::fetchDeviceList(this->userId); return this->deviceList; } +QString +UserProfile::getUserId (){ + return this->userId; +} + +void +UserProfile::setUserId (const QString &user_id){ + if(this->userId != userId) + return; + else + this->userId = user_id; +} + void -UserProfile::fetchDeviceList(const QString &userId) +UserProfile::fetchDeviceList(const QString &userID) { auto localUser = utils::localUser(); mtx::requests::QueryKeys req; - req.device_keys[userId.toStdString()] = {}; + mtx::responses::QueryKeys res; + req.device_keys[userID.toStdString()] = {}; http::client()->query_keys( req, - [user_id = userId.toStdString()](const mtx::responses::QueryKeys &res, + [user_id = userID.toStdString(),this](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", @@ -39,14 +54,18 @@ UserProfile::fetchDeviceList(const QString &userId) auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; + QVector deviceInfo; for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - deviceInfo.emplace_back(DeviceInfo{ - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)}); + // std::cout<device_id = QString::fromStdString(d.first); + newdevice->display_name = QString::fromStdString(device.unsigned_info.device_display_name) + + deviceInfo.append(std::move(newdevice)); } std::sort(deviceInfo.begin(), @@ -54,5 +73,8 @@ UserProfile::fetchDeviceList(const QString &userId) [](const DeviceInfo &a, const DeviceInfo &b) { return a.device_id > b.device_id; }); + + this->deviceList = deviceInfo; + emit UserProfile::deviceListUpdated(); }); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index d003e6ca..bbf57c7b 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -1,29 +1,49 @@ #pragma once -#include #include #include -struct DeviceInfo +#include "MatrixClient.h" + +class DeviceInfo { +public: + explicit DeviceInfo(QString device_id,QString display_name){ + this->device_id = device_id; + this->display_name = display_name; + } + ~DeviceInfo() = default; + DeviceInfo(const DeviceInfo &device){ + this->device_id = device.device_id; + this->display_name = device.display_name; + } + QString device_id; QString display_name; }; +Q_DECLARE_METATYPE(DeviceInfo); class UserProfile : public QObject { Q_OBJECT - Q_PROPERTY(QMap deviceList READ getDeviceList NOTIFY DeviceListUpdated) + Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) + Q_PROPERTY(QString userId READ getUserId WRITE setUserId) public: + // constructor explicit UserProfile(QObject *parent = 0); - QMap getDeviceList(); + // getters + QVector getDeviceList(); + QString getUserId(); + // setters + void setUserId(const QString &userId); Q_INVOKABLE void fetchDeviceList(const QString &userID); signals: - void DeviceListUpdated(); + void deviceListUpdated(); private: - QMap deviceList; + QVector deviceList; + QString userId; }; \ No newline at end of file -- cgit 1.5.1 From f9c0f4dd5410621a8427e2ef21496b7791d44d2f Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 27 May 2020 14:19:26 +0530 Subject: Add C++ Model for DeviceList --- CMakeLists.txt | 2 ++ resources/qml/TimelineView.qml | 28 +++++++-------- resources/qml/UserProfile.qml | 58 +++++++++++++++++++------------ src/Olm.cpp | 2 -- src/timeline/TimelineViewManager.cpp | 8 +++-- src/ui/UserProfile.cpp | 46 ++++++++++++++----------- src/ui/UserProfile.h | 22 ++++++------ src/ui/UserProfileModel.cpp | 66 ++++++++++++++++++++++++++++++++++++ src/ui/UserProfileModel.h | 29 ++++++++++++++++ 9 files changed, 188 insertions(+), 73 deletions(-) create mode 100644 src/ui/UserProfileModel.cpp create mode 100644 src/ui/UserProfileModel.h (limited to 'src/ui/UserProfile.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index e170495a..654eae37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,6 +277,7 @@ set(SRC_FILES src/ui/Theme.cpp src/ui/ThemeManager.cpp src/ui/UserProfile.cpp + src/ui/UserProfileModel.cpp src/AvatarProvider.cpp src/BlurhashProvider.cpp @@ -482,6 +483,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ui/Theme.h src/ui/ThemeManager.h src/ui/UserProfile.h + src/ui/UserProfileModel.h src/notifications/Manager.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index ed403aa9..e4c820f8 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -273,6 +273,9 @@ Page { color: colors.base } } + + property variant userProfile + Row { height: userName.height spacing: 8 @@ -287,8 +290,10 @@ Page { MouseArea { anchors.fill: parent onClicked: { - userProfile.user_data = modelData - userProfile.show() + if(userProfile) userProfile.destroy() + var component = Qt.createComponent("UserProfile.qml"); + userProfile = component.createObject(timelineRoot,{user_data : modelData}); + userProfile.show(); } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true @@ -303,26 +308,17 @@ Page { MouseArea { anchors.fill: parent + Layout.alignment: Qt.AlignHCenter onClicked: { - userProfile.user_data = modelData - userProfile.show() + if(userProfile) userProfile.destroy() + var component = Qt.createComponent("UserProfile.qml") + userProfile = component.createObject(timelineRoot,{user_data : modelData}) + userProfile.show() } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } } - - Label { - color: colors.buttonText - text: timelineManager.userStatus(modelData.userId) - textFormat: Text.PlainText - elide: Text.ElideRight - width: chat.delegateMaxWidth - parent.spacing*2 - userName.implicitWidth - avatarSize - font.italic: true - } - UserProfile{ - id: userProfile - } } } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index ae91abc4..f29fb4c1 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -16,25 +16,16 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter palette: colors - onAfterRendering: { - userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") - userProfileName.text = user_data.userName - matrixUserID.text = user_data.userId - userProfile.userId = user_data.userId - log_devices() - } - - function log_devices() - { - console.log(userProfile.deviceList); - userProfile.deviceList.forEach((item,index)=>{ - console.log(item.device_id) - console.log(item.display_name) - }) - } - - UserProfileContent{ - id: userProfile + UserProfileList{ + id: userProfileList + userId: user_data.userId + onUserIdChanged : { + console.log(userId) + userProfileList.updateDeviceList() + } + onDeviceListUpdated : { + modelDeviceList.deviceList = userProfileList + } } background: Item{ @@ -52,6 +43,7 @@ ApplicationWindow{ Avatar{ id: userProfileAvatar + url:chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") height: 130 width: 130 displayName: modelData.userName @@ -60,12 +52,14 @@ ApplicationWindow{ Label{ id: userProfileName + text: user_data.userName fontSizeMode: Text.HorizontalFit Layout.alignment: Qt.AlignHCenter } Label{ id: matrixUserID + text: user_data.userId fontSizeMode: Text.HorizontalFit Layout.alignment: Qt.AlignHCenter } @@ -78,9 +72,29 @@ ApplicationWindow{ ScrollBar.horizontal.policy: ScrollBar.AlwaysOn ScrollBar.vertical.policy: ScrollBar.AlwaysOn - Label { - text: "ABC" - font.pixelSize: 700 + ListView{ + id: deviceList + anchors.fill: parent + clip: true + spacing: 10 + + model: UserProfileModel{ + id: modelDeviceList + } + + delegate: RowLayout{ + width: parent.width + Text{ + Layout.fillWidth: true + color: colors.text + text: deviceID + } + Text{ + Layout.fillWidth: true + color:colors.text + text: displayName + } + } } } diff --git a/src/Olm.cpp b/src/Olm.cpp index 494bc201..74fbac9a 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -56,7 +56,6 @@ handle_to_device_messages(const std::vectorwarn("validation error for olm message: {} {}", e.what(), j_msg.dump(2)); - } } else if (msg_type == to_string(mtx::events::EventType::RoomKeyRequest)) { @@ -399,7 +398,6 @@ handle_key_request_message(const mtx::events::DeviceEventdebug("ignoring all key requests for room {}", req.content.room_id); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 227b410f..f4d1c00e 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "BlurhashProvider.h" #include "ChatPage.h" @@ -16,6 +17,8 @@ #include "emoji/EmojiModel.h" #include "emoji/Provider.h" #include "../ui/UserProfile.h" +#include "src/ui/UserProfile.h" +#include "src/ui/UserProfileModel.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) @@ -87,8 +90,9 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); - qmlRegisterType("im.nheko",1,0,"UserProfileContent"); - qRegisterMetaType(); + qmlRegisterType("im.nheko", 1, 0, "UserProfileModel"); + qmlRegisterType("im.nheko", 1, 0, "UserProfileList"); + qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 30785699..588d6969 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -2,29 +2,32 @@ #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" -#include UserProfile::UserProfile(QObject *parent) : QObject(parent) {} QVector -UserProfile::getDeviceList(){ - UserProfile::fetchDeviceList(this->userId); +UserProfile::getDeviceList() +{ return this->deviceList; } QString -UserProfile::getUserId (){ +UserProfile::getUserId() +{ return this->userId; } void -UserProfile::setUserId (const QString &user_id){ - if(this->userId != userId) +UserProfile::setUserId(const QString &user_id) +{ + if (this->userId != userId) return; - else + else { this->userId = user_id; + emit UserProfile::userIdChanged(); + } } void @@ -37,11 +40,11 @@ UserProfile::fetchDeviceList(const QString &userID) http::client()->query_keys( req, - [user_id = userID.toStdString(),this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { if (err) { - nhlog::net()->warn("failed to query device keys: {} {}", - err->matrix_error.error, + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, static_cast(err->status_code)); return; } @@ -53,17 +56,16 @@ UserProfile::fetchDeviceList(const QString &userID) } auto devices = res.device_keys.at(user_id); - QVector deviceInfo; + for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - // std::cout<device_id = QString::fromStdString(d.first); - newdevice->display_name = QString::fromStdString(device.unsigned_info.device_display_name) + DeviceInfo newdevice( + QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name)); + QString::fromStdString(device.unsigned_info.device_display_name); deviceInfo.append(std::move(newdevice)); } @@ -74,7 +76,13 @@ UserProfile::fetchDeviceList(const QString &userID) return a.device_id > b.device_id; }); - this->deviceList = deviceInfo; - emit UserProfile::deviceListUpdated(); + this->deviceList = std::move(deviceInfo); + emit UserProfile::deviceListUpdated(); }); } + +void +UserProfile::updateDeviceList() +{ + fetchDeviceList(this->userId); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index bbf57c7b..c37e23ae 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -2,33 +2,29 @@ #include #include +#include #include "MatrixClient.h" class DeviceInfo { public: - explicit DeviceInfo(QString device_id,QString display_name){ - this->device_id = device_id; - this->display_name = display_name; - } - ~DeviceInfo() = default; - DeviceInfo(const DeviceInfo &device){ - this->device_id = device.device_id; - this->display_name = device.display_name; - } + DeviceInfo(const QString deviceID, const QString displayName) + : device_id(deviceID) + , display_name(displayName) + {} + + DeviceInfo() {} QString device_id; QString display_name; }; -Q_DECLARE_METATYPE(DeviceInfo); class UserProfile : public QObject { Q_OBJECT + Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged) Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) - Q_PROPERTY(QString userId READ getUserId WRITE setUserId) - public: // constructor explicit UserProfile(QObject *parent = 0); @@ -39,8 +35,10 @@ public: void setUserId(const QString &userId); Q_INVOKABLE void fetchDeviceList(const QString &userID); + Q_INVOKABLE void updateDeviceList(); signals: + void userIdChanged(); void deviceListUpdated(); private: diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp new file mode 100644 index 00000000..ec0456cd --- /dev/null +++ b/src/ui/UserProfileModel.cpp @@ -0,0 +1,66 @@ +#include "UserProfileModel.h" +#include "UserProfile.h" +#include + +UserProfileModel::UserProfileModel(QObject *parent) + : QAbstractListModel(parent) + , deviceList(nullptr) +{} + +int +UserProfileModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid() || !this->deviceList) + return 0; + return this->deviceList->getDeviceList().size(); +} + +QVariant +UserProfileModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || !this->deviceList) + return QVariant(); + + const DeviceInfo device = this->deviceList->getDeviceList().at(index.row()); + switch (role) { + case DEVICEID: + return QVariant(device.device_id); + case DISPLAYNAME: + return QVariant(device.display_name); + } + return QVariant(); +} + +QHash +UserProfileModel::roleNames() const +{ + QHash names; + names[DEVICEID] = "deviceID"; + names[DISPLAYNAME] = "displayName"; + return names; +} + +UserProfile * +UserProfileModel::getList() const +{ + return (this->deviceList); +} + +void +UserProfileModel::setList(UserProfile *devices) +{ + beginResetModel(); + + if (devices) + devices->disconnect(this); + + if (this->deviceList) { + const int index = this->deviceList->getDeviceList().size(); + beginInsertRows(QModelIndex(), index, index); + endInsertRows(); + } + + this->deviceList = devices; + + endResetModel(); +} \ No newline at end of file diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h new file mode 100644 index 00000000..c21a806d --- /dev/null +++ b/src/ui/UserProfileModel.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class UserProfile; // forward declaration of the class UserProfile + +class UserProfileModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(UserProfile *deviceList READ getList WRITE setList) + +public: + explicit UserProfileModel(QObject *parent = nullptr); + + enum + { + DEVICEID, + DISPLAYNAME + }; + UserProfile *getList() const; + void setList(UserProfile *devices); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + virtual QHash roleNames() const override; + +private: + UserProfile *deviceList; +}; \ No newline at end of file -- cgit 1.5.1 From 00e36b6068786d64812135a4d505501134ebc214 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Fri, 26 Jun 2020 04:24:42 +0530 Subject: Add some Userprofile buttons --- resources/qml/UserProfile.qml | 62 +++++++++++++++- .../qml/device-verification/DeviceVerification.qml | 15 +++- src/DeviceVerificationFlow.cpp | 83 +++++++++++----------- src/ui/UserProfile.cpp | 28 ++++++++ src/ui/UserProfile.h | 5 +- 5 files changed, 146 insertions(+), 47 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index e5d01625..36c8586d 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -12,7 +12,7 @@ ApplicationWindow{ property var colors: currentActivePalette id:userProfileDialog - height: 500 + height: 650 width: 420 modality:Qt.WindowModal Layout.alignment: Qt.AlignHCenter @@ -43,7 +43,7 @@ ApplicationWindow{ width: userProfileDialog.width height: userProfileDialog.height - Layout.fillHeight : true + // Layout.fillHeight : true ColumnLayout{ anchors.fill: userProfileItem @@ -82,8 +82,62 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter } + RowLayout{ + Layout.alignment: Qt.AlignHCenter + ImageButton{ + image:":/icons/icons/ui/do-not-disturb-rounded-sign.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Ban the user") + onClicked : { + userProfileList.banUser() + } + } + // ImageButton{ + // image:":/icons/icons/ui/volume-off-indicator.png" + // Layout.margins: { + // left: 5 + // right: 5 + // } + // ToolTip.visible: hovered + // ToolTip.text: qsTr("Ignore messages from this user") + // onClicked : { + // userProfileList.ignoreUser() + // } + // } + + ImageButton{ + image:":/icons/icons/ui/round-remove-button.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Kick the user") + onClicked : { + userProfileList.kickUser() + } + } + + ImageButton{ + image:":/icons/icons/ui/black-bubble-speech.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Start a conversation") + onClicked : { + userProfileList.startChat() + } + } + } + ScrollView { - implicitHeight: userProfileDialog.height/2+20 + implicitHeight: userProfileDialog.height/2 + 20 implicitWidth: userProfileDialog.width-20 clip: true Layout.alignment: Qt.AlignHCenter @@ -150,6 +204,8 @@ ApplicationWindow{ id: okbutton text:"OK" onClicked: userProfileDialog.close() + + Layout.alignment: Qt.AlignRight Layout.margins : { right : 10 diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 516bc74a..ca21f484 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -82,8 +82,6 @@ ApplicationWindow { } onClicked: { dialog.close(); - flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -128,16 +126,26 @@ ApplicationWindow { RowLayout { RadioButton { + id: decimalRadio Layout.alignment: Qt.AlignLeft text: qsTr("Decimal") + contentItem: Text { + text: decimalRadio.text + color: colors.text + } onClicked: { flow.method = DeviceVerificationFlow.Decimal } } Item { Layout.fillWidth: true } RadioButton { + id: emojiRadio Layout.alignment: Qt.AlignRight text: qsTr("Emoji") + contentItem: Text { + text: emojiRadio.text + color: colors.text + } onClicked: { flow.method = DeviceVerificationFlow.Emoji } } } @@ -156,7 +164,7 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter } onClicked: { - dialog.close(); + dialog.close(); flow.cancelVerification(); deviceVerificationList.remove(flow.tranId); delete flow; @@ -411,6 +419,7 @@ ApplicationWindow { text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 2 font.family: Settings.emojiFont + color:colors.text } Label { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 9b260892..2c6e9c1e 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -13,7 +13,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { timeout = new QTimer(this); timeout->setSingleShot(true); - this->sas = olm::client()->sas_init(); + this->sas = olm::client()->sas_init(); + this->isMacVerified = false; connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); @@ -134,45 +135,47 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) } } }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationMac, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::string info = - "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + - this->deviceId.toStdString() + - http::client()->user_id().to_string() + - http::client()->device_id() + this->transaction_id; - - std::vector key_list; - std::string key_string; - for (auto mac : msg.content.mac) { - if (mac.second == - this->sas->calculate_mac(this->device_keys[mac.first], - info + mac.first)) { - key_string += mac.first; - } else { - this->cancelVerification(); - return; - } - } - if (msg.content.keys == - this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - // uncomment this in future to be compatible with the - // MSC2366 this->sendVerificationDone(); and remoeve the - // below line - if (this->isMacVerified == true) - emit this->deviceVerified(); - else - this->isMacVerified = true; - } else { - this->cancelVerification(); - } - } - }); + connect( + ChatPage::instance(), + &ChatPage::recievedDeviceVerificationMac, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::string info = + "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + + this->deviceId.toStdString() + http::client()->user_id().to_string() + + http::client()->device_id() + this->transaction_id; + + std::vector key_list; + std::string key_string; + for (auto mac : msg.content.mac) { + key_string += mac.first + ","; + if (device_keys[mac.first] != "") { + if (mac.second == + this->sas->calculate_mac(this->device_keys[mac.first], + info + mac.first)) { + } else { + this->cancelVerification(); + return; + } + } + } + key_string = key_string.substr(0, key_string.length() - 1); + if (msg.content.keys == + this->sas->calculate_mac(key_string, info + "KEY_IDS")) { + // uncomment this in future to be compatible with the + // MSC2366 this->sendVerificationDone(); and remove the + // below line + if (this->isMacVerified == true) + emit this->deviceVerified(); + else + this->isMacVerified = true; + } else { + this->cancelVerification(); + } + } + }); connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationReady, this, diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 588d6969..6aa4deff 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,4 +1,5 @@ #include "UserProfile.h" +#include "ChatPage.h" #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" @@ -86,3 +87,30 @@ UserProfile::updateDeviceList() { fetchDeviceList(this->userId); } + +void +UserProfile::banUser() +{ + ChatPage::instance()->banUser(this->userId, ""); +} + +// void ignoreUser(){ + +// } + +void +UserProfile::kickUser() +{ + ChatPage::instance()->kickUser(this->userId, ""); +} + +void +UserProfile::startChat() +{ + mtx::requests::CreateRoom req; + req.preset = mtx::requests::Preset::PrivateChat; + req.visibility = mtx::requests::Visibility::Private; + if (utils::localUser() != this->userId) + req.invite = {this->userId.toStdString()}; + emit ChatPage::instance()->createRoom(req); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index c37e23ae..ad92d182 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -5,7 +5,6 @@ #include #include "MatrixClient.h" - class DeviceInfo { public: @@ -36,6 +35,10 @@ public: Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void updateDeviceList(); + Q_INVOKABLE void banUser(); + // Q_INVOKABLE void ignoreUser(); + Q_INVOKABLE void kickUser(); + Q_INVOKABLE void startChat(); signals: void userIdChanged(); -- cgit 1.5.1 From 6fae36abc404ffb7e6ae29c9edceda5231400f0a Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 28 Jun 2020 21:01:34 +0530 Subject: [WIP] Add Caching for users --- resources/qml/TimelineView.qml | 18 +++--- resources/qml/UserProfile.qml | 11 ++-- src/Cache.cpp | 138 +++++++++++++++++++++++++++++++++++++++++ src/Cache.h | 17 +++++ src/CacheCryptoStructs.h | 30 +++++++++ src/Cache_p.h | 19 ++++++ src/ui/UserProfile.cpp | 106 +++++++++++++++++-------------- src/ui/UserProfile.h | 4 ++ 8 files changed, 283 insertions(+), 60 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index d9302ed7..3618140a 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -21,6 +21,7 @@ Page { property real highlightHue: colors.highlight.hslHue property real highlightSat: colors.highlight.hslSaturation property real highlightLight: colors.highlight.hslLightness + property variant userProfile palette: colors @@ -238,6 +239,11 @@ Page { } } + Component{ + id: userProfileComponent + UserProfile{} + } + section { property: "section" } @@ -274,8 +280,6 @@ Page { } } - property variant userProfile - Row { height: userName.height spacing: 8 @@ -290,9 +294,7 @@ Page { MouseArea { anchors.fill: parent onClicked: { - if(userProfile) userProfile.destroy() - var component = Qt.createComponent("UserProfile.qml"); - userProfile = component.createObject(timelineRoot,{user_data : modelData}); + userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)}); userProfile.show(); } cursorShape: Qt.PointingHandCursor @@ -310,10 +312,8 @@ Page { anchors.fill: parent Layout.alignment: Qt.AlignHCenter onClicked: { - if(userProfile) userProfile.destroy() - var component = Qt.createComponent("UserProfile.qml") - userProfile = component.createObject(timelineRoot,{user_data : modelData}) - userProfile.show() + userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)}); + userProfile.show(); } cursorShape: Qt.PointingHandCursor propagateComposedEvents: true diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 6ef75031..a0b0f993 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -9,6 +9,7 @@ import "./device-verification" ApplicationWindow{ property var user_data + property var avatarUrl property var colors: currentActivePalette id:userProfileDialog @@ -52,11 +53,11 @@ ApplicationWindow{ Avatar{ id: userProfileAvatar - url:chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") + url: avatarUrl.replace("mxc://", "image://MxcImage/") height: 130 width: 130 - displayName: modelData.userName - userid: modelData.userId + displayName: user_data.userName + userid: user_data.userId Layout.alignment: Qt.AlignHCenter Layout.margins : { top: 10 @@ -68,7 +69,7 @@ ApplicationWindow{ text: user_data.userName fontSizeMode: Text.HorizontalFit font.pixelSize: 20 - color:TimelineManager.userColor(modelData.userId, colors.window) + color:TimelineManager.userColor(user_data.userId, colors.window) font.bold: true Layout.alignment: Qt.AlignHCenter } @@ -207,7 +208,7 @@ ApplicationWindow{ Layout.margins : { right : 10 - bottom : 10 + bottom: 5 } palette { diff --git a/src/Cache.cpp b/src/Cache.cpp index 0c692d07..5afeab06 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2882,6 +2882,115 @@ Cache::statusMessage(const std::string &user_id) return status_msg; } +void +to_json(json &j, const UserCache &info) +{ + j["user_id"] = info.user_id; + j["is_user_verified"] = info.is_user_verified; + j["cross_verified"] = info.cross_verified; + j["keys"] = info.keys; +} + +void +from_json(const json &j, UserCache &info) +{ + info.user_id = j.at("user_id"); + info.is_user_verified = j.at("is_user_verified"); + info.cross_verified = j.at("cross_verified").get>(); + info.keys = j.at("keys").get(); +} + +UserCache +Cache::getUserCache(const std::string &user_id) +{ + lmdb::val verifiedVal; + + auto txn = lmdb::txn::begin(env_); + auto db = getUserCacheDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + + UserCache verified_state; + if (res) { + verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); + } + + txn.commit(); + + return verified_state; +} + +//! be careful when using make sure is_user_verified is not changed +int +Cache::setUserCache(const std::string &user_id, const UserCache &body) +{ + auto txn = lmdb::txn::begin(env_); + auto db = getUserCacheDb(txn); + + auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + + txn.commit(); + + return res; +} + +int +Cache::deleteUserCache(const std::string &user_id) +{ + auto txn = lmdb::txn::begin(env_); + auto db = getUserCacheDb(txn); + auto res = lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); + + txn.commit(); + + return res; +} + +void +to_json(json &j, const DeviceVerifiedCache &info) +{ + j["user_id"] = info.user_id; + j["device_verified"] = info.device_verified; +} + +void +from_json(const json &j, DeviceVerifiedCache &info) +{ + info.user_id = j.at("user_id"); + info.device_verified = j.at("device_verified").get>(); +} + +DeviceVerifiedCache +Cache::getVerifiedCache(const std::string &user_id) +{ + lmdb::val verifiedVal; + + auto txn = lmdb::txn::begin(env_); + auto db = getDeviceVerifiedDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + + DeviceVerifiedCache verified_state; + if (res) { + verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); + } + + txn.commit(); + + return verified_state; +} + +int +Cache::setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) +{ + auto txn = lmdb::txn::begin(env_); + auto db = getDeviceVerifiedDb(txn); + + auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + + txn.commit(); + + return res; +} + void to_json(json &j, const RoomInfo &info) { @@ -3063,6 +3172,35 @@ statusMessage(const std::string &user_id) { return instance_->statusMessage(user_id); } +UserCache +getUserCache(const std::string &user_id) +{ + return instance_->getUserCache(user_id); +} + +int +setUserCache(const std::string &user_id, const UserCache &body) +{ + return instance_->setUserCache(user_id, body); +} + +int +deleteUserCache(const std::string &user_id) +{ + return instance_->deleteUserCache(user_id); +} + +DeviceVerifiedCache +getVerifiedCache(const std::string &user_id) +{ + return instance_->getVerifiedCache(user_id); +} + +int +setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) +{ + return instance_->setVerifiedCache(user_id, body); +} //! Load saved data for the display names & avatars. void diff --git a/src/Cache.h b/src/Cache.h index b5275623..34e79ab5 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -60,6 +60,23 @@ presenceState(const std::string &user_id); std::string statusMessage(const std::string &user_id); +//! user Cache +UserCache +getUserCache(const std::string &user_id); + +int +setUserCache(const std::string &user_id, const UserCache &body); + +int +deleteUserCache(const std::string &user_id); + +//! verified Cache +DeviceVerifiedCache +getVerifiedCache(const std::string &user_id); + +int +setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); + //! Load saved data for the display names & avatars. void populateMembers(); diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 14c9c86b..7344aef9 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -65,3 +65,33 @@ struct OlmSessionStorage std::mutex group_outbound_mtx; std::mutex group_inbound_mtx; }; + +struct UserCache +{ + //! user_id of the user + std::string user_id; + //! this stores if the user is verified (with cross-signing) + bool is_user_verified = false; + //! list of verified device_ids with cross-signing + std::vector cross_verified; + //! map of public key key_ids and their public_key + mtx::responses::QueryKeys keys; +}; + +void +to_json(nlohmann::json &j, const UserCache &info); +void +from_json(const nlohmann::json &j, UserCache &info); + +struct DeviceVerifiedCache +{ + //! user_id of the user + std::string user_id; + //! list of verified device_ids with device-verification + std::vector device_verified; +}; + +void +to_json(nlohmann::json &j, const DeviceVerifiedCache &info); +void +from_json(const nlohmann::json &j, DeviceVerifiedCache &info); diff --git a/src/Cache_p.h b/src/Cache_p.h index 61d91b0c..cf4416ce 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -54,6 +54,15 @@ public: mtx::presence::PresenceState presenceState(const std::string &user_id); std::string statusMessage(const std::string &user_id); + // user cache stores user keys + UserCache getUserCache(const std::string &user_id); + int setUserCache(const std::string &user_id, const UserCache &body); + int deleteUserCache(const std::string &user_id); + + // device verified cache + DeviceVerifiedCache getVerifiedCache(const std::string &user_id); + int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); + static void removeDisplayName(const QString &room_id, const QString &user_id); static void removeAvatarUrl(const QString &room_id, const QString &user_id); @@ -510,6 +519,16 @@ private: return lmdb::dbi::open(txn, "presence", MDB_CREATE); } + lmdb::dbi getUserCacheDb(lmdb::txn &txn) + { + return lmdb::dbi::open(txn, std::string("user_cache").c_str(), MDB_CREATE); + } + + lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn) + { + return lmdb::dbi::open(txn, std::string("verified").c_str(), MDB_CREATE); + } + //! Retrieves or creates the database that stores the open OLM sessions between our device //! and the given curve25519 key which represents another device. //! diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 6aa4deff..c637280b 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,9 +1,12 @@ #include "UserProfile.h" +#include "Cache.h" #include "ChatPage.h" #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" +#include // only for debugging + UserProfile::UserProfile(QObject *parent) : QObject(parent) {} @@ -32,54 +35,65 @@ UserProfile::setUserId(const QString &user_id) } void -UserProfile::fetchDeviceList(const QString &userID) +UserProfile::callback_fn(const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err, + std::string user_id) { - auto localUser = utils::localUser(); - mtx::requests::QueryKeys req; - mtx::responses::QueryKeys res; - req.device_keys[userID.toStdString()] = {}; - - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - - if (res.device_keys.empty() || - (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } - - auto devices = res.device_keys.at(user_id); - QVector deviceInfo; - - for (const auto &d : devices) { - auto device = d.second; - - // TODO: Verify signatures and ignore those that don't pass. - DeviceInfo newdevice( - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)); - QString::fromStdString(device.unsigned_info.device_display_name); - - deviceInfo.append(std::move(newdevice)); - } - - std::sort(deviceInfo.begin(), - deviceInfo.end(), - [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); - - this->deviceList = std::move(deviceInfo); - emit UserProfile::deviceListUpdated(); + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + auto devices = res.device_keys.at(user_id); + QVector deviceInfo; + + for (const auto &d : devices) { + auto device = d.second; + + // TODO: Verify signatures and ignore those that don't pass. + DeviceInfo newdevice( + QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name)); + QString::fromStdString(device.unsigned_info.device_display_name); + + deviceInfo.append(std::move(newdevice)); + } + + std::sort( + deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; }); + + this->deviceList = std::move(deviceInfo); + emit UserProfile::deviceListUpdated(); +} + +void +UserProfile::fetchDeviceList(const QString &userID) +{ + auto localUser = utils::localUser(); + auto user_cache = cache::getUserCache(userID.toStdString()); + + if (user_cache.user_id == userID.toStdString()) { + mtx::http::ClientError error; + this->callback_fn(user_cache.keys, std::move(error), userID.toStdString()); + } else { + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + http::client()->query_keys( + req, + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + this->callback_fn(res, err, user_id); + }); + } } void diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index ad92d182..befd82ec 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -47,4 +47,8 @@ signals: private: QVector deviceList; QString userId; + + void callback_fn(const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err, + std::string user_id); }; \ No newline at end of file -- cgit 1.5.1 From ac1fbbb69fdd4e313072cbf95eb9288db1257a9d Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 1 Jul 2020 17:47:10 +0530 Subject: Some issue with UserProfile --- CMakeLists.txt | 3 ++ resources/qml/UserProfile.qml | 44 +++++++++++++-------------- src/Cache.cpp | 32 +++++++++---------- src/Cache.h | 4 +-- src/CacheCryptoStructs.h | 5 +-- src/Cache_p.h | 4 +-- src/ui/UserProfile.cpp | 71 +++++++++++++++++++++++++++++-------------- src/ui/UserProfile.h | 54 +++++++++++++++++++++----------- src/ui/UserProfileModel.cpp | 45 +++++++++++++-------------- src/ui/UserProfileModel.h | 7 +++-- 10 files changed, 154 insertions(+), 115 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 654eae37..c39ff3af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard") set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported") set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default") +# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + option(HUNTER_ENABLED "Enable Hunter package manager" OFF) include("cmake/HunterGate.cmake") HunterGate( diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index a0b0f993..db44ec15 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -19,17 +19,6 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter palette: colors - UserProfileList{ - id: userProfileList - userId: user_data.userId - onUserIdChanged : { - userProfileList.updateDeviceList() - } - onDeviceListUpdated : { - modelDeviceList.deviceList = userProfileList - } - } - Component { id: deviceVerificationDialog DeviceVerification {} @@ -94,7 +83,7 @@ ApplicationWindow{ ToolTip.visible: hovered ToolTip.text: qsTr("Ban the user") onClicked : { - userProfileList.banUser() + modelDeviceList.deviceList.banUser() } } // ImageButton{ @@ -106,7 +95,7 @@ ApplicationWindow{ // ToolTip.visible: hovered // ToolTip.text: qsTr("Ignore messages from this user") // onClicked : { - // userProfileList.ignoreUser() + // modelDeviceList.deviceList.ignoreUser() // } // } ImageButton{ @@ -118,7 +107,7 @@ ApplicationWindow{ ToolTip.visible: hovered ToolTip.text: qsTr("Start a private chat") onClicked : { - userProfileList.startChat() + modelDeviceList.deviceList.startChat() } } ImageButton{ @@ -130,7 +119,7 @@ ApplicationWindow{ ToolTip.visible: hovered ToolTip.text: qsTr("Kick the user") onClicked : { - userProfileList.kickUser() + modelDeviceList.deviceList.kickUser() } } } @@ -142,14 +131,15 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter ListView{ - id: deviceList + id: devicelist anchors.fill: parent clip: true spacing: 4 model: UserProfileModel{ id: modelDeviceList - } + deviceList.userId : user_data.userId + } delegate: RowLayout{ width: parent.width @@ -157,12 +147,20 @@ ApplicationWindow{ top : 50 } ColumnLayout{ - Text{ - Layout.fillWidth: true - color: colors.text - font.bold: true - Layout.alignment: Qt.AlignRight - text: deviceID + RowLayout{ + Text{ + Layout.fillWidth: true + color: colors.text + font.bold: true + Layout.alignment: Qt.AlignLeft + text: deviceID + } + Text{ + Layout.fillWidth: true + color:colors.text + Layout.alignment: Qt.AlignLeft + text: (verified_status == UserProfileList.VERIFIED?"V":(verified_status == UserProfileList.UNVERIFIED?"NV":"B")) + } } Text{ Layout.fillWidth: true diff --git a/src/Cache.cpp b/src/Cache.cpp index 5afeab06..553d1e97 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2885,7 +2885,6 @@ Cache::statusMessage(const std::string &user_id) void to_json(json &j, const UserCache &info) { - j["user_id"] = info.user_id; j["is_user_verified"] = info.is_user_verified; j["cross_verified"] = info.cross_verified; j["keys"] = info.keys; @@ -2894,13 +2893,12 @@ to_json(json &j, const UserCache &info) void from_json(const json &j, UserCache &info) { - info.user_id = j.at("user_id"); info.is_user_verified = j.at("is_user_verified"); info.cross_verified = j.at("cross_verified").get>(); info.keys = j.at("keys").get(); } -UserCache +std::optional Cache::getUserCache(const std::string &user_id) { lmdb::val verifiedVal; @@ -2909,14 +2907,15 @@ Cache::getUserCache(const std::string &user_id) auto db = getUserCacheDb(txn); auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + txn.commit(); + UserCache verified_state; if (res) { verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); + return verified_state; + } else { + return {}; } - - txn.commit(); - - return verified_state; } //! be careful when using make sure is_user_verified is not changed @@ -2948,18 +2947,18 @@ Cache::deleteUserCache(const std::string &user_id) void to_json(json &j, const DeviceVerifiedCache &info) { - j["user_id"] = info.user_id; j["device_verified"] = info.device_verified; + j["device_blocked"] = info.device_blocked; } void from_json(const json &j, DeviceVerifiedCache &info) { - info.user_id = j.at("user_id"); info.device_verified = j.at("device_verified").get>(); + info.device_blocked = j.at("device_blocked").get>(); } -DeviceVerifiedCache +std::optional Cache::getVerifiedCache(const std::string &user_id) { lmdb::val verifiedVal; @@ -2968,14 +2967,15 @@ Cache::getVerifiedCache(const std::string &user_id) auto db = getDeviceVerifiedDb(txn); auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + txn.commit(); + DeviceVerifiedCache verified_state; if (res) { verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); + return verified_state; + } else { + return {}; } - - txn.commit(); - - return verified_state; } int @@ -3172,7 +3172,7 @@ statusMessage(const std::string &user_id) { return instance_->statusMessage(user_id); } -UserCache +std::optional getUserCache(const std::string &user_id) { return instance_->getUserCache(user_id); @@ -3190,7 +3190,7 @@ deleteUserCache(const std::string &user_id) return instance_->deleteUserCache(user_id); } -DeviceVerifiedCache +std::optional getVerifiedCache(const std::string &user_id) { return instance_->getVerifiedCache(user_id); diff --git a/src/Cache.h b/src/Cache.h index 34e79ab5..0c955c92 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -61,7 +61,7 @@ std::string statusMessage(const std::string &user_id); //! user Cache -UserCache +std::optional getUserCache(const std::string &user_id); int @@ -71,7 +71,7 @@ int deleteUserCache(const std::string &user_id); //! verified Cache -DeviceVerifiedCache +std::optional getVerifiedCache(const std::string &user_id); int diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 7344aef9..241cac76 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -68,8 +68,6 @@ struct OlmSessionStorage struct UserCache { - //! user_id of the user - std::string user_id; //! this stores if the user is verified (with cross-signing) bool is_user_verified = false; //! list of verified device_ids with cross-signing @@ -85,10 +83,9 @@ from_json(const nlohmann::json &j, UserCache &info); struct DeviceVerifiedCache { - //! user_id of the user - std::string user_id; //! list of verified device_ids with device-verification std::vector device_verified; + std::vector device_blocked; }; void diff --git a/src/Cache_p.h b/src/Cache_p.h index cf4416ce..60fa930f 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -55,12 +55,12 @@ public: std::string statusMessage(const std::string &user_id); // user cache stores user keys - UserCache getUserCache(const std::string &user_id); + std::optional getUserCache(const std::string &user_id); int setUserCache(const std::string &user_id, const UserCache &body); int deleteUserCache(const std::string &user_id); // device verified cache - DeviceVerifiedCache getVerifiedCache(const std::string &user_id); + std::optional getVerifiedCache(const std::string &user_id); int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); static void removeDisplayName(const QString &room_id, const QString &user_id); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index c637280b..8c6fb8e4 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -7,11 +7,25 @@ #include // only for debugging +Q_DECLARE_METATYPE(UserProfile::Status) + UserProfile::UserProfile(QObject *parent) : QObject(parent) -{} +{ + qRegisterMetaType(); + connect( + this, &UserProfile::updateDeviceList, this, [this]() { fetchDeviceList(this->userId); }); + connect( + this, + &UserProfile::appendDeviceList, + this, + [this](QString device_id, QString device_name, UserProfile::Status verification_status) { + this->deviceList.push_back( + DeviceInfo{device_id, device_name, verification_status}); + }); +} -QVector +std::vector UserProfile::getDeviceList() { return this->deviceList; @@ -37,7 +51,8 @@ UserProfile::setUserId(const QString &user_id) void UserProfile::callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, - std::string user_id) + std::string user_id, + std::optional> cross_verified) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", @@ -52,24 +67,40 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, } auto devices = res.device_keys.at(user_id); - QVector deviceInfo; + std::vector deviceInfo; + auto device_verified = cache::getVerifiedCache(user_id); for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - DeviceInfo newdevice( + UserProfile::Status verified = UserProfile::Status::UNVERIFIED; + if (cross_verified.has_value()) { + if (std::find(cross_verified->begin(), cross_verified->end(), d.first) != + cross_verified->end()) + verified = UserProfile::Status::VERIFIED; + } else if (device_verified.has_value()) { + if (std::find(device_verified->device_verified.begin(), + device_verified->device_verified.end(), + d.first) != device_verified->device_verified.end()) + verified = UserProfile::Status::VERIFIED; + } else if (device_verified.has_value()) { + if (std::find(device_verified->device_blocked.begin(), + device_verified->device_blocked.end(), + d.first) != device_verified->device_blocked.end()) + verified = UserProfile::Status::BLOCKED; + } + + emit UserProfile::appendDeviceList( QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)); - QString::fromStdString(device.unsigned_info.device_display_name); - - deviceInfo.append(std::move(newdevice)); + QString::fromStdString(device.unsigned_info.device_display_name), + verified); } - std::sort( - deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); + // std::sort( + // deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { + // return a.device_id > b.device_id; + // }); this->deviceList = std::move(deviceInfo); emit UserProfile::deviceListUpdated(); @@ -81,9 +112,9 @@ UserProfile::fetchDeviceList(const QString &userID) auto localUser = utils::localUser(); auto user_cache = cache::getUserCache(userID.toStdString()); - if (user_cache.user_id == userID.toStdString()) { - mtx::http::ClientError error; - this->callback_fn(user_cache.keys, std::move(error), userID.toStdString()); + if (user_cache.has_value()) { + this->callback_fn( + user_cache->keys, {}, userID.toStdString(), user_cache->cross_verified); } else { mtx::requests::QueryKeys req; req.device_keys[userID.toStdString()] = {}; @@ -91,17 +122,11 @@ UserProfile::fetchDeviceList(const QString &userID) req, [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); + this->callback_fn(res, err, user_id, {}); }); } } -void -UserProfile::updateDeviceList() -{ - fetchDeviceList(this->userId); -} - void UserProfile::banUser() { diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index befd82ec..1725b961 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -5,36 +5,32 @@ #include #include "MatrixClient.h" -class DeviceInfo -{ -public: - DeviceInfo(const QString deviceID, const QString displayName) - : device_id(deviceID) - , display_name(displayName) - {} - DeviceInfo() {} - - QString device_id; - QString display_name; -}; +class DeviceInfo; class UserProfile : public QObject { Q_OBJECT Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged) - Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) + Q_PROPERTY(std::vector deviceList READ getDeviceList NOTIFY deviceListUpdated) public: // constructor explicit UserProfile(QObject *parent = 0); // getters - QVector getDeviceList(); + std::vector getDeviceList(); QString getUserId(); // setters void setUserId(const QString &userId); - Q_INVOKABLE void fetchDeviceList(const QString &userID); - Q_INVOKABLE void updateDeviceList(); + enum Status + { + VERIFIED, + UNVERIFIED, + BLOCKED + }; + Q_ENUM(Status) + + void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); @@ -43,12 +39,34 @@ public: signals: void userIdChanged(); void deviceListUpdated(); + void updateDeviceList(); + void appendDeviceList(const QString device_id, + const QString device_naem, + const UserProfile::Status verification_status); private: - QVector deviceList; + std::vector deviceList; QString userId; + std::optional cross_verified; void callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, - std::string user_id); + std::string user_id, + std::optional> cross_verified); +}; + +class DeviceInfo +{ +public: + DeviceInfo(const QString deviceID, + const QString displayName, + UserProfile::Status verification_status_) + : device_id(deviceID) + , display_name(displayName) + , verification_status(verification_status_) + {} + + QString device_id; + QString display_name; + UserProfile::Status verification_status; }; \ No newline at end of file diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp index ec0456cd..3fa8fe2d 100644 --- a/src/ui/UserProfileModel.cpp +++ b/src/ui/UserProfileModel.cpp @@ -1,11 +1,23 @@ #include "UserProfileModel.h" -#include "UserProfile.h" #include UserProfileModel::UserProfileModel(QObject *parent) : QAbstractListModel(parent) , deviceList(nullptr) -{} +{ + this->deviceList = new UserProfile(this); + + connect(this->deviceList, &UserProfile::userIdChanged, this, [this]() { + emit this->deviceList->updateDeviceList(); + }); + connect(this->deviceList, &UserProfile::deviceListUpdated, this, [this]() { + beginResetModel(); + this->beginInsertRows( + QModelIndex(), 0, this->deviceList->getDeviceList().size() - 1); + this->endInsertRows(); + endResetModel(); + }); +} int UserProfileModel::rowCount(const QModelIndex &parent) const @@ -18,7 +30,8 @@ UserProfileModel::rowCount(const QModelIndex &parent) const QVariant UserProfileModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || !this->deviceList) + if (!index.isValid() && + static_cast(this->deviceList->getDeviceList().size()) <= index.row()) return QVariant(); const DeviceInfo device = this->deviceList->getDeviceList().at(index.row()); @@ -27,6 +40,8 @@ UserProfileModel::data(const QModelIndex &index, int role) const return QVariant(device.device_id); case DISPLAYNAME: return QVariant(device.display_name); + case VERIFIED_STATUS: + return device.verification_status; } return QVariant(); } @@ -35,8 +50,9 @@ QHash UserProfileModel::roleNames() const { QHash names; - names[DEVICEID] = "deviceID"; - names[DISPLAYNAME] = "displayName"; + names[DEVICEID] = "deviceID"; + names[DISPLAYNAME] = "displayName"; + names[VERIFIED_STATUS] = "verified_status"; return names; } @@ -45,22 +61,3 @@ UserProfileModel::getList() const { return (this->deviceList); } - -void -UserProfileModel::setList(UserProfile *devices) -{ - beginResetModel(); - - if (devices) - devices->disconnect(this); - - if (this->deviceList) { - const int index = this->deviceList->getDeviceList().size(); - beginInsertRows(QModelIndex(), index, index); - endInsertRows(); - } - - this->deviceList = devices; - - endResetModel(); -} \ No newline at end of file diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h index c21a806d..ba7a2525 100644 --- a/src/ui/UserProfileModel.h +++ b/src/ui/UserProfileModel.h @@ -1,5 +1,6 @@ #pragma once +#include "UserProfile.h" #include class UserProfile; // forward declaration of the class UserProfile @@ -7,7 +8,7 @@ class UserProfile; // forward declaration of the class UserProfile class UserProfileModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(UserProfile *deviceList READ getList WRITE setList) + Q_PROPERTY(UserProfile *deviceList READ getList) public: explicit UserProfileModel(QObject *parent = nullptr); @@ -15,10 +16,10 @@ public: enum { DEVICEID, - DISPLAYNAME + DISPLAYNAME, + VERIFIED_STATUS }; UserProfile *getList() const; - void setList(UserProfile *devices); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; -- cgit 1.5.1 From 08028d5c57d134fb3d0ca9004730f0b2c99e5e67 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 4 Jul 2020 04:24:28 +0200 Subject: Refactor UserProfile --- CMakeLists.txt | 4 - resources/qml/TimelineView.qml | 22 +- resources/qml/UserProfile.qml | 417 +++++++++++++++++------------------ src/MainWindow.cpp | 9 - src/MainWindow.h | 2 - src/dialogs/UserProfile.cpp | 305 ------------------------- src/dialogs/UserProfile.h | 69 ------ src/timeline/TimelineModel.cpp | 4 +- src/timeline/TimelineModel.h | 10 +- src/timeline/TimelineViewManager.cpp | 20 +- src/ui/UserProfile.cpp | 108 +++++---- src/ui/UserProfile.h | 120 ++++++---- src/ui/UserProfileModel.cpp | 63 ------ src/ui/UserProfileModel.h | 30 --- 14 files changed, 383 insertions(+), 800 deletions(-) delete mode 100644 src/dialogs/UserProfile.cpp delete mode 100644 src/dialogs/UserProfile.h delete mode 100644 src/ui/UserProfileModel.cpp delete mode 100644 src/ui/UserProfileModel.h (limited to 'src/ui/UserProfile.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index c39ff3af..5ad8a625 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,7 +240,6 @@ set(SRC_FILES src/dialogs/ReCaptcha.cpp src/dialogs/ReadReceipts.cpp src/dialogs/RoomSettings.cpp - src/dialogs/UserProfile.cpp # Emoji src/emoji/Category.cpp @@ -280,7 +279,6 @@ set(SRC_FILES src/ui/Theme.cpp src/ui/ThemeManager.cpp src/ui/UserProfile.cpp - src/ui/UserProfileModel.cpp src/AvatarProvider.cpp src/BlurhashProvider.cpp @@ -449,7 +447,6 @@ qt5_wrap_cpp(MOC_HEADERS src/dialogs/ReCaptcha.h src/dialogs/ReadReceipts.h src/dialogs/RoomSettings.h - src/dialogs/UserProfile.h # Emoji src/emoji/Category.h @@ -486,7 +483,6 @@ qt5_wrap_cpp(MOC_HEADERS src/ui/Theme.h src/ui/ThemeManager.h src/ui/UserProfile.h - src/ui/UserProfileModel.h src/notifications/Manager.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 3618140a..ec634878 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -106,17 +106,23 @@ Page { } Connections { target: TimelineManager - onNewDeviceVerificationRequest: { + function onNewDeviceVerificationRequest(flow) { flow.userId = userId; flow.sender = false; flow.deviceId = deviceId; flow.tranId = transactionId; deviceVerificationList.add(flow.tranId); - var dialog = deviceVerificationDialog.createObject(timelineRoot, - {flow: flow}); + var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow}); dialog.show(); } } + Connections { + target: TimelineManager.timeline + function onOpenProfile(profile) { + var userProfile = userProfileComponent.createObject(timelineRoot,{profile: profile}); + userProfile.show(); + } + } Label { visible: !TimelineManager.timeline && !TimelineManager.isInitialSync @@ -293,10 +299,7 @@ Page { MouseArea { anchors.fill: parent - onClicked: { - userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)}); - userProfile.show(); - } + onClicked: chat.model.openUserProfile(modelData.userId) cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } @@ -311,10 +314,7 @@ Page { MouseArea { anchors.fill: parent Layout.alignment: Qt.AlignHCenter - onClicked: { - userProfile = userProfileComponent.createObject(timelineRoot,{user_data: modelData,avatarUrl:chat.model.avatarUrl(modelData.userId)}); - userProfile.show(); - } + onClicked: chat.model.openUserProfile(modelData.userId) cursorShape: Qt.PointingHandCursor propagateComposedEvents: true } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index db44ec15..df54367b 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -8,220 +8,211 @@ import im.nheko 1.0 import "./device-verification" ApplicationWindow{ - property var user_data - property var avatarUrl - property var colors: currentActivePalette - - id:userProfileDialog - height: 650 - width: 420 - modality:Qt.WindowModal - Layout.alignment: Qt.AlignHCenter - palette: colors - - Component { + property var profile + + id: userProfileDialog + height: 650 + width: 420 + modality: Qt.WindowModal + Layout.alignment: Qt.AlignHCenter + palette: colors + + Component { id: deviceVerificationDialog DeviceVerification {} } - Component{ - id: deviceVerificationFlow - DeviceVerificationFlow {} - } - - background: Item{ - id: userProfileItem - width: userProfileDialog.width - height: userProfileDialog.height - - // Layout.fillHeight : true - - ColumnLayout{ - anchors.fill: userProfileItem - width: userProfileDialog.width - spacing: 10 - - Avatar{ - id: userProfileAvatar - url: avatarUrl.replace("mxc://", "image://MxcImage/") - height: 130 - width: 130 - displayName: user_data.userName - userid: user_data.userId - Layout.alignment: Qt.AlignHCenter - Layout.margins : { - top: 10 - } - } - - Label{ - id: userProfileName - text: user_data.userName - fontSizeMode: Text.HorizontalFit - font.pixelSize: 20 - color:TimelineManager.userColor(user_data.userId, colors.window) - font.bold: true - Layout.alignment: Qt.AlignHCenter - } - - Label{ - id: matrixUserID - text: user_data.userId - fontSizeMode: Text.HorizontalFit - font.pixelSize: 15 - color:colors.text - Layout.alignment: Qt.AlignHCenter - } - - RowLayout{ - Layout.alignment: Qt.AlignHCenter - ImageButton{ - image:":/icons/icons/ui/do-not-disturb-rounded-sign.png" - Layout.margins: { - left: 5 - right: 5 - } - ToolTip.visible: hovered - ToolTip.text: qsTr("Ban the user") - onClicked : { - modelDeviceList.deviceList.banUser() - } - } - // ImageButton{ - // image:":/icons/icons/ui/volume-off-indicator.png" - // Layout.margins: { - // left: 5 - // right: 5 - // } - // ToolTip.visible: hovered - // ToolTip.text: qsTr("Ignore messages from this user") - // onClicked : { - // modelDeviceList.deviceList.ignoreUser() - // } - // } - ImageButton{ - image:":/icons/icons/ui/black-bubble-speech.png" - Layout.margins: { - left: 5 - right: 5 - } - ToolTip.visible: hovered - ToolTip.text: qsTr("Start a private chat") - onClicked : { - modelDeviceList.deviceList.startChat() - } - } - ImageButton{ - image:":/icons/icons/ui/round-remove-button.png" - Layout.margins: { - left: 5 - right: 5 - } - ToolTip.visible: hovered - ToolTip.text: qsTr("Kick the user") - onClicked : { - modelDeviceList.deviceList.kickUser() - } - } - } - - ScrollView { - implicitHeight: userProfileDialog.height/2 + 20 - implicitWidth: userProfileDialog.width-20 - clip: true - Layout.alignment: Qt.AlignHCenter - - ListView{ - id: devicelist - anchors.fill: parent - clip: true - spacing: 4 - - model: UserProfileModel{ - id: modelDeviceList - deviceList.userId : user_data.userId - } - - delegate: RowLayout{ - width: parent.width - Layout.margins : { - top : 50 - } - ColumnLayout{ - RowLayout{ - Text{ - Layout.fillWidth: true - color: colors.text - font.bold: true - Layout.alignment: Qt.AlignLeft - text: deviceID - } - Text{ - Layout.fillWidth: true - color:colors.text - Layout.alignment: Qt.AlignLeft - text: (verified_status == UserProfileList.VERIFIED?"V":(verified_status == UserProfileList.UNVERIFIED?"NV":"B")) - } - } - Text{ - Layout.fillWidth: true - color:colors.text - Layout.alignment: Qt.AlignRight - text: displayName - } - } - Button{ - id: verifyButton - text:"Verify" - onClicked: { - var newFlow = deviceVerificationFlow.createObject(userProfileDialog, - {userId : user_data.userId,sender: true,deviceId : model.deviceID}); - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: newFlow}); - dialog.show(); - } - Layout.margins:{ - right: 10 - } - palette { - button: "white" - } - contentItem: Text { - text: verifyButton.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - } - } - } - - Button{ - id: okbutton - text:"OK" - onClicked: userProfileDialog.close() - - Layout.alignment: Qt.AlignRight | Qt.AlignBottom - - Layout.margins : { - right : 10 - bottom: 5 - } - - palette { - button: "white" - } - - contentItem: Text { - text: okbutton.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - } - - Item { Layout.fillHeight: true } - } + Component{ + id: deviceVerificationFlow + DeviceVerificationFlow {} + } + + background: Item{ + id: userProfileItem + width: userProfileDialog.width + height: userProfileDialog.height + + // Layout.fillHeight : true + + ColumnLayout{ + anchors.fill: userProfileItem + width: userProfileDialog.width + spacing: 10 + + Avatar { + url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") + height: 130 + width: 130 + displayName: profile.displayName + userid: profile.userid + Layout.alignment: Qt.AlignHCenter + Layout.margins : { + top: 10 + } + } + + Label { + text: profile.displayName + fontSizeMode: Text.HorizontalFit + font.pixelSize: 20 + color: TimelineManager.userColor(profile.userid, colors.window) + font.bold: true + Layout.alignment: Qt.AlignHCenter + } + + Label { + text: profile.userid + fontSizeMode: Text.HorizontalFit + font.pixelSize: 15 + color: colors.text + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + ImageButton { + image:":/icons/icons/ui/do-not-disturb-rounded-sign.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Ban the user") + onClicked : { + profile.banUser() + } + } + // ImageButton{ + // image:":/icons/icons/ui/volume-off-indicator.png" + // Layout.margins: { + // left: 5 + // right: 5 + // } + // ToolTip.visible: hovered + // ToolTip.text: qsTr("Ignore messages from this user") + // onClicked : { + // profile.ignoreUser() + // } + // } + ImageButton{ + image:":/icons/icons/ui/black-bubble-speech.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Start a private chat") + onClicked : { + profile.startChat() + } + } + ImageButton{ + image:":/icons/icons/ui/round-remove-button.png" + Layout.margins: { + left: 5 + right: 5 + } + ToolTip.visible: hovered + ToolTip.text: qsTr("Kick the user") + onClicked : { + profile.kickUser() + } + } + } + + ScrollView { + implicitHeight: userProfileDialog.height/2 + 20 + implicitWidth: userProfileDialog.width-20 + clip: true + Layout.alignment: Qt.AlignHCenter + + ListView{ + id: devicelist + anchors.fill: parent + clip: true + spacing: 4 + + model: profile.deviceList + + delegate: RowLayout{ + width: parent.width + Layout.margins : { + top : 50 + } + ColumnLayout{ + RowLayout{ + Text{ + Layout.fillWidth: true + color: colors.text + font.bold: true + Layout.alignment: Qt.AlignLeft + text: model.deviceId + } + Text{ + Layout.fillWidth: true + color:colors.text + Layout.alignment: Qt.AlignLeft + text: (model.verificationStatus == VerificationStatus.VERIFIED?"V":(model.verificationStatus == VerificationStatus.UNVERIFIED?"NV":"B")) + } + } + Text{ + Layout.fillWidth: true + color:colors.text + Layout.alignment: Qt.AlignRight + text: model.deviceName + } + } + Button{ + id: verifyButton + text:"Verify" + onClicked: { + var newFlow = deviceVerificationFlow.createObject(userProfileDialog, + {userId : profile.userid, sender: true, deviceId : model.deviceID}); + deviceVerificationList.add(newFlow.tranId); + var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow}); + dialog.show(); + } + Layout.margins:{ + right: 10 + } + palette { + button: "white" + } + contentItem: Text { + text: verifyButton.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + } + } + + Button{ + id: okbutton + text:"OK" + onClicked: userProfileDialog.close() + + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + + Layout.margins : { + right : 10 + bottom: 5 + } + + palette { + button: "white" + } + + contentItem: Text { + text: okbutton.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + + Item { Layout.fillHeight: true } + } } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index cc1d868b..63b524c8 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -317,15 +317,6 @@ MainWindow::hasActiveUser() settings.contains("auth/user_id"); } -void -MainWindow::openUserProfile(const QString &user_id, const QString &room_id) -{ - auto dialog = new dialogs::UserProfile(this); - dialog->init(user_id, room_id); - - showDialog(dialog); -} - void MainWindow::openRoomSettings(const QString &room_id) { diff --git a/src/MainWindow.h b/src/MainWindow.h index e3e04698..2fc2d00f 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -25,7 +25,6 @@ #include #include "UserSettingsPage.h" -#include "dialogs/UserProfile.h" #include "ui/OverlayModal.h" #include "jdenticoninterface.h" @@ -76,7 +75,6 @@ public: void openLogoutDialog(); void openRoomSettings(const QString &room_id = ""); void openMemberListDialog(const QString &room_id = ""); - void openUserProfile(const QString &user_id, const QString &room_id); void openReadReceiptsDialog(const QString &event_id); void hideOverlay(); diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp deleted file mode 100644 index 3415b127..00000000 --- a/src/dialogs/UserProfile.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include -#include -#include -#include -#include - -#include "Cache.h" -#include "ChatPage.h" -#include "Logging.h" -#include "MatrixClient.h" -#include "Utils.h" -#include "dialogs/UserProfile.h" -#include "ui/Avatar.h" -#include "ui/FlatButton.h" - -using namespace dialogs; - -Q_DECLARE_METATYPE(std::vector) - -constexpr int BUTTON_SIZE = 36; -constexpr int BUTTON_RADIUS = BUTTON_SIZE / 2; -constexpr int WIDGET_MARGIN = 20; -constexpr int TOP_WIDGET_MARGIN = 2 * WIDGET_MARGIN; -constexpr int WIDGET_SPACING = 15; -constexpr int TEXT_SPACING = 4; -constexpr int DEVICE_SPACING = 5; - -DeviceItem::DeviceItem(DeviceInfo device, QWidget *parent) - : QWidget(parent) - , info_(std::move(device)) -{ - QFont font; - font.setBold(true); - - auto deviceIdLabel = new QLabel(info_.device_id, this); - deviceIdLabel->setFont(font); - - auto layout = new QVBoxLayout{this}; - layout->addWidget(deviceIdLabel); - - if (!info_.display_name.isEmpty()) - layout->addWidget(new QLabel(info_.display_name, this)); - - layout->setMargin(0); - layout->setSpacing(4); -} - -UserProfile::UserProfile(QWidget *parent) - : QWidget(parent) -{ - setAutoFillBackground(true); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - setAttribute(Qt::WA_DeleteOnClose, true); - - QIcon banIcon, kickIcon, ignoreIcon, startChatIcon; - - banIcon.addFile(":/icons/icons/ui/do-not-disturb-rounded-sign.png"); - banBtn_ = new FlatButton(this); - banBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE); - banBtn_->setCornerRadius(BUTTON_RADIUS); - banBtn_->setIcon(banIcon); - banBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS)); - banBtn_->setToolTip(tr("Ban the user from the room")); - - ignoreIcon.addFile(":/icons/icons/ui/volume-off-indicator.png"); - ignoreBtn_ = new FlatButton(this); - ignoreBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE); - ignoreBtn_->setCornerRadius(BUTTON_RADIUS); - ignoreBtn_->setIcon(ignoreIcon); - ignoreBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS)); - ignoreBtn_->setToolTip(tr("Ignore messages from this user")); - ignoreBtn_->setDisabled(true); // Not used yet. - - kickIcon.addFile(":/icons/icons/ui/round-remove-button.png"); - kickBtn_ = new FlatButton(this); - kickBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE); - kickBtn_->setCornerRadius(BUTTON_RADIUS); - kickBtn_->setIcon(kickIcon); - kickBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS)); - kickBtn_->setToolTip(tr("Kick the user from the room")); - - startChatIcon.addFile(":/icons/icons/ui/black-bubble-speech.png"); - startChat_ = new FlatButton(this); - startChat_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE); - startChat_->setCornerRadius(BUTTON_RADIUS); - startChat_->setIcon(startChatIcon); - startChat_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS)); - startChat_->setToolTip(tr("Start a conversation")); - - connect(startChat_, &QPushButton::clicked, this, [this]() { - auto user_id = userIdLabel_->text(); - - mtx::requests::CreateRoom req; - req.preset = mtx::requests::Preset::PrivateChat; - req.visibility = mtx::requests::Visibility::Private; - - if (utils::localUser() != user_id) - req.invite = {user_id.toStdString()}; - - emit ChatPage::instance()->createRoom(req); - }); - - connect(banBtn_, &QPushButton::clicked, this, [this] { - ChatPage::instance()->banUser(userIdLabel_->text(), ""); - }); - connect(kickBtn_, &QPushButton::clicked, this, [this] { - ChatPage::instance()->kickUser(userIdLabel_->text(), ""); - }); - - // Button line - auto btnLayout = new QHBoxLayout; - btnLayout->addStretch(1); - btnLayout->addWidget(startChat_); - btnLayout->addWidget(ignoreBtn_); - - btnLayout->addWidget(kickBtn_); - btnLayout->addWidget(banBtn_); - btnLayout->addStretch(1); - btnLayout->setSpacing(8); - btnLayout->setMargin(0); - - avatar_ = new Avatar(this, 128); - avatar_->setLetter("X"); - - QFont font; - font.setPointSizeF(font.pointSizeF() * 2); - - userIdLabel_ = new QLabel(this); - displayNameLabel_ = new QLabel(this); - displayNameLabel_->setFont(font); - - auto textLayout = new QVBoxLayout; - textLayout->addWidget(displayNameLabel_); - textLayout->addWidget(userIdLabel_); - textLayout->setAlignment(displayNameLabel_, Qt::AlignCenter | Qt::AlignTop); - textLayout->setAlignment(userIdLabel_, Qt::AlignCenter | Qt::AlignTop); - textLayout->setSpacing(TEXT_SPACING); - textLayout->setMargin(0); - - devices_ = new QListWidget{this}; - devices_->setFrameStyle(QFrame::NoFrame); - devices_->setSelectionMode(QAbstractItemView::NoSelection); - devices_->setAttribute(Qt::WA_MacShowFocusRect, 0); - devices_->setSpacing(DEVICE_SPACING); - - QFont descriptionLabelFont; - descriptionLabelFont.setWeight(65); - - devicesLabel_ = new QLabel(tr("Devices").toUpper(), this); - devicesLabel_->setFont(descriptionLabelFont); - devicesLabel_->hide(); - devicesLabel_->setFixedSize(devicesLabel_->sizeHint()); - - auto okBtn = new QPushButton("OK", this); - - auto closeLayout = new QHBoxLayout(); - closeLayout->setSpacing(15); - closeLayout->addStretch(1); - closeLayout->addWidget(okBtn); - - auto vlayout = new QVBoxLayout{this}; - vlayout->addWidget(avatar_, 0, Qt::AlignCenter | Qt::AlignTop); - vlayout->addLayout(textLayout); - vlayout->addLayout(btnLayout); - vlayout->addWidget(devicesLabel_, 0, Qt::AlignLeft); - vlayout->addWidget(devices_, 1); - vlayout->addLayout(closeLayout); - - QFont largeFont; - largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5); - - setMinimumWidth( - std::max(devices_->sizeHint().width() + 4 * WIDGET_MARGIN, conf::window::minModalWidth)); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - vlayout->setSpacing(WIDGET_SPACING); - vlayout->setContentsMargins(WIDGET_MARGIN, TOP_WIDGET_MARGIN, WIDGET_MARGIN, WIDGET_MARGIN); - - qRegisterMetaType>(); - - auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this); - connect(closeShortcut, &QShortcut::activated, this, &UserProfile::close); - connect(okBtn, &QPushButton::clicked, this, &UserProfile::close); -} - -void -UserProfile::resetToDefaults() -{ - avatar_->setLetter("X"); - devices_->clear(); - - ignoreBtn_->show(); - devices_->hide(); - devicesLabel_->hide(); -} - -void -UserProfile::init(const QString &userId, const QString &roomId) -{ - resetToDefaults(); - - auto displayName = cache::displayName(roomId, userId); - - userIdLabel_->setText(userId); - displayNameLabel_->setText(displayName); - avatar_->setLetter(utils::firstChar(displayName)); - - avatar_->setImage(roomId, userId); - - auto localUser = utils::localUser(); - - try { - bool hasMemberRights = - cache::hasEnoughPowerLevel({mtx::events::EventType::RoomMember}, - roomId.toStdString(), - localUser.toStdString()); - if (!hasMemberRights) { - kickBtn_->hide(); - banBtn_->hide(); - } else { - kickBtn_->show(); - banBtn_->show(); - } - } catch (const lmdb::error &e) { - nhlog::db()->warn("lmdb error: {}", e.what()); - } - - if (localUser == userId) { - // TODO: click on display name & avatar to change. - kickBtn_->hide(); - banBtn_->hide(); - ignoreBtn_->hide(); - } - - mtx::requests::QueryKeys req; - req.device_keys[userId.toStdString()] = {}; - - // A proxy object is used to emit the signal instead of the original object - // which might be destroyed by the time the http call finishes. - auto proxy = std::make_shared(); - QObject::connect(proxy.get(), &Proxy::done, this, &UserProfile::updateDeviceList); - - http::client()->query_keys( - req, - [user_id = userId.toStdString(), proxy = std::move(proxy)]( - const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - // TODO: Notify the UI. - return; - } - - if (res.device_keys.empty() || - (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } - - auto devices = res.device_keys.at(user_id); - - std::vector deviceInfo; - for (const auto &d : devices) { - auto device = d.second; - - // TODO: Verify signatures and ignore those that don't pass. - deviceInfo.emplace_back(DeviceInfo{ - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)}); - } - - std::sort(deviceInfo.begin(), - deviceInfo.end(), - [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); - - if (!deviceInfo.empty()) - emit proxy->done(QString::fromStdString(user_id), deviceInfo); - }); -} - -void -UserProfile::updateDeviceList(const QString &user_id, const std::vector &devices) -{ - if (user_id != userIdLabel_->text()) - return; - - for (const auto &dev : devices) { - auto deviceItem = new DeviceItem(dev, this); - auto item = new QListWidgetItem; - - item->setSizeHint(deviceItem->minimumSizeHint()); - item->setFlags(Qt::NoItemFlags); - item->setTextAlignment(Qt::AlignCenter); - - devices_->insertItem(devices_->count() - 1, item); - devices_->setItemWidget(item, deviceItem); - } - - devicesLabel_->show(); - devices_->show(); - adjustSize(); -} diff --git a/src/dialogs/UserProfile.h b/src/dialogs/UserProfile.h deleted file mode 100644 index 81276d2a..00000000 --- a/src/dialogs/UserProfile.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -class Avatar; -class FlatButton; -class QLabel; -class QListWidget; -class Toggle; - -struct DeviceInfo -{ - QString device_id; - QString display_name; -}; - -class Proxy : public QObject -{ - Q_OBJECT - -signals: - void done(const QString &user_id, const std::vector &devices); -}; - -namespace dialogs { - -class DeviceItem : public QWidget -{ - Q_OBJECT - -public: - explicit DeviceItem(DeviceInfo device, QWidget *parent); - -private: - DeviceInfo info_; - - // Toggle *verifyToggle_; -}; - -class UserProfile : public QWidget -{ - Q_OBJECT -public: - explicit UserProfile(QWidget *parent = nullptr); - - void init(const QString &userId, const QString &roomId); - -private slots: - void updateDeviceList(const QString &user_id, const std::vector &devices); - -private: - void resetToDefaults(); - - Avatar *avatar_; - - QLabel *userIdLabel_; - QLabel *displayNameLabel_; - - FlatButton *banBtn_; - FlatButton *kickBtn_; - FlatButton *ignoreBtn_; - FlatButton *startChat_; - - QLabel *devicesLabel_; - QListWidget *devices_; -}; - -} // dialogs diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index f41e7712..773a5a23 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -654,9 +654,9 @@ TimelineModel::viewDecryptedRawMessage(QString id) const } void -TimelineModel::openUserProfile(QString userid) const +TimelineModel::openUserProfile(QString userid) { - MainWindow::instance()->openUserProfile(userid, room_id_); + emit openProfile(new UserProfile(room_id_, userid, this)); } void diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index f8a84f17..104a475c 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -9,7 +9,12 @@ #include #include "CacheCryptoStructs.h" +<<<<<<< HEAD #include "EventStore.h" +======= +#include "ReactionsModel.h" +#include "ui/UserProfile.h" +>>>>>>> Refactor UserProfile namespace mtx::http { using RequestErr = const std::optional &; @@ -188,7 +193,7 @@ public: Q_INVOKABLE QString escapeEmoji(QString str) const; Q_INVOKABLE void viewRawMessage(QString id) const; Q_INVOKABLE void viewDecryptedRawMessage(QString id) const; - Q_INVOKABLE void openUserProfile(QString userid) const; + Q_INVOKABLE void openUserProfile(QString userid); Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void readReceiptsAction(QString id) const; Q_INVOKABLE void redactEvent(QString id); @@ -256,8 +261,7 @@ signals: void replyChanged(QString reply); void paginationInProgressChanged(const bool); - void newMessageToSend(mtx::events::collections::TimelineEvents event); - void addPendingMessageToStore(mtx::events::collections::TimelineEvents event); + void openProfile(UserProfile *profile); private: void sendEncryptedMessage(const std::string txn_id, nlohmann::json content); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 0b732232..81c8d6d3 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -16,10 +16,9 @@ #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" -#include "src/ui/UserProfile.h" -#include "src/ui/UserProfileModel.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) +Q_DECLARE_METATYPE(std::vector) namespace msgs = mtx::events::msg; @@ -109,15 +108,28 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin 0, "MtxEvent", "Can't instantiate enum!"); + qmlRegisterUncreatableMetaObject(verification::staticMetaObject, + "im.nheko", + 1, + 0, + "VerificationStatus", + "Can't instantiate enum!"); + qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); - qmlRegisterType("im.nheko", 1, 0, "UserProfileModel"); - qmlRegisterType("im.nheko", 1, 0, "UserProfileList"); + qmlRegisterUncreatableType( + "im.nheko", + 1, + 0, + "UserProfileModel", + "UserProfile needs to be instantiated on the C++ side"); qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", this); qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", settings.data()); qRegisterMetaType(); + qRegisterMetaType>(); + qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); qmlRegisterUncreatableType( diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 8c6fb8e4..fde0044b 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -5,47 +5,72 @@ #include "Utils.h" #include "mtx/responses/crypto.hpp" -#include // only for debugging +UserProfile::UserProfile(QString roomid, QString userid, QObject *parent) + : QObject(parent) + , roomid_(roomid) + , userid_(userid) +{ + fetchDeviceList(this->userid_); +} -Q_DECLARE_METATYPE(UserProfile::Status) +QHash +DeviceInfoModel::roleNames() const +{ + return { + {DeviceId, "deviceId"}, + {DeviceName, "deviceName"}, + {VerificationStatus, "verificationStatus"}, + }; +} -UserProfile::UserProfile(QObject *parent) - : QObject(parent) +QVariant +DeviceInfoModel::data(const QModelIndex &index, int role) const { - qRegisterMetaType(); - connect( - this, &UserProfile::updateDeviceList, this, [this]() { fetchDeviceList(this->userId); }); - connect( - this, - &UserProfile::appendDeviceList, - this, - [this](QString device_id, QString device_name, UserProfile::Status verification_status) { - this->deviceList.push_back( - DeviceInfo{device_id, device_name, verification_status}); - }); + if (!index.isValid() || index.row() >= (int)deviceList_.size() || index.row() < 0) + return {}; + + switch (role) { + case DeviceId: + return deviceList_[index.row()].device_id; + case DeviceName: + return deviceList_[index.row()].display_name; + case VerificationStatus: + return QVariant::fromValue(deviceList_[index.row()].verification_status); + default: + return {}; + } } -std::vector -UserProfile::getDeviceList() +void +DeviceInfoModel::reset(const std::vector &deviceList) { - return this->deviceList; + beginResetModel(); + this->deviceList_ = std::move(deviceList); + endResetModel(); +} + +DeviceInfoModel * +UserProfile::deviceList() +{ + return &this->deviceList_; } QString -UserProfile::getUserId() +UserProfile::userid() { - return this->userId; + return this->userid_; } -void -UserProfile::setUserId(const QString &user_id) +QString +UserProfile::displayName() { - if (this->userId != userId) - return; - else { - this->userId = user_id; - emit UserProfile::userIdChanged(); - } + return cache::displayName(roomid_, userid_); +} + +QString +UserProfile::avatarUrl() +{ + return cache::avatarUrl(roomid_, userid_); } void @@ -74,27 +99,27 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - UserProfile::Status verified = UserProfile::Status::UNVERIFIED; + verification::Status verified = verification::Status::UNVERIFIED; if (cross_verified.has_value()) { if (std::find(cross_verified->begin(), cross_verified->end(), d.first) != cross_verified->end()) - verified = UserProfile::Status::VERIFIED; + verified = verification::Status::VERIFIED; } else if (device_verified.has_value()) { if (std::find(device_verified->device_verified.begin(), device_verified->device_verified.end(), d.first) != device_verified->device_verified.end()) - verified = UserProfile::Status::VERIFIED; + verified = verification::Status::VERIFIED; } else if (device_verified.has_value()) { if (std::find(device_verified->device_blocked.begin(), device_verified->device_blocked.end(), d.first) != device_verified->device_blocked.end()) - verified = UserProfile::Status::BLOCKED; + verified = verification::Status::BLOCKED; } - emit UserProfile::appendDeviceList( - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name), - verified); + deviceInfo.push_back( + {QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name), + verified}); } // std::sort( @@ -102,8 +127,7 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, // return a.device_id > b.device_id; // }); - this->deviceList = std::move(deviceInfo); - emit UserProfile::deviceListUpdated(); + this->deviceList_.queueReset(std::move(deviceInfo)); } void @@ -130,7 +154,7 @@ UserProfile::fetchDeviceList(const QString &userID) void UserProfile::banUser() { - ChatPage::instance()->banUser(this->userId, ""); + ChatPage::instance()->banUser(this->userid_, ""); } // void ignoreUser(){ @@ -140,7 +164,7 @@ UserProfile::banUser() void UserProfile::kickUser() { - ChatPage::instance()->kickUser(this->userId, ""); + ChatPage::instance()->kickUser(this->userid_, ""); } void @@ -149,7 +173,7 @@ UserProfile::startChat() mtx::requests::CreateRoom req; req.preset = mtx::requests::Preset::PrivateChat; req.visibility = mtx::requests::Visibility::Private; - if (utils::localUser() != this->userId) - req.invite = {this->userId.toStdString()}; + if (utils::localUser() != this->userid_) + req.invite = {this->userid_.toStdString()}; emit ChatPage::instance()->createRoom(req); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 1725b961..38002fff 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -1,34 +1,92 @@ #pragma once +#include #include #include #include #include "MatrixClient.h" -class DeviceInfo; +namespace verification { +Q_NAMESPACE -class UserProfile : public QObject +enum Status +{ + VERIFIED, + UNVERIFIED, + BLOCKED +}; +Q_ENUM_NS(Status) +} + +class DeviceInfo +{ +public: + DeviceInfo(const QString deviceID, + const QString displayName, + verification::Status verification_status_) + : device_id(deviceID) + , display_name(displayName) + , verification_status(verification_status_) + {} + DeviceInfo() + : verification_status(verification::UNVERIFIED) + {} + + QString device_id; + QString display_name; + + verification::Status verification_status; +}; + +class DeviceInfoModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString userId READ getUserId WRITE setUserId NOTIFY userIdChanged) - Q_PROPERTY(std::vector deviceList READ getDeviceList NOTIFY deviceListUpdated) public: - // constructor - explicit UserProfile(QObject *parent = 0); - // getters - std::vector getDeviceList(); - QString getUserId(); - // setters - void setUserId(const QString &userId); - - enum Status + enum Roles { - VERIFIED, - UNVERIFIED, - BLOCKED + DeviceId, + DeviceName, + VerificationStatus, }; - Q_ENUM(Status) + + explicit DeviceInfoModel(QObject *parent = nullptr) + { + (void)parent; + connect(this, &DeviceInfoModel::queueReset, this, &DeviceInfoModel::reset); + }; + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const + { + (void)parent; + return (int)deviceList_.size(); + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +signals: + void queueReset(const std::vector &deviceList); +public slots: + void reset(const std::vector &deviceList); + +private: + std::vector deviceList_; +}; + +class UserProfile : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString displayName READ displayName CONSTANT) + Q_PROPERTY(QString userid READ userid CONSTANT) + Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT) + Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) +public: + UserProfile(QString roomid, QString userid, QObject *parent = 0); + + DeviceInfoModel *deviceList(); + + QString userid(); + QString displayName(); + QString avatarUrl(); void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); @@ -36,37 +94,13 @@ public: Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat(); -signals: - void userIdChanged(); - void deviceListUpdated(); - void updateDeviceList(); - void appendDeviceList(const QString device_id, - const QString device_naem, - const UserProfile::Status verification_status); - private: - std::vector deviceList; - QString userId; + QString roomid_, userid_; std::optional cross_verified; + DeviceInfoModel deviceList_; void callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, std::string user_id, std::optional> cross_verified); }; - -class DeviceInfo -{ -public: - DeviceInfo(const QString deviceID, - const QString displayName, - UserProfile::Status verification_status_) - : device_id(deviceID) - , display_name(displayName) - , verification_status(verification_status_) - {} - - QString device_id; - QString display_name; - UserProfile::Status verification_status; -}; \ No newline at end of file diff --git a/src/ui/UserProfileModel.cpp b/src/ui/UserProfileModel.cpp deleted file mode 100644 index 3fa8fe2d..00000000 --- a/src/ui/UserProfileModel.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "UserProfileModel.h" -#include - -UserProfileModel::UserProfileModel(QObject *parent) - : QAbstractListModel(parent) - , deviceList(nullptr) -{ - this->deviceList = new UserProfile(this); - - connect(this->deviceList, &UserProfile::userIdChanged, this, [this]() { - emit this->deviceList->updateDeviceList(); - }); - connect(this->deviceList, &UserProfile::deviceListUpdated, this, [this]() { - beginResetModel(); - this->beginInsertRows( - QModelIndex(), 0, this->deviceList->getDeviceList().size() - 1); - this->endInsertRows(); - endResetModel(); - }); -} - -int -UserProfileModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid() || !this->deviceList) - return 0; - return this->deviceList->getDeviceList().size(); -} - -QVariant -UserProfileModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() && - static_cast(this->deviceList->getDeviceList().size()) <= index.row()) - return QVariant(); - - const DeviceInfo device = this->deviceList->getDeviceList().at(index.row()); - switch (role) { - case DEVICEID: - return QVariant(device.device_id); - case DISPLAYNAME: - return QVariant(device.display_name); - case VERIFIED_STATUS: - return device.verification_status; - } - return QVariant(); -} - -QHash -UserProfileModel::roleNames() const -{ - QHash names; - names[DEVICEID] = "deviceID"; - names[DISPLAYNAME] = "displayName"; - names[VERIFIED_STATUS] = "verified_status"; - return names; -} - -UserProfile * -UserProfileModel::getList() const -{ - return (this->deviceList); -} diff --git a/src/ui/UserProfileModel.h b/src/ui/UserProfileModel.h deleted file mode 100644 index ba7a2525..00000000 --- a/src/ui/UserProfileModel.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "UserProfile.h" -#include - -class UserProfile; // forward declaration of the class UserProfile - -class UserProfileModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(UserProfile *deviceList READ getList) - -public: - explicit UserProfileModel(QObject *parent = nullptr); - - enum - { - DEVICEID, - DISPLAYNAME, - VERIFIED_STATUS - }; - UserProfile *getList() const; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - virtual QHash roleNames() const override; - -private: - UserProfile *deviceList; -}; \ No newline at end of file -- cgit 1.5.1 From 1103cc15cfe59b35e540855090af381b0f2e5f8e Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 5 Jul 2020 21:33:27 +0530 Subject: Adding icons to UserProfile --- CMakeLists.txt | 3 - resources/qml/TimelineView.qml | 2 +- resources/qml/UserProfile.qml | 81 +++++++------ .../qml/device-verification/DeviceVerification.qml | 4 + src/DeviceVerificationFlow.cpp | 130 +++++++++++++++------ src/DeviceVerificationFlow.h | 6 + src/timeline/TimelineViewManager.h | 2 + src/ui/UserProfile.cpp | 11 +- src/ui/UserProfile.h | 4 +- 9 files changed, 163 insertions(+), 80 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ad8a625..2aff9914 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,6 @@ set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard") set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported") set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default") -# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") -# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - option(HUNTER_ENABLED "Enable Hunter package manager" OFF) include("cmake/HunterGate.cmake") HunterGate( diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index ec634878..699efc54 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -106,7 +106,7 @@ Page { } Connections { target: TimelineManager - function onNewDeviceVerificationRequest(flow) { + function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId) { flow.userId = userId; flow.sender = false; flow.deviceId = deviceId; diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index df54367b..5bdccb4d 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -17,6 +17,13 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter palette: colors + Connections{ + target: deviceVerificationList + onUpdateProfile: { + profile.fetchDeviceList(profile.userid) + } + } + Component { id: deviceVerificationDialog DeviceVerification {} @@ -139,20 +146,12 @@ ApplicationWindow{ top : 50 } ColumnLayout{ - RowLayout{ - Text{ - Layout.fillWidth: true - color: colors.text - font.bold: true - Layout.alignment: Qt.AlignLeft - text: model.deviceId - } - Text{ - Layout.fillWidth: true - color:colors.text - Layout.alignment: Qt.AlignLeft - text: (model.verificationStatus == VerificationStatus.VERIFIED?"V":(model.verificationStatus == VerificationStatus.UNVERIFIED?"NV":"B")) - } + Text{ + Layout.fillWidth: true + color: colors.text + font.bold: true + Layout.alignment: Qt.AlignLeft + text: model.deviceId } Text{ Layout.fillWidth: true @@ -161,27 +160,41 @@ ApplicationWindow{ text: model.deviceName } } - Button{ - id: verifyButton - text:"Verify" - onClicked: { - var newFlow = deviceVerificationFlow.createObject(userProfileDialog, - {userId : profile.userid, sender: true, deviceId : model.deviceID}); - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow}); - dialog.show(); + RowLayout{ + Image{ + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + source: ((model.verificationStatus == VerificationStatus.VERIFIED)?"image://colorimage/:/icons/icons/ui/lock.png?green": + ((model.verificationStatus == VerificationStatus.UNVERIFIED)?"image://colorimage/:/icons/icons/ui/unlock.png?yellow": + "image://colorimage/:/icons/icons/ui/unlock.png?red")) } - Layout.margins:{ - right: 10 - } - palette { - button: "white" - } - contentItem: Text { - text: verifyButton.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + Button{ + id: verifyButton + text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify" + onClicked: { + var newFlow = deviceVerificationFlow.createObject(userProfileDialog, + {userId : profile.userid, sender: true, deviceId : model.deviceId}); + if(model.verificationStatus == VerificationStatus.VERIFIED){ + newFlow.unverify(); + deviceVerificationList.updateProfile(newFlow.userId); + }else{ + deviceVerificationList.add(newFlow.tranId); + var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow}); + dialog.show(); + } + } + Layout.margins:{ + right: 10 + } + palette { + button: "white" + } + contentItem: Text { + text: verifyButton.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } } } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 15c2d7a2..4d734a68 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -33,6 +33,10 @@ ApplicationWindow { case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break; case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break; } + + onRefreshProfile: { + deviceVerificationList.updateProfile(flow.userId); + } } Component { diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index b5134a3b..7829c41d 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,4 +1,5 @@ #include "DeviceVerificationFlow.h" +#include "Cache.h" #include "ChatPage.h" #include "Logging.h" @@ -181,9 +182,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) // uncomment this in future to be compatible with the // MSC2366 this->sendVerificationDone(); and remove the // below line - if (this->isMacVerified == true) - emit this->deviceVerified(); - else + if (this->isMacVerified == true) { + this->acceptDevice(); + } else this->isMacVerified = true; } else { this->cancelVerification( @@ -208,7 +209,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - emit this->deviceVerified(); + this->acceptDevice(); } }); timeout->start(TIMEOUT); @@ -259,36 +260,22 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_) void DeviceVerificationFlow::setUserId(QString userID) { - this->userId = userID; - this->toClient = mtx::identifiers::parse(userID.toStdString()); - - mtx::responses::QueryKeys res; - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - - for (auto x : res.device_keys) { - for (auto y : x.second) { - auto z = y.second; - if (z.user_id == user_id && - z.device_id == this->deviceId.toStdString()) { - for (auto a : z.keys) { - // TODO: Verify Signatures - this->device_keys[a.first] = a.second; - } - } - } - } - }); + this->userId = userID; + this->toClient = mtx::identifiers::parse(userID.toStdString()); + auto user_cache = cache::getUserCache(userID.toStdString()); + + if (user_cache.has_value()) { + this->callback_fn(user_cache->keys, {}, userID.toStdString()); + } else { + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + http::client()->query_keys( + req, + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + this->callback_fn(res, err, user_id); + }); + } } void @@ -482,6 +469,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c nhlog::net()->warn("failed to cancel verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); + auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); + if (verified_cache.has_value()) { + verified_cache->device_blocked.push_back(this->deviceId.toStdString()); + cache::setVerifiedCache(this->userId.toStdString(), + verified_cache.value()); + } else { + cache::setVerifiedCache( + this->userId.toStdString(), + DeviceVerifiedCache{{}, {this->deviceId.toStdString()}}); + } this->deleteLater(); }); } @@ -546,7 +543,7 @@ DeviceVerificationFlow::sendVerificationMac() static_cast(err->status_code)); if (this->isMacVerified == true) - emit this->deviceVerified(); + this->acceptDevice(); else this->isMacVerified = true; }); @@ -555,8 +552,69 @@ DeviceVerificationFlow::sendVerificationMac() void DeviceVerificationFlow::acceptDevice() { + auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); + if (verified_cache.has_value()) { + verified_cache->device_verified.push_back(this->deviceId.toStdString()); + for (auto it = verified_cache->device_blocked.begin(); + it != verified_cache->device_blocked.end(); + it++) { + if (*it == this->deviceId.toStdString()) { + verified_cache->device_blocked.erase(it); + } + } + cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); + } else { + cache::setVerifiedCache(this->userId.toStdString(), + DeviceVerifiedCache{{this->deviceId.toStdString()}, {}}); + } + emit deviceVerified(); + emit refreshProfile(); this->deleteLater(); +} +//! callback function to keep track of devices +void +DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err, + std::string user_id) +{ + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } - // Yet to add send to_device message + for (auto x : res.device_keys) { + for (auto y : x.second) { + auto z = y.second; + if (z.user_id == user_id && z.device_id == this->deviceId.toStdString()) { + for (auto a : z.keys) { + // TODO: Verify Signatures + this->device_keys[a.first] = a.second; + } + } + } + } +} + +void +DeviceVerificationFlow::unverify() +{ + auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); + if (verified_cache.has_value()) { + auto it = std::remove(verified_cache->device_verified.begin(), + verified_cache->device_verified.end(), + this->deviceId.toStdString()); + verified_cache->device_verified.erase(it); + cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); + } + + emit refreshProfile(); + this->deleteLater(); } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 891c6aea..edff7c8e 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -51,6 +51,9 @@ public: void setDeviceId(QString deviceID); void setMethod(Method method_); void setSender(bool sender_); + void callback_fn(const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err, + std::string user_id); nlohmann::json canonical_json; @@ -73,12 +76,15 @@ public slots: void sendVerificationMac(); //! Completes the verification flow void acceptDevice(); + //! unverifies a device + void unverify(); signals: void verificationRequestAccepted(Method method); void deviceVerified(); void timedout(); void verificationCanceled(); + void refreshProfile(); private: QString userId; diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index af8bc4b6..a438ef5e 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -29,6 +29,8 @@ public: Q_INVOKABLE void add(QString tran_id); Q_INVOKABLE void remove(QString tran_id); Q_INVOKABLE bool exist(QString tran_id); +signals: + void updateProfile(QString userId); private: QVector deviceVerificationList; diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index fde0044b..b4938e8d 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,6 +1,7 @@ #include "UserProfile.h" #include "Cache.h" #include "ChatPage.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" @@ -122,10 +123,10 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, verified}); } - // std::sort( - // deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { - // return a.device_id > b.device_id; - // }); + std::sort( + deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); this->deviceList_.queueReset(std::move(deviceInfo)); } @@ -176,4 +177,4 @@ UserProfile::startChat() if (utils::localUser() != this->userid_) req.invite = {this->userid_.toStdString()}; emit ChatPage::instance()->createRoom(req); -} +} \ No newline at end of file diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 38002fff..99c6a755 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -19,6 +19,8 @@ enum Status Q_ENUM_NS(Status) } +class DeviceVerificationFlow; + class DeviceInfo { public: @@ -88,7 +90,7 @@ public: QString displayName(); QString avatarUrl(); - void fetchDeviceList(const QString &userID); + Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); -- cgit 1.5.1 From a2979c2df1b2059e2e8a969f5c1a3a804bd2550c Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Mon, 6 Jul 2020 21:32:21 +0530 Subject: Updating keys of outdated encrypted users --- src/Cache.cpp | 56 ++++++++++++++++++++++++++++++++++-------- src/Cache.h | 3 +++ src/CacheCryptoStructs.h | 29 +++++++++++++++++++--- src/Cache_p.h | 5 ++-- src/DeviceVerificationFlow.cpp | 8 +++--- src/ui/UserProfile.cpp | 17 ++++++------- src/ui/UserProfile.h | 3 +-- 7 files changed, 89 insertions(+), 32 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 553d1e97..1008277a 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -31,8 +31,10 @@ #include "Cache.h" #include "Cache_p.h" +#include "ChatPage.h" #include "EventAccessors.h" #include "Logging.h" +#include "MatrixClient.h" #include "Utils.h" //! Should be changed when a breaking change occurs in the cache format. @@ -1009,6 +1011,8 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); + updateUserCache(res.device_lists); + removeLeftRooms(txn, res.rooms.leave); txn.commit(); @@ -2885,17 +2889,13 @@ Cache::statusMessage(const std::string &user_id) void to_json(json &j, const UserCache &info) { - j["is_user_verified"] = info.is_user_verified; - j["cross_verified"] = info.cross_verified; - j["keys"] = info.keys; + j["keys"] = info.keys; } void from_json(const json &j, UserCache &info) { - info.is_user_verified = j.at("is_user_verified"); - info.cross_verified = j.at("cross_verified").get>(); - info.keys = j.at("keys").get(); + info.keys = j.at("keys").get(); } std::optional @@ -2932,6 +2932,32 @@ Cache::setUserCache(const std::string &user_id, const UserCache &body) return res; } +void +Cache::updateUserCache(const mtx::responses::DeviceLists body) +{ + for (auto user_id : body.changed) { + mtx::requests::QueryKeys req; + req.device_keys[user_id] = {}; + + http::client()->query_keys( + req, + [user_id, this](const mtx::responses::QueryKeys res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + setUserCache(user_id, UserCache{std::move(res)}); + }); + } + + for (std::string user_id : body.left) { + deleteUserCache(user_id); + } +} + int Cache::deleteUserCache(const std::string &user_id) { @@ -2947,15 +2973,19 @@ Cache::deleteUserCache(const std::string &user_id) void to_json(json &j, const DeviceVerifiedCache &info) { - j["device_verified"] = info.device_verified; - j["device_blocked"] = info.device_blocked; + j["is_user_verified"] = info.is_user_verified; + j["cross_verified"] = info.cross_verified; + j["device_verified"] = info.device_verified; + j["device_blocked"] = info.device_blocked; } void from_json(const json &j, DeviceVerifiedCache &info) { - info.device_verified = j.at("device_verified").get>(); - info.device_blocked = j.at("device_blocked").get>(); + info.is_user_verified = j.at("is_user_verified"); + info.cross_verified = j.at("cross_verified").get>(); + info.device_verified = j.at("device_verified").get>(); + info.device_blocked = j.at("device_blocked").get>(); } std::optional @@ -3178,6 +3208,12 @@ getUserCache(const std::string &user_id) return instance_->getUserCache(user_id); } +void +updateUserCache(const mtx::responses::DeviceLists body) +{ + instance_->updateUserCache(body); +} + int setUserCache(const std::string &user_id, const UserCache &body) { diff --git a/src/Cache.h b/src/Cache.h index 0c955c92..82d909ae 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -64,6 +64,9 @@ statusMessage(const std::string &user_id); std::optional getUserCache(const std::string &user_id); +void +updateUserCache(const mtx::responses::DeviceLists body); + int setUserCache(const std::string &user_id, const UserCache &body); diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 241cac76..ba746f59 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -66,14 +66,16 @@ struct OlmSessionStorage std::mutex group_inbound_mtx; }; +// this will store the keys of the user with whom a encrypted room is shared with struct UserCache { - //! this stores if the user is verified (with cross-signing) - bool is_user_verified = false; - //! list of verified device_ids with cross-signing - std::vector cross_verified; //! map of public key key_ids and their public_key mtx::responses::QueryKeys keys; + + UserCache(mtx::responses::QueryKeys res) + : keys(res) + {} + UserCache() {} }; void @@ -81,11 +83,30 @@ to_json(nlohmann::json &j, const UserCache &info); void from_json(const nlohmann::json &j, UserCache &info); +// the reason these are stored in a seperate cache rather than storing it in the user cache is +// UserCache stores only keys of users with which encrypted room is shared struct DeviceVerifiedCache { //! list of verified device_ids with device-verification std::vector device_verified; + //! list of verified device_ids with cross-signing + std::vector cross_verified; + //! list of devices the user blocks std::vector device_blocked; + //! this stores if the user is verified (with cross-signing) + bool is_user_verified = false; + + DeviceVerifiedCache(std::vector device_verified_, + std::vector cross_verified_, + std::vector device_blocked_, + bool is_user_verified_ = false) + : device_verified(device_verified_) + , cross_verified(cross_verified_) + , device_blocked(device_blocked_) + , is_user_verified(is_user_verified_) + {} + + DeviceVerifiedCache() {} }; void diff --git a/src/Cache_p.h b/src/Cache_p.h index 60fa930f..f75b0f4e 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -56,6 +56,7 @@ public: // user cache stores user keys std::optional getUserCache(const std::string &user_id); + void updateUserCache(const mtx::responses::DeviceLists body); int setUserCache(const std::string &user_id, const UserCache &body); int deleteUserCache(const std::string &user_id); @@ -521,12 +522,12 @@ private: lmdb::dbi getUserCacheDb(lmdb::txn &txn) { - return lmdb::dbi::open(txn, std::string("user_cache").c_str(), MDB_CREATE); + return lmdb::dbi::open(txn, "user_cache", MDB_CREATE); } lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn) { - return lmdb::dbi::open(txn, std::string("verified").c_str(), MDB_CREATE); + return lmdb::dbi::open(txn, "verified", MDB_CREATE); } //! Retrieves or creates the database that stores the open OLM sessions between our device diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 7829c41d..0122e691 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -477,7 +477,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c } else { cache::setVerifiedCache( this->userId.toStdString(), - DeviceVerifiedCache{{}, {this->deviceId.toStdString()}}); + DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}}); } this->deleteLater(); }); @@ -564,8 +564,9 @@ DeviceVerificationFlow::acceptDevice() } cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); } else { - cache::setVerifiedCache(this->userId.toStdString(), - DeviceVerifiedCache{{this->deviceId.toStdString()}, {}}); + cache::setVerifiedCache( + this->userId.toStdString(), + DeviceVerifiedCache{{this->deviceId.toStdString()}, {}, {}}); } emit deviceVerified(); @@ -616,5 +617,4 @@ DeviceVerificationFlow::unverify() } emit refreshProfile(); - this->deleteLater(); } diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index b4938e8d..6ae04d0b 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -77,8 +77,7 @@ UserProfile::avatarUrl() void UserProfile::callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, - std::string user_id, - std::optional> cross_verified) + std::string user_id) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", @@ -101,16 +100,15 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, // TODO: Verify signatures and ignore those that don't pass. verification::Status verified = verification::Status::UNVERIFIED; - if (cross_verified.has_value()) { - if (std::find(cross_verified->begin(), cross_verified->end(), d.first) != - cross_verified->end()) + if (device_verified.has_value()) { + if (std::find(device_verified->cross_verified.begin(), + device_verified->cross_verified.end(), + d.first) != device_verified->cross_verified.end()) verified = verification::Status::VERIFIED; - } else if (device_verified.has_value()) { if (std::find(device_verified->device_verified.begin(), device_verified->device_verified.end(), d.first) != device_verified->device_verified.end()) verified = verification::Status::VERIFIED; - } else if (device_verified.has_value()) { if (std::find(device_verified->device_blocked.begin(), device_verified->device_blocked.end(), d.first) != device_verified->device_blocked.end()) @@ -138,8 +136,7 @@ UserProfile::fetchDeviceList(const QString &userID) auto user_cache = cache::getUserCache(userID.toStdString()); if (user_cache.has_value()) { - this->callback_fn( - user_cache->keys, {}, userID.toStdString(), user_cache->cross_verified); + this->callback_fn(user_cache->keys, {}, userID.toStdString()); } else { mtx::requests::QueryKeys req; req.device_keys[userID.toStdString()] = {}; @@ -147,7 +144,7 @@ UserProfile::fetchDeviceList(const QString &userID) req, [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id, {}); + this->callback_fn(res, err, user_id); }); } } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 99c6a755..4e048400 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -103,6 +103,5 @@ private: void callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, - std::string user_id, - std::optional> cross_verified); + std::string user_id); }; -- cgit 1.5.1 From 1fcd768f88f7e84978d19283c9fa6205624f2544 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sat, 18 Jul 2020 01:46:30 +0530 Subject: Adding Room Key Verification Stuff --- resources/qml/UserProfile.qml | 22 +- resources/qml/delegates/MessageDelegate.qml | 54 +++ src/Cache.cpp | 2 +- src/ChatPage.h | 18 +- src/DeviceVerificationFlow.cpp | 665 +++++++++++++++------------- src/DeviceVerificationFlow.h | 17 +- src/EventAccessors.cpp | 11 +- src/Olm.cpp | 39 +- src/timeline/TimelineModel.cpp | 187 +++++++- src/timeline/TimelineModel.h | 8 + src/timeline/TimelineViewManager.cpp | 76 ++-- src/timeline/TimelineViewManager.h | 9 +- src/ui/UserProfile.cpp | 34 ++ src/ui/UserProfile.h | 4 + 14 files changed, 786 insertions(+), 360 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 5bdccb4d..c7dbc9aa 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -74,6 +74,26 @@ ApplicationWindow{ Layout.alignment: Qt.AlignHCenter } + Button { + id: verifyUserButton + text: "Verify" + Layout.alignment: Qt.AlignHCenter + enabled: profile.isUserVerified?false:true + visible: profile.isUserVerified?false:true + palette { + button: "white" + } + contentItem: Text { + text: verifyUserButton.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + profile.verifyUser(); + } + } + RowLayout { Layout.alignment: Qt.AlignHCenter ImageButton { @@ -127,7 +147,7 @@ ApplicationWindow{ } ScrollView { - implicitHeight: userProfileDialog.height/2 + 20 + implicitHeight: userProfileDialog.height/2-13 implicitWidth: userProfileDialog.width-20 clip: true Layout.alignment: Qt.AlignHCenter diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index 6f69f026..c556a978 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -127,6 +127,60 @@ Item { text: TimelineManager.timeline.formatMemberEvent(model.data.id); } } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationRequest + NoticeMessage { + text: "KeyVerificationRequest"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationStart + NoticeMessage { + text: "KeyVerificationStart"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationReady + NoticeMessage { + text: "KeyVerificationReady"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationCancel + NoticeMessage { + text: "KeyVerificationCancel"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationKey + NoticeMessage { + text: "KeyVerificationKey"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationMac + NoticeMessage { + text: "KeyVerificationMac"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationDone + NoticeMessage { + text: "KeyVerificationDone"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationDone + NoticeMessage { + text: "KeyVerificationDone"; + } + } + DelegateChoice { + roleValue: MtxEvent.KeyVerificationAccept + NoticeMessage { + text: "KeyVerificationAccept"; + } + } DelegateChoice { Placeholder {} } diff --git a/src/Cache.cpp b/src/Cache.cpp index 1008277a..8cee3453 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -1011,7 +1011,7 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); - updateUserCache(res.device_lists); + // updateUserCache(res.device_lists); removeLeftRooms(txn, res.rooms.leave); diff --git a/src/ChatPage.h b/src/ChatPage.h index 172f74fe..0e7c889f 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -167,16 +167,18 @@ signals: //! Signals for device verificaiton void recievedDeviceVerificationAccept( - const mtx::events::collections::DeviceEvents &message); + const mtx::events::msg::KeyVerificationAccept &message); void recievedDeviceVerificationRequest( - const mtx::events::collections::DeviceEvents &message); + const mtx::events::msg::KeyVerificationRequest &message, + std::string sender); void recievedDeviceVerificationCancel( - const mtx::events::collections::DeviceEvents &message); - void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message); - void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message); - void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message); - void recievedDeviceVerificationReady(const mtx::events::collections::DeviceEvents &message); - void recievedDeviceVerificationDone(const mtx::events::collections::DeviceEvents &message); + const mtx::events::msg::KeyVerificationCancel &message); + void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message); + void recievedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message); + void recievedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message, + std::string sender); + void recievedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); + void recievedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); private slots: void showUnreadMessageNotification(int count); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 0122e691..69de4937 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -6,11 +6,13 @@ #include #include +#include + static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes namespace msgs = mtx::events::msg; -DeviceVerificationFlow::DeviceVerificationFlow(QObject *) +DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type) { timeout = new QTimer(this); timeout->setSingleShot(true); @@ -26,192 +28,218 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) ChatPage::instance(), &ChatPage::recievedDeviceVerificationStart, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - if ((std::find(msg.content.key_agreement_protocols.begin(), - msg.content.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.content.key_agreement_protocols.end()) && - (std::find(msg.content.hashes.begin(), - msg.content.hashes.end(), - "sha256") != msg.content.hashes.end()) && - (std::find(msg.content.message_authentication_codes.begin(), - msg.content.message_authentication_codes.end(), - "hmac-sha256") != - msg.content.message_authentication_codes.end())) { - if (std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.content.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; - } else if (std::find( - msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.content.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Decimal; - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - return; - } - this->acceptVerificationRequest(); - this->canonical_json = nlohmann::json(msg.content); - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } + [this](const mtx::events::msg::KeyVerificationStart &msg, std::string) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; } - }); - connect( - ChatPage::instance(), - &ChatPage::recievedDeviceVerificationAccept, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - if ((msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && - (msg.content.hash == "sha256") && - (msg.content.message_authentication_code == "hkdf-hmac-sha256")) { - this->commitment = msg.content.commitment; - if (std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.content.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; - } else { - this->method = DeviceVerificationFlow::Method::Decimal; - } - this->mac_method = msg.content.message_authentication_code; - this->sendVerificationKey(); + if ((std::find(msg.key_agreement_protocols.begin(), + msg.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) && + (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != + msg.hashes.end()) && + (std::find(msg.message_authentication_codes.begin(), + msg.message_authentication_codes.end(), + "hmac-sha256") != msg.message_authentication_codes.end())) { + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.short_authentication_string.end()) { + this->method = DeviceVerificationFlow::Method::Emoji; + } else if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.short_authentication_string.end()) { + this->method = DeviceVerificationFlow::Method::Decimal; } else { this->cancelVerification( DeviceVerificationFlow::Error::UnknownMethod); + return; } + this->acceptVerificationRequest(); + this->canonical_json = nlohmann::json(msg); + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); } }); + + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationAccept, + this, + [this](const mtx::events::msg::KeyVerificationAccept &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; + } + if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") && + (msg.hash == "sha256") && + (msg.message_authentication_code == "hkdf-hmac-sha256")) { + this->commitment = msg.commitment; + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.short_authentication_string.end()) { + this->method = DeviceVerificationFlow::Method::Emoji; + } else { + this->method = DeviceVerificationFlow::Method::Decimal; + } + this->mac_method = msg.message_authentication_code; + this->sendVerificationKey(); + } else { + this->cancelVerification( + DeviceVerificationFlow::Error::UnknownMethod); + } + }); + connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationCancel, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - emit verificationCanceled(); + [this](const mtx::events::msg::KeyVerificationCancel &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; + } + emit verificationCanceled(); + }); + + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationKey, + this, + [this](const mtx::events::msg::KeyVerificationKey &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; + } + this->sas->set_their_key(msg.key); + std::string info; + if (this->sender == true) { + info = "MATRIX_KEY_VERIFICATION_SAS|" + + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + this->sas->public_key() + + "|" + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.key + "|" + + this->transaction_id; + } else { + info = "MATRIX_KEY_VERIFICATION_SAS|" + this->toClient.to_string() + + "|" + this->deviceId.toStdString() + "|" + msg.key + "|" + + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + this->sas->public_key() + + "|" + this->transaction_id; + } + + if (this->method == DeviceVerificationFlow::Method::Emoji) { + this->sasList = this->sas->generate_bytes_emoji(info); + } else if (this->method == DeviceVerificationFlow::Method::Decimal) { + this->sasList = this->sas->generate_bytes_decimal(info); + } + if (this->sender == false) { + emit this->verificationRequestAccepted(this->method); + this->sendVerificationKey(); + } else { + if (this->commitment == + mtx::crypto::bin2base64_unpadded( + mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) { + emit this->verificationRequestAccepted(this->method); + } else { + this->cancelVerification( + DeviceVerificationFlow::Error::MismatchedCommitment); + } } }); + connect( ChatPage::instance(), - &ChatPage::recievedDeviceVerificationKey, + &ChatPage::recievedDeviceVerificationMac, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - this->sas->set_their_key(msg.content.key); - std::string info; - if (this->sender == true) { - info = "MATRIX_KEY_VERIFICATION_SAS|" + - http::client()->user_id().to_string() + "|" + - http::client()->device_id() + "|" + - this->sas->public_key() + "|" + - this->toClient.to_string() + "|" + - this->deviceId.toStdString() + "|" + msg.content.key + - "|" + this->transaction_id; - } else { - info = "MATRIX_KEY_VERIFICATION_SAS|" + - this->toClient.to_string() + "|" + - this->deviceId.toStdString() + "|" + msg.content.key + - "|" + http::client()->user_id().to_string() + "|" + - http::client()->device_id() + "|" + - this->sas->public_key() + "|" + this->transaction_id; - } - - if (this->method == DeviceVerificationFlow::Method::Emoji) { - this->sasList = this->sas->generate_bytes_emoji(info); - } else if (this->method == DeviceVerificationFlow::Method::Decimal) { - this->sasList = this->sas->generate_bytes_decimal(info); - } - if (this->sender == false) { - emit this->verificationRequestAccepted(this->method); - this->sendVerificationKey(); - } else { - if (this->commitment == - mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( - msg.content.key + this->canonical_json.dump()))) { - emit this->verificationRequestAccepted(this->method); + [this](const mtx::events::msg::KeyVerificationMac &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; + } + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + + this->deviceId.toStdString() + + http::client()->user_id().to_string() + + http::client()->device_id() + this->transaction_id; + + std::vector key_list; + std::string key_string; + for (auto mac : msg.mac) { + key_string += mac.first + ","; + if (device_keys[mac.first] != "") { + if (mac.second == + this->sas->calculate_mac(this->device_keys[mac.first], + info + mac.first)) { } else { this->cancelVerification( - DeviceVerificationFlow::Error::MismatchedCommitment); + DeviceVerificationFlow::Error::KeyMismatch); + return; } } } - }); - connect( - ChatPage::instance(), - &ChatPage::recievedDeviceVerificationMac, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::string info = - "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + - this->deviceId.toStdString() + http::client()->user_id().to_string() + - http::client()->device_id() + this->transaction_id; - - std::vector key_list; - std::string key_string; - for (auto mac : msg.content.mac) { - key_string += mac.first + ","; - if (device_keys[mac.first] != "") { - if (mac.second == - this->sas->calculate_mac(this->device_keys[mac.first], - info + mac.first)) { - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::KeyMismatch); - return; - } - } - } - key_string = key_string.substr(0, key_string.length() - 1); - if (msg.content.keys == - this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - // uncomment this in future to be compatible with the - // MSC2366 this->sendVerificationDone(); and remove the - // below line - if (this->isMacVerified == true) { - this->acceptDevice(); - } else - this->isMacVerified = true; - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::KeyMismatch); - } + key_string = key_string.substr(0, key_string.length() - 1); + if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) { + // uncomment this in future to be compatible with the + // MSC2366 this->sendVerificationDone(); and remove the + // below line + if (this->isMacVerified == true) { + this->acceptDevice(); + } else + this->isMacVerified = true; + } else { + this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); } }); + connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationReady, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - this->startVerificationRequest(); + [this](const mtx::events::msg::KeyVerificationReady &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; } + this->startVerificationRequest(); }); + connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationDone, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - this->acceptDevice(); + [this](const mtx::events::msg::KeyVerificationDone &msg) { + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().in_reply_to.event_id != + this->relation.in_reply_to.event_id) + return; } + this->acceptDevice(); }); + timeout->start(TIMEOUT); } @@ -294,18 +322,18 @@ void DeviceVerificationFlow::setSender(bool sender_) { this->sender = sender_; - if (this->sender == true) + if (this->sender == true && this->type == DeviceVerificationFlow::Type::ToDevice) this->transaction_id = http::client()->generate_txn_id(); + else if (this->sender == true && this->type == DeviceVerificationFlow::Type::RoomMsg) + this->relation.in_reply_to.event_id = http::client()->generate_txn_id(); } //! accepts a verification void DeviceVerificationFlow::acceptVerificationRequest() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationAccept req; - req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; @@ -317,126 +345,152 @@ DeviceVerificationFlow::acceptVerificationRequest() req.commitment = mtx::crypto::bin2base64_unpadded( mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump())); - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to accept verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + req.transaction_id = this->transaction_id; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn( + "failed to accept verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! responds verification request void DeviceVerificationFlow::sendVerificationReady() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationReady req; - req.from_device = http::client()->device_id(); - req.transaction_id = this->transaction_id; - req.methods = {mtx::events::msg::VerificationMethods::SASv1}; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification ready: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + req.from_device = http::client()->device_id(); + req.methods = {mtx::events::msg::VerificationMethods::SASv1}; + + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + req.transaction_id = this->transaction_id; + mtx::requests::ToDeviceMessages body; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification ready: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! accepts a verification void DeviceVerificationFlow::sendVerificationDone() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationDone req; - req.transaction_id = this->transaction_id; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification done: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + req.transaction_id = this->transaction_id; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification done: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationStart req; req.from_device = http::client()->device_id(); - req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; req.key_agreement_protocols = {"curve25519-hkdf-sha256"}; req.hashes = {"sha256"}; - req.message_authentication_codes = {"hkdf-hmac-sha256", "hmac-sha256"}; + req.message_authentication_codes = {"hkdf-hmac-sha256"}; req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, mtx::events::msg::SASMethods::Emoji}; - body[this->toClient][this->deviceId.toStdString()] = req; - this->canonical_json = nlohmann::json(req); - - http::client() - ->send_to_device( - this->transaction_id, body, [body](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to start verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + req.transaction_id = this->transaction_id; + this->canonical_json = nlohmann::json(req); + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [body](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn( + "failed to start verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! sends a verification request void DeviceVerificationFlow::sendVerificationRequest() { - QDateTime CurrentTime = QDateTime::currentDateTimeUtc(); - - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationRequest req; - req.from_device = http::client()->device_id(); - req.transaction_id = this->transaction_id; + req.from_device = http::client()->device_id(); req.methods.resize(1); req.methods[0] = mtx::events::msg::VerificationMethods::SASv1; - req.timestamp = (uint64_t)CurrentTime.toTime_t(); - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + QDateTime CurrentTime = QDateTime::currentDateTimeUtc(); + + req.transaction_id = this->transaction_id; + req.timestamp = (uint64_t)CurrentTime.toTime_t(); + + mtx::requests::ToDeviceMessages body; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + std::cout << "lulz" << std::endl; + } } //! cancels a verification flow void DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_code) { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationCancel req; - req.transaction_id = this->transaction_id; if (error_code == DeviceVerificationFlow::Error::UnknownMethod) { req.code = "m.unknown_method"; req.reason = "unknown method recieved"; @@ -457,65 +511,79 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c req.reason = "user cancelled the verification"; } - body[this->toClient][deviceId.toStdString()] = req; - emit this->verificationCanceled(); - http::client() - ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to cancel verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - if (verified_cache.has_value()) { - verified_cache->device_blocked.push_back(this->deviceId.toStdString()); - cache::setVerifiedCache(this->userId.toStdString(), - verified_cache.value()); - } else { - cache::setVerifiedCache( - this->userId.toStdString(), - DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}}); - } - this->deleteLater(); - }); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + req.transaction_id = this->transaction_id; + mtx::requests::ToDeviceMessages body; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn( + "failed to cancel verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + + this->deleteLater(); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } + + // TODO : Handle Blocking user better + // auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); + // if (verified_cache.has_value()) { + // verified_cache->device_blocked.push_back(this->deviceId.toStdString()); + // cache::setVerifiedCache(this->userId.toStdString(), + // verified_cache.value()); + // } else { + // cache::setVerifiedCache( + // this->userId.toStdString(), + // DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}}); + // } } //! sends the verification key void DeviceVerificationFlow::sendVerificationKey() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationKey req; - req.key = this->sas->public_key(); - req.transaction_id = this->transaction_id; - - body[this->toClient][deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification key: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + req.key = this->sas->public_key(); + + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + req.transaction_id = this->transaction_id; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification key: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! sends the mac of the keys void DeviceVerificationFlow::sendVerificationMac() { - mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationMac req; std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() + http::client()->device_id() + this->toClient.to_string() + this->deviceId.toStdString() + this->transaction_id; - req.transaction_id = this->transaction_id; //! this vector stores the type of the key and the key std::vector> key_list; key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519)); @@ -531,22 +599,28 @@ DeviceVerificationFlow::sendVerificationMac() req.keys = this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS"); - body[this->toClient][deviceId.toStdString()] = req; - - http::client() - ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification MAC: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - if (this->isMacVerified == true) - this->acceptDevice(); - else - this->isMacVerified = true; - }); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + req.transaction_id = this->transaction_id; + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification MAC: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + + if (this->isMacVerified == true) + this->acceptDevice(); + else + this->isMacVerified = true; + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) { + req.relates_to = this->relation; + } } //! Completes the verification flow void @@ -555,14 +629,11 @@ DeviceVerificationFlow::acceptDevice() auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); if (verified_cache.has_value()) { verified_cache->device_verified.push_back(this->deviceId.toStdString()); - for (auto it = verified_cache->device_blocked.begin(); - it != verified_cache->device_blocked.end(); - it++) { - if (*it == this->deviceId.toStdString()) { - verified_cache->device_blocked.erase(it); - } - } - cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); + verified_cache->device_blocked.erase( + std::remove(verified_cache->device_blocked.begin(), + verified_cache->device_blocked.end(), + this->deviceId.toStdString()), + verified_cache->device_blocked.end()); } else { cache::setVerifiedCache( this->userId.toStdString(), diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index edff7c8e..3f999e80 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -2,8 +2,8 @@ #include "Olm.h" +#include "MatrixClient.h" #include "mtx/responses/crypto.hpp" -#include #include class QTimer; @@ -19,15 +19,22 @@ class DeviceVerificationFlow : public QObject Q_PROPERTY(QString userId READ getUserId WRITE setUserId) Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) Q_PROPERTY(Method method READ getMethod WRITE setMethod) - Q_PROPERTY(std::vector sasList READ getSasList) + Q_PROPERTY(std::vector sasList READ getSasList CONSTANT) public: + enum Type + { + ToDevice, + RoomMsg + }; + enum Method { Decimal, Emoji }; Q_ENUM(Method) + enum Error { UnknownMethod, @@ -39,7 +46,9 @@ public: }; Q_ENUM(Error) - DeviceVerificationFlow(QObject *parent = nullptr); + DeviceVerificationFlow( + QObject *parent = nullptr, + DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice); QString getTransactionId(); QString getUserId(); QString getDeviceId(); @@ -90,6 +99,7 @@ private: QString userId; QString deviceId; Method method; + Type type; bool sender; QTimer *timeout = nullptr; @@ -101,4 +111,5 @@ private: mtx::identifiers::User toClient; std::vector sasList; std::map device_keys; + mtx::common::ReplyRelatesTo relation; }; diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp index 0618206c..869687f4 100644 --- a/src/EventAccessors.cpp +++ b/src/EventAccessors.cpp @@ -72,8 +72,15 @@ struct EventBody template std::string operator()(const mtx::events::Event &e) { - if constexpr (is_detected::value) - return e.content.body; + if constexpr (is_detected::value) { + if constexpr (std::is_same_v, + std::remove_cv_t>) + return e.content.body ? e.content.body.value() : ""; + else if constexpr (std::is_same_v< + std::string, + std::remove_cv_t>) + return e.content.body; + } return ""; } }; diff --git a/src/Olm.cpp b/src/Olm.cpp index 7d7037c9..ff6ea2f4 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -5,10 +5,10 @@ #include "Cache.h" #include "ChatPage.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" -#include static const std::string STORAGE_SECRET_KEY("secret"); constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; @@ -77,21 +77,42 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg); + auto message = std::get< + mtx::events::DeviceEvent>(msg); + ChatPage::instance()->recievedDeviceVerificationAccept(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { - ChatPage::instance()->recievedDeviceVerificationRequest(msg); + auto message = std::get< + mtx::events::DeviceEvent>(msg); + ChatPage::instance()->recievedDeviceVerificationRequest(message.content, + message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { - ChatPage::instance()->recievedDeviceVerificationCancel(msg); + auto message = std::get< + mtx::events::DeviceEvent>(msg); + ChatPage::instance()->recievedDeviceVerificationCancel(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { - ChatPage::instance()->recievedDeviceVerificationKey(msg); + auto message = + std::get>( + msg); + ChatPage::instance()->recievedDeviceVerificationKey(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { - ChatPage::instance()->recievedDeviceVerificationMac(msg); + auto message = + std::get>( + msg); + ChatPage::instance()->recievedDeviceVerificationMac(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { - ChatPage::instance()->recievedDeviceVerificationStart(msg); + auto message = std::get< + mtx::events::DeviceEvent>(msg); + ChatPage::instance()->recievedDeviceVerificationStart(message.content, + message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { - ChatPage::instance()->recievedDeviceVerificationReady(msg); + auto message = std::get< + mtx::events::DeviceEvent>(msg); + ChatPage::instance()->recievedDeviceVerificationReady(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { - ChatPage::instance()->recievedDeviceVerificationDone(msg); + auto message = + std::get>( + msg); + ChatPage::instance()->recievedDeviceVerificationDone(message.content); } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 773a5a23..71cc53c5 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -22,6 +22,8 @@ #include "Utils.h" #include "dialogs/RawMessage.h" +#include + Q_DECLARE_METATYPE(QModelIndex) namespace std { @@ -116,7 +118,41 @@ struct RoomEventType { return qml_mtx_events::EventType::VideoMessage; } - + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationRequest; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationStart; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationMac; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationAccept; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationCancel; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationKey; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event &) + { + return qml_mtx_events::EventType::KeyVerificationDone; + } qml_mtx_events::EventType operator()(const mtx::events::Event &) { return qml_mtx_events::EventType::Redacted; @@ -572,6 +608,155 @@ TimelineModel::updateLastMessage() } } +std::vector +TimelineModel::internalAddEvents( + const std::vector &timeline) +{ + std::vector ids; + for (auto e : timeline) { + QString id = QString::fromStdString(mtx::accessors::event_id(e)); + + if (this->events.contains(id)) { + this->events.insert(id, e); + int idx = idToIndex(id); + emit dataChanged(index(idx, 0), index(idx, 0)); + continue; + } + + QString txid = QString::fromStdString(mtx::accessors::transaction_id(e)); + if (this->pending.removeOne(txid)) { + this->events.insert(id, e); + this->events.remove(txid); + int idx = idToIndex(txid); + if (idx < 0) { + nhlog::ui()->warn("Received index out of range"); + continue; + } + eventOrder[idx] = id; + emit dataChanged(index(idx, 0), index(idx, 0)); + continue; + } + + if (std::get_if>( + &e)) { + std::cout << "got a request" << std::endl; + } + + if (auto cancelVerification = + std::get_if>( + &e)) { + std::cout<<"it is happening"<content.relates_to.has_value()) { + QString event_id = QString::fromStdString( + cancelVerification->content.relates_to.value() + .in_reply_to.event_id); + auto request = + std::find(eventOrder.begin(), eventOrder.end(), event_id); + if (request != eventOrder.end()) { + auto event = events.value(event_id); + auto e = std::get_if>(&event); + std::cout<>(&e)) { + QString redacts = QString::fromStdString(redaction->redacts); + auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts); + + auto event = events.value(redacts); + if (auto reaction = + std::get_if>( + &event)) { + QString reactedTo = + QString::fromStdString(reaction->content.relates_to.event_id); + reactions[reactedTo].removeReaction(*reaction); + int idx = idToIndex(reactedTo); + if (idx >= 0) + emit dataChanged(index(idx, 0), index(idx, 0)); + } + + if (redacted != eventOrder.end()) { + auto redactedEvent = std::visit( + [](const auto &ev) + -> mtx::events::RoomEvent { + mtx::events::RoomEvent + replacement = {}; + replacement.event_id = ev.event_id; + replacement.room_id = ev.room_id; + replacement.sender = ev.sender; + replacement.origin_server_ts = ev.origin_server_ts; + replacement.type = ev.type; + return replacement; + }, + e); + events.insert(redacts, redactedEvent); + + int row = (int)std::distance(eventOrder.begin(), redacted); + emit dataChanged(index(row, 0), index(row, 0)); + } + + continue; // don't insert redaction into timeline + } + + if (auto reaction = + std::get_if>(&e)) { + QString reactedTo = + QString::fromStdString(reaction->content.relates_to.event_id); + events.insert(id, e); + + // remove local echo + if (!txid.isEmpty()) { + auto rCopy = *reaction; + rCopy.event_id = txid.toStdString(); + reactions[reactedTo].removeReaction(rCopy); + } + + reactions[reactedTo].addReaction(room_id_.toStdString(), *reaction); + int idx = idToIndex(reactedTo); + if (idx >= 0) + emit dataChanged(index(idx, 0), index(idx, 0)); + continue; // don't insert reaction into timeline + } + + if (auto event = + std::get_if>(&e)) { + auto e_ = decryptEvent(*event).event; + auto encInfo = mtx::accessors::file(e_); + + if (encInfo) + emit newEncryptedImage(encInfo.value()); + } + + this->events.insert(id, e); + ids.push_back(id); + + auto replyTo = mtx::accessors::in_reply_to_event(e); + auto qReplyTo = QString::fromStdString(replyTo); + if (!replyTo.empty() && !events.contains(qReplyTo)) { + http::client()->get_event( + this->room_id_.toStdString(), + replyTo, + [this, id, replyTo]( + const mtx::events::collections::TimelineEvents &timeline, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->error( + "Failed to retrieve event with id {}, which was " + "requested to show the replyTo for event {}", + replyTo, + id.toStdString()); + return; + } + emit eventFetched(id, timeline); + }); + } + } + return ids; +} + void TimelineModel::setCurrentIndex(int index) { diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 104a475c..708ed38e 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -84,6 +84,14 @@ enum EventType VideoMessage, Redacted, UnknownMessage, + KeyVerificationRequest, + KeyVerificationStart, + KeyVerificationMac, + KeyVerificationAccept, + KeyVerificationCancel, + KeyVerificationKey, + KeyVerificationDone, + KeyVerificationReady }; Q_ENUM_NS(EventType) diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 81c8d6d3..02b74d20 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -101,7 +101,15 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin , blurhashProvider(new BlurhashProvider()) , settings(userSettings) { - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, "im.nheko", 1, @@ -181,21 +189,19 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin dynamic_cast(parent), &ChatPage::recievedDeviceVerificationRequest, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); + [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { auto flow = new DeviceVerificationFlow(this); - if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { - if (std::find(msg.content.methods.begin(), - msg.content.methods.end(), + if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) { + if (std::find(msg.methods.begin(), + msg.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != - msg.content.methods.end()) { + msg.methods.end()) { // flow->sendVerificationReady(); emit newDeviceVerificationRequest( std::move(flow), - QString::fromStdString(msg.content.transaction_id), - QString::fromStdString(msg.sender), - QString::fromStdString(msg.content.from_device)); + QString::fromStdString(msg.transaction_id.value()), + QString::fromStdString(sender), + QString::fromStdString(msg.from_device)); } else { flow->cancelVerification( DeviceVerificationFlow::Error::UnknownMethod); @@ -206,33 +212,29 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin dynamic_cast(parent), &ChatPage::recievedDeviceVerificationStart, this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); + [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { auto flow = new DeviceVerificationFlow(this); - flow->canonical_json = nlohmann::json(msg.content); - if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { - if ((std::find(msg.content.key_agreement_protocols.begin(), - msg.content.key_agreement_protocols.end(), + flow->canonical_json = nlohmann::json(msg); + if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) { + if ((std::find(msg.key_agreement_protocols.begin(), + msg.key_agreement_protocols.end(), "curve25519-hkdf-sha256") != - msg.content.key_agreement_protocols.end()) && - (std::find(msg.content.hashes.begin(), - msg.content.hashes.end(), - "sha256") != msg.content.hashes.end()) && - (std::find(msg.content.message_authentication_codes.begin(), - msg.content.message_authentication_codes.end(), + msg.key_agreement_protocols.end()) && + (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != + msg.hashes.end()) && + (std::find(msg.message_authentication_codes.begin(), + msg.message_authentication_codes.end(), "hmac-sha256") != - msg.content.message_authentication_codes.end())) { - if (std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), + msg.message_authentication_codes.end())) { + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), mtx::events::msg::SASMethods::Emoji) != - msg.content.short_authentication_string.end()) { + msg.short_authentication_string.end()) { flow->setMethod(DeviceVerificationFlow::Method::Emoji); - } else if (std::find( - msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.content.short_authentication_string.end()) { + } else if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.short_authentication_string.end()) { flow->setMethod(DeviceVerificationFlow::Method::Decimal); } else { flow->cancelVerification( @@ -241,10 +243,10 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin } emit newDeviceVerificationRequest( std::move(flow), - QString::fromStdString(msg.content.transaction_id), - QString::fromStdString(msg.sender), - QString::fromStdString(msg.content.from_device)); - flow->canonical_json = nlohmann::json(msg.content); + QString::fromStdString(msg.transaction_id.value()), + QString::fromStdString(sender), + QString::fromStdString(msg.from_device)); + flow->canonical_json = nlohmann::json(msg); } else { flow->cancelVerification( DeviceVerificationFlow::Error::UnknownMethod); diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index a438ef5e..71aee5ef 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -133,4 +133,11 @@ private: DeviceVerificationList *dvList; }; -Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationDone) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationKey) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationMac) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationReady) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationRequest) +Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationStart) diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 6ae04d0b..3499384c 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -6,6 +6,8 @@ #include "Utils.h" #include "mtx/responses/crypto.hpp" +#include // only for debugging + UserProfile::UserProfile(QString roomid, QString userid, QObject *parent) : QObject(parent) , roomid_(roomid) @@ -74,6 +76,12 @@ UserProfile::avatarUrl() return cache::avatarUrl(roomid_, userid_); } +bool +UserProfile::getUserStatus() +{ + return isUserVerified; +} + void UserProfile::callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, @@ -100,6 +108,7 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res, // TODO: Verify signatures and ignore those that don't pass. verification::Status verified = verification::Status::UNVERIFIED; + isUserVerified = device_verified->is_user_verified; if (device_verified.has_value()) { if (std::find(device_verified->cross_verified.begin(), device_verified->cross_verified.end(), @@ -174,4 +183,29 @@ UserProfile::startChat() if (utils::localUser() != this->userid_) req.invite = {this->userid_.toStdString()}; emit ChatPage::instance()->createRoom(req); +} + +void +UserProfile::verifyUser() +{ + std::cout << "Checking if to start to device verification or room message verification" + << std::endl; + 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(), + (this->userid()).toStdString()) != room_members.end()) { + std::cout << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id + << std::endl; + return; + } + } + } + + std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; } \ No newline at end of file diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 4e048400..3f9cbe6f 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -81,6 +81,7 @@ class UserProfile : public QObject Q_PROPERTY(QString userid READ userid CONSTANT) Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT) Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) + Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) public: UserProfile(QString roomid, QString userid, QObject *parent = 0); @@ -89,17 +90,20 @@ public: QString userid(); QString displayName(); QString avatarUrl(); + bool getUserStatus(); Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat(); + Q_INVOKABLE void verifyUser(); private: QString roomid_, userid_; std::optional cross_verified; DeviceInfoModel deviceList_; + bool isUserVerified = false; void callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, -- cgit 1.5.1 From 2e20049b3695d0aa7ca09db079bcc39c0485d098 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 9 Aug 2020 08:35:15 +0530 Subject: [WIP] Room-Verification Messages --- resources/qml/Reactions.qml | 7 +- resources/qml/TimelineRow.qml | 2 +- resources/qml/TimelineView.qml | 5 - resources/qml/UserProfile.qml | 15 +- .../qml/device-verification/DeviceVerification.qml | 4 +- src/ChatPage.cpp | 11 +- src/DeviceVerificationFlow.cpp | 197 ++++++------ src/DeviceVerificationFlow.h | 2 +- src/EventAccessors.cpp | 11 +- src/timeline/EventStore.cpp | 221 ++++++++------ src/timeline/EventStore.h | 8 + src/timeline/TimelineModel.cpp | 333 ++++++--------------- src/timeline/TimelineModel.h | 16 +- src/ui/UserProfile.cpp | 56 ++-- src/ui/UserProfile.h | 6 +- 15 files changed, 401 insertions(+), 493 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml index 11109d7f..9fc30f61 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml @@ -35,13 +35,8 @@ Flow { ToolTip.text: modelData.users onClicked: { -<<<<<<< HEAD console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent) - timelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key) -======= - console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent) - TimelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent) ->>>>>>> Fix presence indicator + TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key) } diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index db58eb22..b464b76c 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -48,7 +48,7 @@ Item { // fancy reply, if this is a reply Reply { visible: model.replyTo - modelData: chat.model.getDump(model.replyTo) + modelData: chat.model.getDump(model.replyTo,model.id) userColor: TimelineManager.userColor(modelData.userId, colors.window) } diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index c6fc3851..86b78a1e 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -388,13 +388,8 @@ Page { anchors.rightMargin: 20 anchors.bottom: parent.bottom -<<<<<<< HEAD modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {} - userColor: timelineManager.userColor(modelData.userId, colors.window) -======= - modelData: chat.model ? chat.model.getDump(chat.model.reply) : {} userColor: TimelineManager.userColor(modelData.userId, colors.window) ->>>>>>> Fix presence indicator } ImageButton { diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index c7dbc9aa..9b53ff35 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -90,7 +90,12 @@ ApplicationWindow{ verticalAlignment: Text.AlignVCenter } onClicked: { - profile.verifyUser(); + var newFlow = profile.createFlow(true); + newFlow.userId = profile.userid; + newFlow.sender = true; + deviceVerificationList.add(newFlow.tranId); + var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true}); + dialog.show(); } } @@ -192,14 +197,16 @@ ApplicationWindow{ id: verifyButton text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify" onClicked: { - var newFlow = deviceVerificationFlow.createObject(userProfileDialog, - {userId : profile.userid, sender: true, deviceId : model.deviceId}); + var newFlow = profile.createFlow(false); + newFlow.userId = profile.userid; + newFlow.sender = true; + newFlow.deviceId = model.deviceId; if(model.verificationStatus == VerificationStatus.VERIFIED){ newFlow.unverify(); deviceVerificationList.updateProfile(newFlow.userId); }else{ deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow}); + var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false}); dialog.show(); } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 8e74d1cb..f40a7b8f 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -100,7 +100,9 @@ ApplicationWindow { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } - onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } + onClicked: { + stack.replace(awaitingVerificationRequestAccept); + isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); } } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index aba1f75d..b97b6b30 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -562,12 +562,11 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect( this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection); connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection); - connect( - this, - &ChatPage::tryDelayedSyncCb, - this, - [this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); }, - Qt::QueuedConnection); + connect(this, + &ChatPage::tryDelayedSyncCb, + this, + [this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); }, + Qt::QueuedConnection); connect(this, &ChatPage::newSyncResponse, diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 0f521f92..5069ff9d 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -22,6 +22,11 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow this->sas = olm::client()->sas_init(); this->isMacVerified = false; + connect(this->model_, + &TimelineModel::updateFlowEventId, + this, + [this](std::string event_id) { this->relation.in_reply_to.event_id = event_id; }); + connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->cancelVerification(DeviceVerificationFlow::Error::Timeout); @@ -222,6 +227,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { + // this is just a workaround + this->relation.in_reply_to.event_id = + msg.relates_to.value().in_reply_to.event_id; if (msg.relates_to.value().in_reply_to.event_id != this->relation.in_reply_to.event_id) return; @@ -343,11 +351,8 @@ DeviceVerificationFlow::setType(Type type) void DeviceVerificationFlow::setSender(bool sender_) { - this->sender = sender_; - if (this->sender == true && this->type == DeviceVerificationFlow::Type::ToDevice) - this->transaction_id = http::client()->generate_txn_id(); - else if (this->sender == true && this->type == DeviceVerificationFlow::Type::RoomMsg) - this->relation.in_reply_to.event_id = http::client()->generate_txn_id(); + this->sender = sender_; + this->transaction_id = http::client()->generate_txn_id(); } void @@ -380,19 +385,16 @@ DeviceVerificationFlow::acceptVerificationRequest() body[this->toClient][this->deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn( - "failed to accept verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to accept verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! responds verification request @@ -410,18 +412,16 @@ DeviceVerificationFlow::sendVerificationReady() body[this->toClient][this->deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification ready: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification ready: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! accepts a verification @@ -436,18 +436,16 @@ DeviceVerificationFlow::sendVerificationDone() body[this->toClient][this->deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification done: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification done: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! starts the verification flow @@ -470,19 +468,16 @@ DeviceVerificationFlow::startVerificationRequest() this->canonical_json = nlohmann::json(req); body[this->toClient][this->deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [body](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn( - "failed to start verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [body](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to start verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! sends a verification request @@ -505,17 +500,20 @@ DeviceVerificationFlow::sendVerificationRequest() body[this->toClient][this->deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { - (model_.value())->sendMessage(req); + http::client()->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { + req.to = this->userId.toStdString(); + req.msgtype = "m.key.verification.request"; + req.body = "User is requesting to verify keys with you. However, your client does " + "not support this method, so you will need to use the legacy method of " + "key verification."; + (model_)->sendMessage(req); } } //! cancels a verification flow @@ -552,21 +550,18 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c body[this->toClient][deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn( - "failed to cancel verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - this->deleteLater(); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to cancel verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + + this->deleteLater(); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } // TODO : Handle Blocking user better @@ -595,18 +590,16 @@ DeviceVerificationFlow::sendVerificationKey() body[this->toClient][deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification key: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification key: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! sends the mac of the keys @@ -639,23 +632,21 @@ DeviceVerificationFlow::sendVerificationMac() req.transaction_id = this->transaction_id; body[this->toClient][deviceId.toStdString()] = req; - http::client() - ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification MAC: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - if (this->isMacVerified == true) - this->acceptDevice(); - else - this->isMacVerified = true; - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { + http::client()->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification MAC: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + + if (this->isMacVerified == true) + this->acceptDevice(); + else + this->isMacVerified = true; + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; - (model_.value())->sendMessage(req); + (model_)->sendMessage(req); } } //! Completes the verification flow diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index bec9f1e0..1ad3b1d0 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -126,6 +126,6 @@ private: // for room messages std::optional room_id; std::optional event_id; - std::optional model_; + TimelineModel *model_; mtx::common::ReplyRelatesTo relation; }; diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp index 869687f4..24e2f35b 100644 --- a/src/EventAccessors.cpp +++ b/src/EventAccessors.cpp @@ -37,8 +37,15 @@ struct EventMsgType template mtx::events::MessageType operator()(const mtx::events::Event &e) { - if constexpr (is_detected::value) - return mtx::events::getMessageType(e.content.msgtype); + if constexpr (is_detected::value) { + if constexpr (std::is_same_v, + std::remove_cv_t>) + return mtx::events::getMessageType(e.content.msgtype.value()); + else if constexpr (std::is_same_v< + std::string, + std::remove_cv_t>) + return mtx::events::getMessageType(e.content.msgtype); + } return mtx::events::MessageType::Unknown; } }; diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 639cae0f..208b20e2 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -5,6 +5,7 @@ #include "Cache.h" #include "Cache_p.h" +#include "ChatPage.h" #include "EventAccessors.h" #include "Logging.h" #include "MatrixClient.h" @@ -31,41 +32,38 @@ EventStore::EventStore(std::string room_id, QObject *) this->last = range->last; } - connect( - this, - &EventStore::eventFetched, - this, - [this](std::string id, - std::string relatedTo, - mtx::events::collections::TimelineEvents timeline) { - cache::client()->storeEvent(room_id_, id, {timeline}); - - if (!relatedTo.empty()) { - auto idx = idToIndex(relatedTo); - if (idx) - emit dataChanged(*idx, *idx); - } - }, - Qt::QueuedConnection); - - connect( - this, - &EventStore::oldMessagesRetrieved, - this, - [this](const mtx::responses::Messages &res) { - // - uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res); - if (newFirst == first) - fetchMore(); - else { - emit beginInsertRows(toExternalIdx(newFirst), - toExternalIdx(this->first - 1)); - this->first = newFirst; - emit endInsertRows(); - emit fetchedMore(); - } - }, - Qt::QueuedConnection); + connect(this, + &EventStore::eventFetched, + this, + [this](std::string id, + std::string relatedTo, + mtx::events::collections::TimelineEvents timeline) { + cache::client()->storeEvent(room_id_, id, {timeline}); + + if (!relatedTo.empty()) { + auto idx = idToIndex(relatedTo); + if (idx) + emit dataChanged(*idx, *idx); + } + }, + Qt::QueuedConnection); + + connect(this, + &EventStore::oldMessagesRetrieved, + this, + [this](const mtx::responses::Messages &res) { + uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res); + if (newFirst == first) + fetchMore(); + else { + emit beginInsertRows(toExternalIdx(newFirst), + toExternalIdx(this->first - 1)); + this->first = newFirst; + emit endInsertRows(); + emit fetchedMore(); + } + }, + Qt::QueuedConnection); connect(this, &EventStore::processPending, this, [this]() { if (!current_txn.empty()) { @@ -116,48 +114,46 @@ EventStore::EventStore(std::string room_id, QObject *) event->data); }); - connect( - this, - &EventStore::messageFailed, - this, - [this](std::string txn_id) { - if (current_txn == txn_id) { - current_txn_error_count++; - if (current_txn_error_count > 10) { - nhlog::ui()->debug("failing txn id '{}'", txn_id); - cache::client()->removePendingStatus(room_id_, txn_id); - current_txn_error_count = 0; - } - } - QTimer::singleShot(1000, this, [this]() { - nhlog::ui()->debug("timeout"); - this->current_txn = ""; - emit processPending(); - }); - }, - Qt::QueuedConnection); - - connect( - this, - &EventStore::messageSent, - this, - [this](std::string txn_id, std::string event_id) { - nhlog::ui()->debug("sent {}", txn_id); - - http::client()->read_event( - room_id_, event_id, [this, event_id](mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn( - "failed to read_event ({}, {})", room_id_, event_id); - } - }); - - cache::client()->removePendingStatus(room_id_, txn_id); - this->current_txn = ""; - this->current_txn_error_count = 0; - emit processPending(); - }, - Qt::QueuedConnection); + connect(this, + &EventStore::messageFailed, + this, + [this](std::string txn_id) { + if (current_txn == txn_id) { + current_txn_error_count++; + if (current_txn_error_count > 10) { + nhlog::ui()->debug("failing txn id '{}'", txn_id); + cache::client()->removePendingStatus(room_id_, txn_id); + current_txn_error_count = 0; + } + } + QTimer::singleShot(1000, this, [this]() { + nhlog::ui()->debug("timeout"); + this->current_txn = ""; + emit processPending(); + }); + }, + Qt::QueuedConnection); + + connect(this, + &EventStore::messageSent, + this, + [this](std::string txn_id, std::string event_id) { + nhlog::ui()->debug("sent {}", txn_id); + + http::client()->read_event( + room_id_, event_id, [this, event_id](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to read_event ({}, {})", room_id_, event_id); + } + }); + + cache::client()->removePendingStatus(room_id_, txn_id); + this->current_txn = ""; + this->current_txn_error_count = 0; + emit processPending(); + }, + Qt::QueuedConnection); } void @@ -245,6 +241,58 @@ EventStore::handleSync(const mtx::responses::Timeline &events) emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx)); } } + + // decrypting and checking some encrypted messages + if (auto encrypted = + std::get_if>( + &event)) { + auto event = decryptEvent({room_id_, encrypted->event_id}, *encrypted); + if (std::visit( + [](auto e) { return (e.sender != utils::localUser().toStdString()); }, + *event)) { + if (auto msg = std::get_if>(event)) { + last_verification_request_event = *msg; + } else if (auto msg = std::get_if>(event)) { + last_verification_cancel_event = *msg; + ChatPage::instance()->recievedDeviceVerificationCancel( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationAccept( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationKey( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationMac( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationReady( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationDone( + msg->content); + } else if (auto msg = std::get_if>(event)) { + ChatPage::instance()->recievedDeviceVerificationStart( + msg->content, msg->sender); + } + } + } + } + + if (last_verification_request_event.has_value()) { + if (last_verification_request_event.value().origin_server_ts > + last_verification_cancel_event.origin_server_ts) { + emit startDMVerification(last_verification_request_event.value()); + last_verification_request_event = {}; + } } } @@ -425,7 +473,8 @@ EventStore::decryptEvent(const IdIndex &idx, e.what()); dummy.content.body = tr("-- Decryption Error (failed to retrieve megolm keys from db) --", - "Placeholder, when the message can't be decrypted, because the DB access " + "Placeholder, when the message can't be decrypted, because the DB " + "access " "failed.") .toStdString(); return asCacheEntry(std::move(dummy)); @@ -437,7 +486,8 @@ EventStore::decryptEvent(const IdIndex &idx, e.what()); dummy.content.body = tr("-- Decryption Error (%1) --", - "Placeholder, when the message can't be decrypted. In this case, the Olm " + "Placeholder, when the message can't be decrypted. In this case, the " + "Olm " "decrytion returned an error, which is passed as %1.") .arg(e.what()) .toStdString(); @@ -470,11 +520,11 @@ EventStore::decryptEvent(const IdIndex &idx, return asCacheEntry(std::move(temp_events[0])); } - dummy.content.body = - tr("-- Encrypted Event (Unknown event type) --", - "Placeholder, when the message was decrypted, but we couldn't parse it, because " - "Nheko/mtxclient don't support that event type yet.") - .toStdString(); + dummy.content.body = tr("-- Encrypted Event (Unknown event type) --", + "Placeholder, when the message was decrypted, but we " + "couldn't parse it, because " + "Nheko/mtxclient don't support that event type yet.") + .toStdString(); return asCacheEntry(std::move(dummy)); } @@ -502,7 +552,8 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt) mtx::http::RequestErr err) { if (err) { nhlog::net()->error( - "Failed to retrieve event with id {}, which was " + "Failed to retrieve event with id {}, which " + "was " "requested to show the replyTo for event {}", relatedTo, id); diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h index b5c17d10..28d46e90 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h @@ -98,6 +98,8 @@ signals: void processPending(); void messageSent(std::string txn_id, std::string event_id); void messageFailed(std::string txn_id); + void startDMVerification( + mtx::events::RoomEvent &msg); public slots: void addPending(mtx::events::collections::TimelineEvents event); @@ -118,4 +120,10 @@ private: std::string current_txn; int current_txn_error_count = 0; + + // probably not the best way to do + std::optional> + last_verification_request_event; + mtx::events::RoomEvent + last_verification_cancel_event; }; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index adf207ac..809fe382 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -186,12 +186,11 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj , room_id_(room_id) , manager_(manager) { - connect( - this, - &TimelineModel::redactionFailed, - this, - [](const QString &msg) { emit ChatPage::instance()->showNotification(msg); }, - Qt::QueuedConnection); + connect(this, + &TimelineModel::redactionFailed, + this, + [](const QString &msg) { emit ChatPage::instance()->showNotification(msg); }, + Qt::QueuedConnection); connect(this, &TimelineModel::newMessageToSend, @@ -200,17 +199,17 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj Qt::QueuedConnection); connect(this, &TimelineModel::addPendingMessageToStore, &events, &EventStore::addPending); - connect( - &events, - &EventStore::dataChanged, - this, - [this](int from, int to) { - nhlog::ui()->debug( - "data changed {} to {}", events.size() - to - 1, events.size() - from - 1); - emit dataChanged(index(events.size() - to - 1, 0), - index(events.size() - from - 1, 0)); - }, - Qt::QueuedConnection); + connect(&events, + &EventStore::dataChanged, + this, + [this](int from, int to) { + nhlog::ui()->debug("data changed {} to {}", + events.size() - to - 1, + events.size() - from - 1); + emit dataChanged(index(events.size() - to - 1, 0), + index(events.size() - from - 1, 0)); + }, + Qt::QueuedConnection); connect(&events, &EventStore::beginInsertRows, this, [this](int from, int to) { int first = events.size() - to; @@ -232,6 +231,12 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage); connect( &events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); }); + connect(&events, + &EventStore::startDMVerification, + this, + [this](mtx::events::RoomEvent msg) { + ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this); + }); } QHash @@ -613,187 +618,6 @@ TimelineModel::updateLastMessage() } } -std::vector -TimelineModel::internalAddEvents( - const std::vector &timeline) -{ - std::vector ids; - for (auto e : timeline) { - QString id = QString::fromStdString(mtx::accessors::event_id(e)); - - if (this->events.contains(id)) { - this->events.insert(id, e); - int idx = idToIndex(id); - emit dataChanged(index(idx, 0), index(idx, 0)); - continue; - } - - QString txid = QString::fromStdString(mtx::accessors::transaction_id(e)); - if (this->pending.removeOne(txid)) { - this->events.insert(id, e); - this->events.remove(txid); - int idx = idToIndex(txid); - if (idx < 0) { - nhlog::ui()->warn("Received index out of range"); - continue; - } - eventOrder[idx] = id; - emit dataChanged(index(idx, 0), index(idx, 0)); - continue; - } - - if (auto redaction = - std::get_if>(&e)) { - QString redacts = QString::fromStdString(redaction->redacts); - auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts); - - auto event = events.value(redacts); - if (auto reaction = - std::get_if>( - &event)) { - QString reactedTo = - QString::fromStdString(reaction->content.relates_to.event_id); - reactions[reactedTo].removeReaction(*reaction); - int idx = idToIndex(reactedTo); - if (idx >= 0) - emit dataChanged(index(idx, 0), index(idx, 0)); - } - - if (redacted != eventOrder.end()) { - auto redactedEvent = std::visit( - [](const auto &ev) - -> mtx::events::RoomEvent { - mtx::events::RoomEvent - replacement = {}; - replacement.event_id = ev.event_id; - replacement.room_id = ev.room_id; - replacement.sender = ev.sender; - replacement.origin_server_ts = ev.origin_server_ts; - replacement.type = ev.type; - return replacement; - }, - e); - events.insert(redacts, redactedEvent); - - int row = (int)std::distance(eventOrder.begin(), redacted); - emit dataChanged(index(row, 0), index(row, 0)); - } - - continue; // don't insert redaction into timeline - } - - if (auto reaction = - std::get_if>(&e)) { - QString reactedTo = - QString::fromStdString(reaction->content.relates_to.event_id); - events.insert(id, e); - - // remove local echo - if (!txid.isEmpty()) { - auto rCopy = *reaction; - rCopy.event_id = txid.toStdString(); - reactions[reactedTo].removeReaction(rCopy); - } - - reactions[reactedTo].addReaction(room_id_.toStdString(), *reaction); - int idx = idToIndex(reactedTo); - if (idx >= 0) - emit dataChanged(index(idx, 0), index(idx, 0)); - continue; // don't insert reaction into timeline - } - - if (auto event = - std::get_if>(&e)) { - auto e_ = decryptEvent(*event).event; - auto encInfo = mtx::accessors::file(e_); - - if (encInfo) - emit newEncryptedImage(encInfo.value()); - - if (auto msg = std::get_if< - mtx::events::RoomEvent>( - &e_)) { - last_verification_request_event = *msg; - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>( - &e_)) { - last_verification_cancel_event = *msg; - ChatPage::instance()->recievedDeviceVerificationCancel( - msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>( - &e_)) { - ChatPage::instance()->recievedDeviceVerificationAccept( - msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>(&e_)) { - ChatPage::instance()->recievedDeviceVerificationKey(msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>(&e_)) { - ChatPage::instance()->recievedDeviceVerificationMac(msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>( - &e_)) { - ChatPage::instance()->recievedDeviceVerificationReady(msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>(&e_)) { - ChatPage::instance()->recievedDeviceVerificationDone(msg->content); - } - - if (auto msg = std::get_if< - mtx::events::RoomEvent>( - &e_)) { - ChatPage::instance()->recievedDeviceVerificationStart(msg->content, - msg->sender); - } - } - - this->events.insert(id, e); - ids.push_back(id); - - auto replyTo = mtx::accessors::in_reply_to_event(e); - auto qReplyTo = QString::fromStdString(replyTo); - if (!replyTo.empty() && !events.contains(qReplyTo)) { - http::client()->get_event( - this->room_id_.toStdString(), - replyTo, - [this, id, replyTo]( - const mtx::events::collections::TimelineEvents &timeline, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->error( - "Failed to retrieve event with id {}, which was " - "requested to show the replyTo for event {}", - replyTo, - id.toStdString()); - return; - } - emit eventFetched(id, timeline); - }); - } - } - - if (last_verification_request_event.origin_server_ts > - last_verification_cancel_event.origin_server_ts) { - ChatPage::instance()->recievedRoomDeviceVerificationRequest( - last_verification_request_event, this); - } - - return ids; -} - void TimelineModel::setCurrentIndex(int index) { @@ -979,15 +803,18 @@ TimelineModel::markEventsAsRead(const std::vector &event_ids) } } +template void -TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json content) +TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg) { const auto room_id = room_id_.toStdString(); using namespace mtx::events; using namespace mtx::identifiers; - json doc = {{"type", "m.room.message"}, {"content", content}, {"room_id", room_id}}; + json doc = { + {"type", to_string(msg.type)}, {"content", json(msg.content)}, {"room_id", room_id}}; + std::cout << doc.dump(2) << std::endl; try { // Check if we have already an outbound megolm session then we can use. @@ -995,7 +822,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con mtx::events::EncryptedEvent event; event.content = olm::encrypt_group_message(room_id, http::client()->device_id(), doc); - event.event_id = txn_id; + event.event_id = msg.event_id; event.room_id = room_id; event.sender = http::client()->user_id().to_string(); event.type = mtx::events::EventType::RoomEncrypted; @@ -1030,25 +857,26 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con const auto members = cache::roomMembers(room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); - auto keeper = std::make_shared([room_id, doc, txn_id, this]() { - try { - mtx::events::EncryptedEvent event; - event.content = olm::encrypt_group_message( - room_id, http::client()->device_id(), doc); - event.event_id = txn_id; - event.room_id = room_id; - event.sender = http::client()->user_id().to_string(); - event.type = mtx::events::EventType::RoomEncrypted; - event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); - - emit this->addPendingMessageToStore(event); - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to save megolm outbound session: {}", - e.what()); - emit ChatPage::instance()->showNotification( - tr("Failed to encrypt event, sending aborted!")); - } - }); + auto keeper = + std::make_shared([room_id, doc, txn_id = msg.event_id, this]() { + try { + mtx::events::EncryptedEvent event; + event.content = olm::encrypt_group_message( + room_id, http::client()->device_id(), doc); + event.event_id = txn_id; + event.room_id = room_id; + event.sender = http::client()->user_id().to_string(); + event.type = mtx::events::EventType::RoomEncrypted; + event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); + + emit this->addPendingMessageToStore(event); + } catch (const lmdb::error &e) { + nhlog::db()->critical( + "failed to save megolm outbound session: {}", e.what()); + emit ChatPage::instance()->showNotification( + tr("Failed to encrypt event, sending aborted!")); + } + }); mtx::requests::QueryKeys req; for (const auto &member : members) @@ -1056,7 +884,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con http::client()->query_keys( req, - [keeper = std::move(keeper), megolm_payload, txn_id, this]( + [keeper = std::move(keeper), megolm_payload, txn_id = msg.event_id, this]( const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", @@ -1265,6 +1093,40 @@ struct SendMessageVisitor : model_(model) {} + void operator()(const mtx::events::RoomEvent &msg) + { + emit model_->updateFlowEventId(msg.event_id); + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + void operator()(const mtx::events::RoomEvent &msg) + { + model_->sendEncryptedMessage(msg); + } + // Do-nothing operator for all unhandled events template void operator()(const mtx::events::Event &) @@ -1280,7 +1142,7 @@ struct SendMessageVisitor if (encInfo) emit model_->newEncryptedImage(encInfo.value()); - model_->sendEncryptedMessage(msg.event_id, nlohmann::json(msg.content)); + model_->sendEncryptedMessage(msg); } else { emit model_->addPendingMessageToStore(msg); } @@ -1300,20 +1162,6 @@ struct SendMessageVisitor TimelineModel *model_; }; -void -TimelineModel::processOnePendingMessage() -{ - if (pending.isEmpty()) - return; - - QString txn_id_qstr = pending.first(); - - auto event = events.value(txn_id_qstr); - std::cout << "Inside the process one pending message" << std::endl; - std::cout << std::visit([](auto &e) { return json(e); }, event).dump(2) << std::endl; - std::visit(SendMessageVisitor{txn_id_qstr, this}, event); -} - void TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) { @@ -1359,18 +1207,7 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) event); } - internalAddEvents({event}); - - QString txn_id_qstr = QString::fromStdString(mtx::accessors::event_id(event)); - pending.push_back(txn_id_qstr); - if (!std::get_if>(&event)) { - beginInsertRows(QModelIndex(), 0, 0); - this->eventOrder.insert(this->eventOrder.begin(), txn_id_qstr); - endInsertRows(); - } - updateLastMessage(); - - emit nextPendingMessage(); + std::visit(SendMessageVisitor{this}, event); } bool diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 1b6f999e..fb9921d3 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -9,12 +9,8 @@ #include #include "CacheCryptoStructs.h" -<<<<<<< HEAD #include "EventStore.h" -======= -#include "ReactionsModel.h" #include "ui/UserProfile.h" ->>>>>>> Refactor UserProfile namespace mtx::http { using RequestErr = const std::optional &; @@ -271,8 +267,13 @@ signals: void openProfile(UserProfile *profile); + void newMessageToSend(mtx::events::collections::TimelineEvents event); + void addPendingMessageToStore(mtx::events::collections::TimelineEvents event); + void updateFlowEventId(std::string event_id); + private: - void sendEncryptedMessage(const std::string txn_id, nlohmann::json content); + template + void sendEncryptedMessage(mtx::events::RoomEvent msg); void handleClaimedKeys(std::shared_ptr keeper, const std::map &room_key, const std::map &pks, @@ -297,11 +298,6 @@ private: std::vector typingUsers_; TimelineViewManager *manager_; - // probably not the best way to do - mtx::events::RoomEvent - last_verification_request_event; - mtx::events::RoomEvent - last_verification_cancel_event; friend struct SendMessageVisitor; }; diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 3499384c..1eaa9d27 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -5,13 +5,15 @@ #include "Logging.h" #include "Utils.h" #include "mtx/responses/crypto.hpp" +#include "timeline/TimelineModel.h" #include // only for debugging -UserProfile::UserProfile(QString roomid, QString userid, QObject *parent) +UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent) : QObject(parent) , roomid_(roomid) , userid_(userid) + , model(parent) { fetchDeviceList(this->userid_); } @@ -185,27 +187,43 @@ UserProfile::startChat() emit ChatPage::instance()->createRoom(req); } -void -UserProfile::verifyUser() +DeviceVerificationFlow * +UserProfile::createFlow(bool isVerifyUser) { - std::cout << "Checking if to start to device verification or room message verification" - << std::endl; - 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(), - (this->userid()).toStdString()) != room_members.end()) { - std::cout << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id + if (!isVerifyUser) + return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + else { + std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION" + << std::endl; + 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(), + (this->userid()).toStdString()) != + room_members.end()) { + std::cout + << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id << std::endl; - return; + if (this->roomid_.toStdString() == room_id) { + auto newflow = new DeviceVerificationFlow( + this, DeviceVerificationFlow::Type::RoomMsg); + newflow->setModel(this->model); + return (std::move(newflow)); + } else { + std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY " + "NOT IN THAT ROOM : " + << room_id << std::endl; + } + } } } - } - std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; + std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; + return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + } } \ No newline at end of file diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 3f9cbe6f..3d0d2981 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -20,6 +20,7 @@ Q_ENUM_NS(Status) } class DeviceVerificationFlow; +class TimelineModel; class DeviceInfo { @@ -83,7 +84,7 @@ class UserProfile : public QObject Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) public: - UserProfile(QString roomid, QString userid, QObject *parent = 0); + UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr); DeviceInfoModel *deviceList(); @@ -92,18 +93,19 @@ public: QString avatarUrl(); bool getUserStatus(); + Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser); Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat(); - Q_INVOKABLE void verifyUser(); private: QString roomid_, userid_; std::optional cross_verified; DeviceInfoModel deviceList_; bool isUserVerified = false; + TimelineModel *model; void callback_fn(const mtx::responses::QueryKeys &res, mtx::http::RequestErr err, -- cgit 1.5.1 From 8a4bd37fead20e24876ec9ce703cabb041ec67ba Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 18 Aug 2020 11:29:02 +0530 Subject: [WIP] Room Verification Works! --- resources/qml/UserProfile.qml | 2 +- src/DeviceVerificationFlow.cpp | 71 ++++++++++++++++++------------------ src/DeviceVerificationFlow.h | 6 +-- src/Olm.cpp | 18 ++++++--- src/timeline/EventStore.cpp | 22 +++++++++-- src/timeline/EventStore.h | 1 + src/timeline/TimelineModel.cpp | 7 ++-- src/timeline/TimelineViewManager.cpp | 71 +++++++++++++++++++----------------- src/ui/UserProfile.cpp | 5 ++- 9 files changed, 114 insertions(+), 89 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 9b53ff35..115a73c4 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -14,7 +14,7 @@ ApplicationWindow{ height: 650 width: 420 modality: Qt.WindowModal - Layout.alignment: Qt.AlignHCenter + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter palette: colors Connections{ diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 5069ff9d..8c230887 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -8,24 +8,31 @@ #include #include -#include - static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes namespace msgs = mtx::events::msg; -DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type flow_type) +DeviceVerificationFlow::DeviceVerificationFlow(QObject *, + DeviceVerificationFlow::Type flow_type, + TimelineModel *model) : type(flow_type) + , model_(model) { timeout = new QTimer(this); timeout->setSingleShot(true); this->sas = olm::client()->sas_init(); this->isMacVerified = false; - connect(this->model_, - &TimelineModel::updateFlowEventId, - this, - [this](std::string event_id) { this->relation.in_reply_to.event_id = event_id; }); + if (model) { + connect(this->model_, + &TimelineModel::updateFlowEventId, + this, + [this](std::string event_id) { + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = event_id; + this->transaction_id = event_id; + }); + } connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); @@ -42,8 +49,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } if ((std::find(msg.key_agreement_protocols.begin(), @@ -69,8 +75,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow DeviceVerificationFlow::Error::UnknownMethod); return; } - this->acceptVerificationRequest(); this->canonical_json = nlohmann::json(msg); + this->acceptVerificationRequest(); } else { this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); } @@ -84,8 +90,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") && @@ -116,8 +121,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } emit verificationCanceled(); @@ -131,8 +135,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } this->sas->set_their_key(msg.key); @@ -157,6 +160,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow } else if (this->method == DeviceVerificationFlow::Method::Decimal) { this->sasList = this->sas->generate_bytes_decimal(info); } + if (this->sender == false) { emit this->verificationRequestAccepted(this->method); this->sendVerificationKey(); @@ -181,8 +185,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } std::string info = "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + @@ -227,12 +230,11 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - // this is just a workaround - this->relation.in_reply_to.event_id = - msg.relates_to.value().in_reply_to.event_id; - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; + else { + this->deviceId = QString::fromStdString(msg.from_device); + } } this->startVerificationRequest(); }); @@ -245,8 +247,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow if (msg.transaction_id.value() != this->transaction_id) return; } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().in_reply_to.event_id != - this->relation.in_reply_to.event_id) + if (msg.relates_to.value().event_id != this->relation.event_id) return; } this->acceptDevice(); @@ -297,12 +298,6 @@ DeviceVerificationFlow::getSasList() return this->sasList; } -void -DeviceVerificationFlow::setModel(TimelineModel *&model) -{ - this->model_ = model; -} - void DeviceVerificationFlow::setTransactionId(QString transaction_id_) { @@ -351,15 +346,17 @@ DeviceVerificationFlow::setType(Type type) void DeviceVerificationFlow::setSender(bool sender_) { - this->sender = sender_; - this->transaction_id = http::client()->generate_txn_id(); + this->sender = sender_; + if (this->sender) + this->transaction_id = http::client()->generate_txn_id(); } void DeviceVerificationFlow::setEventId(std::string event_id) { - this->relation.in_reply_to.event_id = event_id; - this->transaction_id = event_id; + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = event_id; + this->transaction_id = event_id; } //! accepts a verification @@ -476,7 +473,8 @@ DeviceVerificationFlow::startVerificationRequest() static_cast(err->status_code)); }); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; + req.relates_to = this->relation; + this->canonical_json = nlohmann::json(req); (model_)->sendMessage(req); } } @@ -562,6 +560,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; (model_)->sendMessage(req); + this->deleteLater(); } // TODO : Handle Blocking user better diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 1ad3b1d0..4c3e5171 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -52,7 +52,8 @@ public: DeviceVerificationFlow( QObject *parent = nullptr, - DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice); + DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice, + TimelineModel *model = nullptr); // getters QString getTransactionId(); QString getUserId(); @@ -62,7 +63,6 @@ public: std::vector getSasList(); bool getSender(); // setters - void setModel(TimelineModel *&model); void setTransactionId(QString transaction_id_); void setUserId(QString userID); void setDeviceId(QString deviceID); @@ -127,5 +127,5 @@ private: std::optional room_id; std::optional event_id; TimelineModel *model_; - mtx::common::ReplyRelatesTo relation; + mtx::common::ReactionRelatesTo relation; }; diff --git a/src/Olm.cpp b/src/Olm.cpp index ff6ea2f4..48439fa3 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -211,10 +211,15 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id, // relations shouldn't be encrypted... mtx::common::ReplyRelatesTo relation; + mtx::common::ReactionRelatesTo r_relation; + if (body["content"].contains("m.relates_to") && body["content"]["m.relates_to"].contains("m.in_reply_to")) { relation = body["content"]["m.relates_to"]; body["content"].erase("m.relates_to"); + } else if (body["content"]["m.relates_to"].contains("event_id")) { + r_relation = body["content"]["m.relates_to"]; + body["content"].erase("m.relates_to"); } // Always check before for existence. @@ -223,12 +228,13 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id, // Prepare the m.room.encrypted event. msg::Encrypted data; - data.ciphertext = std::string((char *)payload.data(), payload.size()); - data.sender_key = olm::client()->identity_keys().curve25519; - data.session_id = res.data.session_id; - data.device_id = device_id; - data.algorithm = MEGOLM_ALGO; - data.relates_to = relation; + data.ciphertext = std::string((char *)payload.data(), payload.size()); + data.sender_key = olm::client()->identity_keys().curve25519; + data.session_id = res.data.session_id; + data.device_id = device_id; + data.algorithm = MEGOLM_ALGO; + data.relates_to = relation; + data.r_relates_to = r_relation; auto message_index = olm_outbound_group_session_message_index(res.session); nhlog::crypto()->debug("next message_index {}", message_index); diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 208b20e2..b210e157 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -95,8 +95,8 @@ EventStore::EventStore(std::string room_id, QObject *) room_id_, txn_id, e.content, - [this, txn_id](const mtx::responses::EventId &event_id, - mtx::http::RequestErr err) { + [this, txn_id, e](const mtx::responses::EventId &event_id, + mtx::http::RequestErr err) { if (err) { const int status_code = static_cast(err->status_code); @@ -108,7 +108,21 @@ EventStore::EventStore(std::string room_id, QObject *) emit messageFailed(txn_id); return; } + emit messageSent(txn_id, event_id.event_id.to_string()); + if constexpr (mtx::events::message_content_to_type< + decltype(e.content)> == + mtx::events::EventType::RoomEncrypted) { + auto event = + decryptEvent({room_id_, e.event_id}, e); + if (auto dec = + std::get_if>(event)) { + emit updateFlowEventId( + event_id.event_id.to_string()); + } + } }); }, event->data); @@ -318,12 +332,12 @@ EventStore::reactions(const std::string &event_id) if (auto reaction = std::get_if>( related_event)) { - auto &agg = aggregation[reaction->content.relates_to.key]; + auto &agg = aggregation[reaction->content.relates_to.key.value()]; if (agg.count == 0) { Reaction temp{}; temp.key_ = - QString::fromStdString(reaction->content.relates_to.key); + QString::fromStdString(reaction->content.relates_to.key.value()); reactions.push_back(temp); } diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h index 28d46e90..55a66f49 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h @@ -100,6 +100,7 @@ signals: void messageFailed(std::string txn_id); void startDMVerification( mtx::events::RoomEvent &msg); + void updateFlowEventId(std::string event_id); public slots: void addPending(mtx::events::collections::TimelineEvents event); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 809fe382..dc5eb8cc 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -22,8 +22,6 @@ #include "Utils.h" #include "dialogs/RawMessage.h" -#include - Q_DECLARE_METATYPE(QModelIndex) namespace std { @@ -237,6 +235,9 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj [this](mtx::events::RoomEvent msg) { ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this); }); + connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { + this->updateFlowEventId(event_id); + }); } QHash @@ -814,7 +815,6 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg) json doc = { {"type", to_string(msg.type)}, {"content", json(msg.content)}, {"room_id", room_id}}; - std::cout << doc.dump(2) << std::endl; try { // Check if we have already an outbound megolm session then we can use. @@ -1095,7 +1095,6 @@ struct SendMessageVisitor void operator()(const mtx::events::RoomEvent &msg) { - emit model_->updateFlowEventId(msg.event_id); model_->sendEncryptedMessage(msg); } void operator()(const mtx::events::RoomEvent &msg) diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index c16e09d1..fb4a094e 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -194,13 +194,12 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin [this](const mtx::events::RoomEvent &message, TimelineModel *model) { if (!(this->dvList->exist(QString::fromStdString(message.event_id)))) { - auto flow = - new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::RoomMsg); + auto flow = new DeviceVerificationFlow( + this, DeviceVerificationFlow::Type::RoomMsg, model); if (std::find(message.content.methods.begin(), message.content.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != message.content.methods.end()) { - flow->setModel(model); flow->setEventId(message.event_id); emit newDeviceVerificationRequest( std::move(flow), @@ -241,42 +240,48 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &ChatPage::recievedDeviceVerificationStart, this, [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { - if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) { - auto flow = new DeviceVerificationFlow(this); - flow->canonical_json = nlohmann::json(msg); - if ((std::find(msg.key_agreement_protocols.begin(), - msg.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.key_agreement_protocols.end()) && - (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != - msg.hashes.end()) && - (std::find(msg.message_authentication_codes.begin(), - msg.message_authentication_codes.end(), - "hmac-sha256") != - msg.message_authentication_codes.end())) { - if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.short_authentication_string.end()) { - flow->setMethod(DeviceVerificationFlow::Method::Emoji); - } else if (std::find(msg.short_authentication_string.begin(), + if (msg.transaction_id.has_value()) { + if (!(this->dvList->exist( + QString::fromStdString(msg.transaction_id.value())))) { + auto flow = new DeviceVerificationFlow(this); + flow->canonical_json = nlohmann::json(msg); + if ((std::find(msg.key_agreement_protocols.begin(), + msg.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != + msg.key_agreement_protocols.end()) && + (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != + msg.hashes.end()) && + (std::find(msg.message_authentication_codes.begin(), + msg.message_authentication_codes.end(), + "hmac-sha256") != + msg.message_authentication_codes.end())) { + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.short_authentication_string.end()) { + flow->setMethod( + DeviceVerificationFlow::Method::Emoji); + } else if (std::find( + msg.short_authentication_string.begin(), msg.short_authentication_string.end(), mtx::events::msg::SASMethods::Decimal) != - msg.short_authentication_string.end()) { - flow->setMethod(DeviceVerificationFlow::Method::Decimal); + msg.short_authentication_string.end()) { + flow->setMethod( + DeviceVerificationFlow::Method::Decimal); + } else { + flow->cancelVerification( + DeviceVerificationFlow::Error::UnknownMethod); + return; + } + emit newDeviceVerificationRequest( + std::move(flow), + QString::fromStdString(msg.transaction_id.value()), + QString::fromStdString(sender), + QString::fromStdString(msg.from_device)); } else { flow->cancelVerification( DeviceVerificationFlow::Error::UnknownMethod); - return; } - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(msg.transaction_id.value()), - QString::fromStdString(sender), - QString::fromStdString(msg.from_device)); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); } } }); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 1eaa9d27..87eae001 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -211,8 +211,9 @@ UserProfile::createFlow(bool isVerifyUser) << std::endl; if (this->roomid_.toStdString() == room_id) { auto newflow = new DeviceVerificationFlow( - this, DeviceVerificationFlow::Type::RoomMsg); - newflow->setModel(this->model); + this, + DeviceVerificationFlow::Type::RoomMsg, + this->model); return (std::move(newflow)); } else { std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY " -- cgit 1.5.1 From 1d299951b61390c381013ca0503fb09df548c6ec Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Mon, 24 Aug 2020 13:56:50 +0530 Subject: Cache Fix --- src/Cache.cpp | 46 ++++++------ src/CacheCryptoStructs.h | 5 +- src/Cache_p.h | 2 + src/ChatPage.cpp | 35 ++++++++++ src/ChatPage.h | 3 + src/ui/UserProfile.cpp | 177 +++++++++++++++++++++++++++++------------------ 6 files changed, 181 insertions(+), 87 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 8cee3453..cff0029e 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -111,6 +111,24 @@ Cache::Cache(const QString &userId, QObject *parent) , localUserId_{userId} { setup(); + connect(this, + &Cache::updateUserCacheFlag, + this, + [this](const std::string &user_id) { + std::optional cache_ = getUserCache(user_id); + if (cache_.has_value()) { + cache_.value().isUpdated = false; + setUserCache(user_id, cache_.value()); + } else { + setUserCache(user_id, UserCache{}); + } + }, + Qt::QueuedConnection); + connect(this, + &Cache::deleteLeftUsers, + this, + [this](const std::string &user_id) { deleteUserCache(user_id); }, + Qt::QueuedConnection); } void @@ -1011,7 +1029,7 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); - // updateUserCache(res.device_lists); + updateUserCache(res.device_lists); removeLeftRooms(txn, res.rooms.leave); @@ -2889,13 +2907,15 @@ Cache::statusMessage(const std::string &user_id) void to_json(json &j, const UserCache &info) { - j["keys"] = info.keys; + j["keys"] = info.keys; + j["isUpdated"] = info.isUpdated; } void from_json(const json &j, UserCache &info) { - info.keys = j.at("keys").get(); + info.keys = j.at("keys").get(); + info.isUpdated = j.at("isUpdated").get(); } std::optional @@ -2935,26 +2955,12 @@ Cache::setUserCache(const std::string &user_id, const UserCache &body) void Cache::updateUserCache(const mtx::responses::DeviceLists body) { - for (auto user_id : body.changed) { - mtx::requests::QueryKeys req; - req.device_keys[user_id] = {}; - - http::client()->query_keys( - req, - [user_id, this](const mtx::responses::QueryKeys res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - - setUserCache(user_id, UserCache{std::move(res)}); - }); + for (std::string user_id : body.changed) { + emit updateUserCacheFlag(user_id); } for (std::string user_id : body.left) { - deleteUserCache(user_id); + emit deleteLeftUsers(user_id); } } diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index ba746f59..1dde21ce 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -71,9 +71,12 @@ struct UserCache { //! map of public key key_ids and their public_key mtx::responses::QueryKeys keys; + //! if the current cache is updated or not + bool isUpdated = false; - UserCache(mtx::responses::QueryKeys res) + UserCache(mtx::responses::QueryKeys res, bool isUpdated_ = false) : keys(res) + , isUpdated(isUpdated_) {} UserCache() {} }; diff --git a/src/Cache_p.h b/src/Cache_p.h index f75b0f4e..174090a9 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -269,6 +269,8 @@ signals: void newReadReceipts(const QString &room_id, const std::vector &event_ids); void roomReadStatus(const std::map &status); void removeNotification(const QString &room_id, const QString &event_id); + void updateUserCacheFlag(const std::string &user_id); + void deleteLeftUsers(const std::string &user_id); private: //! Save an invited room. diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index b97b6b30..f8cb31a2 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -52,6 +52,8 @@ #include "blurhash.hpp" +#include // only for debugging + // TODO: Needs to be updated with an actual secret. static const std::string STORAGE_SECRET_KEY("secret"); @@ -1446,3 +1448,36 @@ ChatPage::initiateLogout() emit showOverlayProgressBar(); } + +void +ChatPage::query_keys( + const mtx::requests::QueryKeys &req, + std::function cb) +{ + std::string user_id = req.device_keys.begin()->first; + auto cache_ = cache::getUserCache(user_id); + + if (cache_.has_value()) { + if (cache_.value().isUpdated) { + cb(cache_.value().keys, {}); + } else { + http::client()->query_keys( + req, + [cb, user_id](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + std::cout << "Over here " << user_id << std::endl; + cache::setUserCache(std::move(user_id), + std::move(UserCache{res, true})); + cb(res, err); + }); + } + } else { + http::client()->query_keys(req, cb); + } +} diff --git a/src/ChatPage.h b/src/ChatPage.h index 72adfe19..10801342 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -89,6 +89,9 @@ public: //! Show the room/group list (if it was visible). void showSideBars(); void initiateLogout(); + void query_keys( + const mtx::requests::QueryKeys &req, + std::function cb); void focusMessageInput(); QString status() const; diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 87eae001..2426fe6c 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -85,79 +85,124 @@ UserProfile::getUserStatus() } void -UserProfile::callback_fn(const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err, - std::string user_id) +UserProfile::fetchDeviceList(const QString &userID) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } + auto localUser = utils::localUser(); - if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + ChatPage::instance()->query_keys( + req, + [user_id = userID.toStdString(), local_user_id = localUser.toStdString(), this]( + const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } - auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; - auto device_verified = cache::getVerifiedCache(user_id); - - for (const auto &d : devices) { - auto device = d.second; - - // TODO: Verify signatures and ignore those that don't pass. - verification::Status verified = verification::Status::UNVERIFIED; - isUserVerified = device_verified->is_user_verified; - if (device_verified.has_value()) { - if (std::find(device_verified->cross_verified.begin(), - device_verified->cross_verified.end(), - d.first) != device_verified->cross_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_verified.begin(), - device_verified->device_verified.end(), - d.first) != device_verified->device_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_blocked.begin(), - device_verified->device_blocked.end(), - d.first) != device_verified->device_blocked.end()) - verified = verification::Status::BLOCKED; - } + if (res.device_keys.empty() || + (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } - deviceInfo.push_back( - {QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name), - verified}); - } + auto devices = res.device_keys.at(user_id); + std::vector deviceInfo; + auto device_verified = cache::getVerifiedCache(user_id); - std::sort( - deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); + for (const auto &d : devices) { + auto device = d.second; - this->deviceList_.queueReset(std::move(deviceInfo)); -} + // TODO: Verify signatures and ignore those that don't pass. + verification::Status verified = verification::Status::UNVERIFIED; + isUserVerified = device_verified->is_user_verified; + if (device_verified.has_value()) { + if (std::find(device_verified->cross_verified.begin(), + device_verified->cross_verified.end(), + d.first) != device_verified->cross_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_verified.begin(), + device_verified->device_verified.end(), + d.first) != device_verified->device_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_blocked.begin(), + device_verified->device_blocked.end(), + d.first) != device_verified->device_blocked.end()) + verified = verification::Status::BLOCKED; + } -void -UserProfile::fetchDeviceList(const QString &userID) -{ - auto localUser = utils::localUser(); - auto user_cache = cache::getUserCache(userID.toStdString()); - - if (user_cache.has_value()) { - this->callback_fn(user_cache->keys, {}, userID.toStdString()); - } else { - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); - } + deviceInfo.push_back( + {QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name), + verified}); + } + + // Finding if the User is Verified or not based on the Signatures + mtx::requests::QueryKeys req; + req.device_keys[local_user_id] = {}; + + ChatPage::instance()->query_keys( + req, + [&local_user_id, &user_id, other_res = res, this]( + const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + using namespace mtx; + + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + std::optional lmk, lsk, luk, mk, sk, uk; + + if (res.master_keys.find(local_user_id) != res.master_keys.end()) + lmk = res.master_keys.at(local_user_id); + if (res.user_signing_keys.find(local_user_id) != + res.user_signing_keys.end()) + luk = res.user_signing_keys.at(local_user_id); + if (res.self_signing_keys.find(local_user_id) != + res.self_signing_keys.end()) + lsk = res.self_signing_keys.at(local_user_id); + if (other_res.master_keys.find(user_id) != other_res.master_keys.end()) + mk = other_res.master_keys.at(user_id); + if (other_res.user_signing_keys.find(user_id) != + other_res.user_signing_keys.end()) + uk = other_res.user_signing_keys.at(user_id); + if (other_res.self_signing_keys.find(user_id) != + other_res.self_signing_keys.end()) + sk = other_res.self_signing_keys.at(user_id); + + // First checking if the user is verified + if (lmk.has_value() && luk.has_value()) { + bool is_user_verified = false; + for (auto sign_key : lmk.value().keys) { + if (!luk.value().signatures.empty()) { + for (auto signature : + luk.value().signatures.at(local_user_id)) { + is_user_verified = + is_user_verified || + (olm::client()->ed25519_verify_sig( + sign_key.second, + json(luk.value()), + signature.second)); + } + } + } + std::cout << (isUserVerified ? "Yes" : "No") << std::endl; + } + }); + + std::sort(deviceInfo.begin(), + deviceInfo.end(), + [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); + + this->deviceList_.queueReset(std::move(deviceInfo)); + }); } void -- cgit 1.5.1 From 19cfd08a554e20862c1187148982d542e311411d Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 25 Aug 2020 15:41:27 +0530 Subject: Verify signatures and find trusted devices --- src/ui/UserProfile.cpp | 181 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 117 insertions(+), 64 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 2426fe6c..48a3ffa3 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -93,8 +93,8 @@ UserProfile::fetchDeviceList(const QString &userID) req.device_keys[userID.toStdString()] = {}; ChatPage::instance()->query_keys( req, - [user_id = userID.toStdString(), local_user_id = localUser.toStdString(), this]( - const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", err->matrix_error.errcode, @@ -108,46 +108,16 @@ UserProfile::fetchDeviceList(const QString &userID) return; } - auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; - auto device_verified = cache::getVerifiedCache(user_id); - - for (const auto &d : devices) { - auto device = d.second; - - // TODO: Verify signatures and ignore those that don't pass. - verification::Status verified = verification::Status::UNVERIFIED; - isUserVerified = device_verified->is_user_verified; - if (device_verified.has_value()) { - if (std::find(device_verified->cross_verified.begin(), - device_verified->cross_verified.end(), - d.first) != device_verified->cross_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_verified.begin(), - device_verified->device_verified.end(), - d.first) != device_verified->device_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_blocked.begin(), - device_verified->device_blocked.end(), - d.first) != device_verified->device_blocked.end()) - verified = verification::Status::BLOCKED; - } - - deviceInfo.push_back( - {QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name), - verified}); - } - // Finding if the User is Verified or not based on the Signatures mtx::requests::QueryKeys req; - req.device_keys[local_user_id] = {}; + req.device_keys[utils::localUser().toStdString()] = {}; ChatPage::instance()->query_keys( req, - [&local_user_id, &user_id, other_res = res, this]( - const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + [user_id, other_res = res, this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { using namespace mtx; + std::string local_user_id = utils::localUser().toStdString(); if (err) { nhlog::net()->warn("failed to query device keys: {},{}", @@ -156,52 +126,135 @@ UserProfile::fetchDeviceList(const QString &userID) return; } + if (res.device_keys.empty() || + (res.device_keys.find(local_user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + std::vector deviceInfo; + auto devices = other_res.device_keys.at(user_id); + auto device_verified = cache::getVerifiedCache(user_id); + + if (device_verified.has_value()) { + isUserVerified = device_verified.value().is_user_verified; + } + std::optional lmk, lsk, luk, mk, sk, uk; - if (res.master_keys.find(local_user_id) != res.master_keys.end()) + if (!res.master_keys.empty()) lmk = res.master_keys.at(local_user_id); - if (res.user_signing_keys.find(local_user_id) != - res.user_signing_keys.end()) + if (!res.user_signing_keys.empty()) luk = res.user_signing_keys.at(local_user_id); - if (res.self_signing_keys.find(local_user_id) != - res.self_signing_keys.end()) + if (!res.self_signing_keys.empty()) lsk = res.self_signing_keys.at(local_user_id); - if (other_res.master_keys.find(user_id) != other_res.master_keys.end()) + if (!other_res.master_keys.empty()) mk = other_res.master_keys.at(user_id); - if (other_res.user_signing_keys.find(user_id) != - other_res.user_signing_keys.end()) + if (!other_res.user_signing_keys.empty()) uk = other_res.user_signing_keys.at(user_id); - if (other_res.self_signing_keys.find(user_id) != - other_res.self_signing_keys.end()) + if (!other_res.self_signing_keys.empty()) sk = other_res.self_signing_keys.at(user_id); // First checking if the user is verified - if (lmk.has_value() && luk.has_value()) { - bool is_user_verified = false; - for (auto sign_key : lmk.value().keys) { - if (!luk.value().signatures.empty()) { - for (auto signature : - luk.value().signatures.at(local_user_id)) { - is_user_verified = - is_user_verified || + if (luk.has_value() && mk.has_value()) { + // iterating through the public key of local user_signing keys + for (auto sign_key : luk.value().keys) { + // checking if the signatures are empty as "at" could + // cause exceptions + if (!mk.value().signatures.empty()) { + auto signs = + mk.value().signatures.at(local_user_id); + try { + isUserVerified = + isUserVerified || (olm::client()->ed25519_verify_sig( sign_key.second, - json(luk.value()), - signature.second)); + json(mk.value()), + signs.at(sign_key.first))); + } catch (std::out_of_range) { + isUserVerified = + isUserVerified || false; } } } - std::cout << (isUserVerified ? "Yes" : "No") << std::endl; } - }); - std::sort(deviceInfo.begin(), - deviceInfo.end(), - [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); + for (const auto &d : devices) { + auto device = d.second; + verification::Status verified = + verification::Status::UNVERIFIED; + + if (device_verified.has_value()) { + if (std::find(device_verified->cross_verified.begin(), + device_verified->cross_verified.end(), + d.first) != + device_verified->cross_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_verified.begin(), + device_verified->device_verified.end(), + d.first) != + device_verified->device_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_blocked.begin(), + device_verified->device_blocked.end(), + d.first) != + device_verified->device_blocked.end()) + verified = verification::Status::BLOCKED; + } else if (isUserVerified) { + device_verified = DeviceVerifiedCache{}; + } - this->deviceList_.queueReset(std::move(deviceInfo)); + // won't check for already verified devices + if (verified != verification::Status::VERIFIED && + isUserVerified) { + if ((sk.has_value()) && (!device.signatures.empty())) { + for (auto sign_key : sk.value().keys) { + auto signs = + device.signatures.at(user_id); + try { + if (olm::client() + ->ed25519_verify_sig( + sign_key.second, + json(device), + signs.at( + sign_key.first))) { + verified = + verification::Status:: + VERIFIED; + device_verified.value() + .cross_verified + .push_back(d.first); + } + } catch (std::out_of_range) { + } + } + } + } + + if (device_verified.has_value()) { + device_verified.value().is_user_verified = + isUserVerified; + cache::setVerifiedCache(user_id, + device_verified.value()); + } + + deviceInfo.push_back( + {QString::fromStdString(d.first), + QString::fromStdString( + device.unsigned_info.device_display_name), + verified}); + } + + std::cout << (isUserVerified ? "Yes" : "No") << std::endl; + + std::sort(deviceInfo.begin(), + deviceInfo.end(), + [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); + + this->deviceList_.queueReset(std::move(deviceInfo)); + }); }); } -- cgit 1.5.1 From 0d1dd29b19a3f4459b036bd63f03c518e000d71f Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sat, 29 Aug 2020 13:37:51 +0530 Subject: Small Fixes --- resources/qml/device-verification/DeviceVerification.qml | 1 - src/ChatPage.cpp | 3 --- src/DeviceVerificationFlow.cpp | 16 +++++++++++++++- src/timeline/EventStore.cpp | 7 +++++++ src/ui/UserProfile.cpp | 9 +++++---- 5 files changed, 27 insertions(+), 9 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index f40a7b8f..94cb1e33 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -579,7 +579,6 @@ ApplicationWindow { onClicked: { dialog.close(); deviceVerificationList.remove(flow.tranId); - delete flow; } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index f8cb31a2..909d81eb 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -52,8 +52,6 @@ #include "blurhash.hpp" -#include // only for debugging - // TODO: Needs to be updated with an actual secret. static const std::string STORAGE_SECRET_KEY("secret"); @@ -1471,7 +1469,6 @@ ChatPage::query_keys( static_cast(err->status_code)); return; } - std::cout << "Over here " << user_id << std::endl; cache::setUserCache(std::move(user_id), std::move(UserCache{res, true})); cb(res, err); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 8c230887..dd828421 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -7,6 +7,7 @@ #include #include +#include static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -75,7 +76,14 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Error::UnknownMethod); return; } - this->canonical_json = nlohmann::json(msg); + if (!sender) + this->canonical_json = nlohmann::json(msg); + else { + if (utils::localUser().toStdString() < + this->toClient.to_string()) { + this->canonical_json = nlohmann::json(msg); + } + } this->acceptVerificationRequest(); } else { this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); @@ -124,6 +132,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } + this->deleteLater(); emit verificationCanceled(); }); @@ -226,6 +235,11 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, &ChatPage::recievedDeviceVerificationReady, this, [this](const mtx::events::msg::KeyVerificationReady &msg) { + if (!sender) { + this->deleteLater(); + emit verificationCanceled(); + return; + } if (msg.transaction_id.has_value()) { if (msg.transaction_id.value() != this->transaction_id) return; diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index b210e157..6326e98e 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -298,6 +298,13 @@ EventStore::handleSync(const mtx::responses::Timeline &events) msg->content, msg->sender); } } + // only the key.verification.ready sent by localuser's other device is of + // significance as it is used for detecting accepted request + if (auto msg = std::get_if< + mtx::events::RoomEvent>( + event)) { + ChatPage::instance()->recievedDeviceVerificationReady(msg->content); + } } } diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 48a3ffa3..59be3464 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -161,16 +161,17 @@ UserProfile::fetchDeviceList(const QString &userID) for (auto sign_key : luk.value().keys) { // checking if the signatures are empty as "at" could // cause exceptions - if (!mk.value().signatures.empty()) { - auto signs = - mk.value().signatures.at(local_user_id); + auto signs = mk->signatures; + if (!signs.empty() && + signs.find(local_user_id) != signs.end()) { + auto sign = signs.at(local_user_id); try { isUserVerified = isUserVerified || (olm::client()->ed25519_verify_sig( sign_key.second, json(mk.value()), - signs.at(sign_key.first))); + sign.at(sign_key.first))); } catch (std::out_of_range) { isUserVerified = isUserVerified || false; -- cgit 1.5.1 From 10f09d4f432d8f582972a22ebb2d1436d1335eb9 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 30 Aug 2020 19:33:10 +0200 Subject: Fix catch by value warning --- src/ui/UserProfile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 59be3464..08c30097 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -172,7 +172,7 @@ UserProfile::fetchDeviceList(const QString &userID) sign_key.second, json(mk.value()), sign.at(sign_key.first))); - } catch (std::out_of_range) { + } catch (std::out_of_range &) { isUserVerified = isUserVerified || false; } @@ -226,7 +226,7 @@ UserProfile::fetchDeviceList(const QString &userID) .cross_verified .push_back(d.first); } - } catch (std::out_of_range) { + } catch (std::out_of_range &) { } } } @@ -326,4 +326,4 @@ UserProfile::createFlow(bool isVerifyUser) std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); } -} \ No newline at end of file +} -- cgit 1.5.1 From 94690ebd4c22c8928b92c4f1723d1c6c5b798698 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 2 Oct 2020 01:14:42 +0200 Subject: Clean up verification and key cache a bit --- resources/qml/UserProfile.qml | 2 +- src/Cache.cpp | 331 ++++++++++++++++++++++++------------ src/Cache.h | 30 ++-- src/CacheCryptoStructs.h | 50 ++---- src/Cache_p.h | 34 ++-- src/ChatPage.cpp | 56 +++--- src/ChatPage.h | 6 +- src/DeviceVerificationFlow.cpp | 126 +++++++------- src/DeviceVerificationFlow.h | 12 +- src/timeline/.TimelineModel.cpp.swn | Bin 237568 -> 0 bytes src/ui/UserProfile.cpp | 70 +++----- 11 files changed, 399 insertions(+), 318 deletions(-) delete mode 100644 src/timeline/.TimelineModel.cpp.swn (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 1ca9dcc8..dc6bc165 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -136,7 +136,7 @@ ApplicationWindow{ model: profile.deviceList delegate: RowLayout{ - width: parent.width + width: devicelist.width spacing: 4 ColumnLayout{ diff --git a/src/Cache.cpp b/src/Cache.cpp index 667506c5..8b47c357 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -91,6 +91,7 @@ Q_DECLARE_METATYPE(RoomMember) Q_DECLARE_METATYPE(mtx::responses::Timeline) Q_DECLARE_METATYPE(RoomSearchResult) Q_DECLARE_METATYPE(RoomInfo) +Q_DECLARE_METATYPE(mtx::responses::QueryKeys) namespace { std::unique_ptr instance_ = nullptr; @@ -155,26 +156,7 @@ Cache::Cache(const QString &userId, QObject *parent) , localUserId_{userId} { setup(); - connect( - this, - &Cache::updateUserCacheFlag, - this, - [this](const std::string &user_id) { - std::optional cache_ = getUserCache(user_id); - if (cache_.has_value()) { - cache_.value().isUpdated = false; - setUserCache(user_id, cache_.value()); - } else { - setUserCache(user_id, UserCache{}); - } - }, - Qt::QueuedConnection); - connect( - this, - &Cache::deleteLeftUsers, - this, - [this](const std::string &user_id) { deleteUserCache(user_id); }, - Qt::QueuedConnection); + connect(this, &Cache::userKeysUpdate, this, &Cache::updateUserKeys, Qt::QueuedConnection); } void @@ -1017,6 +999,8 @@ Cache::saveState(const mtx::responses::Sync &res) using namespace mtx::events; auto user_id = this->localUserId_.toStdString(); + auto currentBatchToken = nextBatchToken(); + auto txn = lmdb::txn::begin(env_); setNextBatchToken(txn, res.next_batch); @@ -1034,6 +1018,8 @@ Cache::saveState(const mtx::responses::Sync &res) ev); } + auto userKeyCacheDb = getUserKeysDb(txn); + // Save joined rooms for (const auto &room : res.rooms.join) { auto statesdb = getStatesDb(txn, room.first); @@ -1107,7 +1093,8 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); - updateUserCache(res.device_lists); + markUserKeysOutOfDate(txn, userKeyCacheDb, res.device_lists.changed, currentBatchToken); + deleteUserKeys(txn, userKeyCacheDb, res.device_lists.left); removeLeftRooms(txn, res.rooms.leave); @@ -3098,126 +3085,246 @@ Cache::statusMessage(const std::string &user_id) } void -to_json(json &j, const UserCache &info) +to_json(json &j, const UserKeyCache &info) { - j["keys"] = info.keys; - j["isUpdated"] = info.isUpdated; + j["device_keys"] = info.device_keys; + j["master_keys"] = info.master_keys; + j["user_signing_keys"] = info.user_signing_keys; + j["self_signing_keys"] = info.self_signing_keys; + j["updated_at"] = info.updated_at; + j["last_changed"] = info.last_changed; } void -from_json(const json &j, UserCache &info) +from_json(const json &j, UserKeyCache &info) { - info.keys = j.at("keys").get(); - info.isUpdated = j.at("isUpdated").get(); + info.device_keys = j.value("device_keys", std::map{}); + info.master_keys = j.value("master_keys", mtx::crypto::CrossSigningKeys{}); + info.user_signing_keys = j.value("user_signing_keys", mtx::crypto::CrossSigningKeys{}); + info.self_signing_keys = j.value("self_signing_keys", mtx::crypto::CrossSigningKeys{}); + info.updated_at = j.value("updated_at", ""); + info.last_changed = j.value("last_changed", ""); } -std::optional -Cache::getUserCache(const std::string &user_id) +std::optional +Cache::userKeys(const std::string &user_id) { - lmdb::val verifiedVal; + lmdb::val keys; - auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); - - txn.commit(); + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + auto db = getUserKeysDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), keys); - UserCache verified_state; - if (res) { - verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); - return verified_state; - } else { + if (res) { + return json::parse(std::string_view(keys.data(), keys.size())) + .get(); + } else { + return {}; + } + } catch (std::exception &) { return {}; } } -//! be careful when using make sure is_user_verified is not changed -int -Cache::setUserCache(const std::string &user_id, const UserCache &body) +void +Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery) { auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); + auto db = getUserKeysDb(txn); - auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + std::map updates; - txn.commit(); + for (const auto &[user, keys] : keyQuery.device_keys) + updates[user].device_keys = keys; + for (const auto &[user, keys] : keyQuery.master_keys) + updates[user].master_keys = keys; + for (const auto &[user, keys] : keyQuery.user_signing_keys) + updates[user].user_signing_keys = keys; + for (const auto &[user, keys] : keyQuery.self_signing_keys) + updates[user].self_signing_keys = keys; - return res; + for (auto &[user, update] : updates) { + lmdb::val oldKeys; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); + + if (res) { + auto last_changed = + json::parse(std::string_view(oldKeys.data(), oldKeys.size())) + .get() + .last_changed; + // skip if we are tracking this and expect it to be up to date with the last + // sync token + if (!last_changed.empty() && last_changed != sync_token) + continue; + } + lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(update).dump())); + } + + txn.commit(); } void -Cache::updateUserCache(const mtx::responses::DeviceLists body) +Cache::deleteUserKeys(lmdb::txn &txn, lmdb::dbi &db, const std::vector &user_ids) { - for (std::string user_id : body.changed) { - emit updateUserCacheFlag(user_id); - } - - for (std::string user_id : body.left) { - emit deleteLeftUsers(user_id); - } + for (const auto &user_id : user_ids) + lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); } -int -Cache::deleteUserCache(const std::string &user_id) +void +Cache::markUserKeysOutOfDate(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids, + const std::string &sync_token) { - auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); - auto res = lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); + mtx::requests::QueryKeys query; + query.token = sync_token; - txn.commit(); + for (const auto &user : user_ids) { + lmdb::val oldKeys; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); - return res; + if (!res) + continue; + + auto cacheEntry = + json::parse(std::string_view(oldKeys.data(), oldKeys.size())).get(); + cacheEntry.last_changed = sync_token; + lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(cacheEntry).dump())); + + query.device_keys[user] = {}; + } + + if (!query.device_keys.empty()) + http::client()->query_keys(query, + [this, sync_token](const mtx::responses::QueryKeys &keys, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to query device keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + emit userKeysUpdate(sync_token, keys); + }); } void -to_json(json &j, const DeviceVerifiedCache &info) +to_json(json &j, const VerificationCache &info) { - j["is_user_verified"] = info.is_user_verified; - j["cross_verified"] = info.cross_verified; - j["device_verified"] = info.device_verified; - j["device_blocked"] = info.device_blocked; + j["verified_master_key"] = info.verified_master_key; + j["cross_verified"] = info.cross_verified; + j["device_verified"] = info.device_verified; + j["device_blocked"] = info.device_blocked; } void -from_json(const json &j, DeviceVerifiedCache &info) +from_json(const json &j, VerificationCache &info) { - info.is_user_verified = j.at("is_user_verified"); - info.cross_verified = j.at("cross_verified").get>(); - info.device_verified = j.at("device_verified").get>(); - info.device_blocked = j.at("device_blocked").get>(); + info.verified_master_key = j.at("verified_master_key"); + info.cross_verified = j.at("cross_verified").get>(); + info.device_verified = j.at("device_verified").get>(); + info.device_blocked = j.at("device_blocked").get>(); } -std::optional -Cache::getVerifiedCache(const std::string &user_id) +std::optional +Cache::verificationStatus(const std::string &user_id) { lmdb::val verifiedVal; auto txn = lmdb::txn::begin(env_); - auto db = getDeviceVerifiedDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + auto db = getVerificationDb(txn); - txn.commit(); - - DeviceVerifiedCache verified_state; - if (res) { - verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); - return verified_state; - } else { + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + if (res) { + verified_state = + json::parse(std::string_view(verifiedVal.data(), verifiedVal.size())); + return verified_state; + } else { + return {}; + } + } catch (std::exception &) { return {}; } } -int -Cache::setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) +void +Cache::markDeviceVerified(const std::string &user_id, const std::string &key) { + lmdb::val val; + auto txn = lmdb::txn::begin(env_); - auto db = getDeviceVerifiedDb(txn); + auto db = getVerificationDb(txn); - auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } - txn.commit(); + for (const auto &device : verified_state.device_verified) + if (device == key) + return; - return res; + verified_state.device_verified.push_back(key); + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } +} + +void +Cache::markDeviceUnverified(const std::string &user_id, const std::string &key) +{ + lmdb::val val; + + auto txn = lmdb::txn::begin(env_); + auto db = getVerificationDb(txn); + + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } + + verified_state.device_verified.erase( + std::remove(verified_state.device_verified.begin(), + verified_state.device_verified.end(), + key), + verified_state.device_verified.end()); + + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } +} + +void +Cache::markMasterKeyVerified(const std::string &user_id, const std::string &key) +{ + lmdb::val val; + + auto txn = lmdb::txn::begin(env_); + auto db = getVerificationDb(txn); + + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } + + verified_state.verified_master_key = key; + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } } void @@ -3401,47 +3508,49 @@ statusMessage(const std::string &user_id) { return instance_->statusMessage(user_id); } -std::optional -getUserCache(const std::string &user_id) -{ - return instance_->getUserCache(user_id); -} +//! Load saved data for the display names & avatars. void -updateUserCache(const mtx::responses::DeviceLists body) +populateMembers() { - instance_->updateUserCache(body); + instance_->populateMembers(); } -int -setUserCache(const std::string &user_id, const UserCache &body) +// user cache stores user keys +std::optional +userKeys(const std::string &user_id) +{ + return instance_->userKeys(user_id); +} +void +updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery) { - return instance_->setUserCache(user_id, body); + instance_->updateUserKeys(sync_token, keyQuery); } -int -deleteUserCache(const std::string &user_id) +// device & user verification cache +std::optional +verificationStatus(const std::string &user_id) { - return instance_->deleteUserCache(user_id); + return instance_->verificationStatus(user_id); } -std::optional -getVerifiedCache(const std::string &user_id) +void +markDeviceVerified(const std::string &user_id, const std::string &key) { - return instance_->getVerifiedCache(user_id); + instance_->markDeviceVerified(user_id, key); } -int -setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) +void +markDeviceUnverified(const std::string &user_id, const std::string &key) { - return instance_->setVerifiedCache(user_id, body); + instance_->markDeviceUnverified(user_id, key); } -//! Load saved data for the display names & avatars. void -populateMembers() +markMasterKeyVerified(const std::string &user_id, const std::string &key) { - instance_->populateMembers(); + instance_->markMasterKeyVerified(user_id, key); } std::vector diff --git a/src/Cache.h b/src/Cache.h index 82d909ae..edad5993 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -60,25 +60,21 @@ presenceState(const std::string &user_id); std::string statusMessage(const std::string &user_id); -//! user Cache -std::optional -getUserCache(const std::string &user_id); - +// user cache stores user keys +std::optional +userKeys(const std::string &user_id); void -updateUserCache(const mtx::responses::DeviceLists body); - -int -setUserCache(const std::string &user_id, const UserCache &body); - -int -deleteUserCache(const std::string &user_id); - -//! verified Cache -std::optional -getVerifiedCache(const std::string &user_id); +updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery); -int -setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); +// device & user verification cache +std::optional +verificationStatus(const std::string &user_id); +void +markDeviceVerified(const std::string &user_id, const std::string &key); +void +markDeviceUnverified(const std::string &user_id, const std::string &key); +void +markMasterKeyVerified(const std::string &user_id, const std::string &key); //! Load saved data for the display names & avatars. void diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 1dde21ce..10636ac6 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -67,52 +67,38 @@ struct OlmSessionStorage }; // this will store the keys of the user with whom a encrypted room is shared with -struct UserCache +struct UserKeyCache { - //! map of public key key_ids and their public_key - mtx::responses::QueryKeys keys; - //! if the current cache is updated or not - bool isUpdated = false; - - UserCache(mtx::responses::QueryKeys res, bool isUpdated_ = false) - : keys(res) - , isUpdated(isUpdated_) - {} - UserCache() {} + //! Device id to device keys + std::map device_keys; + //! corss signing keys + mtx::crypto::CrossSigningKeys master_keys, user_signing_keys, self_signing_keys; + //! Sync token when nheko last fetched the keys + std::string updated_at; + //! Sync token when the keys last changed. updated != last_changed means they are outdated. + std::string last_changed; }; void -to_json(nlohmann::json &j, const UserCache &info); +to_json(nlohmann::json &j, const UserKeyCache &info); void -from_json(const nlohmann::json &j, UserCache &info); +from_json(const nlohmann::json &j, UserKeyCache &info); // the reason these are stored in a seperate cache rather than storing it in the user cache is -// UserCache stores only keys of users with which encrypted room is shared -struct DeviceVerifiedCache +// UserKeyCache stores only keys of users with which encrypted room is shared +struct VerificationCache { //! list of verified device_ids with device-verification std::vector device_verified; - //! list of verified device_ids with cross-signing + //! list of verified device_ids with cross-signing, calculated from master key std::vector cross_verified; //! list of devices the user blocks std::vector device_blocked; - //! this stores if the user is verified (with cross-signing) - bool is_user_verified = false; - - DeviceVerifiedCache(std::vector device_verified_, - std::vector cross_verified_, - std::vector device_blocked_, - bool is_user_verified_ = false) - : device_verified(device_verified_) - , cross_verified(cross_verified_) - , device_blocked(device_blocked_) - , is_user_verified(is_user_verified_) - {} - - DeviceVerifiedCache() {} + //! The verified master key. + std::string verified_master_key; }; void -to_json(nlohmann::json &j, const DeviceVerifiedCache &info); +to_json(nlohmann::json &j, const VerificationCache &info); void -from_json(const nlohmann::json &j, DeviceVerifiedCache &info); +from_json(const nlohmann::json &j, VerificationCache &info); diff --git a/src/Cache_p.h b/src/Cache_p.h index ce6414ab..034c6d76 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -55,14 +55,22 @@ public: std::string statusMessage(const std::string &user_id); // user cache stores user keys - std::optional getUserCache(const std::string &user_id); - void updateUserCache(const mtx::responses::DeviceLists body); - int setUserCache(const std::string &user_id, const UserCache &body); - int deleteUserCache(const std::string &user_id); - - // device verified cache - std::optional getVerifiedCache(const std::string &user_id); - int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); + std::optional userKeys(const std::string &user_id); + void updateUserKeys(const std::string &sync_token, + const mtx::responses::QueryKeys &keyQuery); + void markUserKeysOutOfDate(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids, + const std::string &sync_token); + void deleteUserKeys(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids); + + // device & user verification cache + std::optional verificationStatus(const std::string &user_id); + void markDeviceVerified(const std::string &user_id, const std::string &key); + void markDeviceUnverified(const std::string &user_id, const std::string &key); + void markMasterKeyVerified(const std::string &user_id, const std::string &key); static void removeDisplayName(const QString &room_id, const QString &user_id); static void removeAvatarUrl(const QString &room_id, const QString &user_id); @@ -272,8 +280,8 @@ signals: void newReadReceipts(const QString &room_id, const std::vector &event_ids); void roomReadStatus(const std::map &status); void removeNotification(const QString &room_id, const QString &event_id); - void updateUserCacheFlag(const std::string &user_id); - void deleteLeftUsers(const std::string &user_id); + void userKeysUpdate(const std::string &sync_token, + const mtx::responses::QueryKeys &keyQuery); private: //! Save an invited room. @@ -539,12 +547,12 @@ private: return lmdb::dbi::open(txn, "presence", MDB_CREATE); } - lmdb::dbi getUserCacheDb(lmdb::txn &txn) + lmdb::dbi getUserKeysDb(lmdb::txn &txn) { - return lmdb::dbi::open(txn, "user_cache", MDB_CREATE); + return lmdb::dbi::open(txn, "user_key", MDB_CREATE); } - lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn) + lmdb::dbi getVerificationDb(lmdb::txn &txn) { return lmdb::dbi::open(txn, "verified", MDB_CREATE); } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c6978a59..6abe4078 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -1466,35 +1466,43 @@ ChatPage::initiateLogout() } void -ChatPage::query_keys( - const mtx::requests::QueryKeys &req, - std::function cb) +ChatPage::query_keys(const std::string &user_id, + std::function cb) { - std::string user_id = req.device_keys.begin()->first; - auto cache_ = cache::getUserCache(user_id); + auto cache_ = cache::userKeys(user_id); if (cache_.has_value()) { - if (cache_.value().isUpdated) { - cb(cache_.value().keys, {}); - } else { - http::client()->query_keys( - req, - [cb, user_id](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - cache::setUserCache(std::move(user_id), - std::move(UserCache{res, true})); - cb(res, err); - }); + if (!cache_->updated_at.empty() && cache_->updated_at == cache_->last_changed) { + cb(cache_.value(), {}); + return; } - } else { - http::client()->query_keys(req, cb); } + + mtx::requests::QueryKeys req; + req.device_keys[user_id] = {}; + + std::string last_changed; + if (cache_) + last_changed = cache_->last_changed; + req.token = last_changed; + + http::client()->query_keys(req, + [cb, user_id, last_changed](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + cb({}, err); + return; + } + + cache::updateUserKeys(last_changed, res); + + auto keys = cache::userKeys(user_id); + cb(keys.value_or(UserKeyCache{}), err); + }); } template diff --git a/src/ChatPage.h b/src/ChatPage.h index 9d8abb24..f363c4fe 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -35,6 +35,7 @@ #include #include +#include "CacheCryptoStructs.h" #include "CacheStructs.h" #include "CallManager.h" #include "CommunitiesList.h" @@ -89,9 +90,8 @@ public: //! Show the room/group list (if it was visible). void showSideBars(); void initiateLogout(); - void query_keys( - const mtx::requests::QueryKeys &req, - std::function cb); + void query_keys(const std::string &req, + std::function cb); void focusMessageInput(); QString status() const; diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 96fed55a..aa8b5b44 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -328,22 +328,14 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_) void DeviceVerificationFlow::setUserId(QString userID) { - this->userId = userID; - this->toClient = mtx::identifiers::parse(userID.toStdString()); - auto user_cache = cache::getUserCache(userID.toStdString()); - - if (user_cache.has_value()) { - this->callback_fn(user_cache->keys, {}, userID.toStdString()); - } else { - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); - } + this->userId = userID; + this->toClient = mtx::identifiers::parse(userID.toStdString()); + + auto user_id = userID.toStdString(); + ChatPage::instance()->query_keys( + user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { + this->callback_fn(res, err, user_id); + }); } void @@ -622,30 +614,52 @@ DeviceVerificationFlow::sendVerificationKey() (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey); } } -//! sends the mac of the keys -void -DeviceVerificationFlow::sendVerificationMac() + +mtx::events::msg::KeyVerificationMac +key_verification_mac(mtx::crypto::SAS *sas, + mtx::identifiers::User sender, + const std::string &senderDevice, + mtx::identifiers::User receiver, + const std::string &receiverDevice, + const std::string &transactionId, + std::map keys) { mtx::events::msg::KeyVerificationMac req; - std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() + - http::client()->device_id() + this->toClient.to_string() + - this->deviceId.toStdString() + this->transaction_id; - - //! this vector stores the type of the key and the key - std::vector> key_list; - key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519)); - std::sort(key_list.begin(), key_list.end()); - for (auto x : key_list) { - req.mac.insert( - std::make_pair(x.first + ":" + http::client()->device_id(), - this->sas->calculate_mac( - x.second, info + x.first + ":" + http::client()->device_id()))); - req.keys += x.first + ":" + http::client()->device_id() + ","; + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + sender.to_string() + senderDevice + + receiver.to_string() + receiverDevice + transactionId; + + std::string key_list; + bool first = true; + for (const auto &[key_id, key] : keys) { + req.mac[key_id] = sas->calculate_mac(key, info + key_id); + + if (!first) + key_list += ","; + key_list += key_id; + first = false; } - req.keys = - this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS"); + req.keys = sas->calculate_mac(key_list, info + "KEY_IDS"); + + return req; +} + +//! sends the mac of the keys +void +DeviceVerificationFlow::sendVerificationMac() +{ + std::map key_list; + key_list["ed25519:" + http::client()->device_id()] = olm::client()->identity_keys().ed25519; + + mtx::events::msg::KeyVerificationMac req = + key_verification_mac(sas.get(), + http::client()->user_id(), + http::client()->device_id(), + this->toClient, + this->deviceId.toStdString(), + this->transaction_id, + key_list); if (this->type == DeviceVerificationFlow::Type::ToDevice) { mtx::requests::ToDeviceMessages body; @@ -673,27 +687,16 @@ DeviceVerificationFlow::sendVerificationMac() void DeviceVerificationFlow::acceptDevice() { - auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - if (verified_cache.has_value()) { - verified_cache->device_verified.push_back(this->deviceId.toStdString()); - verified_cache->device_blocked.erase( - std::remove(verified_cache->device_blocked.begin(), - verified_cache->device_blocked.end(), - this->deviceId.toStdString()), - verified_cache->device_blocked.end()); - } else { - cache::setVerifiedCache( - this->userId.toStdString(), - DeviceVerifiedCache{{this->deviceId.toStdString()}, {}, {}}); - } + cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.toStdString()); emit deviceVerified(); emit refreshProfile(); this->deleteLater(); } + //! callback function to keep track of devices void -DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, +DeviceVerificationFlow::callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id) { @@ -704,35 +707,22 @@ DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, return; } - if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { + if (res.device_keys.empty() || + (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { nhlog::net()->warn("no devices retrieved {}", user_id); return; } - for (auto x : res.device_keys) { - for (auto y : x.second) { - auto z = y.second; - if (z.user_id == user_id && z.device_id == this->deviceId.toStdString()) { - for (auto a : z.keys) { - // TODO: Verify Signatures - this->device_keys[a.first] = a.second; - } - } - } + for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) { + // TODO: Verify Signatures + this->device_keys[algorithm] = key; } } void DeviceVerificationFlow::unverify() { - auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - if (verified_cache.has_value()) { - auto it = std::remove(verified_cache->device_verified.begin(), - verified_cache->device_verified.end(), - this->deviceId.toStdString()); - verified_cache->device_verified.erase(it); - cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); - } + cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString()); emit refreshProfile(); } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index b85cbec2..31d2facc 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,10 +1,12 @@ #pragma once -#include "Olm.h" +#include + +#include +#include "CacheCryptoStructs.h" #include "MatrixClient.h" -#include "mtx/responses/crypto.hpp" -#include +#include "Olm.h" class QTimer; @@ -71,9 +73,7 @@ public: void setSender(bool sender_); void setEventId(std::string event_id); - void callback_fn(const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err, - std::string user_id); + void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id); nlohmann::json canonical_json; diff --git a/src/timeline/.TimelineModel.cpp.swn b/src/timeline/.TimelineModel.cpp.swn deleted file mode 100644 index 9e965702..00000000 Binary files a/src/timeline/.TimelineModel.cpp.swn and /dev/null differ diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 08c30097..2ea3f7b6 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -89,12 +89,10 @@ UserProfile::fetchDeviceList(const QString &userID) { auto localUser = utils::localUser(); - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; ChatPage::instance()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + userID.toStdString(), + [other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", err->matrix_error.errcode, @@ -102,20 +100,11 @@ UserProfile::fetchDeviceList(const QString &userID) return; } - if (res.device_keys.empty() || - (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } - // Finding if the User is Verified or not based on the Signatures - mtx::requests::QueryKeys req; - req.device_keys[utils::localUser().toStdString()] = {}; - ChatPage::instance()->query_keys( - req, - [user_id, other_res = res, this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + utils::localUser().toStdString(), + [other_user_id, other_user_keys, this](const UserKeyCache &res, + mtx::http::RequestErr err) { using namespace mtx; std::string local_user_id = utils::localUser().toStdString(); @@ -126,34 +115,28 @@ UserProfile::fetchDeviceList(const QString &userID) return; } - if (res.device_keys.empty() || - (res.device_keys.find(local_user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); + if (res.device_keys.empty()) { + nhlog::net()->warn("no devices retrieved {}", local_user_id); return; } std::vector deviceInfo; - auto devices = other_res.device_keys.at(user_id); - auto device_verified = cache::getVerifiedCache(user_id); + auto devices = other_user_keys.device_keys; + auto device_verified = cache::verificationStatus(other_user_id); if (device_verified.has_value()) { - isUserVerified = device_verified.value().is_user_verified; + // TODO: properly check cross-signing signatures here + isUserVerified = !device_verified->verified_master_key.empty(); } std::optional lmk, lsk, luk, mk, sk, uk; - if (!res.master_keys.empty()) - lmk = res.master_keys.at(local_user_id); - if (!res.user_signing_keys.empty()) - luk = res.user_signing_keys.at(local_user_id); - if (!res.self_signing_keys.empty()) - lsk = res.self_signing_keys.at(local_user_id); - if (!other_res.master_keys.empty()) - mk = other_res.master_keys.at(user_id); - if (!other_res.user_signing_keys.empty()) - uk = other_res.user_signing_keys.at(user_id); - if (!other_res.self_signing_keys.empty()) - sk = other_res.self_signing_keys.at(user_id); + lmk = res.master_keys; + luk = res.user_signing_keys; + lsk = res.self_signing_keys; + mk = other_user_keys.master_keys; + uk = other_user_keys.user_signing_keys; + sk = other_user_keys.self_signing_keys; // First checking if the user is verified if (luk.has_value() && mk.has_value()) { @@ -202,7 +185,7 @@ UserProfile::fetchDeviceList(const QString &userID) device_verified->device_blocked.end()) verified = verification::Status::BLOCKED; } else if (isUserVerified) { - device_verified = DeviceVerifiedCache{}; + device_verified = VerificationCache{}; } // won't check for already verified devices @@ -211,7 +194,7 @@ UserProfile::fetchDeviceList(const QString &userID) if ((sk.has_value()) && (!device.signatures.empty())) { for (auto sign_key : sk.value().keys) { auto signs = - device.signatures.at(user_id); + device.signatures.at(other_user_id); try { if (olm::client() ->ed25519_verify_sig( @@ -232,12 +215,13 @@ UserProfile::fetchDeviceList(const QString &userID) } } - if (device_verified.has_value()) { - device_verified.value().is_user_verified = - isUserVerified; - cache::setVerifiedCache(user_id, - device_verified.value()); - } + // TODO(Nico): properly show cross-signing + // if (device_verified.has_value()) { + // device_verified.value().is_user_verified = + // isUserVerified; + // cache::setVerifiedCache(user_id, + // device_verified.value()); + //} deviceInfo.push_back( {QString::fromStdString(d.first), -- cgit 1.5.1 From bca29a4227a871caac21236c29430b69264018ce Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 5 Oct 2020 22:12:10 +0200 Subject: Make steps in verification flow explicit --- resources/qml/TimelineView.qml | 14 +- resources/qml/UserProfile.qml | 31 +- .../AcceptNewVerificationRequest.qml | 49 -- .../AwaitingVerificationConfirmation.qml | 5 +- .../AwaitingVerificationRequest.qml | 40 -- .../qml/device-verification/DeviceVerification.qml | 80 +-- .../qml/device-verification/DigitVerification.qml | 12 +- .../qml/device-verification/EmojiVerification.qml | 12 +- resources/qml/device-verification/Failed.qml | 42 ++ .../device-verification/NewVerificationRequest.qml | 37 +- .../qml/device-verification/PartnerAborted.qml | 34 -- resources/qml/device-verification/Success.qml | 31 ++ resources/qml/device-verification/TimedOut.qml | 35 -- .../device-verification/VerificationSuccess.qml | 35 -- resources/qml/device-verification/Waiting.qml | 45 ++ resources/res.qrc | 9 +- src/ChatPage.h | 18 +- src/DeviceVerificationFlow.cpp | 613 ++++++++++----------- src/DeviceVerificationFlow.h | 215 ++++++-- src/Olm.cpp | 32 +- src/timeline/EventStore.cpp | 16 +- src/timeline/TimelineModel.cpp | 4 +- src/timeline/TimelineViewManager.cpp | 194 +++---- src/timeline/TimelineViewManager.h | 25 +- src/ui/UserProfile.cpp | 55 +- src/ui/UserProfile.h | 10 +- 26 files changed, 798 insertions(+), 895 deletions(-) delete mode 100644 resources/qml/device-verification/AcceptNewVerificationRequest.qml delete mode 100644 resources/qml/device-verification/AwaitingVerificationRequest.qml create mode 100644 resources/qml/device-verification/Failed.qml delete mode 100644 resources/qml/device-verification/PartnerAborted.qml create mode 100644 resources/qml/device-verification/Success.qml delete mode 100644 resources/qml/device-verification/TimedOut.qml delete mode 100644 resources/qml/device-verification/VerificationSuccess.qml create mode 100644 resources/qml/device-verification/Waiting.qml (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 3f72a7dd..1dbe7c1a 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -106,19 +106,7 @@ Page { Connections { target: TimelineManager function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) { - flow.userId = userId; - flow.sender = false; - flow.deviceId = deviceId; - switch(flow.type){ - case DeviceVerificationFlow.ToDevice: - flow.tranId = transactionId; - deviceVerificationList.add(flow.tranId); - break; - case DeviceVerificationFlow.RoomMsg: - deviceVerificationList.add(flow.tranId); - break; - } - var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest: isRequest,tran_id: flow.tranId}); + var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow}); dialog.show(); } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index dc6bc165..e7dcc777 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -15,24 +15,12 @@ ApplicationWindow{ width: 420 minimumHeight: 420 - modality: Qt.WindowModal palette: colors - Connections{ - target: deviceVerificationList - function onUpdateProfile() { - profile.fetchDeviceList(profile.userid) - } - } - Component { id: deviceVerificationDialog DeviceVerification {} } - Component{ - id: deviceVerificationFlow - DeviceVerificationFlow {} - } ColumnLayout{ id: contentL @@ -73,14 +61,7 @@ ApplicationWindow{ enabled: !profile.isUserVerified visible: !profile.isUserVerified - onClicked: { - var newFlow = profile.createFlow(true); - newFlow.userId = profile.userid; - newFlow.sender = true; - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true,tran_id: newFlow.tranId}); - dialog.show(); - } + onClicked: profile.verify() } RowLayout { @@ -172,17 +153,11 @@ ApplicationWindow{ id: verifyButton text: (model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify" onClicked: { - var newFlow = profile.createFlow(false); - newFlow.userId = profile.userid; - newFlow.sender = true; - newFlow.deviceId = model.deviceId; if(model.verificationStatus == VerificationStatus.VERIFIED){ - newFlow.unverify(); + profile.unverify(model.deviceId) deviceVerificationList.updateProfile(newFlow.userId); }else{ - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false,tran_id: newFlow.tranId}); - dialog.show(); + profile.verify(model.deviceId); } } } diff --git a/resources/qml/device-verification/AcceptNewVerificationRequest.qml b/resources/qml/device-verification/AcceptNewVerificationRequest.qml deleted file mode 100644 index 5bdbc4a6..00000000 --- a/resources/qml/device-verification/AcceptNewVerificationRequest.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -import im.nheko 1.0 - -Pane { - property string title: qsTr("Recieving Device Verification Request") - Component { - id: awaitingVerificationRequestAccept - AwaitingVerificationRequest {} - } - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("The device was requested to be verified") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("Deny") - - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Accept") - - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - flow.sender ?flow.sendVerificationReady():flow.acceptVerificationRequest(); - } - } - } - } -} diff --git a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml index aaebba6a..cd8ccfd9 100644 --- a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml +++ b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml @@ -27,9 +27,8 @@ Pane { text: qsTr("Cancel") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { diff --git a/resources/qml/device-verification/AwaitingVerificationRequest.qml b/resources/qml/device-verification/AwaitingVerificationRequest.qml deleted file mode 100644 index b4b9178a..00000000 --- a/resources/qml/device-verification/AwaitingVerificationRequest.qml +++ /dev/null @@ -1,40 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -import im.nheko 1.0 - -Pane { - property string title: qsTr("Waiting for other party") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - id: content - text: qsTr("Waiting for other side to accept the verification request.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - BusyIndicator { - Layout.alignment: Qt.AlignHCenter - } - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("Cancel") - - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - } - } -} diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ca980987..4e93df06 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -18,36 +18,31 @@ ApplicationWindow { height: stack.implicitHeight width: stack.implicitWidth - Component{ - id: newVerificationRequest - NewVerificationRequest {} - } - - Component{ - id: acceptNewVerificationRequest - AcceptNewVerificationRequest {} - } - StackView { id: stack - initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest + initialItem: newVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } + Component{ + id: newVerificationRequest + NewVerificationRequest {} + } + Component { - id: partnerAborted - PartnerAborted {} + id: waiting + Waiting {} } Component { - id: timedout - TimedOut {} + id: success + Success {} } Component { - id: verificationSuccess - VerificationSuccess {} + id: failed + Failed {} } Component { @@ -60,19 +55,42 @@ ApplicationWindow { EmojiVerification {} } - Connections { - target: flow - onVerificationCanceled: stack.replace(partnerAborted) - onTimedout: stack.replace(timedout) - onDeviceVerified: stack.replace(verificationSuccess) - - onVerificationRequestAccepted: switch(method) { - case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break; - case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break; + Item { + state: flow.state + + states: [ + State { + name: "PromptStartVerification" + StateChangeScript { script: stack.replace(newVerificationRequest) } + }, + State { + name: "CompareEmoji" + StateChangeScript { script: stack.replace(emojiVerification) } + }, + State { + name: "CompareNumber" + StateChangeScript { script: stack.replace(digitVerification) } + }, + State { + name: "WaitingForKeys" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "WaitingForOtherToAccept" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "WaitingForMac" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "Success" + StateChangeScript { script: stack.replace(success) } + }, + State { + name: "Failed" + StateChangeScript { script: stack.replace(failed); } } - - onRefreshProfile: { - deviceVerificationList.updateProfile(flow.userId); - } - } + ] +} } diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml index f3b1f5cf..ff878a50 100644 --- a/resources/qml/device-verification/DigitVerification.qml +++ b/resources/qml/device-verification/DigitVerification.qml @@ -6,10 +6,7 @@ import im.nheko 1.0 Pane { property string title: qsTr("Verification Code") - Component { - id: awaitingVerificationConfirmation - AwaitingVerificationConfirmation {} - } + ColumnLayout { spacing: 16 Label { @@ -45,9 +42,8 @@ Pane { text: qsTr("They do not match!") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -57,7 +53,7 @@ Pane { Layout.alignment: Qt.AlignRight text: qsTr("They match!") - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + onClicked: flow.next(); } } } diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml index 19faf1b7..ed7727aa 100644 --- a/resources/qml/device-verification/EmojiVerification.qml +++ b/resources/qml/device-verification/EmojiVerification.qml @@ -6,10 +6,7 @@ import im.nheko 1.0 Pane { property string title: qsTr("Verification Code") - Component { - id: awaitingVerificationConfirmation - AwaitingVerificationConfirmation{} - } + ColumnLayout { spacing: 16 Label { @@ -125,9 +122,8 @@ Pane { text: qsTr("They do not match!") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -137,7 +133,7 @@ Pane { Layout.alignment: Qt.AlignRight text: qsTr("They match!") - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + onClicked: flow.next() } } } diff --git a/resources/qml/device-verification/Failed.qml b/resources/qml/device-verification/Failed.qml new file mode 100644 index 00000000..6b5d57ef --- /dev/null +++ b/resources/qml/device-verification/Failed.qml @@ -0,0 +1,42 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +Pane { + property string title: qsTr("Verification timed out") + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: switch (flow.error) { + case VerificationStatus.UnknownMethod: return qsTr("Device verification timed out.") + case VerificationStatus.MismatchedCommitment: return qsTr("Device verification timed out.") + case VerificationStatus.MismatchedSAS: return qsTr("Device verification timed out.") + case VerificationStatus.KeyMismatch: return qsTr("Device verification timed out.") + case VerificationStatus.Timeout: return qsTr("Device verification timed out.") + case VerificationStatus.OutOfOrder: return qsTr("Device verification timed out.") + } + color:colors.text + verticalAlignment: Text.AlignVCenter + } + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("Close") + + onClicked: { + deviceVerificationList.remove(tran_id); + flow.deleteFlow(); + dialog.close() + } + } + } + } +} diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml index ef730b13..bd25bb90 100644 --- a/resources/qml/device-verification/NewVerificationRequest.qml +++ b/resources/qml/device-verification/NewVerificationRequest.qml @@ -2,12 +2,11 @@ import QtQuick 2.3 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.10 +import im.nheko 1.0 + Pane { - property string title: qsTr("Sending Device Verification Request") - Component { - id: awaitingVerificationRequestAccept - AwaitingVerificationRequest {} - } + property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request") + ColumnLayout { spacing: 16 Label { @@ -15,28 +14,20 @@ Pane { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: qsTr("A new device was added.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device.") + text: flow.sender ? + qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.") + : qsTr("The device was requested to be verified") color:colors.text verticalAlignment: Text.AlignVCenter } RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: qsTr("Cancel") + text: flow.sender ? qsTr("Cancel") : qsTr("Deny") onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -44,12 +35,10 @@ Pane { } Button { Layout.alignment: Qt.AlignRight - text: qsTr("Start verification") + text: flow.sender ? qsTr("Start verification") : qsTr("Accept") - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - flow.sender ?flow.sendVerificationRequest():flow.startVerificationRequest(); } - } + onClicked: flow.next(); } } } +} diff --git a/resources/qml/device-verification/PartnerAborted.qml b/resources/qml/device-verification/PartnerAborted.qml deleted file mode 100644 index 6174477d..00000000 --- a/resources/qml/device-verification/PartnerAborted.qml +++ /dev/null @@ -1,34 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -Pane { - property string title: qsTr("Verification aborted!") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - id: content - text: qsTr("Verification canceled by the other party!") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Close") - - onClicked: { - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - } - } -} diff --git a/resources/qml/device-verification/Success.qml b/resources/qml/device-verification/Success.qml new file mode 100644 index 00000000..b17b293c --- /dev/null +++ b/resources/qml/device-verification/Success.qml @@ -0,0 +1,31 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +Pane { + property string title: qsTr("Successful Verification") + ColumnLayout { + spacing: 16 + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: qsTr("Verification successful! Both sides verified their devices!") + color:colors.text + verticalAlignment: Text.AlignVCenter + } + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("Close") + + onClicked: dialog.close(); + } + } + } +} diff --git a/resources/qml/device-verification/TimedOut.qml b/resources/qml/device-verification/TimedOut.qml deleted file mode 100644 index 7dd0ab69..00000000 --- a/resources/qml/device-verification/TimedOut.qml +++ /dev/null @@ -1,35 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -Pane { - property string title: qsTr("Verification timed out") - ColumnLayout { - spacing: 16 - Text { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - id: content - text: qsTr("Device verification timed out.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Close") - - onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy() - } - } - } - } -} diff --git a/resources/qml/device-verification/VerificationSuccess.qml b/resources/qml/device-verification/VerificationSuccess.qml deleted file mode 100644 index bc1e64f7..00000000 --- a/resources/qml/device-verification/VerificationSuccess.qml +++ /dev/null @@ -1,35 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -Pane { - property string title: qsTr("Successful Verification") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - id: content - text: qsTr("Verification successful! Both sides verified their devices!") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Close") - - onClicked: { - deviceVerificationList.remove(tran_id); - if(flow) flow.deleteFlow(); - dialog.destroy(); - } - } - } - } -} diff --git a/resources/qml/device-verification/Waiting.qml b/resources/qml/device-verification/Waiting.qml new file mode 100644 index 00000000..f36910e7 --- /dev/null +++ b/resources/qml/device-verification/Waiting.qml @@ -0,0 +1,45 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +import im.nheko 1.0 + +Pane { + property string title: qsTr("Waiting for other party") + ColumnLayout { + spacing: 16 + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: switch (flow.state) { + case "WaitingForOtherToAccept": return qsTr("Waiting for other side to accept the verification request.") + case "WaitingForKeys": return qsTr("Waiting for other side to continue the verification request.") + case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.") + } + + color:colors.text + verticalAlignment: Text.AlignVCenter + } + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + palette: color + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: qsTr("Cancel") + + onClicked: { + flow.cancel(); + dialog.close(); + } + } + Item { + Layout.fillWidth: true + } + } + } +} diff --git a/resources/res.qrc b/resources/res.qrc index 7ef7ecf9..64e5b084 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -141,16 +141,13 @@ qml/delegates/Pill.qml qml/delegates/Placeholder.qml qml/delegates/Reply.qml - qml/device-verification/AcceptNewVerificationRequest.qml - qml/device-verification/AwaitingVerificationConfirmation.qml - qml/device-verification/AwaitingVerificationRequest.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/PartnerAborted.qml - qml/device-verification/TimedOut.qml - qml/device-verification/VerificationSuccess.qml + qml/device-verification/Failed.qml + qml/device-verification/Success.qml media/ring.ogg diff --git a/src/ChatPage.h b/src/ChatPage.h index f363c4fe..c37aa915 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -169,22 +169,22 @@ signals: void decryptSidebarChanged(); //! Signals for device verificaiton - void recievedDeviceVerificationAccept( + void receivedDeviceVerificationAccept( const mtx::events::msg::KeyVerificationAccept &message); - void recievedDeviceVerificationRequest( + void receivedDeviceVerificationRequest( const mtx::events::msg::KeyVerificationRequest &message, std::string sender); - void recievedRoomDeviceVerificationRequest( + void receivedRoomDeviceVerificationRequest( const mtx::events::RoomEvent &message, TimelineModel *model); - void recievedDeviceVerificationCancel( + void receivedDeviceVerificationCancel( const mtx::events::msg::KeyVerificationCancel &message); - void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message); - void recievedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message); - void recievedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message, + void receivedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message); + void receivedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message); + void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message, std::string sender); - void recievedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); - void recievedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); + void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); + void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); private slots: void showUnreadMessageNotification(int count); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 7b367de9..99fd7bed 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -15,8 +15,12 @@ namespace msgs = mtx::events::msg; DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type flow_type, - TimelineModel *model) - : type(flow_type) + TimelineModel *model, + QString userID, + QString deviceId_) + : sender(false) + , type(flow_type) + , deviceId(deviceId_) , model_(model) { timeout = new QTimer(this); @@ -24,6 +28,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, this->sas = olm::client()->sas_init(); this->isMacVerified = false; + auto user_id = userID.toStdString(); + this->toClient = mtx::identifiers::parse(user_id); + ChatPage::instance()->query_keys( + user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + if (!this->deviceId.isEmpty() && + (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + for (const auto &[algorithm, key] : + res.device_keys.at(deviceId.toStdString()).keys) { + // TODO: Verify Signatures + this->device_keys[algorithm] = key; + } + }); + if (model) { connect(this->model_, &TimelineModel::updateFlowEventId, @@ -36,64 +64,15 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, } connect(timeout, &QTimer::timeout, this, [this]() { - emit timedout(); this->cancelVerification(DeviceVerificationFlow::Error::Timeout); - this->deleteLater(); }); - connect(this, &DeviceVerificationFlow::deleteFlow, this, [this]() { this->deleteLater(); }); - - connect( - ChatPage::instance(), - &ChatPage::recievedDeviceVerificationStart, - this, - [this](const mtx::events::msg::KeyVerificationStart &msg, std::string) { - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().event_id != this->relation.event_id) - return; - } - if ((std::find(msg.key_agreement_protocols.begin(), - msg.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) && - (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != - msg.hashes.end()) && - (std::find(msg.message_authentication_codes.begin(), - msg.message_authentication_codes.end(), - "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) { - if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; - } else if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Decimal; - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - return; - } - if (!sender) - this->canonical_json = nlohmann::json(msg); - else { - if (utils::localUser().toStdString() < - this->toClient.to_string()) { - this->canonical_json = nlohmann::json(msg); - } - } - this->acceptVerificationRequest(); - } else { - this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationAccept, + &ChatPage::receivedDeviceVerificationStart, + this, + &DeviceVerificationFlow::handleStartMessage); + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationAccept, this, [this](const mtx::events::msg::KeyVerificationAccept &msg) { if (msg.transaction_id.has_value()) { @@ -111,9 +90,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, msg.short_authentication_string.end(), mtx::events::msg::SASMethods::Emoji) != msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; + this->method = mtx::events::msg::SASMethods::Emoji; } else { - this->method = DeviceVerificationFlow::Method::Decimal; + this->method = mtx::events::msg::SASMethods::Decimal; } this->mac_method = msg.message_authentication_code; this->sendVerificationKey(); @@ -124,7 +103,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationCancel, + &ChatPage::receivedDeviceVerificationCancel, this, [this](const mtx::events::msg::KeyVerificationCancel &msg) { if (msg.transaction_id.has_value()) { @@ -134,12 +113,13 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } - this->deleteLater(); - emit verificationCanceled(); + error_ = User; + emit errorChanged(); + setState(Failed); }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationKey, + &ChatPage::receivedDeviceVerificationKey, this, [this](const mtx::events::msg::KeyVerificationKey &msg) { if (msg.transaction_id.has_value()) { @@ -149,6 +129,19 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } + + if (sender) { + if (state_ != WaitingForOtherToAccept) { + this->cancelVerification(OutOfOrder); + return; + } + } else { + if (state_ != WaitingForKeys) { + this->cancelVerification(OutOfOrder); + return; + } + } + this->sas->set_their_key(msg.key); std::string info; if (this->sender == true) { @@ -166,31 +159,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, "|" + this->transaction_id; } - if (this->method == DeviceVerificationFlow::Method::Emoji) { - std::cout << info << std::endl; - this->sasList = this->sas->generate_bytes_emoji(info); - } else if (this->method == DeviceVerificationFlow::Method::Decimal) { - this->sasList = this->sas->generate_bytes_decimal(info); - } - if (this->sender == false) { - emit this->verificationRequestAccepted(this->method); this->sendVerificationKey(); } else { - if (this->commitment == + if (this->commitment != mtx::crypto::bin2base64_unpadded( mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) { - emit this->verificationRequestAccepted(this->method); - } else { this->cancelVerification( DeviceVerificationFlow::Error::MismatchedCommitment); + return; } } + + if (this->method == mtx::events::msg::SASMethods::Emoji) { + this->sasList = this->sas->generate_bytes_emoji(info); + setState(CompareEmoji); + } else if (this->method == mtx::events::msg::SASMethods::Decimal) { + this->sasList = this->sas->generate_bytes_decimal(info); + setState(CompareNumber); + } }); connect( ChatPage::instance(), - &ChatPage::recievedDeviceVerificationMac, + &ChatPage::receivedDeviceVerificationMac, this, [this](const mtx::events::msg::KeyVerificationMac &msg) { if (msg.transaction_id.has_value()) { @@ -222,26 +214,22 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, } key_string = key_string.substr(0, key_string.length() - 1); if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - // uncomment this in future to be compatible with the - // MSC2366 this->sendVerificationDone(); and remove the - // below line - if (this->isMacVerified == true) { - this->acceptDevice(); - } else - this->isMacVerified = true; + this->isMacVerified = true; + this->acceptDevice(); } else { this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); } }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationReady, + &ChatPage::receivedDeviceVerificationReady, this, [this](const mtx::events::msg::KeyVerificationReady &msg) { if (!sender) { if (msg.from_device != http::client()->device_id()) { - this->deleteLater(); - emit verificationCanceled(); + error_ = User; + emit errorChanged(); + setState(Failed); } return; @@ -261,7 +249,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationDone, + &ChatPage::receivedDeviceVerificationDone, this, [this](const mtx::events::msg::KeyVerificationDone &msg) { if (msg.transaction_id.has_value()) { @@ -271,22 +259,85 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } - this->acceptDevice(); + nhlog::ui()->info("Flow done on other side"); }); timeout->start(TIMEOUT); } QString -DeviceVerificationFlow::getTransactionId() +DeviceVerificationFlow::state() { - return QString::fromStdString(this->transaction_id); + switch (state_) { + case PromptStartVerification: + return "PromptStartVerification"; + case CompareEmoji: + return "CompareEmoji"; + case CompareNumber: + return "CompareNumber"; + case WaitingForKeys: + return "WaitingForKeys"; + case WaitingForOtherToAccept: + return "WaitingForOtherToAccept"; + case WaitingForMac: + return "WaitingForMac"; + case Success: + return "Success"; + case Failed: + return "Failed"; + default: + return ""; + } +} + +void +DeviceVerificationFlow::next() +{ + if (sender) { + switch (state_) { + case PromptStartVerification: + sendVerificationRequest(); + break; + case CompareEmoji: + case CompareNumber: + sendVerificationMac(); + break; + case WaitingForKeys: + case WaitingForOtherToAccept: + case WaitingForMac: + case Success: + case Failed: + nhlog::db()->error("verification: Invalid state transition!"); + break; + } + } else { + switch (state_) { + case PromptStartVerification: + if (canonical_json.is_null()) + sendVerificationReady(); + else // legacy path without request and ready + acceptVerificationRequest(); + break; + case CompareEmoji: + [[fallthrough]]; + case CompareNumber: + sendVerificationMac(); + break; + case WaitingForKeys: + case WaitingForOtherToAccept: + case WaitingForMac: + case Success: + case Failed: + nhlog::db()->error("verification: Invalid state transition!"); + break; + } + } } QString DeviceVerificationFlow::getUserId() { - return this->userId; + return QString::fromStdString(this->toClient.to_string()); } QString @@ -295,18 +346,6 @@ DeviceVerificationFlow::getDeviceId() return this->deviceId; } -DeviceVerificationFlow::Method -DeviceVerificationFlow::getMethod() -{ - return this->method; -} - -DeviceVerificationFlow::Type -DeviceVerificationFlow::getType() -{ - return this->type; -} - bool DeviceVerificationFlow::getSender() { @@ -320,56 +359,58 @@ DeviceVerificationFlow::getSasList() } void -DeviceVerificationFlow::setTransactionId(QString transaction_id_) -{ - this->transaction_id = transaction_id_.toStdString(); -} - -void -DeviceVerificationFlow::setUserId(QString userID) -{ - this->userId = userID; - this->toClient = mtx::identifiers::parse(userID.toStdString()); - - auto user_id = userID.toStdString(); - ChatPage::instance()->query_keys( - user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); -} - -void -DeviceVerificationFlow::setDeviceId(QString deviceID) -{ - this->deviceId = deviceID; -} - -void -DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) -{ - this->method = method_; -} - -void -DeviceVerificationFlow::setType(Type type_) +DeviceVerificationFlow::setEventId(std::string event_id_) { - this->type = type_; + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = event_id_; + this->transaction_id = event_id_; } void -DeviceVerificationFlow::setSender(bool sender_) +DeviceVerificationFlow::handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg, + std::string) { - this->sender = sender_; - if (this->sender) - this->transaction_id = http::client()->generate_txn_id(); -} + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().event_id != this->relation.event_id) + return; + } + if ((std::find(msg.key_agreement_protocols.begin(), + msg.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) && + (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != msg.hashes.end()) && + (std::find(msg.message_authentication_codes.begin(), + msg.message_authentication_codes.end(), + "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) { + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.short_authentication_string.end()) { + this->method = mtx::events::msg::SASMethods::Emoji; + } else if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.short_authentication_string.end()) { + this->method = mtx::events::msg::SASMethods::Decimal; + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); + return; + } + if (!sender) + this->canonical_json = nlohmann::json(msg); + else { + if (utils::localUser().toStdString() < this->toClient.to_string()) { + this->canonical_json = nlohmann::json(msg); + } + } -void -DeviceVerificationFlow::setEventId(std::string event_id_) -{ - this->relation.rel_type = mtx::common::RelationType::Reference; - this->relation.event_id = event_id_; - this->transaction_id = event_id_; + if (state_ != PromptStartVerification) + this->acceptVerificationRequest(); + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); + } } //! accepts a verification @@ -382,30 +423,15 @@ DeviceVerificationFlow::acceptVerificationRequest() req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; req.message_authentication_code = "hkdf-hmac-sha256"; - if (this->method == DeviceVerificationFlow::Method::Emoji) + if (this->method == mtx::events::msg::SASMethods::Emoji) req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; - else if (this->method == DeviceVerificationFlow::Method::Decimal) + else if (this->method == mtx::events::msg::SASMethods::Decimal) req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal}; req.commitment = mtx::crypto::bin2base64_unpadded( mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump())); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to accept verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationAccept); - } + send(req); + setState(WaitingForKeys); } //! responds verification request void @@ -416,23 +442,8 @@ DeviceVerificationFlow::sendVerificationReady() req.from_device = http::client()->device_id(); req.methods = {mtx::events::msg::VerificationMethods::SASv1}; - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - req.transaction_id = this->transaction_id; - mtx::requests::ToDeviceMessages body; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification ready: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationReady); - } + send(req); + setState(WaitingForKeys); } //! accepts a verification void @@ -440,23 +451,7 @@ DeviceVerificationFlow::sendVerificationDone() { mtx::events::msg::KeyVerificationDone req; - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification done: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationDone); - } + send(req); } //! starts the verification flow void @@ -474,22 +469,14 @@ DeviceVerificationFlow::startVerificationRequest() if (this->type == DeviceVerificationFlow::Type::ToDevice) { mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - this->canonical_json = nlohmann::json(req); - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [body](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to start verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + req.transaction_id = this->transaction_id; + this->canonical_json = nlohmann::json(req); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; this->canonical_json = nlohmann::json(req); - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationStart); } + send(req); + setState(WaitingForOtherToAccept); } //! sends a verification request void @@ -503,28 +490,18 @@ DeviceVerificationFlow::sendVerificationRequest() if (this->type == DeviceVerificationFlow::Type::ToDevice) { QDateTime currentTime = QDateTime::currentDateTimeUtc(); - req.transaction_id = this->transaction_id; - req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - - mtx::requests::ToDeviceMessages body; + req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.to = this->userId.toStdString(); + req.to = this->toClient.to_string(); req.msgtype = "m.key.verification.request"; req.body = "User is requesting to verify keys with you. However, your client does " "not support this method, so you will need to use the legacy method of " "key verification."; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationRequest); } + + send(req); + setState(WaitingForOtherToAccept); } //! cancels a verification flow void @@ -534,7 +511,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c if (error_code == DeviceVerificationFlow::Error::UnknownMethod) { req.code = "m.unknown_method"; - req.reason = "unknown method recieved"; + req.reason = "unknown method received"; } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) { req.code = "m.mismatched_commitment"; req.reason = "commitment didn't match"; @@ -550,42 +527,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c } else if (error_code == DeviceVerificationFlow::Error::User) { req.code = "m.user"; req.reason = "user cancelled the verification"; + } else if (error_code == DeviceVerificationFlow::Error::OutOfOrder) { + req.code = "m.unexpected_message"; + req.reason = "received messages out of order"; } - emit this->verificationCanceled(); + this->error_ = error_code; + emit errorChanged(); + this->setState(Failed); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - req.transaction_id = this->transaction_id; - mtx::requests::ToDeviceMessages body; - - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to cancel verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - this->deleteLater(); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationCancel); - this->deleteLater(); - } - - // TODO : Handle Blocking user better - // auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - // if (verified_cache.has_value()) { - // verified_cache->device_blocked.push_back(this->deviceId.toStdString()); - // cache::setVerifiedCache(this->userId.toStdString(), - // verified_cache.value()); - // } else { - // cache::setVerifiedCache( - // this->userId.toStdString(), - // DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}}); - // } + send(req); } //! sends the verification key void @@ -595,23 +546,7 @@ DeviceVerificationFlow::sendVerificationKey() req.key = this->sas->public_key(); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification key: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey); - } + send(req); } mtx::events::msg::KeyVerificationMac @@ -660,68 +595,102 @@ DeviceVerificationFlow::sendVerificationMac() this->transaction_id, key_list); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification MAC: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - if (this->isMacVerified == true) - this->acceptDevice(); - else - this->isMacVerified = true; - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationMac); - } + send(req); + + setState(WaitingForMac); + acceptDevice(); } //! Completes the verification flow void DeviceVerificationFlow::acceptDevice() { - cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.toStdString()); + if (!isMacVerified) { + setState(WaitingForMac); + } else if (state_ == WaitingForMac) { + cache::markDeviceVerified(this->toClient.to_string(), this->deviceId.toStdString()); + this->sendVerificationDone(); + setState(Success); + } +} + +void +DeviceVerificationFlow::unverify() +{ + cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString()); - emit deviceVerified(); emit refreshProfile(); - this->deleteLater(); } -//! callback function to keep track of devices -void -DeviceVerificationFlow::callback_fn(const UserKeyCache &res, - mtx::http::RequestErr err, - std::string user_id) +QSharedPointer +DeviceVerificationFlow::NewInRoomVerification(QObject *parent_, + TimelineModel *timelineModel_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString event_id_) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, other_user_, "")); + + flow->event_id = event_id_.toStdString(); - if (res.device_keys.empty() || - (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; + if (std::find(msg.methods.begin(), + msg.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) { + flow->cancelVerification(UnknownMethod); } - for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) { - // TODO: Verify Signatures - this->device_keys[algorithm] = key; + return flow; +} +QSharedPointer +DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString txn_id_) +{ + QSharedPointer flow(new DeviceVerificationFlow( + parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device))); + flow->transaction_id = txn_id_.toStdString(); + + if (std::find(msg.methods.begin(), + msg.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) { + flow->cancelVerification(UnknownMethod); } + + return flow; } +QSharedPointer +DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, + const mtx::events::msg::KeyVerificationStart &msg, + QString other_user_, + QString txn_id_) +{ + QSharedPointer flow(new DeviceVerificationFlow( + parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device))); + flow->transaction_id = txn_id_.toStdString(); -void -DeviceVerificationFlow::unverify() + flow->handleStartMessage(msg, ""); + + return flow; +} +QSharedPointer +DeviceVerificationFlow::InitiateUserVerification(QObject *parent_, + TimelineModel *timelineModel_, + QString userid) { - cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString()); + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, userid, "")); + flow->sender = true; + return flow; +} +QSharedPointer +DeviceVerificationFlow::InitiateDeviceVerification(QObject *parent_, QString userid, QString device) +{ + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::ToDevice, nullptr, userid, device)); - emit refreshProfile(); + flow->sender = true; + flow->transaction_id = http::client()->generate_txn_id(); + + return flow; } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index de7a4567..1fe3919b 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -5,41 +5,84 @@ #include #include "CacheCryptoStructs.h" +#include "Logging.h" #include "MatrixClient.h" #include "Olm.h" +#include "timeline/TimelineModel.h" class QTimer; using sas_ptr = std::unique_ptr; -class TimelineModel; - +// clang-format off +/* + * Stolen from fluffy chat :D + * + * State | +-------------+ +-----------+ | + * | | AliceDevice | | BobDevice | | + * | | (sender) | | | | + * | +-------------+ +-----------+ | + * promptStartVerify | | | | + * | o | (m.key.verification.request) | | + * | p |-------------------------------->| (ASK FOR VERIFICATION REQUEST) | + * waitForOtherAccept | t | | | promptStartVerify + * && | i | (m.key.verification.ready) | | + * no commitment | o |<--------------------------------| | + * && | n | | | + * no canonical_json | a | (m.key.verification.start) | | waitingForKeys + * | l |<--------------------------------| Not sending to prevent the glare resolve| && no commitment + * | | | | && no canonical_json + * | | m.key.verification.start | | + * waitForOtherAccept | |-------------------------------->| (IF NOT ALREADY ASKED, | + * && | | | ASK FOR VERIFICATION REQUEST) | promptStartVerify, if not accepted + * canonical_json | | m.key.verification.accept | | + * | |<--------------------------------| | + * waitForOtherAccept | | | | waitingForKeys + * && | | m.key.verification.key | | && canonical_json + * commitment | |-------------------------------->| | && commitment + * | | | | + * | | m.key.verification.key | | + * | |<--------------------------------| | + * compareEmoji/Number| | | | compareEmoji/Number + * | | COMPARE EMOJI / NUMBERS | | + * | | | | + * waitingForMac | | m.key.verification.mac | | waitingForMac + * | success |<------------------------------->| success | + * | | | | + * success/fail | | m.key.verification.done | | success/fail + * | |<------------------------------->| | + */ +// clang-format on class DeviceVerificationFlow : public QObject { Q_OBJECT // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") - Q_PROPERTY(QString tranId READ getTransactionId WRITE setTransactionId) - Q_PROPERTY(bool sender READ getSender WRITE setSender) - Q_PROPERTY(QString userId READ getUserId WRITE setUserId) - Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) - Q_PROPERTY(Method method READ getMethod WRITE setMethod) - Q_PROPERTY(Type type READ getType WRITE setType) + Q_PROPERTY(QString state READ state NOTIFY stateChanged) + Q_PROPERTY(Error error READ error CONSTANT) + Q_PROPERTY(QString userId READ getUserId CONSTANT) + Q_PROPERTY(QString deviceId READ getDeviceId CONSTANT) + Q_PROPERTY(bool sender READ getSender CONSTANT) Q_PROPERTY(std::vector sasList READ getSasList CONSTANT) public: - enum Type + enum State { - ToDevice, - RoomMsg + PromptStartVerification, + WaitingForOtherToAccept, + WaitingForKeys, + CompareEmoji, + CompareNumber, + WaitingForMac, + Success, + Failed, }; - Q_ENUM(Type) + Q_ENUM(State) - enum Method + enum Type { - Decimal, - Emoji + ToDevice, + RoomMsg }; - Q_ENUM(Method) enum Error { @@ -48,36 +91,75 @@ public: MismatchedSAS, KeyMismatch, Timeout, - User + User, + OutOfOrder, }; Q_ENUM(Error) - DeviceVerificationFlow( - QObject *parent = nullptr, - DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice, - TimelineModel *model = nullptr); + static QSharedPointer NewInRoomVerification( + QObject *parent_, + TimelineModel *timelineModel_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString event_id_); + static QSharedPointer NewToDeviceVerification( + QObject *parent_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString txn_id_); + static QSharedPointer NewToDeviceVerification( + QObject *parent_, + const mtx::events::msg::KeyVerificationStart &msg, + QString other_user_, + QString txn_id_); + static QSharedPointer + InitiateUserVerification(QObject *parent_, TimelineModel *timelineModel_, QString userid); + static QSharedPointer InitiateDeviceVerification(QObject *parent, + QString userid, + QString device); + // getters - QString getTransactionId(); + QString state(); + Error error() { return error_; } QString getUserId(); QString getDeviceId(); - Method getMethod(); - Type getType(); - std::vector getSasList(); bool getSender(); + std::vector getSasList(); + QString transactionId() { return QString::fromStdString(this->transaction_id); } // setters - void setTransactionId(QString transaction_id_); - void setUserId(QString userID); void setDeviceId(QString deviceID); - void setMethod(Method method_); - void setType(Type type_); - void setSender(bool sender_); void setEventId(std::string event_id); void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id); - nlohmann::json canonical_json; - public slots: + //! unverifies a device + void unverify(); + //! Continues the flow + void next(); + //! Cancel the flow + void cancel() { cancelVerification(User); } + +signals: + void refreshProfile(); + void stateChanged(); + void errorChanged(); + +private: + DeviceVerificationFlow(QObject *, + DeviceVerificationFlow::Type flow_type, + TimelineModel *model, + QString userID, + QString deviceId_); + void setState(State state) + { + if (state != state_) { + state_ = state; + emit stateChanged(); + } + } + + void handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg, std::string); //! sends a verification request void sendVerificationRequest(); //! accepts a verification request @@ -96,37 +178,60 @@ public slots: void sendVerificationMac(); //! Completes the verification flow void acceptDevice(); - //! unverifies a device - void unverify(); -signals: - void verificationRequestAccepted(Method method); - void deviceVerified(); - void timedout(); - void verificationCanceled(); - void refreshProfile(); - void deleteFlow(); + // for to_device messages + std::string transaction_id; + // for room messages + std::optional room_id; + std::optional event_id; -private: - // general - QString userId; - QString deviceId; - Method method = Method::Emoji; - Type type; bool sender; - QTimer *timeout = nullptr; + Type type; + mtx::identifiers::User toClient; + QString deviceId; + + mtx::events::msg::SASMethods method = mtx::events::msg::SASMethods::Emoji; + QTimer *timeout = nullptr; sas_ptr sas; - bool isMacVerified = false; std::string mac_method; std::string commitment; - mtx::identifiers::User toClient; + nlohmann::json canonical_json; + std::vector sasList; std::map device_keys; - // for to_device messages - std::string transaction_id; - // for room messages - std::optional room_id; - std::optional event_id; TimelineModel *model_; mtx::common::RelatesTo relation; + + State state_ = PromptStartVerification; + Error error_; + + bool isMacVerified = false; + + template + void send(T msg) + { + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + msg.transaction_id = this->transaction_id; + body[this->toClient][deviceId.toStdString()] = msg; + + http::client()->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn( + "failed to send verification to_device message: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { + if constexpr (!std::is_same_v) + msg.relates_to = this->relation; + (model_)->sendMessageEvent(msg, mtx::events::to_device_content_to_type); + } + + nhlog::net()->debug( + "Sent verification step: {} in state: {}", + mtx::events::to_string(mtx::events::to_device_content_to_type), + state().toStdString()); + } }; diff --git a/src/Olm.cpp b/src/Olm.cpp index 8219ce4c..f4cb2209 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -80,40 +80,40 @@ handle_to_device_messages(const std::vector>(msg); - ChatPage::instance()->recievedDeviceVerificationAccept(message.content); + ChatPage::instance()->receivedDeviceVerificationAccept(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationRequest(message.content, + ChatPage::instance()->receivedDeviceVerificationRequest(message.content, message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationCancel(message.content); + ChatPage::instance()->receivedDeviceVerificationCancel(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationKey(message.content); + ChatPage::instance()->receivedDeviceVerificationKey(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationMac(message.content); + ChatPage::instance()->receivedDeviceVerificationMac(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationStart(message.content, + ChatPage::instance()->receivedDeviceVerificationStart(message.content, message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationReady(message.content); + ChatPage::instance()->receivedDeviceVerificationReady(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationDone(message.content); + ChatPage::instance()->receivedDeviceVerificationDone(message.content); } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } @@ -153,42 +153,42 @@ handle_olm_message(const OlmMessage &msg) std::string msg_type = payload["type"]; if (msg_type == to_string(mtx::events::EventType::KeyVerificationAccept)) { - ChatPage::instance()->recievedDeviceVerificationAccept( + ChatPage::instance()->receivedDeviceVerificationAccept( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { - ChatPage::instance()->recievedDeviceVerificationRequest( + ChatPage::instance()->receivedDeviceVerificationRequest( payload["content"], payload["sender"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { - ChatPage::instance()->recievedDeviceVerificationCancel( + ChatPage::instance()->receivedDeviceVerificationCancel( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { - ChatPage::instance()->recievedDeviceVerificationKey( + ChatPage::instance()->receivedDeviceVerificationKey( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { - ChatPage::instance()->recievedDeviceVerificationMac( + ChatPage::instance()->receivedDeviceVerificationMac( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { - ChatPage::instance()->recievedDeviceVerificationStart( + ChatPage::instance()->receivedDeviceVerificationStart( payload["content"], payload["sender"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { - ChatPage::instance()->recievedDeviceVerificationReady( + ChatPage::instance()->receivedDeviceVerificationReady( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { - ChatPage::instance()->recievedDeviceVerificationDone( + ChatPage::instance()->receivedDeviceVerificationDone( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::RoomKey)) { diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 29b3c239..dc92a37f 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -299,7 +299,7 @@ EventStore::handleSync(const mtx::responses::Timeline &events) mtx::events::msg::KeyVerificationReady>>(d_event)) { auto msg = std::get_if>(d_event); - ChatPage::instance()->recievedDeviceVerificationReady( + ChatPage::instance()->receivedDeviceVerificationReady( msg->content); } } @@ -328,43 +328,43 @@ EventStore::handle_room_verification(mtx::events::collections::TimelineEvents ev auto msg = std::get>(event); last_verification_cancel_event = msg; - ChatPage::instance()->recievedDeviceVerificationCancel(msg.content); + ChatPage::instance()->receivedDeviceVerificationCancel(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationAccept(msg.content); + ChatPage::instance()->receivedDeviceVerificationAccept(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationKey(msg.content); + ChatPage::instance()->receivedDeviceVerificationKey(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationMac(msg.content); + ChatPage::instance()->receivedDeviceVerificationMac(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationReady(msg.content); + ChatPage::instance()->receivedDeviceVerificationReady(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationDone(msg.content); + ChatPage::instance()->receivedDeviceVerificationDone(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationStart(msg.content, msg.sender); + ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender); return; } } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 5e8952fc..e8d381df 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -254,7 +254,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj &EventStore::startDMVerification, this, [this](mtx::events::RoomEvent msg) { - ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this); + ChatPage::instance()->receivedRoomDeviceVerificationRequest(msg, this); }); connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { this->updateFlowEventId(event_id); @@ -793,7 +793,7 @@ TimelineModel::viewDecryptedRawMessage(QString id) const void TimelineModel::openUserProfile(QString userid) { - emit openProfile(new UserProfile(room_id_, userid, this)); + emit openProfile(new UserProfile(room_id_, userid, manager_, this)); } void diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 03dd4773..250cd5f0 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -28,22 +28,6 @@ Q_DECLARE_METATYPE(std::vector) namespace msgs = mtx::events::msg; -void -DeviceVerificationList::add(QString tran_id) -{ - this->deviceVerificationList.push_back(tran_id); -} -void -DeviceVerificationList::remove(QString tran_id) -{ - this->deviceVerificationList.removeOne(tran_id); -} -bool -DeviceVerificationList::exist(QString tran_id) -{ - return this->deviceVerificationList.contains(tran_id); -} - void TimelineViewManager::updateEncryptedDescriptions() { @@ -134,7 +118,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); - qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); + qmlRegisterUncreatableType( + "im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!"); qmlRegisterUncreatableType( "im.nheko", 1, @@ -163,7 +148,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin 0, "EmojiCategory", "Error: Only enums"); - this->dvList = new DeviceVerificationList; #ifdef USE_QUICK_VIEW view = new QQuickView(); @@ -183,7 +167,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin }); #endif container->setMinimumSize(200, 200); - view->rootContext()->setContextProperty("deviceVerificationList", this->dvList); updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); @@ -197,102 +180,55 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &TimelineViewManager::updateEncryptedDescriptions); connect( dynamic_cast(parent), - &ChatPage::recievedRoomDeviceVerificationRequest, + &ChatPage::receivedRoomDeviceVerificationRequest, this, [this](const mtx::events::RoomEvent &message, TimelineModel *model) { - if (!(this->dvList->exist(QString::fromStdString(message.event_id)))) { - auto flow = new DeviceVerificationFlow( - this, DeviceVerificationFlow::Type::RoomMsg, model); - if (std::find(message.content.methods.begin(), - message.content.methods.end(), - mtx::events::msg::VerificationMethods::SASv1) != - message.content.methods.end()) { - flow->setEventId(message.event_id); - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(message.event_id), - QString::fromStdString(message.sender), - QString::fromStdString(message.content.from_device), - true); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } - } - }); - connect( - dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationRequest, - this, - [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { - if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) { - auto flow = new DeviceVerificationFlow(this); - if (std::find(msg.methods.begin(), - msg.methods.end(), - mtx::events::msg::VerificationMethods::SASv1) != - msg.methods.end()) { - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(msg.transaction_id.value()), - QString::fromStdString(sender), - QString::fromStdString(msg.from_device)); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } - } - }); - connect( - dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationStart, - this, - [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { - if (msg.transaction_id.has_value()) { - if (!(this->dvList->exist( - QString::fromStdString(msg.transaction_id.value())))) { - auto flow = new DeviceVerificationFlow(this); - flow->canonical_json = nlohmann::json(msg); - if ((std::find(msg.key_agreement_protocols.begin(), - msg.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.key_agreement_protocols.end()) && - (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != - msg.hashes.end()) && - (std::find(msg.message_authentication_codes.begin(), - msg.message_authentication_codes.end(), - "hmac-sha256") != - msg.message_authentication_codes.end())) { - if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.short_authentication_string.end()) { - flow->setMethod( - DeviceVerificationFlow::Method::Emoji); - } else if (std::find( - msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.short_authentication_string.end()) { - flow->setMethod( - DeviceVerificationFlow::Method::Decimal); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - return; - } - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(msg.transaction_id.value()), - QString::fromStdString(sender), - QString::fromStdString(msg.from_device)); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } + 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()); } } }); + connect(dynamic_cast(parent), + &ChatPage::receivedDeviceVerificationRequest, + this, + [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { + 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()); + } + } + }); + connect(dynamic_cast(parent), + &ChatPage::receivedDeviceVerificationStart, + this, + [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { + 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()); + } + } + }); connect(parent, &ChatPage::loggedOut, this, [this]() { isInitialSync_ = true; emit initialSyncChanged(true); @@ -428,6 +364,46 @@ TimelineViewManager::openRoomSettings() const MainWindow::instance()->openRoomSettings(timeline_->roomId()); } +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()) { + auto model = models.value(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 share room with this user found. Create an " + "encrypted room with this user and try again.")); +} +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 4779d3cd..12e49080 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -24,20 +24,6 @@ class ColorImageProvider; class UserSettings; class ChatPage; -class DeviceVerificationList : public QObject -{ - Q_OBJECT -public: - Q_INVOKABLE void add(QString tran_id); - Q_INVOKABLE void remove(QString tran_id); - Q_INVOKABLE bool exist(QString tran_id); -signals: - void updateProfile(QString userId); - -private: - QVector deviceVerificationList; -}; - class TimelineViewManager : public QObject { Q_OBJECT @@ -77,6 +63,9 @@ public: Q_INVOKABLE void openLeaveRoomDialog() const; Q_INVOKABLE void openRoomSettings() const; + void verifyUser(QString userid); + void verifyDevice(QString userid, QString deviceid); + signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info); @@ -84,11 +73,7 @@ signals: void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void newDeviceVerificationRequest(DeviceVerificationFlow *flow, - QString transactionId, - QString userId, - QString deviceId, - bool isRequest = false); + void newDeviceVerificationRequest(DeviceVerificationFlow *flow); void inviteUsers(QStringList users); void showRoomList(); void narrowViewChanged(); @@ -180,7 +165,7 @@ private: QSharedPointer settings; QHash userColors; - DeviceVerificationList *dvList; + QHash> dvList; }; 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 2ea3f7b6..2a1eecdf 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -6,13 +6,18 @@ #include "Utils.h" #include "mtx/responses/crypto.hpp" #include "timeline/TimelineModel.h" +#include "timeline/TimelineViewManager.h" #include // only for debugging -UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent) +UserProfile::UserProfile(QString roomid, + QString userid, + TimelineViewManager *manager_, + TimelineModel *parent) : QObject(parent) , roomid_(roomid) , userid_(userid) + , manager(manager_) , model(parent) { fetchDeviceList(this->userid_); @@ -270,44 +275,18 @@ UserProfile::startChat() emit ChatPage::instance()->createRoom(req); } -DeviceVerificationFlow * -UserProfile::createFlow(bool isVerifyUser) +void +UserProfile::verify(QString device) { - if (!isVerifyUser) - return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + if (!device.isEmpty()) + manager->verifyDevice(userid_, device); else { - std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION" - << std::endl; - 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(), - (this->userid()).toStdString()) != - room_members.end()) { - std::cout - << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id - << std::endl; - if (this->roomid_.toStdString() == room_id) { - auto newflow = new DeviceVerificationFlow( - this, - DeviceVerificationFlow::Type::RoomMsg, - this->model); - return (std::move(newflow)); - } else { - std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY " - "NOT IN THAT ROOM : " - << room_id << std::endl; - } - } - } - } - - std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; - return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + manager->verifyUser(userid_); } } + +void +UserProfile::unverify(QString device) +{ + cache::markDeviceUnverified(userid_.toStdString(), device.toStdString()); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index de55b6ab..18933727 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -21,6 +21,7 @@ Q_ENUM_NS(Status) class DeviceVerificationFlow; class TimelineModel; +class TimelineViewManager; class DeviceInfo { @@ -84,7 +85,10 @@ class UserProfile : public QObject Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) public: - UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr); + UserProfile(QString roomid, + QString userid, + TimelineViewManager *manager_, + TimelineModel *parent = nullptr); DeviceInfoModel *deviceList(); @@ -93,7 +97,8 @@ public: QString avatarUrl(); bool getUserStatus(); - Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser); + Q_INVOKABLE void verify(QString device = ""); + Q_INVOKABLE void unverify(QString device = ""); Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); @@ -105,6 +110,7 @@ private: std::optional cross_verified; DeviceInfoModel deviceList_; bool isUserVerified = false; + TimelineViewManager *manager; TimelineModel *model; void callback_fn(const mtx::responses::QueryKeys &res, -- cgit 1.5.1 From 7b6fab33731e369a860ab217709190e9457d6d76 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 7 Oct 2020 23:03:14 +0200 Subject: Calculate verification status from cross-signing sigs and update dynamically --- resources/qml/UserProfile.qml | 3 +- .../qml/device-verification/DeviceVerification.qml | 4 +- src/Cache.cpp | 178 +++++++++++++++++---- src/Cache.h | 8 +- src/CacheCryptoStructs.h | 21 ++- src/Cache_p.h | 11 +- src/timeline/TimelineModel.cpp | 2 +- src/ui/UserProfile.cpp | 145 +++++------------ src/ui/UserProfile.h | 8 +- 9 files changed, 223 insertions(+), 157 deletions(-) (limited to 'src/ui/UserProfile.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index e7dcc777..562dd4f9 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -56,7 +56,7 @@ ApplicationWindow{ Button { id: verifyUserButton - text: "Verify" + text: qsTr("Verify") Layout.alignment: Qt.AlignHCenter enabled: !profile.isUserVerified visible: !profile.isUserVerified @@ -155,7 +155,6 @@ ApplicationWindow{ onClicked: { if(model.verificationStatus == VerificationStatus.VERIFIED){ profile.unverify(model.deviceId) - deviceVerificationList.updateProfile(newFlow.userId); }else{ profile.verify(model.deviceId); } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index d6185a01..2e8f7504 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -1,6 +1,6 @@ -import QtQuick 2.3 +import QtQuick 2.10 import QtQuick.Controls 2.10 -import QtQuick.Window 2.2 +import QtQuick.Window 2.10 import im.nheko 1.0 diff --git a/src/Cache.cpp b/src/Cache.cpp index 63f6e426..d6da03c6 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -3184,6 +3184,28 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query } txn.commit(); + + std::map tmp; + const auto local_user = utils::localUser().toStdString(); + + { + std::unique_lock lock(verification_storage.verification_storage_mtx); + for (auto &[user_id, update] : updates) { + if (user_id == local_user) { + std::swap(tmp, verification_storage.status); + } else { + verification_storage.status.erase(user_id); + } + } + } + for (auto &[user_id, update] : updates) { + if (user_id == local_user) { + for (const auto &[user, status] : tmp) + emit verificationStatusChanged(user); + } else { + emit verificationStatusChanged(user_id); + } + } } void @@ -3236,23 +3258,19 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn, void to_json(json &j, const VerificationCache &info) { - j["verified_master_key"] = info.verified_master_key; - j["cross_verified"] = info.cross_verified; - j["device_verified"] = info.device_verified; - j["device_blocked"] = info.device_blocked; + j["device_verified"] = info.device_verified; + j["device_blocked"] = info.device_blocked; } void from_json(const json &j, VerificationCache &info) { - info.verified_master_key = j.at("verified_master_key"); - info.cross_verified = j.at("cross_verified").get>(); - info.device_verified = j.at("device_verified").get>(); - info.device_blocked = j.at("device_blocked").get>(); + info.device_verified = j.at("device_verified").get>(); + info.device_blocked = j.at("device_blocked").get>(); } std::optional -Cache::verificationStatus(const std::string &user_id) +Cache::verificationCache(const std::string &user_id) { lmdb::val verifiedVal; @@ -3298,6 +3316,23 @@ Cache::markDeviceVerified(const std::string &user_id, const std::string &key) txn.commit(); } catch (std::exception &) { } + + const auto local_user = utils::localUser().toStdString(); + std::map tmp; + { + std::unique_lock lock(verification_storage.verification_storage_mtx); + if (user_id == local_user) { + std::swap(tmp, verification_storage.status); + } else { + verification_storage.status.erase(user_id); + } + } + if (user_id == local_user) { + for (const auto &[user, status] : tmp) + emit verificationStatusChanged(user); + } else { + emit verificationStatusChanged(user_id); + } } void @@ -3325,27 +3360,112 @@ Cache::markDeviceUnverified(const std::string &user_id, const std::string &key) txn.commit(); } catch (std::exception &) { } + + const auto local_user = utils::localUser().toStdString(); + std::map tmp; + { + std::unique_lock lock(verification_storage.verification_storage_mtx); + if (user_id == local_user) { + std::swap(tmp, verification_storage.status); + } else { + verification_storage.status.erase(user_id); + } + } + if (user_id == local_user) { + for (const auto &[user, status] : tmp) + emit verificationStatusChanged(user); + } else { + emit verificationStatusChanged(user_id); + } } -void -Cache::markMasterKeyVerified(const std::string &user_id, const std::string &key) +VerificationStatus +Cache::verificationStatus(const std::string &user_id) { - lmdb::val val; + std::unique_lock lock(verification_storage.verification_storage_mtx); + if (verification_storage.status.count(user_id)) + return verification_storage.status.at(user_id); - auto txn = lmdb::txn::begin(env_); - auto db = getVerificationDb(txn); + VerificationStatus status; + + if (auto verifCache = verificationCache(user_id)) { + status.verified_devices = verifCache->device_verified; + } + + const auto local_user = utils::localUser().toStdString(); + + if (user_id == local_user) + status.verified_devices.push_back(http::client()->device_id()); + + verification_storage.status[user_id] = status; + + auto verifyAtLeastOneSig = [](const auto &toVerif, + const std::map &keys, + const std::string &keyOwner) { + if (!toVerif.signatures.count(keyOwner)) + return false; + + for (const auto &[key_id, signature] : toVerif.signatures.at(keyOwner)) { + if (!keys.count(key_id)) + continue; + + if (mtx::crypto::ed25519_verify_signature( + keys.at(key_id), json(toVerif), signature)) + return true; + } + return false; + }; try { - VerificationCache verified_state; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); - if (res) { - verified_state = json::parse(std::string_view(val.data(), val.size())); + // for local user verify this device_key -> our master_key -> our self_signing_key + // -> our device_keys + // + // for other user verify this device_key -> our master_key -> our user_signing_key + // -> their master_key -> their self_signing_key -> their device_keys + // + // This means verifying the other user adds 2 extra steps,verifying our user_signing + // key and their master key + auto ourKeys = userKeys(local_user); + auto theirKeys = userKeys(user_id); + if (!ourKeys || !theirKeys) + return status; + + if (!mtx::crypto::ed25519_verify_signature( + olm::client()->identity_keys().ed25519, + json(ourKeys->master_keys), + ourKeys->master_keys.signatures.at(local_user) + .at("ed25519:" + http::client()->device_id()))) + return status; + + auto master_keys = ourKeys->master_keys.keys; + + if (user_id != local_user) { + if (!verifyAtLeastOneSig( + ourKeys->user_signing_keys, master_keys, local_user)) + return status; + + if (!verifyAtLeastOneSig( + theirKeys->master_keys, ourKeys->user_signing_keys.keys, local_user)) + return status; + + master_keys = theirKeys->master_keys.keys; } - verified_state.verified_master_key = key; - lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); - txn.commit(); + status.user_verified = true; + + if (!verifyAtLeastOneSig(theirKeys->self_signing_keys, master_keys, user_id)) + return status; + + for (const auto &[device, device_key] : theirKeys->device_keys) { + if (verifyAtLeastOneSig( + device_key, theirKeys->self_signing_keys.keys, user_id)) + status.verified_devices.push_back(device_key.device_id); + } + + verification_storage.status[user_id] = status; + return status; } catch (std::exception &) { + return status; } } @@ -3551,28 +3671,22 @@ updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &k } // device & user verification cache -std::optional +std::optional verificationStatus(const std::string &user_id) { return instance_->verificationStatus(user_id); } void -markDeviceVerified(const std::string &user_id, const std::string &key) -{ - instance_->markDeviceVerified(user_id, key); -} - -void -markDeviceUnverified(const std::string &user_id, const std::string &key) +markDeviceVerified(const std::string &user_id, const std::string &device) { - instance_->markDeviceUnverified(user_id, key); + instance_->markDeviceVerified(user_id, device); } void -markMasterKeyVerified(const std::string &user_id, const std::string &key) +markDeviceUnverified(const std::string &user_id, const std::string &device) { - instance_->markMasterKeyVerified(user_id, key); + instance_->markDeviceUnverified(user_id, device); } std::vector diff --git a/src/Cache.h b/src/Cache.h index fca80145..cd96708e 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -67,14 +67,12 @@ void updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery); // device & user verification cache -std::optional +std::optional verificationStatus(const std::string &user_id); void -markDeviceVerified(const std::string &user_id, const std::string &key); +markDeviceVerified(const std::string &user_id, const std::string &device); void -markDeviceUnverified(const std::string &user_id, const std::string &key); -void -markMasterKeyVerified(const std::string &user_id, const std::string &key); +markDeviceUnverified(const std::string &user_id, const std::string &device); //! Load saved data for the display names & avatars. void diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 10636ac6..935d6493 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -66,6 +66,23 @@ struct OlmSessionStorage std::mutex group_inbound_mtx; }; +//! Verification status of a single user +struct VerificationStatus +{ + //! True, if the users master key is verified + bool user_verified = false; + //! List of all devices marked as verified + std::vector verified_devices; +}; + +//! In memory cache of verification status +struct VerificationStorage +{ + //! mapping of user to verification status + std::map status; + std::mutex verification_storage_mtx; +}; + // this will store the keys of the user with whom a encrypted room is shared with struct UserKeyCache { @@ -90,12 +107,8 @@ struct VerificationCache { //! list of verified device_ids with device-verification std::vector device_verified; - //! list of verified device_ids with cross-signing, calculated from master key - std::vector cross_verified; //! list of devices the user blocks std::vector device_blocked; - //! The verified master key. - std::string verified_master_key; }; void diff --git a/src/Cache_p.h b/src/Cache_p.h index b37eae58..b3f4c58c 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -67,10 +67,9 @@ public: const std::vector &user_ids); // device & user verification cache - std::optional verificationStatus(const std::string &user_id); - void markDeviceVerified(const std::string &user_id, const std::string &key); - void markDeviceUnverified(const std::string &user_id, const std::string &key); - void markMasterKeyVerified(const std::string &user_id, const std::string &key); + VerificationStatus verificationStatus(const std::string &user_id); + void markDeviceVerified(const std::string &user_id, const std::string &device); + void markDeviceUnverified(const std::string &user_id, const std::string &device); static void removeDisplayName(const QString &room_id, const QString &user_id); static void removeAvatarUrl(const QString &room_id, const QString &user_id); @@ -283,6 +282,7 @@ signals: void removeNotification(const QString &room_id, const QString &event_id); void userKeysUpdate(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery); + void verificationStatusChanged(const std::string &userid); private: //! Save an invited room. @@ -576,6 +576,8 @@ private: return QString::fromStdString(event.state_key); } + std::optional verificationCache(const std::string &user_id); + void setNextBatchToken(lmdb::txn &txn, const std::string &token); void setNextBatchToken(lmdb::txn &txn, const QString &token); @@ -600,6 +602,7 @@ private: static QHash AvatarUrls; OlmSessionStorage session_storage; + VerificationStorage verification_storage; }; namespace cache { diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index e8d381df..359e95bc 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1031,7 +1031,7 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events:: try { if (!mtx::crypto::verify_identity_signature( - json(dev.second), device_id, user_id)) { + dev.second, device_id, user_id)) { nhlog::crypto()->warn( "failed to verify identity keys: {}", json(dev.second).dump(2)); diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 2a1eecdf..2bb0370f 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,5 +1,5 @@ #include "UserProfile.h" -#include "Cache.h" +#include "Cache_p.h" #include "ChatPage.h" #include "DeviceVerificationFlow.h" #include "Logging.h" @@ -8,8 +8,6 @@ #include "timeline/TimelineModel.h" #include "timeline/TimelineViewManager.h" -#include // only for debugging - UserProfile::UserProfile(QString roomid, QString userid, TimelineViewManager *manager_, @@ -21,6 +19,31 @@ UserProfile::UserProfile(QString roomid, , model(parent) { fetchDeviceList(this->userid_); + + connect(cache::client(), + &Cache::verificationStatusChanged, + this, + [this](const std::string &user_id) { + if (user_id != this->userid_.toStdString()) + return; + + auto status = cache::verificationStatus(user_id); + if (!status) + return; + this->isUserVerified = status->user_verified; + emit userStatusChanged(); + + for (auto &deviceInfo : deviceList_.deviceList_) { + deviceInfo.verification_status = + std::find(status->verified_devices.begin(), + status->verified_devices.end(), + deviceInfo.device_id.toStdString()) == + status->verified_devices.end() + ? verification::UNVERIFIED + : verification::VERIFIED; + } + deviceList_.reset(deviceList_.deviceList_); + }); } QHash @@ -126,107 +149,27 @@ UserProfile::fetchDeviceList(const QString &userID) } std::vector deviceInfo; - auto devices = other_user_keys.device_keys; - auto device_verified = cache::verificationStatus(other_user_id); - - if (device_verified.has_value()) { - // TODO: properly check cross-signing signatures here - isUserVerified = !device_verified->verified_master_key.empty(); - } - - std::optional lmk, lsk, luk, mk, sk, uk; + auto devices = other_user_keys.device_keys; + auto verificationStatus = + cache::client()->verificationStatus(other_user_id); - lmk = res.master_keys; - luk = res.user_signing_keys; - lsk = res.self_signing_keys; - mk = other_user_keys.master_keys; - uk = other_user_keys.user_signing_keys; - sk = other_user_keys.self_signing_keys; - - // First checking if the user is verified - if (luk.has_value() && mk.has_value()) { - // iterating through the public key of local user_signing keys - for (auto sign_key : luk.value().keys) { - // checking if the signatures are empty as "at" could - // cause exceptions - auto signs = mk->signatures; - if (!signs.empty() && - signs.find(local_user_id) != signs.end()) { - auto sign = signs.at(local_user_id); - try { - isUserVerified = - isUserVerified || - (olm::client()->ed25519_verify_sig( - sign_key.second, - json(mk.value()), - sign.at(sign_key.first))); - } catch (std::out_of_range &) { - isUserVerified = - isUserVerified || false; - } - } - } - } + isUserVerified = verificationStatus.user_verified; + emit userStatusChanged(); for (const auto &d : devices) { auto device = d.second; verification::Status verified = verification::Status::UNVERIFIED; - if (device_verified.has_value()) { - if (std::find(device_verified->cross_verified.begin(), - device_verified->cross_verified.end(), - d.first) != - device_verified->cross_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_verified.begin(), - device_verified->device_verified.end(), - d.first) != - device_verified->device_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_blocked.begin(), - device_verified->device_blocked.end(), - d.first) != - device_verified->device_blocked.end()) - verified = verification::Status::BLOCKED; - } else if (isUserVerified) { - device_verified = VerificationCache{}; - } - - // won't check for already verified devices - if (verified != verification::Status::VERIFIED && - isUserVerified) { - if ((sk.has_value()) && (!device.signatures.empty())) { - for (auto sign_key : sk.value().keys) { - auto signs = - device.signatures.at(other_user_id); - try { - if (olm::client() - ->ed25519_verify_sig( - sign_key.second, - json(device), - signs.at( - sign_key.first))) { - verified = - verification::Status:: - VERIFIED; - device_verified.value() - .cross_verified - .push_back(d.first); - } - } catch (std::out_of_range &) { - } - } - } - } - - // TODO(Nico): properly show cross-signing - // if (device_verified.has_value()) { - // device_verified.value().is_user_verified = - // isUserVerified; - // cache::setVerifiedCache(user_id, - // device_verified.value()); - //} + if (std::find(verificationStatus.verified_devices.begin(), + verificationStatus.verified_devices.end(), + device.device_id) != + verificationStatus.verified_devices.end() && + mtx::crypto::verify_identity_signature( + device, + DeviceId(device.device_id), + UserId(other_user_id))) + verified = verification::Status::VERIFIED; deviceInfo.push_back( {QString::fromStdString(d.first), @@ -235,14 +178,6 @@ UserProfile::fetchDeviceList(const QString &userID) verified}); } - std::cout << (isUserVerified ? "Yes" : "No") << std::endl; - - std::sort(deviceInfo.begin(), - deviceInfo.end(), - [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); - this->deviceList_.queueReset(std::move(deviceInfo)); }); }); diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 18933727..77b22323 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -74,6 +74,8 @@ public slots: private: std::vector deviceList_; + + friend class UserProfile; }; class UserProfile : public QObject @@ -83,7 +85,7 @@ class UserProfile : public QObject Q_PROPERTY(QString userid READ userid CONSTANT) Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT) Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) - Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) + Q_PROPERTY(bool isUserVerified READ getUserStatus NOTIFY userStatusChanged) public: UserProfile(QString roomid, QString userid, @@ -105,9 +107,11 @@ public: Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat(); +signals: + void userStatusChanged(); + private: QString roomid_, userid_; - std::optional cross_verified; DeviceInfoModel deviceList_; bool isUserVerified = false; TimelineViewManager *manager; -- cgit 1.5.1