From 1eb162cb6fa81c7388725414e9c68f5991372d08 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 9 Jun 2020 22:06:41 +0530 Subject: Handle Device Verification related to_device messages --- src/ChatPage.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/ChatPage.cpp') diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 518be31c..17ea2551 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -27,6 +27,7 @@ #include "Cache_p.h" #include "ChatPage.h" #include "EventAccessors.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" -- cgit 1.5.1 From 41b6ef0c32877384d4157a87aee01f67090b15a3 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 17 Jun 2020 23:58:35 +0530 Subject: Add DeviceVerificationList to keep track of all flows and Popup on recieving start or request --- resources/qml/TimelineView.qml | 18 +++--- resources/qml/UserProfile.qml | 10 +-- .../qml/device-verification/DeviceVerification.qml | 60 ++++++++++++++--- src/ChatPage.cpp | 2 +- src/DeviceVerificationFlow.cpp | 69 +++++++++++++------- src/DeviceVerificationFlow.h | 10 ++- src/emoji/EmojiModel.h | 1 - src/timeline/TimelineViewManager.cpp | 75 ++++++++++++++++++---- src/timeline/TimelineViewManager.h | 20 +++++- 9 files changed, 203 insertions(+), 62 deletions(-) (limited to 'src/ChatPage.cpp') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index e4c820f8..5170a41a 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -103,20 +103,22 @@ Page { id: deviceVerificationDialog DeviceVerification {} } + Component{ + id: deviceVerificationFlow + DeviceVerificationFlow {} + } Connections { target: timelineManager - onDeviceVerificationRequest: { - var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: deviceVerificationFlow}); + onNewDeviceVerificationRequest: { + var newFlow = deviceVerificationFlow.createObject(timelineRoot, + {userId : userId,sender: false,deviceId : deviceId,tranId:transactionId}); + deviceVerificationList.add(newFlow.tranId); + var dialog = deviceVerificationDialog.createObject(timelineRoot, + {flow: newFlow,sender: false}); dialog.show(); } } - Button { - text: "test device verification" - onClicked: timelineManager.startDummyVerification() - z: 5 - } - Label { visible: !timelineManager.timeline && !timelineManager.isInitialSync anchors.centerIn: parent diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index c40e6765..80415a29 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -33,8 +33,9 @@ ApplicationWindow{ id: deviceVerificationDialog DeviceVerification {} } - DeviceVerificationFlow { + Component{ id: deviceVerificationFlow + DeviceVerificationFlow {} } background: Item{ @@ -111,10 +112,11 @@ ApplicationWindow{ id: verifyButton text:"Verify" onClicked: { - deviceVerificationFlow.userId = user_data.userId - deviceVerificationFlow.deviceId = model.deviceID + var newFlow = deviceVerificationFlow.createObject(userProfileDialog, + {userId : user_data.userId,sender: true,deviceId : model.deviceID}); + deviceVerificationList.add(newFlow.tranId); var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: true}); + {flow: newFlow,sender: true}); dialog.show(); } contentItem: Text { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index fca360f7..ad0edeb7 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -75,7 +75,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -127,7 +132,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Deny" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -166,7 +176,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -212,7 +227,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -346,7 +366,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -385,7 +410,12 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: { dialog.close(); flow.cancelVerification(); } + onClicked: { + dialog.close(); + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } } Item { Layout.fillWidth: true @@ -419,7 +449,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } @@ -450,7 +484,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } @@ -481,7 +519,11 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Close" - onClicked: dialog.close() + onClicked: { + dialog.close() + deviceVerificationList.remove(flow.tranId); + delete flow; + } } } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 17ea2551..aba1f75d 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -26,8 +26,8 @@ #include "Cache.h" #include "Cache_p.h" #include "ChatPage.h" -#include "EventAccessors.h" #include "DeviceVerificationFlow.h" +#include "EventAccessors.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index c6652d03..efb9882b 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,7 +1,7 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" - #include "Logging.h" + #include #include // only for debugging #include @@ -13,9 +13,10 @@ namespace msgs = mtx::events::msg; DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { - qRegisterMetaType(); timeout = new QTimer(this); timeout->setSingleShot(true); + if (this->sender == true) + this->transaction_id = http::client()->generate_txn_id(); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); @@ -73,6 +74,12 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } +QString +DeviceVerificationFlow::getTransactionId() +{ + return QString::fromStdString(this->transaction_id); +} + QString DeviceVerificationFlow::getUserId() { @@ -91,10 +98,22 @@ DeviceVerificationFlow::getMethod() return this->method; } +bool +DeviceVerificationFlow::getSender() +{ + return this->sender; +} + +void +DeviceVerificationFlow::setTransactionId(QString transaction_id_) +{ + this->transaction_id = transaction_id_.toStdString(); +} + void DeviceVerificationFlow::setUserId(QString userID) { - this->userId = userID; + this->userId = userID; this->toClient = mtx::identifiers::parse(userID.toStdString()); } @@ -110,6 +129,12 @@ DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) this->method = method_; } +void +DeviceVerificationFlow::setSender(bool sender_) +{ + this->sender = sender_; +} + //! accepts a verification void DeviceVerificationFlow::acceptVerificationRequest() @@ -119,11 +144,12 @@ DeviceVerificationFlow::acceptVerificationRequest() req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = "curve25519"; + req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; - req.message_authentication_code = ""; - // req.short_authentication_string = ""; - req.commitment = ""; + req.message_authentication_code = "hkdf-hmac-sha256"; + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, + mtx::events::msg::SASMethods::Emoji}; + req.commitment = ""; emit this->verificationRequestAccepted(this->method); @@ -132,12 +158,12 @@ DeviceVerificationFlow::acceptVerificationRequest() http::client() ->send_to_device( - "m.key.verification.accept", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [this](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to accept verification request: {} {}", err->matrix_error.error, static_cast(err->status_code)); - // emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); }); } //! starts the verification flow @@ -150,22 +176,23 @@ DeviceVerificationFlow::startVerificationRequest() req.from_device = http::client()->device_id(); req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocols = {}; - req.hashes = {}; - req.message_authentication_codes = {}; - // req.short_authentication_string = ""; - qDebug()<<"Inside Start Verification"; - qDebug()<userId; + req.key_agreement_protocols = {"curve25519-hkdf-sha256"}; + req.hashes = {"sha256"}; + req.message_authentication_codes = {"hkdf-hmac-sha256", "hmac-sha256"}; + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, + mtx::events::msg::SASMethods::Emoji}; + body[this->toClient][this->deviceId.toStdString()] = req; http::client() ->send_to_device( - "m.key.verification.start", body, [](mtx::http::RequestErr err) { + 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)); + std::cout << nlohmann::json(body).dump(2) << std::endl; }); } //! sends a verification request @@ -177,8 +204,6 @@ DeviceVerificationFlow::sendVerificationRequest() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationRequest req; - this->transaction_id = http::client()->generate_txn_id(); - req.from_device = http::client()->device_id(); req.transaction_id = this->transaction_id; req.methods.resize(1); @@ -190,7 +215,7 @@ DeviceVerificationFlow::sendVerificationRequest() http::client() ->send_to_device( - "m.key.verification.request", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification request: {} {}", err->matrix_error.error, @@ -214,7 +239,7 @@ DeviceVerificationFlow::cancelVerification() http::client() ->send_to_device( - "m.key.verification.cancel", body, [this](mtx::http::RequestErr err) { + this->transaction_id, body, [this](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to cancel verification request: {} {}", err->matrix_error.error, @@ -237,7 +262,7 @@ DeviceVerificationFlow::sendVerificationKey() http::client() ->send_to_device( - "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification key: {} {}", err->matrix_error.error, @@ -260,7 +285,7 @@ DeviceVerificationFlow::sendVerificationMac() http::client() ->send_to_device( - "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + this->transaction_id, body, [](mtx::http::RequestErr err) { if (err) nhlog::net()->warn("failed to send verification MAC: {} {}", err->matrix_error.error, diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 561a3717..b651394b 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,5 +1,7 @@ #pragma once +#include "Olm.h" + #include #include @@ -9,6 +11,8 @@ 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) @@ -22,12 +26,16 @@ public: Q_ENUM(Method) DeviceVerificationFlow(QObject *parent = nullptr); + QString getTransactionId(); QString getUserId(); QString getDeviceId(); Method getMethod(); + void setTransactionId(QString transaction_id_); + bool getSender(); void setUserId(QString userID); void setDeviceId(QString deviceID); void setMethod(Method method_); + void setSender(bool sender_); public slots: //! sends a verification request @@ -55,9 +63,9 @@ private: QString userId; QString deviceId; Method method; + bool sender; QTimer *timeout = nullptr; std::string transaction_id; mtx::identifiers::User toClient; }; -Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) \ No newline at end of file diff --git a/src/emoji/EmojiModel.h b/src/emoji/EmojiModel.h index 8d43e000..88bacdee 100644 --- a/src/emoji/EmojiModel.h +++ b/src/emoji/EmojiModel.h @@ -60,5 +60,4 @@ private: EmojiCategory category_ = EmojiCategory::Search; emoji::Provider emoji_provider_; }; - } \ No newline at end of file diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index f4d1c00e..22fe4d6d 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -16,12 +16,38 @@ #include "dialogs/ImageOverlay.h" #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) +namespace msgs = mtx::events::msg; + +void +DeviceVerificationList::add(QString tran_id) +{ + this->dv_list.push_back(tran_id); +} +void +DeviceVerificationList::remove(QString tran_id) +{ + for (QVector::iterator it = 0; it != (this->dv_list).end(); ++it) { + if (*it == tran_id) { + this->dv_list.erase(it); + break; + } + } +} +bool +DeviceVerificationList::exist(QString tran_id) +{ + for (int i = 0; i < (this->dv_list).size(); ++i) { + if (dv_list[i] == tran_id) + return true; + } + return false; +} + void TimelineViewManager::updateEncryptedDescriptions() { @@ -63,12 +89,12 @@ TimelineViewManager::userColor(QString id, QColor background) return userColors.value(id); } -QString -TimelineViewManager::userPresence(QString id) const -{ - return QString::fromStdString( - mtx::presence::to_string(cache::presenceState(id.toStdString()))); -} +// QString +// TimelineViewManager::userPresence(QString id) const +// { +// return QString::fromStdString( +// mtx::presence::to_string(cache::presenceState(id.toStdString()))); +// } QString TimelineViewManager::userStatus(QString id) const { @@ -81,6 +107,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin , blurhashProvider(new BlurhashProvider()) , settings(userSettings) { + qRegisterMetaType(); qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, "im.nheko", 1, @@ -106,6 +133,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin 0, "EmojiCategory", "Error: Only enums"); + this->dvList = new DeviceVerificationList; #ifdef USE_QUICK_VIEW view = new QQuickView(); @@ -127,6 +155,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin container->setMinimumSize(200, 200); view->rootContext()->setContextProperty("timelineManager", this); view->rootContext()->setContextProperty("settings", settings.data()); + view->rootContext()->setContextProperty("deviceVerificationList", this->dvList); updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); @@ -141,6 +170,32 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &ChatPage::decryptSidebarChanged, this, &TimelineViewManager::updateEncryptedDescriptions); + connect(dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + QString tranID = QString::fromStdString(msg.content.transaction_id); + QString deviceId = QString::fromStdString(msg.content.from_device); + QString userId = QString::fromStdString(msg.sender); + if (!(this->dvList->exist(tranID))) { + emit newDeviceVerificationRequest(tranID, userId, deviceId); + } + }); + connect(dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationStart, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + QString tranID = QString::fromStdString(msg.content.transaction_id); + QString deviceId = QString::fromStdString(msg.content.from_device); + QString userId = QString::fromStdString(msg.sender); + if (!(this->dvList->exist(tranID))) { + emit newDeviceVerificationRequest(tranID, userId, deviceId); + } + }); } void @@ -468,9 +523,3 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, model->sendMessage(video); } - -void -TimelineViewManager::startDummyVerification() -{ - emit deviceVerificationRequest(new DeviceVerificationFlow(this)); -} diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 583a9e4c..70912715 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -22,6 +22,18 @@ class BlurhashProvider; class ColorImageProvider; class UserSettings; +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); + +private: + QVector dv_list; +}; + class TimelineViewManager : public QObject { Q_OBJECT @@ -44,9 +56,8 @@ public: Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); - Q_INVOKABLE void startDummyVerification(); - Q_INVOKABLE QString userPresence(QString id) const; + // Q_INVOKABLE QString userPresence(QString id) const; Q_INVOKABLE QString userStatus(QString id) const; signals: @@ -56,7 +67,7 @@ signals: void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void deviceVerificationRequest(DeviceVerificationFlow *deviceVerificationFlow); + void newDeviceVerificationRequest(QString transactionId, QString userId, QString deviceId); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); @@ -114,4 +125,7 @@ private: QSharedPointer settings; QHash userColors; + + DeviceVerificationList *dvList; }; +Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents) \ No newline at end of file -- 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/ChatPage.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 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/ChatPage.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 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/ChatPage.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 e8eeb480d51b6fc60c9807dd92195e9068582592 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Fri, 4 Sep 2020 12:02:24 +0530 Subject: Fix Wrong Emojis Issue in Room Verification --- resources/qml/MatrixText.qml | 2 +- src/Cache.cpp | 38 +++---- src/ChatPage.cpp | 11 +- src/DeviceVerificationFlow.cpp | 28 ++--- src/DeviceVerificationFlow.h | 2 +- src/timeline/EventStore.cpp | 231 +++++++++++++++++++++++------------------ src/timeline/TimelineModel.cpp | 33 +++--- src/ui/UserProfile.h | 2 +- 8 files changed, 190 insertions(+), 157 deletions(-) (limited to 'src/ChatPage.cpp') diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml index bbbb80cf..29214168 100644 --- a/resources/qml/MatrixText.qml +++ b/resources/qml/MatrixText.qml @@ -19,7 +19,7 @@ TextEdit { TimelineManager.setHistoryView(match[1]) chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain) } - else timelineManager.openLink(link) + else TimelineManager.openLink(link) } MouseArea { diff --git a/src/Cache.cpp b/src/Cache.cpp index 5302218a..07d01819 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -139,24 +139,26 @@ 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::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 diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 31ba38d7..704543b5 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -606,11 +606,12 @@ 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 37866716..ae054af7 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -28,10 +28,10 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, connect(this->model_, &TimelineModel::updateFlowEventId, this, - [this](std::string event_id) { + [this](std::string event_id_) { this->relation.rel_type = mtx::common::RelationType::Reference; - this->relation.event_id = event_id; - this->transaction_id = event_id; + this->relation.event_id = event_id_; + this->transaction_id = event_id_; }); } @@ -60,7 +60,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, msg.hashes.end()) && (std::find(msg.message_authentication_codes.begin(), msg.message_authentication_codes.end(), - "hmac-sha256") != 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) != @@ -236,11 +236,15 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, &ChatPage::recievedDeviceVerificationReady, this, [this](const mtx::events::msg::KeyVerificationReady &msg) { - if (!sender && msg.from_device != http::client()->device_id()) { - this->deleteLater(); - emit verificationCanceled(); + if (!sender) { + if (msg.from_device != http::client()->device_id()) { + this->deleteLater(); + emit verificationCanceled(); + } + return; } + if (msg.transaction_id.has_value()) { if (msg.transaction_id.value() != this->transaction_id) return; @@ -353,9 +357,9 @@ DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) } void -DeviceVerificationFlow::setType(Type type) +DeviceVerificationFlow::setType(Type type_) { - this->type = type; + this->type = type_; } void @@ -367,11 +371,11 @@ DeviceVerificationFlow::setSender(bool sender_) } void -DeviceVerificationFlow::setEventId(std::string event_id) +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; + this->relation.event_id = event_id_; + this->transaction_id = event_id_; } //! accepts a verification diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 6b2ab81f..c2150980 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -10,7 +10,7 @@ class QTimer; using sas_ptr = std::unique_ptr; -struct TimelineModel; +class TimelineModel; class DeviceVerificationFlow : public QObject { diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index bfc16a02..e5eaefb8 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -32,38 +32,40 @@ 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 && !res.chunk.empty()) - 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()) { @@ -128,46 +130,48 @@ 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 @@ -280,50 +284,77 @@ EventStore::handleSync(const mtx::responses::Timeline &events) if (auto encrypted = std::get_if>( &event)) { - auto event = decryptEvent({room_id_, encrypted->event_id}, *encrypted); + auto d_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)) { + *d_event)) { + if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); last_verification_request_event = *msg; - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); last_verification_cancel_event = *msg; ChatPage::instance()->recievedDeviceVerificationCancel( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationAccept( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationKey( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationMac( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationReady( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationDone( msg->content); - } else if (auto msg = std::get_if>(event)) { + continue; + } else if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); ChatPage::instance()->recievedDeviceVerificationStart( msg->content, msg->sender); + continue; + } + } else { + // only the key.verification.ready sent by localuser's other device + // is of significance as it is used for detecting accepted request + if (std::get_if>(d_event)) { + auto msg = std::get_if>(d_event); + ChatPage::instance()->recievedDeviceVerificationReady( + msg->content); } - } - // 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); } } } @@ -614,12 +645,6 @@ 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(); - return asCacheEntry(std::move(dummy)); auto encInfo = mtx::accessors::file(decryptionResult.event.value()); if (encInfo) emit newEncryptedImage(encInfo.value()); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 8f0e470e..570186a5 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -204,11 +204,12 @@ 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, @@ -217,17 +218,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; diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 3d0d2981..de55b6ab 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -59,7 +59,7 @@ public: connect(this, &DeviceInfoModel::queueReset, this, &DeviceInfoModel::reset); }; QHash roleNames() const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const + int rowCount(const QModelIndex &parent = QModelIndex()) const override { (void)parent; return (int)deviceList_.size(); -- cgit 1.5.1 From 8eb74daf766ea32804171154d45cb28e32e39e40 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Mon, 14 Sep 2020 17:27:49 +0530 Subject: Split qml part of Device Verification --- resources/qml/UserProfile.qml | 4 +- .../AcceptNewVerificationRequest.qml | 65 +++ .../AwaitingVerificationConfirmation.qml | 48 ++ .../AwaitingVerificationRequest.qml | 48 ++ .../qml/device-verification/DeviceVerification.qml | 621 ++------------------- .../qml/device-verification/DigitVerification.qml | 80 +++ resources/qml/device-verification/EmojiElement.qml | 1 + .../qml/device-verification/EmojiVerification.qml | 160 ++++++ .../device-verification/NewVerificationRequest.qml | 71 +++ .../qml/device-verification/PartnerAborted.qml | 42 ++ resources/qml/device-verification/TimedOut.qml | 44 ++ .../device-verification/VerificationSuccess.qml | 43 ++ resources/res.qrc | 9 + src/Cache.cpp | 38 +- src/ChatPage.cpp | 11 +- src/emoji/EmojiSearchModel.h | 1 - src/timeline/EventStore.cpp | 148 +++-- src/timeline/TimelineModel.cpp | 33 +- 18 files changed, 757 insertions(+), 710 deletions(-) create mode 100644 resources/qml/device-verification/AcceptNewVerificationRequest.qml create mode 100644 resources/qml/device-verification/AwaitingVerificationConfirmation.qml create mode 100644 resources/qml/device-verification/AwaitingVerificationRequest.qml create mode 100644 resources/qml/device-verification/DigitVerification.qml create mode 100644 resources/qml/device-verification/EmojiVerification.qml create mode 100644 resources/qml/device-verification/NewVerificationRequest.qml create mode 100644 resources/qml/device-verification/PartnerAborted.qml create mode 100644 resources/qml/device-verification/TimedOut.qml create mode 100644 resources/qml/device-verification/VerificationSuccess.qml (limited to 'src/ChatPage.cpp') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 9d59184d..1ca9dcc8 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -70,8 +70,8 @@ ApplicationWindow{ id: verifyUserButton text: "Verify" Layout.alignment: Qt.AlignHCenter - enabled: profile.isUserVerified - visible: profile.isUserVerified + enabled: !profile.isUserVerified + visible: !profile.isUserVerified onClicked: { var newFlow = profile.createFlow(true); diff --git a/resources/qml/device-verification/AcceptNewVerificationRequest.qml b/resources/qml/device-verification/AcceptNewVerificationRequest.qml new file mode 100644 index 00000000..872fabe1 --- /dev/null +++ b/resources/qml/device-verification/AcceptNewVerificationRequest.qml @@ -0,0 +1,65 @@ +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") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + flow.cancelVerification(DeviceVerificationFlow.User); + deviceVerificationList.remove(tran_id); + dialog.destroy(); + } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("Accept") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + stack.replace(awaitingVerificationRequestAccept); + isRequest?flow.sendVerificationReady():flow.acceptVerificationRequest(); + } + } + } + } +} diff --git a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml new file mode 100644 index 00000000..e0786343 --- /dev/null +++ b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml @@ -0,0 +1,48 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +import im.nheko 1.0 + +Pane { + property string title: qsTr("Awaiting Confirmation") + 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 complete verification.") + color:colors.text + verticalAlignment: Text.AlignVCenter + } + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + flow.cancelVerification(DeviceVerificationFlow.User); + deviceVerificationList.remove(tran_id); + dialog.destroy(); + } + } + Item { + Layout.fillWidth: true + } + } + } +} diff --git a/resources/qml/device-verification/AwaitingVerificationRequest.qml b/resources/qml/device-verification/AwaitingVerificationRequest.qml new file mode 100644 index 00000000..22a504c2 --- /dev/null +++ b/resources/qml/device-verification/AwaitingVerificationRequest.qml @@ -0,0 +1,48 @@ +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") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + 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 6e4b4621..e409b0fe 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -1,11 +1,14 @@ import QtQuick 2.3 import QtQuick.Controls 2.10 import QtQuick.Window 2.2 -import QtQuick.Layouts 1.10 import im.nheko 1.0 ApplicationWindow { + property var flow + property bool isRequest + property var tran_id + title: stack.currentItem.title id: dialog @@ -15,6 +18,17 @@ ApplicationWindow { height: stack.implicitHeight width: stack.implicitWidth + + Component{ + id: newVerificationRequest + NewVerificationRequest {} + } + + Component{ + id: acceptNewVerificationRequest + AcceptNewVerificationRequest {} + } + StackView { id: stack initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest @@ -22,613 +36,44 @@ ApplicationWindow { implicitHeight: currentItem.implicitHeight } - property var flow - property bool isRequest - property var tran_id - - 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; - } - - onRefreshProfile: { - deviceVerificationList.updateProfile(flow.userId); - } - } - Component { - id: newVerificationRequest - Pane { - property string title: qsTr("Sending Device Verification Request") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - 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.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("Cancel") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Start verification") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); } - } - } - } - } + id: partnerAborted + PartnerAborted {} } Component { - id: acceptNewVerificationRequest - Pane { - property string title: qsTr("Recieving Device Verification Request") - 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") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Accept") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - isRequest?flow.sendVerificationReady():flow.acceptVerificationRequest(); - } - } - } - } - } + id: timedout + TimedOut {} } Component { - id: awaitingVerificationRequestAccept - 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") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - } - } - } + id: verificationSuccess + VerificationSuccess {} } Component { id: digitVerification - Pane { - property string title: qsTr("Verification Code") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - - RowLayout { - Layout.alignment: Qt.AlignHCenter - Label { - font.pixelSize: Qt.application.font.pixelSize * 2 - text: flow.sasList[0] - color:colors.text - } - Label { - font.pixelSize: Qt.application.font.pixelSize * 2 - text: flow.sasList[1] - color:colors.text - } - Label { - font.pixelSize: Qt.application.font.pixelSize * 2 - text: flow.sasList[2] - color:colors.text - } - } - - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("They do not match!") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("They match!") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } - } - } - } - } + DigitVerification {} } Component { id: emojiVerification - Pane { - property string title: qsTr("Verification Code") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - - RowLayout { - Layout.alignment: Qt.AlignHCenter - - id: emojis - - property var mapping: [ - {"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"}, - {"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"}, - {"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"}, - {"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"}, - {"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"}, - {"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"}, - {"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"}, - {"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"}, - {"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"}, - {"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"}, - {"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"}, - {"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"}, - {"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"}, - {"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"}, - {"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"}, - {"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"}, - {"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"}, - {"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"}, - {"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"}, - {"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"}, - {"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"}, - {"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"}, - {"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"}, - {"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"}, - {"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"}, - {"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"}, - {"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"}, - {"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"}, - {"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"}, - {"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"}, - {"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"}, - {"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"}, - {"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"}, - {"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"}, - {"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"}, - {"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"}, - {"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"}, - {"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"}, - {"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"}, - {"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"}, - {"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"}, - {"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"}, - {"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"}, - {"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"}, - {"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"}, - {"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"}, - {"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"}, - {"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"}, - {"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"}, - {"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"}, - {"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"}, - {"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"}, - {"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"}, - {"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"}, - {"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"}, - {"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"}, - {"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"}, - {"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"}, - {"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"}, - {"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"}, - {"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"}, - {"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"}, - {"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"}, - {"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"} - ] - - Repeater { - id: repeater - model: 7 - delegate: Rectangle { - color: "transparent" - implicitHeight: Qt.application.font.pixelSize * 8 - implicitWidth: col.width - ColumnLayout { - id: col - Layout.fillWidth: true - anchors.bottom: parent.bottom - property var emoji: emojis.mapping[flow.sasList[index]] - Label { - //height: font.pixelSize * 2 - Layout.alignment: Qt.AlignHCenter - 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 - text: col.emoji.description - color:colors.text - } - } - } - } - } - - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("They do not match!") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("They match!") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } - } - } - } - } - } - - Component { - id: awaitingVerificationConfirmation - Pane { - property string title: qsTr("Awaiting Confirmation") - 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 complete verification.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - - BusyIndicator { - Layout.alignment: Qt.AlignHCenter - } - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("Cancel") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - } - } - } + EmojiVerification {} } - Component { - id: verificationSuccess - 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") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy(); - } - } - } - } - } - } - - Component { - id: partnerAborted - 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 - } + Connections { + target: flow + onVerificationCanceled: stack.replace(partnerAborted) + onTimedout: stack.replace(timedout) + onDeviceVerified: stack.replace(verificationSuccess) - RowLayout { - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Close") - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - onClicked: { - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - } - } + onVerificationRequestAccepted: switch(method) { + case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break; + case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break; } - } - Component { - id: timedout - 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 { - id: timedOutCancel - Layout.alignment: Qt.AlignRight - palette { - button: "white" - } - contentItem: Text { - text: parent.text - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - text: qsTr("Close") - onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy() - } - } - } - } + onRefreshProfile: { + deviceVerificationList.updateProfile(flow.userId); } } } diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml new file mode 100644 index 00000000..241ccbd0 --- /dev/null +++ b/resources/qml/device-verification/DigitVerification.qml @@ -0,0 +1,80 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +import im.nheko 1.0 + +Pane { + property string title: qsTr("Verification Code") + Component { + id: awaitingVerificationConfirmation + AwaitingVerificationConfirmation {} + } + ColumnLayout { + spacing: 16 + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr("Please verify the following digits. You should see the same numbers on both sides. If they differ, please press 'They do not match!' to abort verification!") + color:colors.text + verticalAlignment: Text.AlignVCenter + } + RowLayout { + Layout.alignment: Qt.AlignHCenter + Label { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: flow.sasList[0] + color:colors.text + } + Label { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: flow.sasList[1] + color:colors.text + } + Label { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: flow.sasList[2] + color:colors.text + } + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); + deviceVerificationList.remove(tran_id); + dialog.destroy(); + } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("They match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + } + } + } +} \ No newline at end of file diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml index 22f9e414..7e364594 100644 --- a/resources/qml/device-verification/EmojiElement.qml +++ b/resources/qml/device-verification/EmojiElement.qml @@ -1,4 +1,5 @@ import QtQuick 2.3 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.10 Rectangle { diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml new file mode 100644 index 00000000..fae08f25 --- /dev/null +++ b/resources/qml/device-verification/EmojiVerification.qml @@ -0,0 +1,160 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +import im.nheko 1.0 + +Pane { + property string title: qsTr("Verification Code") + Component { + id: awaitingVerificationConfirmation + AwaitingVerificationConfirmation{} + } + ColumnLayout { + spacing: 16 + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr("Please verify the following emoji. You should see the same emoji on both sides. If they differ, please press 'They do not match!' to abort verification!") + color:colors.text + verticalAlignment: Text.AlignVCenter + } + RowLayout { + Layout.alignment: Qt.AlignHCenter + id: emojis + property var mapping: [ + {"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"}, + {"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"}, + {"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"}, + {"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"}, + {"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"}, + {"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"}, + {"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"}, + {"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"}, + {"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"}, + {"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"}, + {"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"}, + {"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"}, + {"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"}, + {"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"}, + {"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"}, + {"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"}, + {"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"}, + {"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"}, + {"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"}, + {"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"}, + {"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"}, + {"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"}, + {"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"}, + {"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"}, + {"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"}, + {"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"}, + {"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"}, + {"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"}, + {"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"}, + {"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"}, + {"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"}, + {"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"}, + {"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"}, + {"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"}, + {"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"}, + {"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"}, + {"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"}, + {"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"}, + {"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"}, + {"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"}, + {"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"}, + {"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"}, + {"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"}, + {"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"}, + {"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"}, + {"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"}, + {"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"}, + {"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"}, + {"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"}, + {"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"}, + {"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"}, + {"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"}, + {"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"}, + {"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"}, + {"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"}, + {"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"}, + {"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"}, + {"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"}, + {"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"}, + {"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"}, + {"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"}, + {"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"}, + {"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"}, + {"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"} + ] + Repeater { + id: repeater + model: 7 + delegate: Rectangle { + color: "transparent" + implicitHeight: Qt.application.font.pixelSize * 8 + implicitWidth: col.width + ColumnLayout { + id: col + Layout.fillWidth: true + anchors.bottom: parent.bottom + property var emoji: emojis.mapping[flow.sasList[index]] + Label { + //height: font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + 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 + text: col.emoji.description + color:colors.text + } + } + } + } + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); + deviceVerificationList.remove(tran_id); + dialog.destroy(); + } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("They match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + } + } + } +} diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml new file mode 100644 index 00000000..d8fc65a0 --- /dev/null +++ b/resources/qml/device-verification/NewVerificationRequest.qml @@ -0,0 +1,71 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.10 + +Pane { + property string title: qsTr("Sending 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("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.") + color:colors.text + verticalAlignment: Text.AlignVCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + deviceVerificationList.remove(tran_id); + flow.deleteFlow(); + dialog.destroy(); + } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: qsTr("Start verification") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + stack.replace(awaitingVerificationRequestAccept); + isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); } + } + } + } +} diff --git a/resources/qml/device-verification/PartnerAborted.qml b/resources/qml/device-verification/PartnerAborted.qml new file mode 100644 index 00000000..62787b18 --- /dev/null +++ b/resources/qml/device-verification/PartnerAborted.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 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") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + deviceVerificationList.remove(tran_id); + dialog.destroy(); + } + } + } + } +} \ No newline at end of file diff --git a/resources/qml/device-verification/TimedOut.qml b/resources/qml/device-verification/TimedOut.qml new file mode 100644 index 00000000..40528693 --- /dev/null +++ b/resources/qml/device-verification/TimedOut.qml @@ -0,0 +1,44 @@ +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 { + id: timedOutCancel + Layout.alignment: Qt.AlignRight + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + 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 new file mode 100644 index 00000000..c87488da --- /dev/null +++ b/resources/qml/device-verification/VerificationSuccess.qml @@ -0,0 +1,43 @@ +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") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + onClicked: { + deviceVerificationList.remove(tran_id); + if(flow) flow.deleteFlow(); + dialog.destroy(); + } + } + } + } +} diff --git a/resources/res.qrc b/resources/res.qrc index e8f1f7be..7ef7ecf9 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -141,7 +141,16 @@ 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/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 media/ring.ogg diff --git a/src/Cache.cpp b/src/Cache.cpp index 07d01819..5302218a 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -139,26 +139,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); + 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 diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 704543b5..31ba38d7 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -606,12 +606,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/emoji/EmojiSearchModel.h b/src/emoji/EmojiSearchModel.h index 1ff5f4e9..13a03934 100644 --- a/src/emoji/EmojiSearchModel.h +++ b/src/emoji/EmojiSearchModel.h @@ -33,5 +33,4 @@ private: return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower(); } }; - } diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index af1f7b23..66a6d799 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -32,40 +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()) { @@ -130,48 +128,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 diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 570186a5..8f0e470e 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -204,12 +204,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, @@ -218,17 +217,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; -- cgit 1.5.1 From 54db9c89ede00bdcc48a64397cb45073b4fc6625 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 20 Sep 2020 23:04:14 +0200 Subject: Simplify outbound session setup Don't send inbound session to self and claim and send all keys at once. --- CMakeLists.txt | 2 +- io.github.NhekoReborn.Nheko.json | 2 +- src/Cache.cpp | 38 +++---- src/ChatPage.cpp | 11 +- src/Olm.cpp | 6 +- src/timeline/.TimelineModel.cpp.swn | Bin 0 -> 237568 bytes src/timeline/EventStore.cpp | 148 +++++++++++++------------- src/timeline/TimelineModel.cpp | 204 +++++++++++++++++------------------- src/timeline/TimelineModel.h | 12 +-- 9 files changed, 209 insertions(+), 214 deletions(-) create mode 100644 src/timeline/.TimelineModel.cpp.swn (limited to 'src/ChatPage.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d83f67..47f08657 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,7 +342,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG 0665c8baf4af0ce192adb8ca97761b63b681d569 + GIT_TAG f84611f129b46746a4b586acaba54fc31a303bc6 ) FetchContent_MakeAvailable(MatrixClient) else() diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json index c71a5771..da1b5a37 100644 --- a/io.github.NhekoReborn.Nheko.json +++ b/io.github.NhekoReborn.Nheko.json @@ -146,7 +146,7 @@ "name": "mtxclient", "sources": [ { - "commit": "0665c8baf4af0ce192adb8ca97761b63b681d569", + "commit": "f84611f129b46746a4b586acaba54fc31a303bc6", "type": "git", "url": "https://github.com/Nheko-Reborn/mtxclient.git" } diff --git a/src/Cache.cpp b/src/Cache.cpp index 5302218a..07d01819 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -139,24 +139,26 @@ 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::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 diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 31ba38d7..704543b5 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -606,11 +606,12 @@ 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/Olm.cpp b/src/Olm.cpp index 9e997801..4f0c5893 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -581,9 +581,11 @@ send_megolm_key_to_device(const std::string &user_id, ->create_room_key_event(UserId(user_id), pks.ed25519, payload) .dump(); + mtx::requests::ClaimKeys claim_keys; + claim_keys.one_time_keys[user_id][device_id] = mtx::crypto::SIGNED_CURVE25519; + http::client()->claim_keys( - user_id, - {device_id}, + claim_keys, [room_key, user_id, device_id, pks](const mtx::responses::ClaimKeys &res, mtx::http::RequestErr err) { if (err) { diff --git a/src/timeline/.TimelineModel.cpp.swn b/src/timeline/.TimelineModel.cpp.swn new file mode 100644 index 00000000..9e965702 Binary files /dev/null and b/src/timeline/.TimelineModel.cpp.swn differ diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 66a6d799..af1f7b23 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -32,38 +32,40 @@ 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()) { @@ -128,46 +130,48 @@ 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 diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 8f0e470e..ddd238b9 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -204,11 +204,12 @@ 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, @@ -217,17 +218,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; @@ -916,10 +917,20 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events:: OutboundGroupSessionData session_data; session_data.session_id = session_id; session_data.session_key = session_key; - session_data.message_index = 0; // TODO Update me + session_data.message_index = 0; cache::saveOutboundMegolmSession( room_id, session_data, std::move(outbound_session)); + { + MegolmSessionIndex index; + index.room_id = room_id; + index.session_id = session_id; + index.sender_key = olm::client()->identity_keys().curve25519; + auto megolm_session = + olm::client()->init_inbound_group_session(session_key); + cache::saveInboundMegolmSession(index, std::move(megolm_session)); + } + const auto members = cache::roomMembers(room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); @@ -961,19 +972,23 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events:: return; } - for (const auto &user : res.device_keys) { - // Mapping from a device_id with valid identity keys to the - // generated room_key event used for sharing the megolm session. - std::map room_key_msgs; - std::map deviceKeys; + mtx::requests::ClaimKeys claim_keys; - room_key_msgs.clear(); - deviceKeys.clear(); + // Mapping from user id to a device_id with valid identity keys to the + // generated room_key event used for sharing the megolm session. + std::map> room_key_msgs; + std::map> deviceKeys; + for (const auto &user : res.device_keys) { for (const auto &dev : user.second) { const auto user_id = ::UserId(dev.second.user_id); const auto device_id = DeviceId(dev.second.device_id); + if (user_id.get() == + http::client()->user_id().to_string() && + device_id.get() == http::client()->device_id()) + continue; + const auto device_keys = dev.second.keys; const auto curveKey = "curve25519:" + device_id.get(); const auto edKey = "ed25519:" + device_id.get(); @@ -1015,42 +1030,25 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events:: user_id, pks.ed25519, megolm_payload) .dump(); - room_key_msgs.emplace(device_id, room_key); - deviceKeys.emplace(device_id, pks); - } - - std::vector valid_devices; - valid_devices.reserve(room_key_msgs.size()); - for (auto const &d : room_key_msgs) { - valid_devices.push_back(d.first); + room_key_msgs[user_id].emplace(device_id, room_key); + deviceKeys[user_id].emplace(device_id, pks); + claim_keys.one_time_keys[user.first][device_id] = + mtx::crypto::SIGNED_CURVE25519; - nhlog::net()->info("{}", d.first); - nhlog::net()->info(" curve25519 {}", - deviceKeys.at(d.first).curve25519); - nhlog::net()->info(" ed25519 {}", - deviceKeys.at(d.first).ed25519); + nhlog::net()->info("{}", device_id.get()); + nhlog::net()->info(" curve25519 {}", pks.curve25519); + nhlog::net()->info(" ed25519 {}", pks.ed25519); } - - nhlog::net()->info( - "sending claim request for user {} with {} devices", - user.first, - valid_devices.size()); - - http::client()->claim_keys( - user.first, - valid_devices, - std::bind(&TimelineModel::handleClaimedKeys, - this, - keeper, - room_key_msgs, - deviceKeys, - user.first, - std::placeholders::_1, - std::placeholders::_2)); - - // TODO: Wait before sending the next batch of requests. - std::this_thread::sleep_for(std::chrono::milliseconds(500)); } + + http::client()->claim_keys(claim_keys, + std::bind(&TimelineModel::handleClaimedKeys, + this, + keeper, + room_key_msgs, + deviceKeys, + std::placeholders::_1, + std::placeholders::_2)); }); // TODO: Let the user know about the errors. @@ -1068,12 +1066,12 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events:: } void -TimelineModel::handleClaimedKeys(std::shared_ptr keeper, - const std::map &room_keys, - const std::map &pks, - const std::string &user_id, - const mtx::responses::ClaimKeys &res, - mtx::http::RequestErr err) +TimelineModel::handleClaimedKeys( + std::shared_ptr keeper, + const std::map> &room_keys, + const std::map> &pks, + const mtx::responses::ClaimKeys &res, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("claim keys error: {} {} {}", @@ -1083,65 +1081,53 @@ TimelineModel::handleClaimedKeys(std::shared_ptr keeper, return; } - nhlog::net()->debug("claimed keys for {}", user_id); - - if (res.one_time_keys.size() == 0) { - nhlog::net()->debug("no one-time keys found for user_id: {}", user_id); - return; - } - - if (res.one_time_keys.find(user_id) == res.one_time_keys.end()) { - nhlog::net()->debug("no one-time keys found for user_id: {}", user_id); - return; - } - - auto retrieved_devices = res.one_time_keys.at(user_id); - // Payload with all the to_device message to be sent. - json body; - body["messages"][user_id] = json::object(); + nlohmann::json body; - for (const auto &rd : retrieved_devices) { - const auto device_id = rd.first; - nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); + for (const auto &[user_id, retrieved_devices] : res.one_time_keys) { + nhlog::net()->debug("claimed keys for {}", user_id); + if (retrieved_devices.size() == 0) { + nhlog::net()->debug("no one-time keys found for user_id: {}", user_id); + return; + } - // TODO: Verify signatures - auto otk = rd.second.begin()->at("key"); + for (const auto &rd : retrieved_devices) { + const auto device_id = rd.first; - if (pks.find(device_id) == pks.end()) { - nhlog::net()->critical("couldn't find public key for device: {}", - device_id); - continue; - } + nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); - auto id_key = pks.at(device_id).curve25519; - auto s = olm::client()->create_outbound_session(id_key, otk); + // TODO: Verify signatures + auto otk = rd.second.begin()->at("key"); - if (room_keys.find(device_id) == room_keys.end()) { - nhlog::net()->critical("couldn't find m.room_key for device: {}", - device_id); - continue; - } + auto id_key = pks.at(user_id).at(device_id).curve25519; + auto s = olm::client()->create_outbound_session(id_key, otk); + + auto device_msg = olm::client()->create_olm_encrypted_content( + s.get(), + room_keys.at(user_id).at(device_id), + pks.at(user_id).at(device_id).curve25519); - auto device_msg = olm::client()->create_olm_encrypted_content( - s.get(), room_keys.at(device_id), pks.at(device_id).curve25519); + try { + cache::saveOlmSession(id_key, std::move(s)); + } catch (const lmdb::error &e) { + nhlog::db()->critical("failed to save outbound olm session: {}", + e.what()); + } catch (const mtx::crypto::olm_exception &e) { + nhlog::crypto()->critical( + "failed to pickle outbound olm session: {}", e.what()); + } - try { - cache::saveOlmSession(id_key, std::move(s)); - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to save outbound olm session: {}", e.what()); - } catch (const mtx::crypto::olm_exception &e) { - nhlog::crypto()->critical("failed to pickle outbound olm session: {}", - e.what()); + body["messages"][user_id][device_id] = device_msg; } - body["messages"][user_id][device_id] = device_msg; + nhlog::net()->info("send_to_device: {}", user_id); } - nhlog::net()->info("send_to_device: {}", user_id); - http::client()->send_to_device( - "m.room.encrypted", body, [keeper](mtx::http::RequestErr err) { + mtx::events::to_string(mtx::events::EventType::RoomEncrypted), + http::client()->generate_txn_id(), + body, + [keeper](mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to send " "send_to_device " diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 390fa1ed..61d00df9 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -285,12 +285,12 @@ signals: private: template void sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events::EventType eventType); - void handleClaimedKeys(std::shared_ptr keeper, - const std::map &room_key, - const std::map &pks, - const std::string &user_id, - const mtx::responses::ClaimKeys &res, - mtx::http::RequestErr err); + void handleClaimedKeys( + std::shared_ptr keeper, + const std::map> &room_keys, + const std::map> &pks, + const mtx::responses::ClaimKeys &res, + mtx::http::RequestErr err); void readEvent(const std::string &id); void setPaginationInProgress(const bool paginationInProgress); -- 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/ChatPage.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 2a79cd2b6b3548ac86a646b16f5a8d658fdb7282 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 3 Oct 2020 18:38:28 +0200 Subject: Don't fail on missing key for a device and /rotate-megolm-session command --- src/Cache.cpp | 24 ++++++++++++++++++++++++ src/Cache.h | 2 ++ src/Cache_p.h | 1 + src/ChatPage.cpp | 4 ++++ src/TextInputWidget.cpp | 2 ++ src/TextInputWidget.h | 1 + src/timeline/TimelineModel.cpp | 6 ++++++ 7 files changed, 40 insertions(+) (limited to 'src/ChatPage.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index b7a25343..63f6e426 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -372,6 +372,25 @@ Cache::updateOutboundMegolmSession(const std::string &room_id, int message_index txn.commit(); } +void +Cache::dropOutboundMegolmSession(const std::string &room_id) +{ + using namespace mtx::crypto; + + if (!outboundMegolmSessionExists(room_id)) + return; + + { + std::unique_lock lock(session_storage.group_outbound_mtx); + session_storage.group_outbound_session_data.erase(room_id); + session_storage.group_outbound_sessions.erase(room_id); + + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_del(txn, outboundMegolmSessionDb_, lmdb::val(room_id), nullptr); + txn.commit(); + } +} + void Cache::saveOutboundMegolmSession(const std::string &room_id, const OutboundGroupSessionData &data, @@ -3889,6 +3908,11 @@ updateOutboundMegolmSession(const std::string &room_id, int message_index) { instance_->updateOutboundMegolmSession(room_id, message_index); } +void +dropOutboundMegolmSession(const std::string &room_id) +{ + instance_->dropOutboundMegolmSession(room_id); +} void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys) diff --git a/src/Cache.h b/src/Cache.h index edad5993..fca80145 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -271,6 +271,8 @@ bool outboundMegolmSessionExists(const std::string &room_id) noexcept; void updateOutboundMegolmSession(const std::string &room_id, int message_index); +void +dropOutboundMegolmSession(const std::string &room_id); void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys); diff --git a/src/Cache_p.h b/src/Cache_p.h index 034c6d76..b37eae58 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -251,6 +251,7 @@ public: OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id); bool outboundMegolmSessionExists(const std::string &room_id) noexcept; void updateOutboundMegolmSession(const std::string &room_id, int message_index); + void dropOutboundMegolmSession(const std::string &room_id); void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys); mtx::crypto::ExportedSessionKeys exportSessionKeys(); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 6abe4078..87b4c277 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -168,6 +168,10 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) view_manager_, &TimelineViewManager::clearCurrentRoomTimeline); + connect(text_input_, &TextInputWidget::rotateMegolmSession, this, [this]() { + cache::dropOutboundMegolmSession(current_room_.toStdString()); + }); + connect( new QShortcut(QKeySequence("Ctrl+Down"), this), &QShortcut::activated, this, [this]() { if (isVisible()) diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 4a25c4cf..d1be7fb4 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -709,6 +709,8 @@ TextInputWidget::command(QString command, QString args) emit sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\"); } else if (command == "clear-timeline") { emit clearRoomTimeline(); + } else if (command == "rotate-megolm-session") { + emit rotateMegolmSession(); } } diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 3aa05c39..092e0ff2 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -186,6 +186,7 @@ signals: void sendBanRoomRequest(const QString &userid, const QString &reason); void sendUnbanRoomRequest(const QString &userid, const QString &reason); void changeRoomNick(const QString &displayname); + void rotateMegolmSession(); void startedTyping(); void stoppedTyping(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index af26a543..5e8952fc 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1120,6 +1120,12 @@ TimelineModel::handleClaimedKeys( nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); + if (rd.second.empty() || !rd.second.begin()->contains("key")) { + nhlog::net()->warn("Skipping device {} as it has no key.", + device_id); + continue; + } + // TODO: Verify signatures auto otk = rd.second.begin()->at("key"); -- cgit 1.5.1