From 488cc5e73be3c0aba2caf4df6ae38225c6a3d641 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 9 Mar 2020 00:25:00 +0100 Subject: First design iteration of device verification dialogs --- .../qml/device-verification/DeviceVerification.qml | 392 +++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 resources/qml/device-verification/DeviceVerification.qml (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml new file mode 100644 index 00000000..c32e3414 --- /dev/null +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -0,0 +1,392 @@ +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.10 + +Window { + title: stack.currentItem.title + id: dialog + + flags: Qt.Dialog + + + height: stack.implicitHeight + width: stack.implicitWidth + StackView { + id: stack + initialItem: newVerificationRequest + implicitWidth: currentItem.implicitWidth + implicitHeight: currentItem.implicitHeight + } + + onClosing: stack.replace(newVerificationRequest) + + Component { + id: newVerificationRequest + Pane { + property string title: "Device Verification Request" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "A new device was added." + + verticalAlignment: Text.AlignVCenter + } + + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "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." + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Start verification" + onClicked: stack.replace(awaitingVerificationRequestAccept) + } + } + } + } + } + + Component { + id: awaitingVerificationRequestAccept + Pane { + property string title: "Waiting for other party" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Waiting for other side to accept the verification request." + + verticalAlignment: Text.AlignVCenter + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + } + Timer { + // temporary, until it is bound to a backend + interval: 5000; running: true; + onTriggered: if (Math.random() > 0.5) stack.replace(emojiVerification); else stack.replace(digitVerification); + } + } + } + } + + Component { + id: digitVerification + Pane { + property string title: "Verification Code" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "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!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Layout.alignment: Qt.AlignHCenter + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + Text { + font.pixelSize: Qt.application.font.pixelSize * 2 + text: "1234" + } + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "They do not match!" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "They match." + onClicked: stack.replace(awaitingVerificationConfirmation) + } + } + } + } + } + + Component { + id: emojiVerification + Pane { + property string title: "Verification Code" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "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!" + + 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: "red" + implicitHeight: Qt.application.font.pixelSize * 8 + implicitWidth: col.width + ColumnLayout { + id: col + anchors.bottom: parent.bottom + property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + Text { + height: font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + text: col.emoji.emoji + font.pixelSize: Qt.application.font.pixelSize * 4 + } + Text { + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + text: col.emoji.description + } + } + } + } + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "They do not match!" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "They match." + onClicked: stack.replace(awaitingVerificationConfirmation) + } + } + } + } + } + + Component { + id: awaitingVerificationConfirmation + Pane { + property string title: "Awaiting Confirmation" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Waiting for other side to complete verification." + + verticalAlignment: Text.AlignVCenter + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Cancel" + onClicked: dialog.close() + } + Item { + Layout.fillWidth: true + } + } + Timer { + // temporary, until it is bound to a backend + interval: 5000; running: true; + onTriggered: Math.random() > 0.5 ? stack.replace(verificationSuccess) : stack.replace(partnerAborted) + } + } + } + } + + Component { + id: verificationSuccess + Pane { + property string title: "Successful Verification" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Verification successful! Both sides verified their devices!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } + + Component { + id: partnerAborted + Pane { + property string title: "Verification aborted!" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Verification canceled by the other party!" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } +} -- cgit 1.5.1 From 2088053d26fc124058fafb434d41b7c9516f0da0 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 13 Mar 2020 21:05:18 +0100 Subject: Add DeviceVerificationFlow dummy and verification test button --- CMakeLists.txt | 2 + resources/qml/TimelineView.qml | 19 ++++++ .../qml/device-verification/DeviceVerification.qml | 77 ++++++++++++++++------ .../device-verification/DeviceVerificationTest.qml | 13 ---- resources/res.qrc | 1 + src/DeviceVerificationFlow.cpp | 36 ++++++++++ src/DeviceVerificationFlow.h | 38 +++++++++++ src/timeline/TimelineViewManager.cpp | 7 ++ src/timeline/TimelineViewManager.h | 3 + 9 files changed, 163 insertions(+), 33 deletions(-) delete mode 100644 resources/qml/device-verification/DeviceVerificationTest.qml create mode 100644 src/DeviceVerificationFlow.cpp create mode 100644 src/DeviceVerificationFlow.h (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/CMakeLists.txt b/CMakeLists.txt index 69261046..69a46bde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,7 @@ set(SRC_FILES src/ColorImageProvider.cpp src/CommunitiesList.cpp src/CommunitiesListItem.cpp + src/DeviceVerificationFlow.cpp src/EventAccessors.cpp src/InviteeItem.cpp src/Logging.cpp @@ -488,6 +489,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ChatPage.h src/CommunitiesList.h src/CommunitiesListItem.h + src/DeviceVerificationFlow.h src/InviteeItem.h src/LoginPage.h src/MainWindow.h diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 8a5612d2..dd35473c 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -9,6 +9,7 @@ import im.nheko.EmojiModel 1.0 import "./delegates" import "./emoji" +import "./device-verification" Page { id: timelineRoot @@ -98,6 +99,24 @@ Page { anchors.fill: parent color: colors.window + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + Connections { + target: timelineManager + onDeviceVerificationRequest: { + var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: deviceVerificationFlow}); + 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/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index c32e3414..d8752316 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -3,12 +3,15 @@ import QtQuick.Controls 2.10 import QtQuick.Window 2.2 import QtQuick.Layouts 1.10 -Window { +import im.nheko 1.0 + +ApplicationWindow { title: stack.currentItem.title id: dialog flags: Qt.Dialog + palette: colors height: stack.implicitHeight width: stack.implicitWidth @@ -21,6 +24,19 @@ Window { onClosing: stack.replace(newVerificationRequest) + property var flow + 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; + } + } + Component { id: newVerificationRequest Pane { @@ -51,7 +67,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -59,7 +75,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "Start verification" - onClicked: stack.replace(awaitingVerificationRequestAccept) + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } } @@ -90,17 +106,12 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true } } - Timer { - // temporary, until it is bound to a backend - interval: 5000; running: true; - onTriggered: if (Math.random() > 0.5) stack.replace(emojiVerification); else stack.replace(digitVerification); - } } } } @@ -141,7 +152,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -149,7 +160,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: stack.replace(awaitingVerificationConfirmation) + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } } } } @@ -248,7 +259,7 @@ Window { id: repeater model: 7 delegate: Rectangle { - color: "red" + color: "transparent" implicitHeight: Qt.application.font.pixelSize * 8 implicitWidth: col.width ColumnLayout { @@ -274,7 +285,7 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "They do not match!" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true @@ -282,7 +293,7 @@ Window { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: stack.replace(awaitingVerificationConfirmation) + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } } } } @@ -313,17 +324,12 @@ Window { Button { Layout.alignment: Qt.AlignLeft text: "Cancel" - onClicked: dialog.close() + onClicked: { dialog.close(); flow.cancelVerification(); } } Item { Layout.fillWidth: true } } - Timer { - // temporary, until it is bound to a backend - interval: 5000; running: true; - onTriggered: Math.random() > 0.5 ? stack.replace(verificationSuccess) : stack.replace(partnerAborted) - } } } } @@ -389,4 +395,35 @@ Window { } } } + + Component { + id: timedout + Pane { + property string title: "Verification timed out" + ColumnLayout { + spacing: 16 + Text { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + id: content + text: "Device verification timed out." + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Close" + onClicked: dialog.close() + } + } + } + } + } } diff --git a/resources/qml/device-verification/DeviceVerificationTest.qml b/resources/qml/device-verification/DeviceVerificationTest.qml deleted file mode 100644 index 6682e7ec..00000000 --- a/resources/qml/device-verification/DeviceVerificationTest.qml +++ /dev/null @@ -1,13 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -Item { - DeviceVerification { - id: deviceVerification - } - - Button { - text: "Test DeviceVerification" - onClicked: deviceVerification.show() - } -} diff --git a/resources/res.qrc b/resources/res.qrc index 439ed97b..ec086b3a 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -135,5 +135,6 @@ qml/delegates/Pill.qml qml/delegates/Placeholder.qml qml/delegates/Reply.qml + qml/device-verification/DeviceVerification.qml diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp new file mode 100644 index 00000000..69d6ab9c --- /dev/null +++ b/src/DeviceVerificationFlow.cpp @@ -0,0 +1,36 @@ +#include "DeviceVerificationFlow.h" + +#include + +static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes + +DeviceVerificationFlow::DeviceVerificationFlow(QObject *) +{ + timeout = new QTimer(this); + timeout->setSingleShot(true); + connect(timeout, &QTimer::timeout, this, [this]() { + emit timedout(); + this->deleteLater(); + }); + timeout->start(TIMEOUT); +} + +//! accepts a verification and starts the verification flow +void +DeviceVerificationFlow::acceptVerificationRequest() +{ + emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); +} +//! cancels a verification flow +void +DeviceVerificationFlow::cancelVerification() +{ + this->deleteLater(); +} +//! Completes the verification flow +void +DeviceVerificationFlow::acceptDevice() +{ + emit deviceVerified(); + this->deleteLater(); +} diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h new file mode 100644 index 00000000..038f1e13 --- /dev/null +++ b/src/DeviceVerificationFlow.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +class QTimer; + +class DeviceVerificationFlow : public QObject +{ + Q_OBJECT + // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +public: + enum Method + { + Decimal, + Emoji + }; + Q_ENUM(Method) + + DeviceVerificationFlow(QObject *parent = nullptr); + +public slots: + //! accepts a verification and starts the verification flow + void acceptVerificationRequest(); + //! cancels a verification flow + void cancelVerification(); + //! Completes the verification flow + void acceptDevice(); + +signals: + void verificationRequestAccepted(Method method); + void deviceVerified(); + void timedout(); + void verificationCanceled(); + +private: + QTimer *timeout = nullptr; +}; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 975dd5fb..8447619a 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -85,6 +85,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin "Can't instantiate enum!"); qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); + qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); @@ -460,3 +461,9 @@ 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 63106916..583a9e4c 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -10,6 +10,7 @@ #include #include "Cache.h" +#include "DeviceVerificationFlow.h" #include "Logging.h" #include "TimelineModel.h" #include "Utils.h" @@ -43,6 +44,7 @@ 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 userStatus(QString id) const; @@ -54,6 +56,7 @@ signals: void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); + void deviceVerificationRequest(DeviceVerificationFlow *deviceVerificationFlow); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); -- cgit 1.5.1 From 480c4bc8f5f9bff50c832d10861b9fabce03464d Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 14 Mar 2020 11:17:10 +0100 Subject: Set proper emoji font for device verification --- resources/qml/device-verification/DeviceVerification.qml | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index d8752316..d0f44463 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -2,6 +2,7 @@ import QtQuick 2.3 import QtQuick.Controls 2.10 import QtQuick.Window 2.2 import QtQuick.Layouts 1.10 +import Qt.labs.settings 1.0 import im.nheko 1.0 @@ -13,6 +14,12 @@ ApplicationWindow { palette: colors + Settings { + id: settings + category: "user" + property bool emoji_font_family: true + } + height: stack.implicitHeight width: stack.implicitWidth StackView { @@ -271,6 +278,7 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 4 + font.family: settings.emoji_font_family } Text { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom -- cgit 1.5.1 From fed0463e57307a3ecaf01dd7d2b54f4d42044e15 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 20 Mar 2020 21:07:26 +0100 Subject: Make emojis a bit smaller --- .../qml/device-verification/DeviceVerification.qml | 6 +++--- resources/qml/device-verification/EmojiElement.qml | 25 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 resources/qml/device-verification/EmojiElement.qml (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index d0f44463..f8fe2bd4 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,17 +267,17 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" - implicitHeight: Qt.application.font.pixelSize * 8 + implicitHeight: Qt.application.font.pixelSize * 3 implicitWidth: col.width ColumnLayout { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] Text { - height: font.pixelSize * 2 + //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji - font.pixelSize: Qt.application.font.pixelSize * 4 + font.pixelSize: Qt.application.font.pixelSize * 2 font.family: settings.emoji_font_family } Text { diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml new file mode 100644 index 00000000..fa207b91 --- /dev/null +++ b/resources/qml/device-verification/EmojiElement.qml @@ -0,0 +1,25 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.10 + +Rectangle { + color: "red" + implicitHeight: Qt.application.font.pixelSize * 4 + implicitWidth: col.width + height: Qt.application.font.pixelSize * 4 + width: col.width + ColumnLayout { + id: col + anchors.bottom: parent.bottom + property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + Text { + height: font.pixelSize * 2 + Layout.alignment: Qt.AlignHCenter + text: col.emoji.emoji + font.pixelSize: Qt.application.font.pixelSize * 2 + } + Text { + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + text: col.emoji.description + } + } +} -- cgit 1.5.1 From b1362ca69fe2c028546b1e4bab5506395f431e07 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 31 Mar 2020 00:47:56 +0200 Subject: Use label in device verification dialogs (for proper theming) --- .../qml/device-verification/DeviceVerification.qml | 28 +++++++++++----------- resources/qml/device-verification/EmojiElement.qml | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index f8fe2bd4..19e0c7dd 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -50,7 +50,7 @@ ApplicationWindow { property string title: "Device Verification Request" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -60,7 +60,7 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter } - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -95,7 +95,7 @@ ApplicationWindow { property string title: "Waiting for other party" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -129,7 +129,7 @@ ApplicationWindow { property string title: "Verification Code" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -141,15 +141,15 @@ ApplicationWindow { RowLayout { Layout.alignment: Qt.AlignHCenter - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } - Text { + Label { font.pixelSize: Qt.application.font.pixelSize * 2 text: "1234" } @@ -180,7 +180,7 @@ ApplicationWindow { property string title: "Verification Code" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -273,14 +273,14 @@ ApplicationWindow { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] - Text { + Label { //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 2 font.family: settings.emoji_font_family } - Text { + Label { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom text: col.emoji.description } @@ -314,7 +314,7 @@ ApplicationWindow { property string title: "Awaiting Confirmation" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -348,7 +348,7 @@ ApplicationWindow { property string title: "Successful Verification" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -379,7 +379,7 @@ ApplicationWindow { property string title: "Verification aborted!" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true @@ -410,7 +410,7 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 - Text { + Label { Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/resources/qml/device-verification/EmojiElement.qml b/resources/qml/device-verification/EmojiElement.qml index fa207b91..22f9e414 100644 --- a/resources/qml/device-verification/EmojiElement.qml +++ b/resources/qml/device-verification/EmojiElement.qml @@ -11,13 +11,13 @@ Rectangle { id: col anchors.bottom: parent.bottom property var emoji: emojis.mapping[Math.floor(Math.random()*64)] - Text { + Label { height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter text: col.emoji.emoji font.pixelSize: Qt.application.font.pixelSize * 2 } - Text { + Label { Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom text: col.emoji.description } -- cgit 1.5.1 From 707248fea39eafdfc4ce6b69987ab144ab781110 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 13 Mar 2020 21:05:18 +0100 Subject: Add DeviceVerificationFlow dummy and verification test button --- resources/qml/device-verification/DeviceVerification.qml | 8 ++++++++ src/timeline/TimelineViewManager.cpp | 6 ++++++ 2 files changed, 14 insertions(+) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 19e0c7dd..2c9486ae 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,7 +267,11 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" +<<<<<<< HEAD implicitHeight: Qt.application.font.pixelSize * 3 +======= + implicitHeight: Qt.application.font.pixelSize * 8 +>>>>>>> Add DeviceVerificationFlow dummy and verification test button implicitWidth: col.width ColumnLayout { id: col @@ -410,7 +414,11 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 +<<<<<<< HEAD Label { +======= + Text { +>>>>>>> Add DeviceVerificationFlow dummy and verification test button Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 8447619a..afd1acb6 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -467,3 +467,9 @@ TimelineViewManager::startDummyVerification() { emit deviceVerificationRequest(new DeviceVerificationFlow(this)); } + +void +TimelineViewManager::startDummyVerification() +{ + emit deviceVerificationRequest(new DeviceVerificationFlow(this)); +} -- cgit 1.5.1 From a54a973ad6be4a1e71be6d8f993600fb02601574 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Fri, 22 May 2020 11:17:02 +0530 Subject: Adding DeviceList for userprofile --- resources/qml/UserProfile.qml | 18 ++++++++- .../qml/device-verification/DeviceVerification.qml | 8 ---- src/timeline/TimelineViewManager.cpp | 9 ++--- src/ui/UserProfile.cpp | 44 ++++++++++++++++------ src/ui/UserProfile.h | 32 +++++++++++++--- 5 files changed, 79 insertions(+), 32 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f019ee25..ae91abc4 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQuick.Window 2.3 +import im.nheko 1.0 + ApplicationWindow{ property var user_data property var colors: currentActivePalette @@ -18,7 +20,21 @@ ApplicationWindow{ userProfileAvatar.url = chat.model.avatarUrl(user_data.userId).replace("mxc://", "image://MxcImage/") userProfileName.text = user_data.userName matrixUserID.text = user_data.userId - console.log("this is happening"); + userProfile.userId = user_data.userId + log_devices() + } + + function log_devices() + { + console.log(userProfile.deviceList); + userProfile.deviceList.forEach((item,index)=>{ + console.log(item.device_id) + console.log(item.display_name) + }) + } + + UserProfileContent{ + id: userProfile } background: Item{ diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 2c9486ae..dd637e59 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -267,11 +267,7 @@ ApplicationWindow { model: 7 delegate: Rectangle { color: "transparent" -<<<<<<< HEAD - implicitHeight: Qt.application.font.pixelSize * 3 -======= implicitHeight: Qt.application.font.pixelSize * 8 ->>>>>>> Add DeviceVerificationFlow dummy and verification test button implicitWidth: col.width ColumnLayout { id: col @@ -414,11 +410,7 @@ ApplicationWindow { property string title: "Verification timed out" ColumnLayout { spacing: 16 -<<<<<<< HEAD - Label { -======= Text { ->>>>>>> Add DeviceVerificationFlow dummy and verification test button Layout.maximumWidth: 400 Layout.fillHeight: true Layout.fillWidth: true diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index afd1acb6..227b410f 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -15,6 +15,7 @@ #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" +#include "../ui/UserProfile.h" Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) @@ -86,6 +87,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); + qmlRegisterType("im.nheko",1,0,"UserProfileContent"); + qRegisterMetaType(); qRegisterMetaType(); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel"); qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiProxyModel"); @@ -467,9 +470,3 @@ TimelineViewManager::startDummyVerification() { emit deviceVerificationRequest(new DeviceVerificationFlow(this)); } - -void -TimelineViewManager::startDummyVerification() -{ - emit deviceVerificationRequest(new DeviceVerificationFlow(this)); -} diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index ac35f1d4..30785699 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -1,28 +1,43 @@ #include "UserProfile.h" #include "Logging.h" -#include "MatrixClient.h" #include "Utils.h" +#include "mtx/responses/crypto.hpp" +#include UserProfile::UserProfile(QObject *parent) : QObject(parent) {} -QMap -UserProfile::getDeviceList() -{ +QVector +UserProfile::getDeviceList(){ + UserProfile::fetchDeviceList(this->userId); return this->deviceList; } +QString +UserProfile::getUserId (){ + return this->userId; +} + +void +UserProfile::setUserId (const QString &user_id){ + if(this->userId != userId) + return; + else + this->userId = user_id; +} + void -UserProfile::fetchDeviceList(const QString &userId) +UserProfile::fetchDeviceList(const QString &userID) { auto localUser = utils::localUser(); mtx::requests::QueryKeys req; - req.device_keys[userId.toStdString()] = {}; + mtx::responses::QueryKeys res; + req.device_keys[userID.toStdString()] = {}; http::client()->query_keys( req, - [user_id = userId.toStdString()](const mtx::responses::QueryKeys &res, + [user_id = userID.toStdString(),this](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", @@ -39,14 +54,18 @@ UserProfile::fetchDeviceList(const QString &userId) auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; + QVector deviceInfo; for (const auto &d : devices) { auto device = d.second; // TODO: Verify signatures and ignore those that don't pass. - deviceInfo.emplace_back(DeviceInfo{ - QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name)}); + // std::cout<device_id = QString::fromStdString(d.first); + newdevice->display_name = QString::fromStdString(device.unsigned_info.device_display_name) + + deviceInfo.append(std::move(newdevice)); } std::sort(deviceInfo.begin(), @@ -54,5 +73,8 @@ UserProfile::fetchDeviceList(const QString &userId) [](const DeviceInfo &a, const DeviceInfo &b) { return a.device_id > b.device_id; }); + + this->deviceList = deviceInfo; + emit UserProfile::deviceListUpdated(); }); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index d003e6ca..bbf57c7b 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -1,29 +1,49 @@ #pragma once -#include #include #include -struct DeviceInfo +#include "MatrixClient.h" + +class DeviceInfo { +public: + explicit DeviceInfo(QString device_id,QString display_name){ + this->device_id = device_id; + this->display_name = display_name; + } + ~DeviceInfo() = default; + DeviceInfo(const DeviceInfo &device){ + this->device_id = device.device_id; + this->display_name = device.display_name; + } + QString device_id; QString display_name; }; +Q_DECLARE_METATYPE(DeviceInfo); class UserProfile : public QObject { Q_OBJECT - Q_PROPERTY(QMap deviceList READ getDeviceList NOTIFY DeviceListUpdated) + Q_PROPERTY(QVector deviceList READ getDeviceList NOTIFY deviceListUpdated) + Q_PROPERTY(QString userId READ getUserId WRITE setUserId) public: + // constructor explicit UserProfile(QObject *parent = 0); - QMap getDeviceList(); + // getters + QVector getDeviceList(); + QString getUserId(); + // setters + void setUserId(const QString &userId); Q_INVOKABLE void fetchDeviceList(const QString &userID); signals: - void DeviceListUpdated(); + void deviceListUpdated(); private: - QMap deviceList; + QVector deviceList; + QString userId; }; \ No newline at end of file -- cgit 1.5.1 From b628f485ff1cc66c20b9888b73e3427eb86d062e Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Thu, 4 Jun 2020 19:14:15 +0530 Subject: Tweak UI for device verification and Add more slots --- resources/qml/UserProfile.qml | 38 +++++++++--- .../qml/device-verification/DeviceVerification.qml | 41 ++++++++++++- src/DeviceVerificationFlow.cpp | 70 +++++++++++++++++++++- src/DeviceVerificationFlow.h | 10 +++- 4 files changed, 146 insertions(+), 13 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index f29fb4c1..a85c41c3 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -5,6 +5,8 @@ import QtQuick.Window 2.3 import im.nheko 1.0 +import "./device-verification" + ApplicationWindow{ property var user_data property var colors: currentActivePalette @@ -20,7 +22,6 @@ ApplicationWindow{ id: userProfileList userId: user_data.userId onUserIdChanged : { - console.log(userId) userProfileList.updateDeviceList() } onDeviceListUpdated : { @@ -84,15 +85,34 @@ ApplicationWindow{ delegate: RowLayout{ width: parent.width - Text{ - Layout.fillWidth: true - color: colors.text - text: deviceID + ColumnLayout{ + Text{ + Layout.fillWidth: true + color: colors.text + Layout.alignment: Qt.AlignRight + text: deviceID + } + Text{ + Layout.fillWidth: true + color:colors.text + Layout.alignment: Qt.AlignRight + text: displayName + } + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + DeviceVerificationFlow { + id: deviceVerificationFlow + } } - Text{ - Layout.fillWidth: true - color:colors.text - text: displayName + Button{ + text:"Verify" + onClicked: { + var dialog = deviceVerificationDialog.createObject(userProfileDialog, + {flow: deviceVerificationFlow,sender: true}); + dialog.show(); + } } } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index dd637e59..ce2485ff 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -7,6 +7,7 @@ import Qt.labs.settings 1.0 import im.nheko 1.0 ApplicationWindow { + property bool sender: true title: stack.currentItem.title id: dialog @@ -24,7 +25,7 @@ ApplicationWindow { width: stack.implicitWidth StackView { id: stack - initialItem: newVerificationRequest + initialItem: sender == true?newVerificationRequest:acceptNewVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } @@ -47,7 +48,7 @@ ApplicationWindow { Component { id: newVerificationRequest Pane { - property string title: "Device Verification Request" + property string title: "Sending Device Verification Request" ColumnLayout { spacing: 16 Label { @@ -82,6 +83,42 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Start verification" + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.sendVerificationRequest(); } + } + } + } + } + } + + Component { + id: acceptNewVerificationRequest + Pane { + property string title: "Recieving Device Verification Request" + ColumnLayout { + spacing: 16 + + Label { + Layout.maximumWidth: 400 + Layout.fillHeight: true + Layout.fillWidth: true + wrapMode: Text.Wrap + text: "The device was requested to be verified" + + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + Button { + Layout.alignment: Qt.AlignLeft + text: "Deny" + onClicked: { dialog.close(); flow.cancelVerification(); } + } + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + text: "Accept" onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 69d6ab9c..12e31c04 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,5 +1,8 @@ #include "DeviceVerificationFlow.h" +#include +#include +#include // only for debugging #include static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -15,17 +18,80 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } -//! accepts a verification and starts the verification flow +std::string +DeviceVerificationFlow::generate_txn_id() +{ + this->transaction_id = mtx::client::utils::random_token(32, false); + return this->transaction_id; +} + +//! accepts a verification void DeviceVerificationFlow::acceptVerificationRequest() { + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationAccept req; + + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocol = ""; + req.hash = ""; + req.message_authentication_code = ""; + // req.short_authentication_string = ""; + req.commitment = ""; + emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + + // Yet to add send to_device message +} +//! starts the verification flow +void +DeviceVerificationFlow::startVerificationRequest() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationAccept req; + + // req.from_device = ""; + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocol = {}; + // req.hashes = {}; + req.message_authentication_code = {}; + // req.short_authentication_string = ""; + + // Yet to add send to_device message +} +//! sends a verification request +void +DeviceVerificationFlow::sendVerificationRequest() +{ + QDateTime CurrentTime = QDateTime::currentDateTimeUtc(); + + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationRequest req; + + req.from_device = ""; + req.transaction_id = generate_txn_id(); + req.methods.resize(1); + req.methods[0] = mtx::events::msg::VerificationMethods::SASv1; + req.timestamp = (uint64_t)CurrentTime.toTime_t(); + + // Yet to add send to_device message } //! cancels a verification flow void DeviceVerificationFlow::cancelVerification() { + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationCancel req; + + req.transaction_id = this->transaction_id; + req.reason = ""; + req.code = ""; + this->deleteLater(); + + // Yet to add send to_device message } //! Completes the verification flow void @@ -33,4 +99,6 @@ DeviceVerificationFlow::acceptDevice() { emit deviceVerified(); this->deleteLater(); + + // Yet to add send to_device message } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 038f1e13..71c40cd5 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -20,8 +20,12 @@ public: DeviceVerificationFlow(QObject *parent = nullptr); public slots: - //! accepts a verification and starts the verification flow + //! sends a verification request + void sendVerificationRequest(); + //! accepts a verification void acceptVerificationRequest(); + //! starts the verification flow + void startVerificationRequest(); //! cancels a verification flow void cancelVerification(); //! Completes the verification flow @@ -34,5 +38,9 @@ signals: void verificationCanceled(); private: + //! generates a unique transaction id + std::string generate_txn_id(); + QTimer *timeout = nullptr; + std::string transaction_id; }; -- cgit 1.5.1 From cd5dd0e39b23c5a258d0f6811f6b5987d8f6f391 Mon Sep 17 00:00:00 2001 From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com> Date: Sun, 7 Jun 2020 17:05:32 +0530 Subject: Add SAS Method choice and Add send_to_device API call --- resources/qml/UserProfile.qml | 19 ++- .../qml/device-verification/DeviceVerification.qml | 16 +++ src/DeviceVerificationFlow.cpp | 131 ++++++++++++++++----- src/DeviceVerificationFlow.h | 16 ++- 4 files changed, 151 insertions(+), 31 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index a85c41c3..6bfee09c 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -107,18 +107,28 @@ ApplicationWindow{ } } Button{ + id: verifyButton text:"Verify" onClicked: { var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: true}); + {flow: deviceVerificationFlow,sender: false}); + deviceVerificationFlow.userId = user_data.userId + deviceVerificationFlow.deviceId = model.deviceID dialog.show(); } + contentItem: Text { + text: verifyButton.text + color: colors.background + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } } } } } Button{ + id: okbutton text:"OK" onClicked: userProfileDialog.close() anchors.margins: { @@ -126,6 +136,13 @@ ApplicationWindow{ bottom:10 } + contentItem: Text { + text: okbutton.text + color: colors.background + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + Layout.alignment: Qt.AlignRight | Qt.AlignBottom } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ce2485ff..31f6f9c1 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -107,6 +107,22 @@ ApplicationWindow { verticalAlignment: Text.AlignVCenter } + RowLayout { + RadioButton { + Layout.alignment: Qt.AlignLeft + text: "Decimal" + onClicked: { flow.method = DeviceVerificationFlow.Decimal } + } + Item { + Layout.fillWidth: true + } + RadioButton { + Layout.alignment: Qt.AlignRight + text: "Emoji" + onClicked: { flow.method = DeviceVerificationFlow.Emoji } + } + } + RowLayout { Button { Layout.alignment: Qt.AlignLeft diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 12e31c04..5bbe2a71 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,9 +1,10 @@ #include "DeviceVerificationFlow.h" -#include +#include "Logging.h" #include #include // only for debugging #include +#include // only for debugging static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -18,11 +19,42 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) timeout->start(TIMEOUT); } -std::string -DeviceVerificationFlow::generate_txn_id() +QString +DeviceVerificationFlow::getUserId() { - this->transaction_id = mtx::client::utils::random_token(32, false); - return this->transaction_id; + toClient = mtx::identifiers::parse((this->userId).toStdString()); + std::cout << http::client()->device_id() << std::endl; + return this->userId; +} + +QString +DeviceVerificationFlow::getDeviceId() +{ + return this->deviceId; +} + +DeviceVerificationFlow::Method +DeviceVerificationFlow::getMethod() +{ + return this->method; +} + +void +DeviceVerificationFlow::setUserId(QString userID) +{ + this->userId = userID; +} + +void +DeviceVerificationFlow::setDeviceId(QString deviceID) +{ + this->deviceId = deviceID; +} + +void +DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) +{ + this->method = method_; } //! accepts a verification @@ -34,32 +66,53 @@ DeviceVerificationFlow::acceptVerificationRequest() req.transaction_id = this->transaction_id; req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = ""; - req.hash = ""; + req.key_agreement_protocol = "curve25519"; + req.hash = "sha256"; req.message_authentication_code = ""; // req.short_authentication_string = ""; req.commitment = ""; - emit verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); + emit this->verificationRequestAccepted(this->method); - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.accept", body, [](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); + }); } //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() { - mtx::requests::ToDeviceMessages body; - mtx::events::msg::KeyVerificationAccept req; - - // req.from_device = ""; - req.transaction_id = this->transaction_id; - req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = {}; - // req.hashes = {}; - req.message_authentication_code = {}; + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationStart req; + + req.from_device = http::client()->device_id(); + req.transaction_id = this->transaction_id; + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocols = {}; + req.hashes = {}; + req.message_authentication_codes = {}; // req.short_authentication_string = ""; - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.start", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to start verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); } //! sends a verification request void @@ -70,13 +123,25 @@ DeviceVerificationFlow::sendVerificationRequest() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationRequest req; - req.from_device = ""; - req.transaction_id = generate_txn_id(); + 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); req.methods[0] = mtx::events::msg::VerificationMethods::SASv1; req.timestamp = (uint64_t)CurrentTime.toTime_t(); - // Yet to add send to_device message + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.request", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification request: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); } //! cancels a verification flow void @@ -86,12 +151,22 @@ DeviceVerificationFlow::cancelVerification() mtx::events::msg::KeyVerificationCancel req; req.transaction_id = this->transaction_id; - req.reason = ""; - req.code = ""; - - this->deleteLater(); - - // Yet to add send to_device message + // TODO: Add Proper Error Messages and Code + req.reason = "Device Verification Cancelled"; + req.code = "400"; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", 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(); + }); } //! Completes the verification flow void diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 71c40cd5..c7701196 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,5 +1,6 @@ #pragma once +#include #include class QTimer; @@ -8,6 +9,9 @@ class DeviceVerificationFlow : public QObject { Q_OBJECT // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + Q_PROPERTY(QString userId READ getUserId WRITE setUserId) + Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) + Q_PROPERTY(Method method READ getMethod WRITE setMethod) public: enum Method @@ -18,6 +22,12 @@ public: Q_ENUM(Method) DeviceVerificationFlow(QObject *parent = nullptr); + QString getUserId(); + QString getDeviceId(); + Method getMethod(); + void setUserId(QString userID); + void setDeviceId(QString deviceID); + void setMethod(Method method_); public slots: //! sends a verification request @@ -38,9 +48,11 @@ signals: void verificationCanceled(); private: - //! generates a unique transaction id - std::string generate_txn_id(); + QString userId; + QString deviceId; + Method method; QTimer *timeout = nullptr; std::string transaction_id; + mtx::identifiers::User toClient; }; -- cgit 1.5.1 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 --- resources/qml/UserProfile.qml | 19 ++-- .../qml/device-verification/DeviceVerification.qml | 2 +- src/ChatPage.cpp | 1 + src/ChatPage.h | 12 +++ src/DeviceVerificationFlow.cpp | 105 ++++++++++++++++++++- src/DeviceVerificationFlow.h | 5 + src/Olm.cpp | 23 ++++- 7 files changed, 153 insertions(+), 14 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 6bfee09c..c40e6765 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -29,6 +29,14 @@ ApplicationWindow{ } } + Component { + id: deviceVerificationDialog + DeviceVerification {} + } + DeviceVerificationFlow { + id: deviceVerificationFlow + } + background: Item{ id: userProfileItem width: userProfileDialog.width @@ -98,22 +106,15 @@ ApplicationWindow{ Layout.alignment: Qt.AlignRight text: displayName } - Component { - id: deviceVerificationDialog - DeviceVerification {} - } - DeviceVerificationFlow { - id: deviceVerificationFlow - } } Button{ id: verifyButton text:"Verify" onClicked: { - var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: deviceVerificationFlow,sender: false}); deviceVerificationFlow.userId = user_data.userId deviceVerificationFlow.deviceId = model.deviceID + var dialog = deviceVerificationDialog.createObject(userProfileDialog, + {flow: deviceVerificationFlow,sender: true}); dialog.show(); } contentItem: Text { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 31f6f9c1..fca360f7 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -83,7 +83,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "Start verification" - onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.sendVerificationRequest(); } + onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } } } } 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" diff --git a/src/ChatPage.h b/src/ChatPage.h index 18bed289..b05a388d 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -164,6 +165,17 @@ signals: void themeChanged(); void decryptSidebarChanged(); + //! Signals for device verificaiton + void recievedDeviceVerificationAccept( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationRequest( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationCancel( + const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message); + private slots: void showUnreadMessageNotification(int count); void updateTopBarAvatar(const QString &roomid, const QString &img); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 5bbe2a71..c6652d03 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,4 +1,5 @@ #include "DeviceVerificationFlow.h" +#include "ChatPage.h" #include "Logging.h" #include @@ -8,22 +9,73 @@ static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes +namespace msgs = mtx::events::msg; + DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { + qRegisterMetaType(); timeout = new QTimer(this); timeout->setSingleShot(true); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationAccept, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Accept" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Request" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationCancel, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Cancel" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationKey, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Key" << std::endl; + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationMac, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + std::cout << "Recieved Event Mac" << std::endl; + } + }); timeout->start(TIMEOUT); } QString DeviceVerificationFlow::getUserId() { - toClient = mtx::identifiers::parse((this->userId).toStdString()); - std::cout << http::client()->device_id() << std::endl; return this->userId; } @@ -43,6 +95,7 @@ void DeviceVerificationFlow::setUserId(QString userID) { this->userId = userID; + this->toClient = mtx::identifiers::parse(userID.toStdString()); } void @@ -101,7 +154,8 @@ DeviceVerificationFlow::startVerificationRequest() req.hashes = {}; req.message_authentication_codes = {}; // req.short_authentication_string = ""; - + qDebug()<<"Inside Start Verification"; + qDebug()<userId; body[this->toClient][this->deviceId.toStdString()] = req; http::client() @@ -168,6 +222,51 @@ DeviceVerificationFlow::cancelVerification() this->deleteLater(); }); } +//! sends the verification key +void +DeviceVerificationFlow::sendVerificationKey() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationKey req; + + req.key = ""; + req.transaction_id = this->transaction_id; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification key: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} +//! sends the mac of the keys +void +DeviceVerificationFlow::sendVerificationMac() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationMac req; + + req.transaction_id = this->transaction_id; + // req.mac = ""; + req.keys = ""; + + body[this->toClient][deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + "m.key.verification.cancel", body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification MAC: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} //! Completes the verification flow void DeviceVerificationFlow::acceptDevice() diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index c7701196..561a3717 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -38,6 +38,10 @@ public slots: void startVerificationRequest(); //! cancels a verification flow void cancelVerification(); + //! sends the verification key + void sendVerificationKey(); + //! sends the mac of the keys + void sendVerificationMac(); //! Completes the verification flow void acceptDevice(); @@ -56,3 +60,4 @@ private: 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/Olm.cpp b/src/Olm.cpp index 74fbac9a..6c1d3fdc 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -1,11 +1,15 @@ +#include #include #include "Olm.h" #include "Cache.h" +#include "ChatPage.h" #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" +#include +#include // only for debugging static const std::string STORAGE_SECRET_KEY("secret"); constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; @@ -27,7 +31,6 @@ handle_to_device_messages(const std::vectorinfo("received {} to_device messages", msgs.size()); nlohmann::json j_msg; @@ -74,6 +77,24 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { + ChatPage::instance()->recievedDeviceVerificationRequest(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { + ChatPage::instance()->recievedDeviceVerificationCancel(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { + ChatPage::instance()->recievedDeviceVerificationKey(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { + ChatPage::instance()->recievedDeviceVerificationMac(msg); + std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { + ChatPage::instance()->recievedDeviceVerificationStart(msg); + std::cout << j_msg.dump(2) << std::endl; } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } -- 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 'resources/qml/device-verification/DeviceVerification.qml') 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 67367d0004abe54445c9c6ca354c787fc0af8a79 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Sat, 20 Jun 2020 17:50:43 +0530 Subject: Shared secret with decimal and emoji works! --- resources/qml/TimelineView.qml | 13 +- .../qml/device-verification/DeviceVerification.qml | 26 ++-- src/DeviceVerificationFlow.cpp | 167 ++++++++++++++++----- src/DeviceVerificationFlow.h | 10 ++ src/timeline/TimelineViewManager.cpp | 64 ++++---- src/timeline/TimelineViewManager.h | 5 +- 6 files changed, 202 insertions(+), 83 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 5170a41a..99d14685 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -103,18 +103,15 @@ Page { id: deviceVerificationDialog DeviceVerification {} } - Component{ - id: deviceVerificationFlow - DeviceVerificationFlow {} - } Connections { target: timelineManager onNewDeviceVerificationRequest: { - var newFlow = deviceVerificationFlow.createObject(timelineRoot, - {userId : userId,sender: false,deviceId : deviceId,tranId:transactionId}); - deviceVerificationList.add(newFlow.tranId); + flow.userId = userId; + flow.sender = false; + flow.deviceId = deviceId; + flow.tranId = transactionId; var dialog = deviceVerificationDialog.createObject(timelineRoot, - {flow: newFlow,sender: false}); + {flow: flow,sender: false}); dialog.show(); } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ad0edeb7..316fbe40 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -78,7 +78,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -135,7 +135,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -179,7 +179,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -211,15 +211,15 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[0] } Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[1] } Label { font.pixelSize: Qt.application.font.pixelSize * 2 - text: "1234" + text: flow.sasList[2] } } @@ -230,7 +230,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -345,7 +345,7 @@ ApplicationWindow { ColumnLayout { id: col anchors.bottom: parent.bottom - property var emoji: emojis.mapping[Math.floor(Math.random()*64)] + property var emoji: emojis.mapping[flow.sasList[index]] Label { //height: font.pixelSize * 2 Layout.alignment: Qt.AlignHCenter @@ -369,7 +369,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -413,7 +413,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -451,7 +451,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -486,7 +486,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -521,7 +521,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - deviceVerificationList.remove(flow.tranId); + // deviceVerificationList.remove(flow.tranId); delete flow; } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index efb9882b..607cc279 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -15,32 +15,80 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) { timeout = new QTimer(this); timeout->setSingleShot(true); - if (this->sender == true) - this->transaction_id = http::client()->generate_txn_id(); + this->sas = olm::client()->sas_init(); connect(timeout, &QTimer::timeout, this, [this]() { emit timedout(); this->deleteLater(); }); + connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationAccept, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Accept" << std::endl; - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationRequest, + &ChatPage::recievedDeviceVerificationStart, this, [this](const mtx::events::collections::DeviceEvents &message) { auto msg = - std::get>(message); + std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Request" << std::endl; + if (std::find(msg.content.key_agreement_protocols.begin(), + msg.content.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != + msg.content.key_agreement_protocols.end() && + std::find(msg.content.hashes.begin(), + msg.content.hashes.end(), + "sha256") != msg.content.hashes.end() && + (std::find(msg.content.message_authentication_codes.begin(), + msg.content.message_authentication_codes.end(), + "hmac-sha256") != + msg.content.message_authentication_codes.end() || + std::find(msg.content.message_authentication_codes.begin(), + msg.content.message_authentication_codes.end(), + "hkdf-hmac-sha256") != + msg.content.message_authentication_codes.end()) && + (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.content.short_authentication_string.end() || + std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end())) { + this->sendVerificationKey(); // Not sure about this maybe + // those optional methods + this->canonical_json = nlohmann::json(msg); + } else { + this->cancelVerification(); + } } }); + connect( + ChatPage::instance(), + &ChatPage::recievedDeviceVerificationAccept, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + if ((msg.content.method == + mtx::events::msg::VerificationMethods::SASv1) && + (msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && + (msg.content.hash == "sha256") && + ((msg.content.message_authentication_code == "hkdf-hmac-sha256") || + (msg.content.message_authentication_code == "hmac-sha256"))) { + this->commitment = msg.content.commitment; + if (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end()) { + this->method = DeviceVerificationFlow::Method::Emoji; + } else { + this->method = DeviceVerificationFlow::Method::Decimal; + } + this->mac_method = msg.content.message_authentication_code; + this->sendVerificationKey(); + } else { + this->cancelVerification(); + } + } + }); connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationCancel, this, @@ -48,19 +96,55 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Cancel" << std::endl; - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationKey, - this, - [this](const mtx::events::collections::DeviceEvents &message) { - auto msg = - std::get>(message); - if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Key" << std::endl; + emit verificationCanceled(); } }); + connect( + ChatPage::instance(), + &ChatPage::recievedDeviceVerificationKey, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->sas->set_their_key(msg.content.key); + std::string info; + if (this->sender == true) { + info = "MATRIX_KEY_VERIFICATION_SAS|" + + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + + this->sas->public_key() + "|" + + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.content.key + + "|" + this->transaction_id; + } else { + info = "MATRIX_KEY_VERIFICATION_SAS|" + + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.content.key + + "|" + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + + this->sas->public_key() + "|" + this->transaction_id; + } + + if (this->method == DeviceVerificationFlow::Method::Emoji) { + this->sasList = this->sas->generate_bytes_emoji(info); + } else if (this->method == DeviceVerificationFlow::Method::Decimal) { + this->sasList = this->sas->generate_bytes_decimal(info); + } + if (this->sender == false) { + emit this->verificationRequestAccepted(this->method); + this->sendVerificationKey(); + } else { + if (this->commitment == + mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( + msg.content.key + + this->canonical_json["content"].dump()))) { + emit this->verificationRequestAccepted(this->method); + } else { + this->cancelVerification(); + } + } + } + }); connect(ChatPage::instance(), &ChatPage::recievedDeviceVerificationMac, this, @@ -104,6 +188,12 @@ DeviceVerificationFlow::getSender() return this->sender; } +std::vector +DeviceVerificationFlow::getSasList() +{ + return this->sasList; +} + void DeviceVerificationFlow::setTransactionId(QString transaction_id_) { @@ -133,6 +223,8 @@ void DeviceVerificationFlow::setSender(bool sender_) { this->sender = sender_; + if (this->sender == true) + this->transaction_id = http::client()->generate_txn_id(); } //! accepts a verification @@ -147,23 +239,26 @@ DeviceVerificationFlow::acceptVerificationRequest() req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; 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); + if (this->method == DeviceVerificationFlow::Method::Emoji) + req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; + else if (this->method == DeviceVerificationFlow::Method::Decimal) + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal}; + req.commitment = mtx::crypto::bin2base64_unpadded( + mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump())); body[this->toClient][this->deviceId.toStdString()] = req; + std::cout << "Accepting the Verification" << std::endl; + std::cout << json(body) << std::endl; + http::client() ->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { + 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)); - emit this->verificationRequestAccepted(rand() % 2 ? Emoji : Decimal); }); } //! starts the verification flow @@ -183,6 +278,7 @@ DeviceVerificationFlow::startVerificationRequest() mtx::events::msg::SASMethods::Emoji}; body[this->toClient][this->deviceId.toStdString()] = req; + this->canonical_json = nlohmann::json(req); http::client() ->send_to_devicewarn("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 @@ -236,6 +331,8 @@ DeviceVerificationFlow::cancelVerification() body[this->toClient][deviceId.toStdString()] = req; + emit this->verificationCanceled(); + http::client() ->send_to_device( @@ -254,7 +351,7 @@ DeviceVerificationFlow::sendVerificationKey() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationKey req; - req.key = ""; + req.key = this->sas->public_key(); req.transaction_id = this->transaction_id; body[this->toClient][deviceId.toStdString()] = req; diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index b651394b..bddf8edd 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -7,6 +7,8 @@ class QTimer; +using sas_ptr = std::unique_ptr; + class DeviceVerificationFlow : public QObject { Q_OBJECT @@ -16,6 +18,7 @@ class DeviceVerificationFlow : public QObject Q_PROPERTY(QString userId READ getUserId WRITE setUserId) Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) Q_PROPERTY(Method method READ getMethod WRITE setMethod) + Q_PROPERTY(std::vector sasList READ getSasList) public: enum Method @@ -30,6 +33,7 @@ public: QString getUserId(); QString getDeviceId(); Method getMethod(); + std::vector getSasList(); void setTransactionId(QString transaction_id_); bool getSender(); void setUserId(QString userID); @@ -37,6 +41,8 @@ public: void setMethod(Method method_); void setSender(bool sender_); + nlohmann::json canonical_json; + public slots: //! sends a verification request void sendVerificationRequest(); @@ -66,6 +72,10 @@ private: bool sender; QTimer *timeout = nullptr; + sas_ptr sas; + std::string mac_method; std::string transaction_id; + std::string commitment; mtx::identifiers::User toClient; + std::vector sasList; }; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 22fe4d6d..aaefaed4 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -170,32 +170,44 @@ 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); - } - }); + connect( + dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationRequest, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + auto flow = new DeviceVerificationFlow(this); + if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { + if (std::find(msg.content.methods.begin(), + msg.content.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) != + msg.content.methods.end()) { + emit newDeviceVerificationRequest( + std::move(flow), + QString::fromStdString(msg.content.transaction_id), + QString::fromStdString(msg.sender), + QString::fromStdString(msg.content.from_device)); + } + } + }); + connect( + dynamic_cast(parent), + &ChatPage::recievedDeviceVerificationStart, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + auto flow = new DeviceVerificationFlow(this); + flow->canonical_json = nlohmann::json(msg.content); + if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) { + emit newDeviceVerificationRequest( + std::move(flow), + QString::fromStdString(msg.content.transaction_id), + QString::fromStdString(msg.sender), + QString::fromStdString(msg.content.from_device)); + } + }); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 70912715..946461f9 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -67,7 +67,10 @@ signals: void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void newDeviceVerificationRequest(QString transactionId, QString userId, QString deviceId); + void newDeviceVerificationRequest(DeviceVerificationFlow *flow, + QString transactionId, + QString userId, + QString deviceId); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); -- cgit 1.5.1 From ce013e67a63edf10b0b0357cd1318ce622a74e97 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 23 Jun 2020 03:35:56 +0530 Subject: Add some more slots and mac --- .../qml/device-verification/DeviceVerification.qml | 4 +- src/ChatPage.h | 2 + src/DeviceVerificationFlow.cpp | 195 +++++++++++++++++---- src/DeviceVerificationFlow.h | 6 + src/Olm.cpp | 11 +- src/timeline/TimelineViewManager.cpp | 1 + 6 files changed, 172 insertions(+), 47 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 316fbe40..1a3d1432 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -240,7 +240,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } } @@ -379,7 +379,7 @@ ApplicationWindow { Button { Layout.alignment: Qt.AlignRight text: "They match." - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.acceptDevice(); } + onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } } } } diff --git a/src/ChatPage.h b/src/ChatPage.h index b05a388d..172f74fe 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -175,6 +175,8 @@ signals: void recievedDeviceVerificationKey(const mtx::events::collections::DeviceEvents &message); void recievedDeviceVerificationMac(const mtx::events::collections::DeviceEvents &message); void recievedDeviceVerificationStart(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationReady(const mtx::events::collections::DeviceEvents &message); + void recievedDeviceVerificationDone(const mtx::events::collections::DeviceEvents &message); private slots: void showUnreadMessageNotification(int count); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 607cc279..0de94c50 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,11 +1,10 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" #include "Logging.h" +#include "Utils.h" #include -#include // only for debugging #include -#include // only for debugging static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes @@ -28,32 +27,27 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - if (std::find(msg.content.key_agreement_protocols.begin(), - msg.content.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.content.key_agreement_protocols.end() && - std::find(msg.content.hashes.begin(), - msg.content.hashes.end(), - "sha256") != msg.content.hashes.end() && + if ((std::find(msg.content.key_agreement_protocols.begin(), + msg.content.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != + msg.content.key_agreement_protocols.end()) && + (std::find(msg.content.hashes.begin(), + msg.content.hashes.end(), + "sha256") != msg.content.hashes.end()) && (std::find(msg.content.message_authentication_codes.begin(), msg.content.message_authentication_codes.end(), "hmac-sha256") != - msg.content.message_authentication_codes.end() || - std::find(msg.content.message_authentication_codes.begin(), - msg.content.message_authentication_codes.end(), - "hkdf-hmac-sha256") != - msg.content.message_authentication_codes.end()) && - (std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.content.short_authentication_string.end() || - std::find(msg.content.short_authentication_string.begin(), - msg.content.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.content.short_authentication_string.end())) { - this->sendVerificationKey(); // Not sure about this maybe - // those optional methods - this->canonical_json = nlohmann::json(msg); + msg.content.message_authentication_codes.end()) && + ((std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.content.short_authentication_string.end()) || + (std::find(msg.content.short_authentication_string.begin(), + msg.content.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.content.short_authentication_string.end()))) { + this->acceptVerificationRequest(); + this->canonical_json = nlohmann::json(msg.content); } else { this->cancelVerification(); } @@ -67,12 +61,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - if ((msg.content.method == - mtx::events::msg::VerificationMethods::SASv1) && - (msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && + if ((msg.content.key_agreement_protocol == "curve25519-hkdf-sha256") && (msg.content.hash == "sha256") && - ((msg.content.message_authentication_code == "hkdf-hmac-sha256") || - (msg.content.message_authentication_code == "hmac-sha256"))) { + (msg.content.message_authentication_code == "hkdf-hmac-sha256")) { this->commitment = msg.content.commitment; if (std::find(msg.content.short_authentication_string.begin(), msg.content.short_authentication_string.end(), @@ -136,8 +127,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) } else { if (this->commitment == mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( - msg.content.key + - this->canonical_json["content"].dump()))) { + msg.content.key + this->canonical_json.dump()))) { emit this->verificationRequestAccepted(this->method); } else { this->cancelVerification(); @@ -152,7 +142,51 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) auto msg = std::get>(message); if (msg.content.transaction_id == this->transaction_id) { - std::cout << "Recieved Event Mac" << std::endl; + std::string info = + "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() + + this->deviceId.toStdString() + + http::client()->user_id().to_string() + + http::client()->device_id() + this->transaction_id; + + std::vector key_list; + std::string key_string; + for (auto mac : msg.content.mac) { + if (mac.second == + this->sas->calculate_mac(this->device_keys[mac.first], + info + mac.first)) { + key_string += mac.first; + } else { + this->cancelVerification(); + return; + } + } + if (msg.content.keys == + this->sas->calculate_mac(key_string, info + "KEY_IDS")) { + this->sendVerificationDone(); + } else { + this->cancelVerification(); + } + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationReady, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->startVerificationRequest(); + } + }); + connect(ChatPage::instance(), + &ChatPage::recievedDeviceVerificationDone, + this, + [this](const mtx::events::collections::DeviceEvents &message) { + auto msg = + std::get>(message); + if (msg.content.transaction_id == this->transaction_id) { + this->startVerificationRequest(); + emit this->deviceVerified(); } }); timeout->start(TIMEOUT); @@ -205,6 +239,34 @@ DeviceVerificationFlow::setUserId(QString userID) { this->userId = userID; this->toClient = mtx::identifiers::parse(userID.toStdString()); + + mtx::responses::QueryKeys res; + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + http::client()->query_keys( + req, + [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + for (auto x : res.device_keys) { + for (auto y : x.second) { + auto z = y.second; + if (z.user_id == user_id && + z.device_id == this->deviceId.toStdString()) { + for (auto a : z.keys) { + // TODO: Verify Signatures + this->device_keys[a.first] = a.second; + } + } + } + } + }); } void @@ -248,9 +310,6 @@ DeviceVerificationFlow::acceptVerificationRequest() body[this->toClient][this->deviceId.toStdString()] = req; - std::cout << "Accepting the Verification" << std::endl; - std::cout << json(body) << std::endl; - http::client() ->send_to_device( @@ -261,6 +320,50 @@ DeviceVerificationFlow::acceptVerificationRequest() static_cast(err->status_code)); }); } +//! responds verification request +void +DeviceVerificationFlow::sendVerificationReady() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationReady req; + + req.from_device = http::client()->device_id(); + req.transaction_id = this->transaction_id; + req.methods = {mtx::events::msg::VerificationMethods::SASv1}; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification ready: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} +//! accepts a verification +void +DeviceVerificationFlow::sendVerificationDone() +{ + mtx::requests::ToDeviceMessages body; + mtx::events::msg::KeyVerificationDone req; + + req.transaction_id = this->transaction_id; + + body[this->toClient][this->deviceId.toStdString()] = req; + + http::client() + ->send_to_device( + this->transaction_id, body, [](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn("failed to send verification done: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); +} //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() @@ -373,9 +476,25 @@ DeviceVerificationFlow::sendVerificationMac() mtx::requests::ToDeviceMessages body; mtx::events::msg::KeyVerificationMac req; + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() + + http::client()->device_id() + this->toClient.to_string() + + this->deviceId.toStdString() + this->transaction_id; + req.transaction_id = this->transaction_id; - // req.mac = ""; - req.keys = ""; + //! 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() + ","; + } + + req.keys = + this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS"); body[this->toClient][deviceId.toStdString()] = req; diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index bddf8edd..81ab9c99 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -2,6 +2,7 @@ #include "Olm.h" +#include "mtx/responses/crypto.hpp" #include #include @@ -46,6 +47,10 @@ public: public slots: //! sends a verification request void sendVerificationRequest(); + //! accepts a verification request + void sendVerificationReady(); + //! completes the verification flow(); + void sendVerificationDone(); //! accepts a verification void acceptVerificationRequest(); //! starts the verification flow @@ -78,4 +83,5 @@ private: std::string commitment; mtx::identifiers::User toClient; std::vector sasList; + std::map device_keys; }; diff --git a/src/Olm.cpp b/src/Olm.cpp index 6c1d3fdc..7d7037c9 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -9,7 +9,6 @@ #include "MatrixClient.h" #include "Utils.h" #include -#include // only for debugging static const std::string STORAGE_SECRET_KEY("secret"); constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; @@ -79,22 +78,20 @@ handle_to_device_messages(const std::vectorrecievedDeviceVerificationAccept(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { ChatPage::instance()->recievedDeviceVerificationRequest(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { ChatPage::instance()->recievedDeviceVerificationCancel(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { ChatPage::instance()->recievedDeviceVerificationKey(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { ChatPage::instance()->recievedDeviceVerificationMac(msg); - std::cout << j_msg.dump(2) << std::endl; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { ChatPage::instance()->recievedDeviceVerificationStart(msg); - std::cout << j_msg.dump(2) << std::endl; + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { + ChatPage::instance()->recievedDeviceVerificationReady(msg); + } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { + ChatPage::instance()->recievedDeviceVerificationDone(msg); } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index aaefaed4..7d016cbd 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -183,6 +183,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin msg.content.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != msg.content.methods.end()) { + flow->sendVerificationReady(); emit newDeviceVerificationRequest( std::move(flow), QString::fromStdString(msg.content.transaction_id), -- cgit 1.5.1 From d49ab156569f4f963a2306ff8074b6a22f28a31c Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Tue, 23 Jun 2020 23:29:00 +0530 Subject: Some Improvements - DeviceVerificationList change to LinkedList to improve time complexity while deleting - Downgrade the flow to not use key.verification.done and key.verification.ready --- resources/qml/TimelineView.qml | 1 + .../qml/device-verification/DeviceVerification.qml | 26 +++++++++++++--------- src/DeviceVerificationFlow.cpp | 6 +++-- src/timeline/TimelineViewManager.cpp | 17 ++++---------- src/timeline/TimelineViewManager.h | 3 ++- 5 files changed, 26 insertions(+), 27 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 99d14685..7ece08e2 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -110,6 +110,7 @@ Page { flow.sender = false; flow.deviceId = deviceId; flow.tranId = transactionId; + deviceVerificationList.add(flow.tranId); var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,sender: false}); dialog.show(); diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 1a3d1432..03fc5055 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -30,7 +30,11 @@ ApplicationWindow { implicitHeight: currentItem.implicitHeight } - onClosing: stack.replace(newVerificationRequest) + onClosing: { + flow.cancelVerification(); + deviceVerificationList.remove(flow.tranId); + delete flow; + } property var flow Connections { @@ -78,7 +82,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -135,7 +139,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -179,7 +183,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -230,7 +234,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -369,7 +373,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -413,7 +417,7 @@ ApplicationWindow { onClicked: { dialog.close(); flow.cancelVerification(); - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -451,7 +455,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -485,8 +489,8 @@ ApplicationWindow { Layout.alignment: Qt.AlignRight text: "Close" onClicked: { - dialog.close() - // deviceVerificationList.remove(flow.tranId); + dialog.close(); + deviceVerificationList.remove(flow.tranId); delete flow; } } @@ -521,7 +525,7 @@ ApplicationWindow { text: "Close" onClicked: { dialog.close() - // deviceVerificationList.remove(flow.tranId); + deviceVerificationList.remove(flow.tranId); delete flow; } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 0de94c50..db76aeb1 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -1,7 +1,6 @@ #include "DeviceVerificationFlow.h" #include "ChatPage.h" #include "Logging.h" -#include "Utils.h" #include #include @@ -162,7 +161,10 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *) } if (msg.content.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - this->sendVerificationDone(); + // uncomment this in future to be compatible with the + // MSC2366 this->sendVerificationDone(); and remoeve the + // below line + emit this->deviceVerified(); } else { this->cancelVerification(); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 7d016cbd..234b0bb6 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -26,26 +26,17 @@ namespace msgs = mtx::events::msg; void DeviceVerificationList::add(QString tran_id) { - this->dv_list.push_back(tran_id); + this->dv_list.append(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; - } - } + this->dv_list.removeOne(tran_id); } 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; + return this->dv_list.contains(tran_id); } void @@ -183,7 +174,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin msg.content.methods.end(), mtx::events::msg::VerificationMethods::SASv1) != msg.content.methods.end()) { - flow->sendVerificationReady(); + // flow->sendVerificationReady(); emit newDeviceVerificationRequest( std::move(flow), QString::fromStdString(msg.content.transaction_id), diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 946461f9..8af6d137 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -31,7 +32,7 @@ public: Q_INVOKABLE bool exist(QString tran_id); private: - QVector dv_list; + QLinkedList dv_list; }; class TimelineViewManager : public QObject -- cgit 1.5.1 From 1633650303e2129ae1e255f4e17a0fbff13638b1 Mon Sep 17 00:00:00 2001 From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com> Date: Wed, 24 Jun 2020 23:05:32 +0530 Subject: Some more changes - remove unnecessary field sender in userprofile.qml - cover user facing string with qsTr to get picked by translations - add spacing and fix theming issue - increase and add color to username - change back to QVector from QLinkedList cause I have mistaken better time complexity to give better benchmark red --- resources/qml/TimelineView.qml | 2 +- resources/qml/UserProfile.qml | 36 ++-- .../qml/device-verification/DeviceVerification.qml | 209 ++++++++++++++++----- src/timeline/TimelineViewManager.cpp | 6 +- src/timeline/TimelineViewManager.h | 3 +- 5 files changed, 193 insertions(+), 63 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 7ece08e2..e52d588c 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -112,7 +112,7 @@ Page { flow.tranId = transactionId; deviceVerificationList.add(flow.tranId); var dialog = deviceVerificationDialog.createObject(timelineRoot, - {flow: flow,sender: false}); + {flow: flow}); dialog.show(); } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 80415a29..f060b0e3 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -13,7 +13,7 @@ ApplicationWindow{ id:userProfileDialog height: 500 - width: 500 + width: 400 modality:Qt.WindowModal Layout.alignment: Qt.AlignHCenter palette: colors @@ -64,6 +64,9 @@ ApplicationWindow{ id: userProfileName text: user_data.userName fontSizeMode: Text.HorizontalFit + font.pixelSize: 16 + color:timelineManager.userColor(modelData.userId, colors.window) + font.bold: true Layout.alignment: Qt.AlignHCenter } @@ -71,6 +74,8 @@ ApplicationWindow{ id: matrixUserID text: user_data.userId fontSizeMode: Text.HorizontalFit + font.pixelSize: 16 + color:colors.text Layout.alignment: Qt.AlignHCenter } @@ -79,8 +84,6 @@ ApplicationWindow{ implicitWidth: userProfileDialog.width-20 clip: true Layout.alignment: Qt.AlignHCenter - ScrollBar.horizontal.policy: ScrollBar.AlwaysOn - ScrollBar.vertical.policy: ScrollBar.AlwaysOn ListView{ id: deviceList @@ -98,6 +101,7 @@ ApplicationWindow{ Text{ Layout.fillWidth: true color: colors.text + font.bold: true Layout.alignment: Qt.AlignRight text: deviceID } @@ -116,12 +120,15 @@ ApplicationWindow{ {userId : user_data.userId,sender: true,deviceId : model.deviceID}); deviceVerificationList.add(newFlow.tranId); var dialog = deviceVerificationDialog.createObject(userProfileDialog, - {flow: newFlow,sender: true}); + {flow: newFlow}); dialog.show(); } + palette { + button: "white" + } contentItem: Text { text: verifyButton.text - color: colors.background + color: "black" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } @@ -134,19 +141,26 @@ ApplicationWindow{ id: okbutton text:"OK" onClicked: userProfileDialog.close() - anchors.margins: { - right:10 - bottom:10 + anchors { + right: parent.right + bottom: parent.bottom + } + + anchors.margins : { + right : 10 + bottom : 10 + } + + palette { + button: "white" } contentItem: Text { text: okbutton.text - color: colors.background + color: "black" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } - - Layout.alignment: Qt.AlignRight | Qt.AlignBottom } } diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index 03fc5055..12e38f29 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -7,7 +7,6 @@ import Qt.labs.settings 1.0 import im.nheko 1.0 ApplicationWindow { - property bool sender: true title: stack.currentItem.title id: dialog @@ -25,7 +24,7 @@ ApplicationWindow { width: stack.implicitWidth StackView { id: stack - initialItem: sender == true?newVerificationRequest:acceptNewVerificationRequest + initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } @@ -52,7 +51,7 @@ ApplicationWindow { Component { id: newVerificationRequest Pane { - property string title: "Sending Device Verification Request" + property string title: qsTr("Sending Device Verification Request") ColumnLayout { spacing: 16 Label { @@ -60,8 +59,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "A new device was added." - + text: qsTr("A new device was added.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -70,15 +69,24 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device." - + text: 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: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -91,7 +99,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Start verification" + text: qsTr("Start verification") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } } } @@ -102,7 +119,7 @@ ApplicationWindow { Component { id: acceptNewVerificationRequest Pane { - property string title: "Recieving Device Verification Request" + property string title: qsTr("Recieving Device Verification Request") ColumnLayout { spacing: 16 @@ -111,15 +128,15 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "The device was requested to be verified" - + text: qsTr("The device was requested to be verified") + color:colors.text verticalAlignment: Text.AlignVCenter } RowLayout { RadioButton { Layout.alignment: Qt.AlignLeft - text: "Decimal" + text: qsTr("Decimal") onClicked: { flow.method = DeviceVerificationFlow.Decimal } } Item { @@ -127,7 +144,7 @@ ApplicationWindow { } RadioButton { Layout.alignment: Qt.AlignRight - text: "Emoji" + text: qsTr("Emoji") onClicked: { flow.method = DeviceVerificationFlow.Emoji } } } @@ -135,7 +152,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Deny" + text: qsTr("Deny") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -148,7 +174,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Accept" + text: qsTr("Accept") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); } } } @@ -159,7 +194,7 @@ ApplicationWindow { Component { id: awaitingVerificationRequestAccept Pane { - property string title: "Waiting for other party" + property string title: qsTr("Waiting for other party") ColumnLayout { spacing: 16 Label { @@ -168,8 +203,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Waiting for other side to accept the verification request." - + text: qsTr("Waiting for other side to accept the verification request.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -179,7 +214,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -198,7 +242,7 @@ ApplicationWindow { Component { id: digitVerification Pane { - property string title: "Verification Code" + property string title: qsTr("Verification Code") ColumnLayout { spacing: 16 Label { @@ -206,8 +250,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "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!" - + 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 } @@ -230,7 +274,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "They do not match!" + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -243,7 +296,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "They match." + 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(); } } } @@ -254,7 +316,7 @@ ApplicationWindow { Component { id: emojiVerification Pane { - property string title: "Verification Code" + property string title: qsTr("Verification Code") ColumnLayout { spacing: 16 Label { @@ -262,8 +324,8 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: "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!" - + 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 } @@ -369,7 +431,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "They do not match!" + text: qsTr("They do not match!") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -382,7 +453,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "They match." + 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(); } } } @@ -393,7 +473,7 @@ ApplicationWindow { Component { id: awaitingVerificationConfirmation Pane { - property string title: "Awaiting Confirmation" + property string title: qsTr("Awaiting Confirmation") ColumnLayout { spacing: 16 Label { @@ -402,8 +482,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Waiting for other side to complete verification." - + text: qsTr("Waiting for other side to complete verification.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -413,7 +493,16 @@ ApplicationWindow { RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: "Cancel" + text: qsTr("Cancel") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); flow.cancelVerification(); @@ -432,7 +521,7 @@ ApplicationWindow { Component { id: verificationSuccess Pane { - property string title: "Successful Verification" + property string title: qsTr("Successful Verification") ColumnLayout { spacing: 16 Label { @@ -441,8 +530,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Verification successful! Both sides verified their devices!" - + text: qsTr("Verification successful! Both sides verified their devices!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -452,7 +541,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Close" + text: qsTr("Close") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close() deviceVerificationList.remove(flow.tranId); @@ -467,7 +565,7 @@ ApplicationWindow { Component { id: partnerAborted Pane { - property string title: "Verification aborted!" + property string title: qsTr("Verification aborted!") ColumnLayout { spacing: 16 Label { @@ -476,8 +574,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Verification canceled by the other party!" - + text: qsTr("Verification canceled by the other party!") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -487,7 +585,16 @@ ApplicationWindow { } Button { Layout.alignment: Qt.AlignRight - text: "Close" + text: qsTr("Close") + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } onClicked: { dialog.close(); deviceVerificationList.remove(flow.tranId); @@ -502,7 +609,7 @@ ApplicationWindow { Component { id: timedout Pane { - property string title: "Verification timed out" + property string title: qsTr("Verification timed out") ColumnLayout { spacing: 16 Text { @@ -511,8 +618,8 @@ ApplicationWindow { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: "Device verification timed out." - + text: qsTr("Device verification timed out.") + color:colors.text verticalAlignment: Text.AlignVCenter } @@ -521,8 +628,18 @@ ApplicationWindow { Layout.fillWidth: true } Button { + id: timedOutCancel Layout.alignment: Qt.AlignRight - text: "Close" + palette { + button: "white" + } + contentItem: Text { + text: parent.text + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + text: qsTr("Close") onClicked: { dialog.close() deviceVerificationList.remove(flow.tranId); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 234b0bb6..a36a5bda 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -26,17 +26,17 @@ namespace msgs = mtx::events::msg; void DeviceVerificationList::add(QString tran_id) { - this->dv_list.append(tran_id); + this->deviceVerificationList.push_back(tran_id); } void DeviceVerificationList::remove(QString tran_id) { - this->dv_list.removeOne(tran_id); + this->deviceVerificationList.removeOne(tran_id); } bool DeviceVerificationList::exist(QString tran_id) { - return this->dv_list.contains(tran_id); + return this->deviceVerificationList.contains(tran_id); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 8af6d137..38aba021 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -32,7 +31,7 @@ public: Q_INVOKABLE bool exist(QString tran_id); private: - QLinkedList dv_list; + QVector deviceVerificationList; }; class TimelineViewManager : public QObject -- cgit 1.5.1 From 4862be06be6e32c1f4963feabfde010a9f64616c Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 24 Jun 2020 16:24:22 +0200 Subject: Fix presence indicator --- resources/qml/Avatar.qml | 10 ++++++---- resources/qml/MatrixText.qml | 6 ++++-- resources/qml/Reactions.qml | 13 ++++++++++--- resources/qml/TimelineRow.qml | 12 ++++++------ resources/qml/TimelineView.qml | 21 +++++++++++++-------- resources/qml/UserProfile.qml | 3 ++- resources/qml/delegates/FileMessage.qml | 4 +++- resources/qml/delegates/ImageMessage.qml | 2 +- resources/qml/delegates/MessageDelegate.qml | 12 ++++++------ resources/qml/delegates/PlayableMediaMessage.qml | 4 ++-- resources/qml/delegates/Reply.qml | 4 +++- resources/qml/delegates/TextMessage.qml | 4 +++- .../qml/device-verification/DeviceVerification.qml | 9 +-------- resources/qml/emoji/EmojiPicker.qml | 4 ++-- src/timeline/TimelineViewManager.cpp | 20 ++++++++++++-------- src/timeline/TimelineViewManager.h | 4 ++-- 16 files changed, 76 insertions(+), 56 deletions(-) (limited to 'resources/qml/device-verification/DeviceVerification.qml') diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index f934e2f6..e687e170 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -2,11 +2,13 @@ import QtQuick 2.6 import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 +import im.nheko 1.0 + Rectangle { id: avatar width: 48 height: 48 - radius: settings.avatarCircles ? height/2 : 3 + radius: Settings.avatarCircles ? height/2 : 3 property alias url: img.source property string userid @@ -40,7 +42,7 @@ Rectangle { anchors.fill: parent width: avatar.width height: avatar.height - radius: settings.avatarCircles ? height/2 : 3 + radius: Settings.avatarCircles ? height/2 : 3 } } @@ -52,8 +54,8 @@ Rectangle { height: avatar.height / 6 width: height - radius: settings.avatarCircles ? height / 2 : height / 4 - color: switch (timelineManager.userPresence(userid)) { + radius: Settings.avatarCircles ? height / 2 : height / 4 + color: switch (TimelineManager.userPresence(userid)) { case "online": return "#00cc66" case "unavailable": return "#ff9933" case "offline": // return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml index cbb55bea..d0910045 100644 --- a/resources/qml/MatrixText.qml +++ b/resources/qml/MatrixText.qml @@ -1,6 +1,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.3 +import im.nheko 1.0 + TextEdit { textFormat: TextEdit.RichText readOnly: true @@ -10,10 +12,10 @@ TextEdit { onLinkActivated: { if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) chat.model.openUserProfile(/^https:\/\/matrix.to\/#\/(@.*)$/.exec(link)[1]) - else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) timelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]) + else if (/^https:\/\/matrix.to\/#\/(![^\/]*)$/.test(link)) TimelineManager.setHistoryView(/^https:\/\/matrix.to\/#\/(!.*)$/.exec(link)[1]) else if (/^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.test(link)) { var match = /^https:\/\/matrix.to\/#\/(![^\/]*)\/(\$.*)$/.exec(link) - timelineManager.setHistoryView(match[1]) + TimelineManager.setHistoryView(match[1]) chat.positionViewAtIndex(chat.model.idToIndex(match[2]), ListView.Contain) } else Qt.openUrlExternally(link) diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml index c1091756..11109d7f 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml @@ -1,6 +1,8 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 +import im.nheko 1.0 + // This class is for showing Reactions in the timeline row, not for // adding new reactions via the emoji picker Flow { @@ -33,8 +35,13 @@ 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 } @@ -46,7 +53,7 @@ Flow { TextMetrics { id: textMetrics - font.family: settings.emojiFont + font.family: Settings.emojiFont elide: Text.ElideRight elideWidth: 150 text: modelData.key @@ -55,8 +62,8 @@ Flow { Text { anchors.baseline: reactionCounter.baseline id: reactionText - text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…") - font.family: settings.emojiFont + text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…") + font.family: Settings.emojiFont color: reaction.hovered ? colors.highlight : colors.text maximumLineCount: 1 } diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index d1c20278..db58eb22 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -29,7 +29,7 @@ Item { } } Rectangle { - color: (settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent" + color: (Settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent" anchors.fill: row } RowLayout { @@ -48,8 +48,8 @@ Item { // fancy reply, if this is a reply Reply { visible: model.replyTo - modelData: chat.model.getDump(model.replyTo, model.id) - userColor: timelineManager.userColor(modelData.userId, colors.window) + modelData: chat.model.getDump(model.replyTo) + userColor: TimelineManager.userColor(modelData.userId, colors.window) } // actual message content @@ -84,7 +84,7 @@ Item { width: 16 } EmojiButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 @@ -96,7 +96,7 @@ Item { event_id: model.id } ImageButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 @@ -112,7 +112,7 @@ Item { onClicked: chat.model.replyAction(model.id) } ImageButton { - visible: settings.buttonsInTimeline + visible: Settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index e52d588c..d9302ed7 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -91,7 +91,7 @@ Page { visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker height: visible ? implicitHeight : 0 text: qsTr("Save as") - onTriggered: timelineManager.timeline.saveMedia(messageContextMenu.eventId) + onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId) } } @@ -104,7 +104,7 @@ Page { DeviceVerification {} } Connections { - target: timelineManager + target: TimelineManager onNewDeviceVerificationRequest: { flow.userId = userId; flow.sender = false; @@ -118,7 +118,7 @@ Page { } Label { - visible: !timelineManager.timeline && !timelineManager.isInitialSync + visible: !TimelineManager.timeline && !TimelineManager.isInitialSync anchors.centerIn: parent text: qsTr("No room open") font.pointSize: 24 @@ -128,7 +128,7 @@ Page { BusyIndicator { visible: running anchors.centerIn: parent - running: timelineManager.isInitialSync + running: TimelineManager.isInitialSync height: 200 width: 200 z: 3 @@ -137,7 +137,7 @@ Page { ListView { id: chat - visible: !!timelineManager.timeline + visible: TimelineManager.timeline != null cacheBuffer: 400 @@ -149,7 +149,7 @@ Page { anchors.leftMargin: 4 anchors.rightMargin: scrollbar.width - model: timelineManager.timeline + model: TimelineManager.timeline boundsBehavior: Flickable.StopAtBounds @@ -197,7 +197,7 @@ Page { onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom - property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32) + property int delegateMaxWidth: (Settings.timelineMaxWidth > 100 && (parent.width - Settings.timelineMaxWidth) > 32) ? Settings.timelineMaxWidth : (parent.width - 32) delegate: Rectangle { // This would normally be previousSection, but our model's order is inverted. @@ -303,7 +303,7 @@ Page { Label { id: userName text: chat.model.escapeEmoji(modelData.userName) - color: timelineManager.userColor(modelData.userId, colors.window) + color: TimelineManager.userColor(modelData.userId, colors.window) textFormat: Text.RichText MouseArea { @@ -381,8 +381,13 @@ 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 f060b0e3..a7ff8a35 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -57,6 +57,7 @@ ApplicationWindow{ height: 130 width: 130 displayName: modelData.userName + userid: modelData.userId Layout.alignment: Qt.AlignHCenter } @@ -65,7 +66,7 @@ ApplicationWindow{ text: user_data.userName fontSizeMode: Text.HorizontalFit font.pixelSize: 16 - color:timelineManager.userColor(modelData.userId, colors.window) + color:TimelineManager.userColor(modelData.userId, colors.window) font.bold: true Layout.alignment: Qt.AlignHCenter } diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml index d8e4215f..158daf45 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml @@ -1,6 +1,8 @@ import QtQuick 2.6 import QtQuick.Layouts 1.2 +import im.nheko 1.0 + Item { height: row.height + 24 width: parent ? parent.width : undefined @@ -29,7 +31,7 @@ Item { } MouseArea { anchors.fill: parent - onClicked: timelineManager.timeline.saveMedia(model.data.id) + onClicked: TimelineManager.timeline.saveMedia(model.data.id) cursorShape: Qt.PointingHandCursor } } diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml index 3885ddae..b5c51c2c 100644 --- a/resources/qml/delegates/ImageMessage.qml +++ b/resources/qml/delegates/ImageMessage.qml @@ -36,7 +36,7 @@ Item { MouseArea { enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready anchors.fill: parent - onClicked: timelineManager.openImageOverlay(model.data.url, model.data.id) + onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id) } } } diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index 9630ae3a..6f69f026 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -37,7 +37,7 @@ Item { roleValue: MtxEvent.EmoteMessage NoticeMessage { formatted: chat.model.escapeEmoji(modelData.userName) + " " + model.data.formattedBody - color: timelineManager.userColor(modelData.userId, colors.window) + color: TimelineManager.userColor(modelData.userId, colors.window) } } DelegateChoice { @@ -100,31 +100,31 @@ Item { // TODO: make a more complex formatter for the power levels. roleValue: MtxEvent.PowerLevels NoticeMessage { - text: timelineManager.timeline.formatPowerLevelEvent(model.data.id) + text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomJoinRules NoticeMessage { - text: timelineManager.timeline.formatJoinRuleEvent(model.data.id) + text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomHistoryVisibility NoticeMessage { - text: timelineManager.timeline.formatHistoryVisibilityEvent(model.data.id) + text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.RoomGuestAccess NoticeMessage { - text: timelineManager.timeline.formatGuestAccessEvent(model.data.id) + text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id) } } DelegateChoice { roleValue: MtxEvent.Member NoticeMessage { - text: timelineManager.timeline.formatMemberEvent(model.data.id); + text: TimelineManager.timeline.formatMemberEvent(model.data.id); } } DelegateChoice { diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml index 8d2fa8a8..893325b6 100644 --- a/resources/qml/delegates/PlayableMediaMessage.qml +++ b/resources/qml/delegates/PlayableMediaMessage.qml @@ -106,7 +106,7 @@ Rectangle { anchors.fill: parent onClicked: { switch (button.state) { - case "": timelineManager.timeline.cacheMedia(model.data.id); break; + case "": TimelineManager.timeline.cacheMedia(model.data.id); break; case "stopped": media.play(); console.log("play"); button.state = "playing" @@ -127,7 +127,7 @@ Rectangle { } Connections { - target: timelineManager.timeline + target: TimelineManager.timeline onMediaCached: { if (mxcUrl == model.data.url) { media.source = "file://" + cacheUrl diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml index f9fd3f11..d4fffb06 100644 --- a/resources/qml/delegates/Reply.qml +++ b/resources/qml/delegates/Reply.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 +import im.nheko 1.0 + Item { id: replyComponent @@ -26,7 +28,7 @@ Item { anchors.bottom: replyContainer.bottom width: 4 - color: timelineManager.userColor(reply.modelData.userId, colors.window) + color: TimelineManager.userColor(reply.modelData.userId, colors.window) } Column { diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml index cc2d2da0..99ff9329 100644 --- a/resources/qml/delegates/TextMessage.qml +++ b/resources/qml/delegates/TextMessage.qml @@ -1,10 +1,12 @@ import ".." +import im.nheko 1.0 + MatrixText { property string formatted: model.data.formattedBody text: "" + formatted.replace("
", "
")
 	width: parent ? parent.width : undefined
 	height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
 	clip: true
-	font.pointSize: (settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? settings.fontSize * 3 : settings.fontSize
+	font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
 }
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 12e38f29..516bc74a 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -2,7 +2,6 @@ import QtQuick 2.3
 import QtQuick.Controls 2.10
 import QtQuick.Window 2.2
 import QtQuick.Layouts 1.10
-import Qt.labs.settings 1.0
 
 import im.nheko 1.0
 
@@ -14,12 +13,6 @@ ApplicationWindow {
 
 	palette: colors
 
-	Settings {
-		id: settings
-		category: "user"
-		property bool emoji_font_family: true
-	}
-
 	height: stack.implicitHeight
 	width: stack.implicitWidth
 	StackView {
@@ -417,7 +410,7 @@ ApplicationWindow {
 									Layout.alignment: Qt.AlignHCenter
 									text: col.emoji.emoji
 									font.pixelSize: Qt.application.font.pixelSize * 2
-									font.family: settings.emoji_font_family
+									font.family: Settings.emojiFont
 								}
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml
index f75221d5..d2ca4da3 100644
--- a/resources/qml/emoji/EmojiPicker.qml
+++ b/resources/qml/emoji/EmojiPicker.qml
@@ -73,7 +73,7 @@ Popup {
                 contentItem: Text {
                     horizontalAlignment: Text.AlignHCenter
                     verticalAlignment: Text.AlignVCenter
-                    font.family: settings.emojiFont
+                    font.family: Settings.emojiFont
                     
                     font.pixelSize: 36
                     text: model.unicode
@@ -104,7 +104,7 @@ Popup {
                 onClicked: {
                     console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id)
                     emojiPopup.close()
-                    timelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode)
+                    TimelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode)
                 }
             }
 
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index a36a5bda..14c66951 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -80,12 +80,16 @@ 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
+{
+        if (id.isEmpty())
+                return "";
+        else
+                return QString::fromStdString(
+                  mtx::presence::to_string(cache::presenceState(id.toStdString())));
+}
+
 QString
 TimelineViewManager::userStatus(QString id) const
 {
@@ -110,6 +114,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
         qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow");
         qmlRegisterType("im.nheko", 1, 0, "UserProfileModel");
         qmlRegisterType("im.nheko", 1, 0, "UserProfileList");
+        qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", this);
+        qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", settings.data());
 
         qRegisterMetaType();
         qmlRegisterType("im.nheko.EmojiModel", 1, 0, "EmojiModel");
@@ -144,8 +150,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
         });
 #endif
         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);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 38aba021..af8bc4b6 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -57,7 +57,7 @@ public:
         Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
         Q_INVOKABLE QColor userColor(QString id, QColor background);
 
-        // Q_INVOKABLE QString userPresence(QString id) const;
+        Q_INVOKABLE QString userPresence(QString id) const;
         Q_INVOKABLE QString userStatus(QString id) const;
 
 signals:
@@ -131,4 +131,4 @@ private:
 
         DeviceVerificationList *dvList;
 };
-Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents)
\ No newline at end of file
+Q_DECLARE_METATYPE(mtx::events::collections::DeviceEvents)
-- 
cgit 1.5.1


From 00e36b6068786d64812135a4d505501134ebc214 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Fri, 26 Jun 2020 04:24:42 +0530
Subject: Add some Userprofile buttons

---
 resources/qml/UserProfile.qml                      | 62 +++++++++++++++-
 .../qml/device-verification/DeviceVerification.qml | 15 +++-
 src/DeviceVerificationFlow.cpp                     | 83 +++++++++++-----------
 src/ui/UserProfile.cpp                             | 28 ++++++++
 src/ui/UserProfile.h                               |  5 +-
 5 files changed, 146 insertions(+), 47 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index e5d01625..36c8586d 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -12,7 +12,7 @@ ApplicationWindow{
     property var colors: currentActivePalette
 
     id:userProfileDialog
-    height: 500
+    height: 650
     width: 420
     modality:Qt.WindowModal
     Layout.alignment: Qt.AlignHCenter
@@ -43,7 +43,7 @@ ApplicationWindow{
         width: userProfileDialog.width
         height: userProfileDialog.height
 
-        Layout.fillHeight : true
+        // Layout.fillHeight : true
 
         ColumnLayout{
             anchors.fill: userProfileItem
@@ -82,8 +82,62 @@ ApplicationWindow{
                 Layout.alignment: Qt.AlignHCenter
             }
 
+            RowLayout{
+                Layout.alignment: Qt.AlignHCenter
+                ImageButton{
+                    image:":/icons/icons/ui/do-not-disturb-rounded-sign.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Ban the user")
+                    onClicked : {
+                        userProfileList.banUser()
+                    }
+                }
+                // ImageButton{
+                //     image:":/icons/icons/ui/volume-off-indicator.png"
+                //     Layout.margins: {
+                //         left: 5
+                //         right: 5
+                //     }
+                //     ToolTip.visible: hovered
+			    //     ToolTip.text: qsTr("Ignore messages from this user")
+                //     onClicked : {
+                //         userProfileList.ignoreUser()
+                //     }
+                // }
+
+                ImageButton{
+                    image:":/icons/icons/ui/round-remove-button.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Kick the user")
+                    onClicked : {
+                        userProfileList.kickUser()
+                    }
+                }
+
+                ImageButton{
+                    image:":/icons/icons/ui/black-bubble-speech.png"
+                    Layout.margins: {
+                        left: 5
+                        right: 5
+                    }
+                    ToolTip.visible: hovered
+			        ToolTip.text: qsTr("Start a conversation")
+                    onClicked : {
+                        userProfileList.startChat()
+                    }
+                }
+            }
+
             ScrollView {
-                implicitHeight: userProfileDialog.height/2+20
+                implicitHeight: userProfileDialog.height/2 + 20
                 implicitWidth: userProfileDialog.width-20
                 clip: true
                 Layout.alignment: Qt.AlignHCenter
@@ -150,6 +204,8 @@ ApplicationWindow{
                 id: okbutton
                 text:"OK"
                 onClicked: userProfileDialog.close()
+                
+                Layout.alignment: Qt.AlignRight
 
                 Layout.margins : {
                     right : 10
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 516bc74a..ca21f484 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -82,8 +82,6 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
-							deviceVerificationList.remove(flow.tranId);
 							delete flow; 
 						}
 					}
@@ -128,16 +126,26 @@ ApplicationWindow {
 
 				RowLayout {
 					RadioButton {
+						id: decimalRadio
 						Layout.alignment: Qt.AlignLeft
 						text: qsTr("Decimal")
+						contentItem: Text {
+    					    text: decimalRadio.text
+    					    color: colors.text
+    					}
 						onClicked: { flow.method = DeviceVerificationFlow.Decimal }
 					}
 					Item {
 						Layout.fillWidth: true
 					}
 					RadioButton {
+						id: emojiRadio
 						Layout.alignment: Qt.AlignRight
 						text: qsTr("Emoji")
+						contentItem: Text {
+    					    text: emojiRadio.text
+    					    color: colors.text
+    					}
 						onClicked: { flow.method = DeviceVerificationFlow.Emoji }
 					}
 				}
@@ -156,7 +164,7 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close(); 
+							dialog.close();
 							flow.cancelVerification();
 							deviceVerificationList.remove(flow.tranId);
 							delete flow; 
@@ -411,6 +419,7 @@ ApplicationWindow {
 									text: col.emoji.emoji
 									font.pixelSize: Qt.application.font.pixelSize * 2
 									font.family: Settings.emojiFont
+									color:colors.text
 								}
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 9b260892..2c6e9c1e 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -13,7 +13,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
 {
         timeout = new QTimer(this);
         timeout->setSingleShot(true);
-        this->sas = olm::client()->sas_init();
+        this->sas           = olm::client()->sas_init();
+        this->isMacVerified = false;
         connect(timeout, &QTimer::timeout, this, [this]() {
                 emit timedout();
                 this->deleteLater();
@@ -134,45 +135,47 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                           }
                   }
           });
-        connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationMac,
-                this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                std::string info =
-                                  "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
-                                  this->deviceId.toStdString() +
-                                  http::client()->user_id().to_string() +
-                                  http::client()->device_id() + this->transaction_id;
-
-                                std::vector key_list;
-                                std::string key_string;
-                                for (auto mac : msg.content.mac) {
-                                        if (mac.second ==
-                                            this->sas->calculate_mac(this->device_keys[mac.first],
-                                                                     info + mac.first)) {
-                                                key_string += mac.first;
-                                        } else {
-                                                this->cancelVerification();
-                                                return;
-                                        }
-                                }
-                                if (msg.content.keys ==
-                                    this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
-                                        // uncomment this in future to be compatible with the
-                                        // MSC2366 this->sendVerificationDone(); and remoeve the
-                                        // below line
-                                        if (this->isMacVerified == true)
-                                                emit this->deviceVerified();
-                                        else
-                                                this->isMacVerified = true;
-                                } else {
-                                        this->cancelVerification();
-                                }
-                        }
-                });
+        connect(
+          ChatPage::instance(),
+          &ChatPage::recievedDeviceVerificationMac,
+          this,
+          [this](const mtx::events::collections::DeviceEvents &message) {
+                  auto msg = std::get>(message);
+                  if (msg.content.transaction_id == this->transaction_id) {
+                          std::string info =
+                            "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
+                            this->deviceId.toStdString() + http::client()->user_id().to_string() +
+                            http::client()->device_id() + this->transaction_id;
+
+                          std::vector key_list;
+                          std::string key_string;
+                          for (auto mac : msg.content.mac) {
+                                  key_string += mac.first + ",";
+                                  if (device_keys[mac.first] != "") {
+                                          if (mac.second ==
+                                              this->sas->calculate_mac(this->device_keys[mac.first],
+                                                                       info + mac.first)) {
+                                          } else {
+                                                  this->cancelVerification();
+                                                  return;
+                                          }
+                                  }
+                          }
+                          key_string = key_string.substr(0, key_string.length() - 1);
+                          if (msg.content.keys ==
+                              this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
+                                  // uncomment this in future to be compatible with the
+                                  // MSC2366 this->sendVerificationDone(); and remove the
+                                  // below line
+                                  if (this->isMacVerified == true)
+                                          emit this->deviceVerified();
+                                  else
+                                          this->isMacVerified = true;
+                          } else {
+                                  this->cancelVerification();
+                          }
+                  }
+          });
         connect(ChatPage::instance(),
                 &ChatPage::recievedDeviceVerificationReady,
                 this,
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 588d6969..6aa4deff 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,4 +1,5 @@
 #include "UserProfile.h"
+#include "ChatPage.h"
 #include "Logging.h"
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
@@ -86,3 +87,30 @@ UserProfile::updateDeviceList()
 {
         fetchDeviceList(this->userId);
 }
+
+void
+UserProfile::banUser()
+{
+        ChatPage::instance()->banUser(this->userId, "");
+}
+
+// void ignoreUser(){
+
+// }
+
+void
+UserProfile::kickUser()
+{
+        ChatPage::instance()->kickUser(this->userId, "");
+}
+
+void
+UserProfile::startChat()
+{
+        mtx::requests::CreateRoom req;
+        req.preset     = mtx::requests::Preset::PrivateChat;
+        req.visibility = mtx::requests::Visibility::Private;
+        if (utils::localUser() != this->userId)
+                req.invite = {this->userId.toStdString()};
+        emit ChatPage::instance()->createRoom(req);
+}
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index c37e23ae..ad92d182 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -5,7 +5,6 @@
 #include 
 
 #include "MatrixClient.h"
-
 class DeviceInfo
 {
 public:
@@ -36,6 +35,10 @@ public:
 
         Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void updateDeviceList();
+        Q_INVOKABLE void banUser();
+        // Q_INVOKABLE void ignoreUser();
+        Q_INVOKABLE void kickUser();
+        Q_INVOKABLE void startChat();
 
 signals:
         void userIdChanged();
-- 
cgit 1.5.1


From ffa61095b8be7c61c3b4cdd693e59239ab668516 Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Fri, 26 Jun 2020 15:10:37 +0530
Subject: Error Handling and some fixes

---
 resources/qml/UserProfile.qml                      |  16 ++-
 .../qml/device-verification/DeviceVerification.qml |  52 ++--------
 src/DeviceVerificationFlow.cpp                     | 109 +++++++++++++--------
 src/DeviceVerificationFlow.h                       |  12 ++-
 src/timeline/TimelineViewManager.cpp               |  45 ++++++++-
 5 files changed, 138 insertions(+), 96 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 36c8586d..6ef75031 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -108,30 +108,28 @@ ApplicationWindow{
                 //         userProfileList.ignoreUser()
                 //     }
                 // }
-
                 ImageButton{
-                    image:":/icons/icons/ui/round-remove-button.png"
+                    image:":/icons/icons/ui/black-bubble-speech.png"
                     Layout.margins: {
                         left: 5
                         right: 5
                     }
                     ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Kick the user")
+			        ToolTip.text: qsTr("Start a private chat")
                     onClicked : {
-                        userProfileList.kickUser()
+                        userProfileList.startChat()
                     }
                 }
-
                 ImageButton{
-                    image:":/icons/icons/ui/black-bubble-speech.png"
+                    image:":/icons/icons/ui/round-remove-button.png"
                     Layout.margins: {
                         left: 5
                         right: 5
                     }
                     ToolTip.visible: hovered
-			        ToolTip.text: qsTr("Start a conversation")
+			        ToolTip.text: qsTr("Kick the user")
                     onClicked : {
-                        userProfileList.startChat()
+                        userProfileList.kickUser()
                     }
                 }
             }
@@ -205,7 +203,7 @@ ApplicationWindow{
                 text:"OK"
                 onClicked: userProfileDialog.close()
                 
-                Layout.alignment: Qt.AlignRight
+                Layout.alignment: Qt.AlignRight | Qt.AlignBottom
 
                 Layout.margins : {
                     right : 10
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index ca21f484..15c2d7a2 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -22,12 +22,6 @@ ApplicationWindow {
 		implicitHeight: currentItem.implicitHeight
 	}
 
-	onClosing: {
-		flow.cancelVerification();
-		deviceVerificationList.remove(flow.tranId);
-		delete flow; 
-	}
-
 	property var flow
 	Connections {
 		target: flow
@@ -123,33 +117,6 @@ ApplicationWindow {
 					color:colors.text
 					verticalAlignment: Text.AlignVCenter
 				}
-
-				RowLayout {
-					RadioButton {
-						id: decimalRadio
-						Layout.alignment: Qt.AlignLeft
-						text: qsTr("Decimal")
-						contentItem: Text {
-    					    text: decimalRadio.text
-    					    color: colors.text
-    					}
-						onClicked: { flow.method = DeviceVerificationFlow.Decimal }
-					}
-					Item {
-						Layout.fillWidth: true
-					}
-					RadioButton {
-						id: emojiRadio
-						Layout.alignment: Qt.AlignRight
-						text: qsTr("Emoji")
-						contentItem: Text {
-    					    text: emojiRadio.text
-    					    color: colors.text
-    					}
-						onClicked: { flow.method = DeviceVerificationFlow.Emoji }
-					}
-				}
-
 				RowLayout {
 					Button {
 						Layout.alignment: Qt.AlignLeft
@@ -165,9 +132,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close();
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -227,9 +193,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -261,14 +226,17 @@ ApplicationWindow {
 					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
 					}
 				}
 
@@ -287,9 +255,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -411,6 +378,7 @@ ApplicationWindow {
 							implicitWidth: col.width
 							ColumnLayout {
 								id: col
+								Layout.fillWidth: true
 								anchors.bottom: parent.bottom
 								property var emoji: emojis.mapping[flow.sasList[index]]
 								Label {
@@ -424,6 +392,7 @@ ApplicationWindow {
 								Label {
 									Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
 									text: col.emoji.description
+									color:colors.text
 								}
 							}
 						}
@@ -445,9 +414,8 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification();
+							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(flow.tranId);
-							delete flow; 
 						}
 					}
 					Item {
@@ -507,7 +475,7 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
-							flow.cancelVerification(); 
+							flow.cancelVerification(DeviceVerificationFlow.User); 
 							deviceVerificationList.remove(flow.tranId);
 							delete flow;
 						}
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 2c6e9c1e..b5134a3b 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -17,42 +17,53 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
         this->isMacVerified = false;
         connect(timeout, &QTimer::timeout, this, [this]() {
                 emit timedout();
+                this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
                 this->deleteLater();
         });
 
-        connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationStart,
-                this,
-                [this](const mtx::events::collections::DeviceEvents &message) {
-                        auto msg =
-                          std::get>(message);
-                        if (msg.content.transaction_id == this->transaction_id) {
-                                if ((std::find(msg.content.key_agreement_protocols.begin(),
-                                               msg.content.key_agreement_protocols.end(),
-                                               "curve25519-hkdf-sha256") !=
-                                     msg.content.key_agreement_protocols.end()) &&
-                                    (std::find(msg.content.hashes.begin(),
-                                               msg.content.hashes.end(),
-                                               "sha256") != msg.content.hashes.end()) &&
-                                    (std::find(msg.content.message_authentication_codes.begin(),
-                                               msg.content.message_authentication_codes.end(),
-                                               "hmac-sha256") !=
-                                     msg.content.message_authentication_codes.end()) &&
-                                    ((std::find(msg.content.short_authentication_string.begin(),
+        connect(
+          ChatPage::instance(),
+          &ChatPage::recievedDeviceVerificationStart,
+          this,
+          [this](const mtx::events::collections::DeviceEvents &message) {
+                  auto msg =
+                    std::get>(message);
+                  if (msg.content.transaction_id == this->transaction_id) {
+                          if ((std::find(msg.content.key_agreement_protocols.begin(),
+                                         msg.content.key_agreement_protocols.end(),
+                                         "curve25519-hkdf-sha256") !=
+                               msg.content.key_agreement_protocols.end()) &&
+                              (std::find(msg.content.hashes.begin(),
+                                         msg.content.hashes.end(),
+                                         "sha256") != msg.content.hashes.end()) &&
+                              (std::find(msg.content.message_authentication_codes.begin(),
+                                         msg.content.message_authentication_codes.end(),
+                                         "hmac-sha256") !=
+                               msg.content.message_authentication_codes.end())) {
+                                  if (std::find(msg.content.short_authentication_string.begin(),
                                                 msg.content.short_authentication_string.end(),
                                                 mtx::events::msg::SASMethods::Decimal) !=
-                                      msg.content.short_authentication_string.end()) ||
-                                     (std::find(msg.content.short_authentication_string.begin(),
-                                                msg.content.short_authentication_string.end(),
-                                                mtx::events::msg::SASMethods::Emoji) !=
-                                      msg.content.short_authentication_string.end()))) {
-                                        this->acceptVerificationRequest();
-                                        this->canonical_json = nlohmann::json(msg.content);
-                                } else {
-                                        this->cancelVerification();
-                                }
-                        }
-                });
+                                      msg.content.short_authentication_string.end()) {
+                                          this->method = DeviceVerificationFlow::Method::Emoji;
+                                  } else if (std::find(
+                                               msg.content.short_authentication_string.begin(),
+                                               msg.content.short_authentication_string.end(),
+                                               mtx::events::msg::SASMethods::Emoji) !=
+                                             msg.content.short_authentication_string.end()) {
+                                          this->method = DeviceVerificationFlow::Method::Decimal;
+                                  } else {
+                                          this->cancelVerification(
+                                            DeviceVerificationFlow::Error::UnknownMethod);
+                                          return;
+                                  }
+                                  this->acceptVerificationRequest();
+                                  this->canonical_json = nlohmann::json(msg.content);
+                          } else {
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
+                          }
+                  }
+          });
         connect(
           ChatPage::instance(),
           &ChatPage::recievedDeviceVerificationAccept,
@@ -76,7 +87,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   this->mac_method = msg.content.message_authentication_code;
                                   this->sendVerificationKey();
                           } else {
-                                  this->cancelVerification();
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
                           }
                   }
           });
@@ -130,7 +142,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                         msg.content.key + this->canonical_json.dump()))) {
                                           emit this->verificationRequestAccepted(this->method);
                                   } else {
-                                          this->cancelVerification();
+                                          this->cancelVerification(
+                                            DeviceVerificationFlow::Error::MismatchedCommitment);
                                   }
                           }
                   }
@@ -156,7 +169,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                               this->sas->calculate_mac(this->device_keys[mac.first],
                                                                        info + mac.first)) {
                                           } else {
-                                                  this->cancelVerification();
+                                                  this->cancelVerification(
+                                                    DeviceVerificationFlow::Error::KeyMismatch);
                                                   return;
                                           }
                                   }
@@ -172,7 +186,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   else
                                           this->isMacVerified = true;
                           } else {
-                                  this->cancelVerification();
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::KeyMismatch);
                           }
                   }
           });
@@ -429,15 +444,31 @@ DeviceVerificationFlow::sendVerificationRequest()
 }
 //! cancels a verification flow
 void
-DeviceVerificationFlow::cancelVerification()
+DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_code)
 {
         mtx::requests::ToDeviceMessages body;
         mtx::events::msg::KeyVerificationCancel req;
 
         req.transaction_id = this->transaction_id;
-        // TODO: Add Proper Error Messages and Code
-        req.reason = "Device Verification Cancelled";
-        req.code   = "400";
+        if (error_code == DeviceVerificationFlow::Error::UnknownMethod) {
+                req.code   = "m.unknown_method";
+                req.reason = "unknown method recieved";
+        } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) {
+                req.code   = "m.mismatched_commitment";
+                req.reason = "commitment didn't match";
+        } else if (error_code == DeviceVerificationFlow::Error::MismatchedSAS) {
+                req.code   = "m.mismatched_sas";
+                req.reason = "sas didn't match";
+        } else if (error_code == DeviceVerificationFlow::Error::KeyMismatch) {
+                req.code   = "m.key_match";
+                req.reason = "keys did not match";
+        } else if (error_code == DeviceVerificationFlow::Error::Timeout) {
+                req.code   = "m.timeout";
+                req.reason = "timed out";
+        } else if (error_code == DeviceVerificationFlow::Error::User) {
+                req.code   = "m.user";
+                req.reason = "user cancelled the verification";
+        }
 
         body[this->toClient][deviceId.toStdString()] = req;
 
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index ea86a10b..891c6aea 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -28,6 +28,16 @@ public:
                 Emoji
         };
         Q_ENUM(Method)
+        enum Error
+        {
+                UnknownMethod,
+                MismatchedCommitment,
+                MismatchedSAS,
+                KeyMismatch,
+                Timeout,
+                User
+        };
+        Q_ENUM(Error)
 
         DeviceVerificationFlow(QObject *parent = nullptr);
         QString getTransactionId();
@@ -56,7 +66,7 @@ public slots:
         //! starts the verification flow
         void startVerificationRequest();
         //! cancels a verification flow
-        void cancelVerification();
+        void cancelVerification(DeviceVerificationFlow::Error error_code);
         //! sends the verification key
         void sendVerificationKey();
         //! sends the mac of the keys
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 14c66951..0b732232 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -184,6 +184,9 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                     QString::fromStdString(msg.content.transaction_id),
                                     QString::fromStdString(msg.sender),
                                     QString::fromStdString(msg.content.from_device));
+                          } else {
+                                  flow->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
                           }
                   }
           });
@@ -197,11 +200,43 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                   auto flow            = new DeviceVerificationFlow(this);
                   flow->canonical_json = nlohmann::json(msg.content);
                   if (!(this->dvList->exist(QString::fromStdString(msg.content.transaction_id)))) {
-                          emit newDeviceVerificationRequest(
-                            std::move(flow),
-                            QString::fromStdString(msg.content.transaction_id),
-                            QString::fromStdString(msg.sender),
-                            QString::fromStdString(msg.content.from_device));
+                          if ((std::find(msg.content.key_agreement_protocols.begin(),
+                                         msg.content.key_agreement_protocols.end(),
+                                         "curve25519-hkdf-sha256") !=
+                               msg.content.key_agreement_protocols.end()) &&
+                              (std::find(msg.content.hashes.begin(),
+                                         msg.content.hashes.end(),
+                                         "sha256") != msg.content.hashes.end()) &&
+                              (std::find(msg.content.message_authentication_codes.begin(),
+                                         msg.content.message_authentication_codes.end(),
+                                         "hmac-sha256") !=
+                               msg.content.message_authentication_codes.end())) {
+                                  if (std::find(msg.content.short_authentication_string.begin(),
+                                                msg.content.short_authentication_string.end(),
+                                                mtx::events::msg::SASMethods::Emoji) !=
+                                      msg.content.short_authentication_string.end()) {
+                                          flow->setMethod(DeviceVerificationFlow::Method::Emoji);
+                                  } else if (std::find(
+                                               msg.content.short_authentication_string.begin(),
+                                               msg.content.short_authentication_string.end(),
+                                               mtx::events::msg::SASMethods::Decimal) !=
+                                             msg.content.short_authentication_string.end()) {
+                                          flow->setMethod(DeviceVerificationFlow::Method::Decimal);
+                                  } else {
+                                          flow->cancelVerification(
+                                            DeviceVerificationFlow::Error::UnknownMethod);
+                                          return;
+                                  }
+                                  emit newDeviceVerificationRequest(
+                                    std::move(flow),
+                                    QString::fromStdString(msg.content.transaction_id),
+                                    QString::fromStdString(msg.sender),
+                                    QString::fromStdString(msg.content.from_device));
+                                  flow->canonical_json = nlohmann::json(msg.content);
+                          } else {
+                                  flow->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
+                          }
                   }
           });
 }
-- 
cgit 1.5.1


From 1103cc15cfe59b35e540855090af381b0f2e5f8e Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Sun, 5 Jul 2020 21:33:27 +0530
Subject: Adding icons to UserProfile

---
 CMakeLists.txt                                     |   3 -
 resources/qml/TimelineView.qml                     |   2 +-
 resources/qml/UserProfile.qml                      |  81 +++++++------
 .../qml/device-verification/DeviceVerification.qml |   4 +
 src/DeviceVerificationFlow.cpp                     | 130 +++++++++++++++------
 src/DeviceVerificationFlow.h                       |   6 +
 src/timeline/TimelineViewManager.h                 |   2 +
 src/ui/UserProfile.cpp                             |  11 +-
 src/ui/UserProfile.h                               |   4 +-
 9 files changed, 163 insertions(+), 80 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ad8a625..2aff9914 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,9 +15,6 @@ set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard")
 set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported")
 set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default")
 
-# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
-# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
-
 option(HUNTER_ENABLED "Enable Hunter package manager" OFF)
 include("cmake/HunterGate.cmake")
 HunterGate(
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index ec634878..699efc54 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -106,7 +106,7 @@ Page {
 		}
 		Connections {
 			target: TimelineManager
-			function onNewDeviceVerificationRequest(flow) {
+			function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId) {
 				flow.userId = userId;
 				flow.sender = false;
 				flow.deviceId = deviceId;
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index df54367b..5bdccb4d 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -17,6 +17,13 @@ ApplicationWindow{
 	Layout.alignment: Qt.AlignHCenter
 	palette: colors
 
+	Connections{
+		target: deviceVerificationList
+		onUpdateProfile: {
+			profile.fetchDeviceList(profile.userid)
+		}
+	}
+
 	Component {
 		id: deviceVerificationDialog
 		DeviceVerification {}
@@ -139,20 +146,12 @@ ApplicationWindow{
 							top : 50
 						}
 						ColumnLayout{
-							RowLayout{
-								Text{
-									Layout.fillWidth: true
-									color: colors.text
-									font.bold: true
-									Layout.alignment: Qt.AlignLeft
-									text: model.deviceId
-								}
-								Text{
-									Layout.fillWidth: true
-									color:colors.text
-									Layout.alignment: Qt.AlignLeft
-									text: (model.verificationStatus ==  VerificationStatus.VERIFIED?"V":(model.verificationStatus ==  VerificationStatus.UNVERIFIED?"NV":"B"))
-								}
+							Text{
+								Layout.fillWidth: true
+								color: colors.text
+								font.bold: true
+								Layout.alignment: Qt.AlignLeft
+								text: model.deviceId
 							}
 							Text{
 								Layout.fillWidth: true
@@ -161,27 +160,41 @@ ApplicationWindow{
 								text: model.deviceName
 							}
 						}
-						Button{
-							id: verifyButton
-							text:"Verify"
-							onClicked: {
-								var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
-								{userId : profile.userid, sender: true, deviceId : model.deviceID});
-								deviceVerificationList.add(newFlow.tranId);
-								var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
-								dialog.show();
+						RowLayout{
+							Image{
+								Layout.preferredWidth: 20
+								Layout.preferredHeight: 20
+								source: ((model.verificationStatus == VerificationStatus.VERIFIED)?"image://colorimage/:/icons/icons/ui/lock.png?green":
+								((model.verificationStatus == VerificationStatus.UNVERIFIED)?"image://colorimage/:/icons/icons/ui/unlock.png?yellow":
+								"image://colorimage/:/icons/icons/ui/unlock.png?red"))
 							}
-							Layout.margins:{
-								right: 10
-							}
-							palette {
-								button: "white"
-							}
-							contentItem: Text {
-								text: verifyButton.text
-								color: "black"
-								horizontalAlignment: Text.AlignHCenter
-								verticalAlignment: Text.AlignVCenter
+							Button{
+								id: verifyButton
+								text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
+								onClicked: {
+									var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
+									{userId : profile.userid, sender: true, deviceId : model.deviceId});
+									if(model.verificationStatus == VerificationStatus.VERIFIED){
+										newFlow.unverify();
+										deviceVerificationList.updateProfile(newFlow.userId);
+									}else{
+										deviceVerificationList.add(newFlow.tranId);
+										var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
+										dialog.show();
+									}
+								}
+								Layout.margins:{
+									right: 10
+								}
+								palette {
+									button: "white"
+								}
+								contentItem: Text {
+									text: verifyButton.text
+									color: "black"
+									horizontalAlignment: Text.AlignHCenter
+									verticalAlignment: Text.AlignVCenter
+								}
 							}
 						}
 					}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 15c2d7a2..4d734a68 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -33,6 +33,10 @@ ApplicationWindow {
 			case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break;
 			case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break;
 		}
+
+		onRefreshProfile: {
+			deviceVerificationList.updateProfile(flow.userId);
+		}
 	}
 
 	Component {
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index b5134a3b..7829c41d 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -1,4 +1,5 @@
 #include "DeviceVerificationFlow.h"
+#include "Cache.h"
 #include "ChatPage.h"
 #include "Logging.h"
 
@@ -181,9 +182,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                                   // uncomment this in future to be compatible with the
                                   // MSC2366 this->sendVerificationDone(); and remove the
                                   // below line
-                                  if (this->isMacVerified == true)
-                                          emit this->deviceVerified();
-                                  else
+                                  if (this->isMacVerified == true) {
+                                          this->acceptDevice();
+                                  } else
                                           this->isMacVerified = true;
                           } else {
                                   this->cancelVerification(
@@ -208,7 +209,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *)
                         auto msg =
                           std::get>(message);
                         if (msg.content.transaction_id == this->transaction_id) {
-                                emit this->deviceVerified();
+                                this->acceptDevice();
                         }
                 });
         timeout->start(TIMEOUT);
@@ -259,36 +260,22 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_)
 void
 DeviceVerificationFlow::setUserId(QString userID)
 {
-        this->userId   = userID;
-        this->toClient = mtx::identifiers::parse(userID.toStdString());
-
-        mtx::responses::QueryKeys res;
-        mtx::requests::QueryKeys req;
-        req.device_keys[userID.toStdString()] = {};
-        http::client()->query_keys(
-          req,
-          [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
-                                                 mtx::http::RequestErr err) {
-                  if (err) {
-                          nhlog::net()->warn("failed to query device keys: {},{}",
-                                             err->matrix_error.errcode,
-                                             static_cast(err->status_code));
-                          return;
-                  }
-
-                  for (auto x : res.device_keys) {
-                          for (auto y : x.second) {
-                                  auto z = y.second;
-                                  if (z.user_id == user_id &&
-                                      z.device_id == this->deviceId.toStdString()) {
-                                          for (auto a : z.keys) {
-                                                  // TODO: Verify Signatures
-                                                  this->device_keys[a.first] = a.second;
-                                          }
-                                  }
-                          }
-                  }
-          });
+        this->userId    = userID;
+        this->toClient  = mtx::identifiers::parse(userID.toStdString());
+        auto user_cache = cache::getUserCache(userID.toStdString());
+
+        if (user_cache.has_value()) {
+                this->callback_fn(user_cache->keys, {}, userID.toStdString());
+        } else {
+                mtx::requests::QueryKeys req;
+                req.device_keys[userID.toStdString()] = {};
+                http::client()->query_keys(
+                  req,
+                  [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res,
+                                                         mtx::http::RequestErr err) {
+                          this->callback_fn(res, err, user_id);
+                  });
+        }
 }
 
 void
@@ -482,6 +469,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
                             nhlog::net()->warn("failed to cancel verification request: {} {}",
                                                err->matrix_error.error,
                                                static_cast(err->status_code));
+                    auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+                    if (verified_cache.has_value()) {
+                            verified_cache->device_blocked.push_back(this->deviceId.toStdString());
+                            cache::setVerifiedCache(this->userId.toStdString(),
+                                                    verified_cache.value());
+                    } else {
+                            cache::setVerifiedCache(
+                              this->userId.toStdString(),
+                              DeviceVerifiedCache{{}, {this->deviceId.toStdString()}});
+                    }
                     this->deleteLater();
             });
 }
@@ -546,7 +543,7 @@ DeviceVerificationFlow::sendVerificationMac()
                                                static_cast(err->status_code));
 
                     if (this->isMacVerified == true)
-                            emit this->deviceVerified();
+                            this->acceptDevice();
                     else
                             this->isMacVerified = true;
             });
@@ -555,8 +552,69 @@ DeviceVerificationFlow::sendVerificationMac()
 void
 DeviceVerificationFlow::acceptDevice()
 {
+        auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+        if (verified_cache.has_value()) {
+                verified_cache->device_verified.push_back(this->deviceId.toStdString());
+                for (auto it = verified_cache->device_blocked.begin();
+                     it != verified_cache->device_blocked.end();
+                     it++) {
+                        if (*it == this->deviceId.toStdString()) {
+                                verified_cache->device_blocked.erase(it);
+                        }
+                }
+                cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
+        } else {
+                cache::setVerifiedCache(this->userId.toStdString(),
+                                        DeviceVerifiedCache{{this->deviceId.toStdString()}, {}});
+        }
+
         emit deviceVerified();
+        emit refreshProfile();
         this->deleteLater();
+}
+//! callback function to keep track of devices
+void
+DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res,
+                                    mtx::http::RequestErr err,
+                                    std::string user_id)
+{
+        if (err) {
+                nhlog::net()->warn("failed to query device keys: {},{}",
+                                   err->matrix_error.errcode,
+                                   static_cast(err->status_code));
+                return;
+        }
+
+        if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) {
+                nhlog::net()->warn("no devices retrieved {}", user_id);
+                return;
+        }
 
-        // Yet to add send to_device message
+        for (auto x : res.device_keys) {
+                for (auto y : x.second) {
+                        auto z = y.second;
+                        if (z.user_id == user_id && z.device_id == this->deviceId.toStdString()) {
+                                for (auto a : z.keys) {
+                                        // TODO: Verify Signatures
+                                        this->device_keys[a.first] = a.second;
+                                }
+                        }
+                }
+        }
+}
+
+void
+DeviceVerificationFlow::unverify()
+{
+        auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
+        if (verified_cache.has_value()) {
+                auto it = std::remove(verified_cache->device_verified.begin(),
+                                      verified_cache->device_verified.end(),
+                                      this->deviceId.toStdString());
+                verified_cache->device_verified.erase(it);
+                cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value());
+        }
+
+        emit refreshProfile();
+        this->deleteLater();
 }
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 891c6aea..edff7c8e 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -51,6 +51,9 @@ public:
         void setDeviceId(QString deviceID);
         void setMethod(Method method_);
         void setSender(bool sender_);
+        void callback_fn(const mtx::responses::QueryKeys &res,
+                         mtx::http::RequestErr err,
+                         std::string user_id);
 
         nlohmann::json canonical_json;
 
@@ -73,12 +76,15 @@ public slots:
         void sendVerificationMac();
         //! Completes the verification flow
         void acceptDevice();
+        //! unverifies a device
+        void unverify();
 
 signals:
         void verificationRequestAccepted(Method method);
         void deviceVerified();
         void timedout();
         void verificationCanceled();
+        void refreshProfile();
 
 private:
         QString userId;
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index af8bc4b6..a438ef5e 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -29,6 +29,8 @@ public:
         Q_INVOKABLE void add(QString tran_id);
         Q_INVOKABLE void remove(QString tran_id);
         Q_INVOKABLE bool exist(QString tran_id);
+signals:
+        void updateProfile(QString userId);
 
 private:
         QVector deviceVerificationList;
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index fde0044b..b4938e8d 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,6 +1,7 @@
 #include "UserProfile.h"
 #include "Cache.h"
 #include "ChatPage.h"
+#include "DeviceVerificationFlow.h"
 #include "Logging.h"
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
@@ -122,10 +123,10 @@ UserProfile::callback_fn(const mtx::responses::QueryKeys &res,
                    verified});
         }
 
-        // std::sort(
-        //   deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
-        //           return a.device_id > b.device_id;
-        //   });
+        std::sort(
+          deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) {
+                  return a.device_id > b.device_id;
+          });
 
         this->deviceList_.queueReset(std::move(deviceInfo));
 }
@@ -176,4 +177,4 @@ UserProfile::startChat()
         if (utils::localUser() != this->userid_)
                 req.invite = {this->userid_.toStdString()};
         emit ChatPage::instance()->createRoom(req);
-}
+}
\ No newline at end of file
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 38002fff..99c6a755 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -19,6 +19,8 @@ enum Status
 Q_ENUM_NS(Status)
 }
 
+class DeviceVerificationFlow;
+
 class DeviceInfo
 {
 public:
@@ -88,7 +90,7 @@ public:
         QString displayName();
         QString avatarUrl();
 
-        void fetchDeviceList(const QString &userID);
+        Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
         // Q_INVOKABLE void ignoreUser();
         Q_INVOKABLE void kickUser();
-- 
cgit 1.5.1


From 3635c185e93a6f7bf6e2f9c11b27966c2ee665ea Mon Sep 17 00:00:00 2001
From: CH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>
Date: Wed, 29 Jul 2020 03:25:47 +0530
Subject: Add Room Verification Messages

---
 resources/qml/TimelineView.qml                     |  15 ++-
 .../qml/device-verification/DeviceVerification.qml |   7 +-
 src/ChatPage.h                                     |   4 +
 src/DeviceVerificationFlow.cpp                     |  56 ++++++--
 src/DeviceVerificationFlow.h                       |  22 +++-
 src/timeline/TimelineModel.cpp                     | 145 +++++++++++++++++----
 src/timeline/TimelineModel.h                       |   5 +
 src/timeline/TimelineViewManager.cpp               |  37 +++++-
 src/timeline/TimelineViewManager.h                 |   3 +-
 9 files changed, 245 insertions(+), 49 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 699efc54..c6fc3851 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -106,13 +106,20 @@ Page {
 		}
 		Connections {
 			target: TimelineManager
-			function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId) {
+			function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) {
 				flow.userId = userId;
 				flow.sender = false;
 				flow.deviceId = deviceId;
-				flow.tranId = transactionId;
-				deviceVerificationList.add(flow.tranId);
-				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow});
+				switch(flow.type){
+					case DeviceVerificationFlow.ToDevice:
+					    flow.tranId = transactionId;
+						deviceVerificationList.add(flow.tranId);
+						break;
+					case DeviceVerificationFlow.RoomMsg:
+						deviceVerificationList.add(flow.tranId);
+						break;
+				}
+				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest = isRequest});
 				dialog.show();
 			}
 		}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 4d734a68..8e74d1cb 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -23,6 +23,8 @@ ApplicationWindow {
 	}
 
 	property var flow
+	property bool isRequest
+
 	Connections {
 		target: flow
 		onVerificationCanceled: stack.replace(partnerAborted)
@@ -155,7 +157,10 @@ ApplicationWindow {
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
                         }
-						onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.acceptVerificationRequest(); }
+						onClicked: { 
+							stack.replace(awaitingVerificationRequestAccept); 
+							isRequest?flow.sendVerificationReady():flow.acceptVerificationRequest(); 
+						}
 					}
 				}
 			}
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 0e7c889f..72adfe19 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -52,6 +52,7 @@ class TopRoomBar;
 class UserInfoWidget;
 class UserSettings;
 class NotificationsManager;
+class TimelineModel;
 
 constexpr int CONSENSUS_TIMEOUT      = 1000;
 constexpr int SHOW_CONTENT_TIMEOUT   = 3000;
@@ -171,6 +172,9 @@ signals:
         void recievedDeviceVerificationRequest(
           const mtx::events::msg::KeyVerificationRequest &message,
           std::string sender);
+        void recievedRoomDeviceVerificationRequest(
+          const mtx::events::RoomEvent &message,
+          TimelineModel *model);
         void recievedDeviceVerificationCancel(
           const mtx::events::msg::KeyVerificationCancel &message);
         void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 69de4937..0f521f92 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -1,7 +1,9 @@
 #include "DeviceVerificationFlow.h"
+
 #include "Cache.h"
 #include "ChatPage.h"
 #include "Logging.h"
+#include "timeline/TimelineModel.h"
 
 #include 
 #include 
@@ -12,12 +14,14 @@ static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes
 
 namespace msgs = mtx::events::msg;
 
-DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type)
+DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type flow_type)
+  : type(flow_type)
 {
         timeout = new QTimer(this);
         timeout->setSingleShot(true);
         this->sas           = olm::client()->sas_init();
         this->isMacVerified = false;
+
         connect(timeout, &QTimer::timeout, this, [this]() {
                 emit timedout();
                 this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
@@ -267,6 +271,12 @@ DeviceVerificationFlow::getMethod()
         return this->method;
 }
 
+DeviceVerificationFlow::Type
+DeviceVerificationFlow::getType()
+{
+        return this->type;
+}
+
 bool
 DeviceVerificationFlow::getSender()
 {
@@ -279,6 +289,12 @@ DeviceVerificationFlow::getSasList()
         return this->sasList;
 }
 
+void
+DeviceVerificationFlow::setModel(TimelineModel *&model)
+{
+        this->model_ = model;
+}
+
 void
 DeviceVerificationFlow::setTransactionId(QString transaction_id_)
 {
@@ -318,6 +334,12 @@ DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_)
         this->method = method_;
 }
 
+void
+DeviceVerificationFlow::setType(Type type)
+{
+        this->type = type;
+}
+
 void
 DeviceVerificationFlow::setSender(bool sender_)
 {
@@ -328,6 +350,13 @@ DeviceVerificationFlow::setSender(bool sender_)
                 this->relation.in_reply_to.event_id = http::client()->generate_txn_id();
 }
 
+void
+DeviceVerificationFlow::setEventId(std::string event_id)
+{
+        this->relation.in_reply_to.event_id = event_id;
+        this->transaction_id                = event_id;
+}
+
 //! accepts a verification
 void
 DeviceVerificationFlow::acceptVerificationRequest()
@@ -361,8 +390,9 @@ DeviceVerificationFlow::acceptVerificationRequest()
                                       err->matrix_error.error,
                                       static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! responds verification request
@@ -389,8 +419,9 @@ DeviceVerificationFlow::sendVerificationReady()
                                                        err->matrix_error.error,
                                                        static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! accepts a verification
@@ -414,8 +445,9 @@ DeviceVerificationFlow::sendVerificationDone()
                                                        err->matrix_error.error,
                                                        static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! starts the verification flow
@@ -448,8 +480,9 @@ DeviceVerificationFlow::startVerificationRequest()
                                       err->matrix_error.error,
                                       static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! sends a verification request
@@ -481,8 +514,8 @@ DeviceVerificationFlow::sendVerificationRequest()
                                                        err->matrix_error.error,
                                                        static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
-                std::cout << "lulz" << std::endl;
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
+                (model_.value())->sendMessage(req);
         }
 }
 //! cancels a verification flow
@@ -531,8 +564,9 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
 
                             this->deleteLater();
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 
         // TODO : Handle Blocking user better
@@ -570,8 +604,9 @@ DeviceVerificationFlow::sendVerificationKey()
                                                        err->matrix_error.error,
                                                        static_cast(err->status_code));
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! sends the mac of the keys
@@ -618,8 +653,9 @@ DeviceVerificationFlow::sendVerificationMac()
                             else
                                     this->isMacVerified = true;
                     });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg) {
+        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
                 req.relates_to = this->relation;
+                (model_.value())->sendMessage(req);
         }
 }
 //! Completes the verification flow
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 3f999e80..bec9f1e0 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -10,6 +10,8 @@ class QTimer;
 
 using sas_ptr = std::unique_ptr;
 
+struct TimelineModel;
+
 class DeviceVerificationFlow : public QObject
 {
         Q_OBJECT
@@ -19,6 +21,7 @@ class DeviceVerificationFlow : public QObject
         Q_PROPERTY(QString userId READ getUserId WRITE setUserId)
         Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId)
         Q_PROPERTY(Method method READ getMethod WRITE setMethod)
+        Q_PROPERTY(Type type READ getType WRITE setType)
         Q_PROPERTY(std::vector sasList READ getSasList CONSTANT)
 
 public:
@@ -27,6 +30,7 @@ public:
                 ToDevice,
                 RoomMsg
         };
+        Q_ENUM(Type)
 
         enum Method
         {
@@ -49,17 +53,24 @@ public:
         DeviceVerificationFlow(
           QObject *parent              = nullptr,
           DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice);
+        // getters
         QString getTransactionId();
         QString getUserId();
         QString getDeviceId();
         Method getMethod();
+        Type getType();
         std::vector getSasList();
-        void setTransactionId(QString transaction_id_);
         bool getSender();
+        // setters
+        void setModel(TimelineModel *&model);
+        void setTransactionId(QString transaction_id_);
         void setUserId(QString userID);
         void setDeviceId(QString deviceID);
         void setMethod(Method method_);
+        void setType(Type type_);
         void setSender(bool sender_);
+        void setEventId(std::string event_id);
+
         void callback_fn(const mtx::responses::QueryKeys &res,
                          mtx::http::RequestErr err,
                          std::string user_id);
@@ -96,20 +107,25 @@ signals:
         void refreshProfile();
 
 private:
+        // general
         QString userId;
         QString deviceId;
         Method method;
         Type type;
         bool sender;
-
         QTimer *timeout = nullptr;
         sas_ptr sas;
         bool isMacVerified = false;
         std::string mac_method;
-        std::string transaction_id;
         std::string commitment;
         mtx::identifiers::User toClient;
         std::vector sasList;
         std::map device_keys;
+        // for to_device messages
+        std::string transaction_id;
+        // for room messages
+        std::optional room_id;
+        std::optional event_id;
+        std::optional model_;
         mtx::common::ReplyRelatesTo relation;
 };
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 71cc53c5..adf207ac 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -138,6 +138,11 @@ struct RoomEventType
         {
                 return qml_mtx_events::EventType::KeyVerificationAccept;
         }
+        qml_mtx_events::EventType operator()(
+          const mtx::events::Event &)
+        {
+                return qml_mtx_events::EventType::KeyVerificationReady;
+        }
         qml_mtx_events::EventType operator()(
           const mtx::events::Event &)
         {
@@ -637,30 +642,6 @@ TimelineModel::internalAddEvents(
                         continue;
                 }
 
-                if (std::get_if>(
-                      &e)) {
-                        std::cout << "got a request" << std::endl;
-                }
-
-                if (auto cancelVerification =
-                      std::get_if>(
-                        &e)) {
-                        std::cout<<"it is happening"<content.relates_to.has_value()) {
-                                QString event_id = QString::fromStdString(
-                                  cancelVerification->content.relates_to.value()
-                                    .in_reply_to.event_id);
-                                auto request =
-                                  std::find(eventOrder.begin(), eventOrder.end(), event_id);
-                                if (request != eventOrder.end()) {
-                                        auto event = events.value(event_id);
-                                        auto e     = std::get_if>(&event);
-                                        std::cout<>(&e)) {
                         QString redacts = QString::fromStdString(redaction->redacts);
@@ -728,6 +709,55 @@ TimelineModel::internalAddEvents(
 
                         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);
@@ -754,6 +784,13 @@ TimelineModel::internalAddEvents(
                           });
                 }
         }
+
+        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;
 }
 
@@ -1263,6 +1300,20 @@ 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)
 {
@@ -1275,7 +1326,51 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
           },
           event);
 
-        std::visit(SendMessageVisitor{this}, event);
+        if (std::get_if>(&event)) {
+                std::visit(
+                  [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationReady; },
+                  event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit(
+                  [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationStart; },
+                  event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit([](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationKey; },
+                           event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit([](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationMac; },
+                           event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit(
+                  [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationDone; }, event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit(
+                  [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationCancel; },
+                  event);
+        }
+        if (std::get_if>(&event)) {
+                std::visit(
+                  [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationAccept; },
+                  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();
 }
 
 bool
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 708ed38e..1b6f999e 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -297,6 +297,11 @@ 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/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 02b74d20..c16e09d1 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -17,6 +17,8 @@
 #include "emoji/EmojiModel.h"
 #include "emoji/Provider.h"
 
+#include  //only for debugging
+
 Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
 Q_DECLARE_METATYPE(std::vector)
 
@@ -185,18 +187,44 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                 &ChatPage::decryptSidebarChanged,
                 this,
                 &TimelineViewManager::updateEncryptedDescriptions);
+        connect(
+          dynamic_cast(parent),
+          &ChatPage::recievedRoomDeviceVerificationRequest,
+          this,
+          [this](const mtx::events::RoomEvent &message,
+                 TimelineModel *model) {
+                  if (!(this->dvList->exist(QString::fromStdString(message.event_id)))) {
+                          auto flow =
+                            new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::RoomMsg);
+                          if (std::find(message.content.methods.begin(),
+                                        message.content.methods.end(),
+                                        mtx::events::msg::VerificationMethods::SASv1) !=
+                              message.content.methods.end()) {
+                                  flow->setModel(model);
+                                  flow->setEventId(message.event_id);
+                                  emit newDeviceVerificationRequest(
+                                    std::move(flow),
+                                    QString::fromStdString(message.event_id),
+                                    QString::fromStdString(message.sender),
+                                    QString::fromStdString(message.content.from_device),
+                                    true);
+                          } else {
+                                  flow->cancelVerification(
+                                    DeviceVerificationFlow::Error::UnknownMethod);
+                          }
+                  }
+          });
         connect(
           dynamic_cast(parent),
           &ChatPage::recievedDeviceVerificationRequest,
           this,
           [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
-                  auto flow = new DeviceVerificationFlow(this);
                   if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) {
+                          auto flow = new DeviceVerificationFlow(this);
                           if (std::find(msg.methods.begin(),
                                         msg.methods.end(),
                                         mtx::events::msg::VerificationMethods::SASv1) !=
                               msg.methods.end()) {
-                                  //   flow->sendVerificationReady();
                                   emit newDeviceVerificationRequest(
                                     std::move(flow),
                                     QString::fromStdString(msg.transaction_id.value()),
@@ -213,9 +241,9 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
           &ChatPage::recievedDeviceVerificationStart,
           this,
           [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) {
-                  auto flow            = new DeviceVerificationFlow(this);
-                  flow->canonical_json = nlohmann::json(msg);
                   if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) {
+                          auto flow            = new DeviceVerificationFlow(this);
+                          flow->canonical_json = nlohmann::json(msg);
                           if ((std::find(msg.key_agreement_protocols.begin(),
                                          msg.key_agreement_protocols.end(),
                                          "curve25519-hkdf-sha256") !=
@@ -246,7 +274,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                     QString::fromStdString(msg.transaction_id.value()),
                                     QString::fromStdString(sender),
                                     QString::fromStdString(msg.from_device));
-                                  flow->canonical_json = nlohmann::json(msg);
                           } else {
                                   flow->cancelVerification(
                                     DeviceVerificationFlow::Error::UnknownMethod);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 71aee5ef..031d07cc 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -72,7 +72,8 @@ signals:
         void newDeviceVerificationRequest(DeviceVerificationFlow *flow,
                                           QString transactionId,
                                           QString userId,
-                                          QString deviceId);
+                                          QString deviceId,
+                                          bool isRequest = false);
 
 public slots:
         void updateReadReceipts(const QString &room_id, const std::vector &event_ids);
-- 
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 'resources/qml/device-verification/DeviceVerification.qml')

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 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 'resources/qml/device-verification/DeviceVerification.qml')

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 e70b4e42680562a308d97ef34f2ad4eb3ac67bd1 Mon Sep 17 00:00:00 2001
From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com>
Date: Thu, 10 Sep 2020 09:56:14 +0530
Subject: Fix wrong tran_id issue

---
 resources/qml/TimelineView.qml                        |  2 +-
 resources/qml/UserProfile.qml                         |  4 ++--
 .../qml/device-verification/DeviceVerification.qml    | 19 ++++++++++---------
 3 files changed, 13 insertions(+), 12 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index f2390b18..30158e3a 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -118,7 +118,7 @@ Page {
 						deviceVerificationList.add(flow.tranId);
 						break;
 				}
-				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest = isRequest});
+				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest: isRequest,tran_id: flow.tranId});
 				dialog.show();
 			}
 		}
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 115a73c4..30d9c958 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -94,7 +94,7 @@ ApplicationWindow{
 					newFlow.userId = profile.userid;
 					newFlow.sender = true;
 					deviceVerificationList.add(newFlow.tranId);
-					var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true});
+					var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true,tran_id: newFlow.tranId});
 					dialog.show();
 				}
 			}
@@ -206,7 +206,7 @@ ApplicationWindow{
 										deviceVerificationList.updateProfile(newFlow.userId);
 									}else{
 										deviceVerificationList.add(newFlow.tranId);
-										var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false});
+										var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false,tran_id: newFlow.tranId});
 										dialog.show();
 									}
 								}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 94cb1e33..b5c53a4a 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -24,6 +24,7 @@ ApplicationWindow {
 
 	property var flow
 	property bool isRequest
+	property var tran_id
 
 	Connections {
 		target: flow
@@ -82,6 +83,7 @@ ApplicationWindow {
                         }
 						onClicked: { 
 							dialog.close(); 
+							deviceVerificationList.remove(tran_id);
 							delete flow; 
 						}
 					}
@@ -141,7 +143,7 @@ ApplicationWindow {
 						onClicked: { 
 							dialog.close();
 							flow.cancelVerification(DeviceVerificationFlow.User);
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 					Item {
@@ -205,7 +207,7 @@ ApplicationWindow {
 						onClicked: { 
 							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.User);
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 					Item {
@@ -267,7 +269,7 @@ ApplicationWindow {
 						onClicked: { 
 							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 					Item {
@@ -426,7 +428,7 @@ ApplicationWindow {
 						onClicked: { 
 							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 					Item {
@@ -487,8 +489,7 @@ ApplicationWindow {
 						onClicked: { 
 							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.User); 
-							deviceVerificationList.remove(flow.tranId);
-							delete flow;
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 					Item {
@@ -534,7 +535,7 @@ ApplicationWindow {
                         }
 						onClicked: {
 							dialog.close()
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 							delete flow;
 						}
 					}
@@ -578,7 +579,7 @@ ApplicationWindow {
                         }
 						onClicked: {
 							dialog.close();
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 						}
 					}
 				}
@@ -622,7 +623,7 @@ ApplicationWindow {
 						text: qsTr("Close")
 						onClicked: {
 							dialog.close()
-							deviceVerificationList.remove(flow.tranId);
+							deviceVerificationList.remove(tran_id);
 							delete flow;
 						}
 					}
-- 
cgit 1.5.1


From a27662dc0844cf9104967717076123ac73a667c5 Mon Sep 17 00:00:00 2001
From: Chethan2k1 <40890937+Chethan2k1@users.noreply.github.com>
Date: Thu, 10 Sep 2020 13:25:49 +0530
Subject: Making sure Verification Objects are deleted properly

---
 .../qml/device-verification/DeviceVerification.qml | 28 +++++++++++-----------
 src/DeviceVerificationFlow.cpp                     |  4 ++++
 src/DeviceVerificationFlow.h                       |  1 +
 3 files changed, 19 insertions(+), 14 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index b5c53a4a..6e4b4621 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -82,9 +82,9 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close(); 
 							deviceVerificationList.remove(tran_id);
-							delete flow; 
+							flow.deleteFlow();
+							dialog.destroy();  
 						}
 					}
 					Item {
@@ -141,9 +141,9 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close();
 							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 					Item {
@@ -205,9 +205,9 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.User);
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 					Item {
@@ -266,10 +266,10 @@ ApplicationWindow {
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
                         }
-						onClicked: { 
-							dialog.close(); 
+						onClicked: {
 							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 					Item {
@@ -425,10 +425,10 @@ ApplicationWindow {
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
                         }
-						onClicked: { 
-							dialog.close(); 
+						onClicked: {  
 							flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 					Item {
@@ -487,9 +487,9 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: { 
-							dialog.close(); 
 							flow.cancelVerification(DeviceVerificationFlow.User); 
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 					Item {
@@ -534,9 +534,9 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: {
-							dialog.close()
 							deviceVerificationList.remove(tran_id);
-							delete flow;
+							flow.deleteFlow();
+							dialog.destroy();
 						}
 					}
 				}
@@ -578,8 +578,8 @@ ApplicationWindow {
                             verticalAlignment: Text.AlignVCenter
                         }
 						onClicked: {
-							dialog.close();
 							deviceVerificationList.remove(tran_id);
+							dialog.destroy();
 						}
 					}
 				}
@@ -622,9 +622,9 @@ ApplicationWindow {
                         }
 						text: qsTr("Close")
 						onClicked: {
-							dialog.close()
 							deviceVerificationList.remove(tran_id);
-							delete flow;
+							flow.deleteFlow();
+							dialog.destroy()
 						}
 					}
 				}
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index ae054af7..70cc0baf 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -41,6 +41,10 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                 this->deleteLater();
         });
 
+        connect(this,&DeviceVerificationFlow::deleteFlow,this,[this](){
+                this->deleteLater();
+        });
+
         connect(
           ChatPage::instance(),
           &ChatPage::recievedDeviceVerificationStart,
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index c2150980..b85cbec2 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -105,6 +105,7 @@ signals:
         void timedout();
         void verificationCanceled();
         void refreshProfile();
+        void deleteFlow();
 
 private:
         // general
-- 
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 'resources/qml/device-verification/DeviceVerification.qml')

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 54e3c2c96b75052da8249c3ce92065b9ad09f139 Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Fri, 2 Oct 2020 12:18:54 +0200
Subject: Fix verification start to start with request

---
 resources/qml/device-verification/DeviceVerification.qml     | 1 -
 resources/qml/device-verification/NewVerificationRequest.qml | 2 +-
 src/DeviceVerificationFlow.cpp                               | 7 +++----
 src/DeviceVerificationFlow.h                                 | 2 +-
 4 files changed, 5 insertions(+), 7 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index e409b0fe..ca980987 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -6,7 +6,6 @@ import im.nheko 1.0
 
 ApplicationWindow {
 	property var flow
-	property bool isRequest
 	property var tran_id
 
 	title: stack.currentItem.title
diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml
index d8fc65a0..06b06cff 100644
--- a/resources/qml/device-verification/NewVerificationRequest.qml
+++ b/resources/qml/device-verification/NewVerificationRequest.qml
@@ -64,7 +64,7 @@ Pane {
                 }
 				onClicked: { 
 					stack.replace(awaitingVerificationRequestAccept); 
-					isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); }
+					flow.sender ?flow.sendVerificationRequest():flow.startVerificationRequest(); }
 			}
 		}
 	}
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index aa8b5b44..7b367de9 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -498,14 +498,13 @@ DeviceVerificationFlow::sendVerificationRequest()
         mtx::events::msg::KeyVerificationRequest req;
 
         req.from_device = http::client()->device_id();
-        req.methods.resize(1);
-        req.methods[0] = mtx::events::msg::VerificationMethods::SASv1;
+        req.methods     = {mtx::events::msg::VerificationMethods::SASv1};
 
         if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                QDateTime CurrentTime = QDateTime::currentDateTimeUtc();
+                QDateTime currentTime = QDateTime::currentDateTimeUtc();
 
                 req.transaction_id = this->transaction_id;
-                req.timestamp      = (uint64_t)CurrentTime.toTime_t();
+                req.timestamp      = (uint64_t)currentTime.toMSecsSinceEpoch();
 
                 mtx::requests::ToDeviceMessages body;
 
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 31d2facc..de7a4567 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -111,7 +111,7 @@ private:
         // general
         QString userId;
         QString deviceId;
-        Method method;
+        Method method = Method::Emoji;
         Type type;
         bool sender;
         QTimer *timeout = nullptr;
-- 
cgit 1.5.1


From bca29a4227a871caac21236c29430b69264018ce Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Mon, 5 Oct 2020 22:12:10 +0200
Subject: Make steps in verification flow explicit

---
 resources/qml/TimelineView.qml                     |  14 +-
 resources/qml/UserProfile.qml                      |  31 +-
 .../AcceptNewVerificationRequest.qml               |  49 --
 .../AwaitingVerificationConfirmation.qml           |   5 +-
 .../AwaitingVerificationRequest.qml                |  40 --
 .../qml/device-verification/DeviceVerification.qml |  80 +--
 .../qml/device-verification/DigitVerification.qml  |  12 +-
 .../qml/device-verification/EmojiVerification.qml  |  12 +-
 resources/qml/device-verification/Failed.qml       |  42 ++
 .../device-verification/NewVerificationRequest.qml |  37 +-
 .../qml/device-verification/PartnerAborted.qml     |  34 --
 resources/qml/device-verification/Success.qml      |  31 ++
 resources/qml/device-verification/TimedOut.qml     |  35 --
 .../device-verification/VerificationSuccess.qml    |  35 --
 resources/qml/device-verification/Waiting.qml      |  45 ++
 resources/res.qrc                                  |   9 +-
 src/ChatPage.h                                     |  18 +-
 src/DeviceVerificationFlow.cpp                     | 613 ++++++++++-----------
 src/DeviceVerificationFlow.h                       | 215 ++++++--
 src/Olm.cpp                                        |  32 +-
 src/timeline/EventStore.cpp                        |  16 +-
 src/timeline/TimelineModel.cpp                     |   4 +-
 src/timeline/TimelineViewManager.cpp               | 194 +++----
 src/timeline/TimelineViewManager.h                 |  25 +-
 src/ui/UserProfile.cpp                             |  55 +-
 src/ui/UserProfile.h                               |  10 +-
 26 files changed, 798 insertions(+), 895 deletions(-)
 delete mode 100644 resources/qml/device-verification/AcceptNewVerificationRequest.qml
 delete mode 100644 resources/qml/device-verification/AwaitingVerificationRequest.qml
 create mode 100644 resources/qml/device-verification/Failed.qml
 delete mode 100644 resources/qml/device-verification/PartnerAborted.qml
 create mode 100644 resources/qml/device-verification/Success.qml
 delete mode 100644 resources/qml/device-verification/TimedOut.qml
 delete mode 100644 resources/qml/device-verification/VerificationSuccess.qml
 create mode 100644 resources/qml/device-verification/Waiting.qml

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 3f72a7dd..1dbe7c1a 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -106,19 +106,7 @@ Page {
 		Connections {
 			target: TimelineManager
 			function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) {
-				flow.userId = userId;
-				flow.sender = false;
-				flow.deviceId = deviceId;
-				switch(flow.type){
-					case DeviceVerificationFlow.ToDevice:
-					    flow.tranId = transactionId;
-						deviceVerificationList.add(flow.tranId);
-						break;
-					case DeviceVerificationFlow.RoomMsg:
-						deviceVerificationList.add(flow.tranId);
-						break;
-				}
-				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest: isRequest,tran_id: flow.tranId});
+				var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow});
 				dialog.show();
 			}
 		}
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index dc6bc165..e7dcc777 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -15,24 +15,12 @@ ApplicationWindow{
 	width: 420
 	minimumHeight: 420
 
-	modality: Qt.WindowModal
 	palette: colors
 
-	Connections{
-		target: deviceVerificationList
-		function onUpdateProfile() {
-			profile.fetchDeviceList(profile.userid)
-		}
-	}
-
 	Component {
 		id: deviceVerificationDialog
 		DeviceVerification {}
 	}
-	Component{
-		id: deviceVerificationFlow
-		DeviceVerificationFlow {}
-	}
 
 	ColumnLayout{
 		id: contentL
@@ -73,14 +61,7 @@ ApplicationWindow{
 			enabled: !profile.isUserVerified
 			visible: !profile.isUserVerified
 
-			onClicked: {
-				var newFlow = profile.createFlow(true);
-				newFlow.userId = profile.userid;
-				newFlow.sender = true;
-				deviceVerificationList.add(newFlow.tranId);
-				var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true,tran_id: newFlow.tranId});
-				dialog.show();
-			}
+			onClicked: profile.verify()
 		}
 
 		RowLayout {
@@ -172,17 +153,11 @@ ApplicationWindow{
 					id: verifyButton
 					text: (model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
 					onClicked: {
-						var newFlow = profile.createFlow(false);
-						newFlow.userId = profile.userid;
-						newFlow.sender = true;
-						newFlow.deviceId = model.deviceId;
 						if(model.verificationStatus == VerificationStatus.VERIFIED){
-							newFlow.unverify();
+							profile.unverify(model.deviceId)
 							deviceVerificationList.updateProfile(newFlow.userId);
 						}else{
-							deviceVerificationList.add(newFlow.tranId);
-							var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false,tran_id: newFlow.tranId});
-							dialog.show();
+							profile.verify(model.deviceId);
 						}
 					}
 				}
diff --git a/resources/qml/device-verification/AcceptNewVerificationRequest.qml b/resources/qml/device-verification/AcceptNewVerificationRequest.qml
deleted file mode 100644
index 5bdbc4a6..00000000
--- a/resources/qml/device-verification/AcceptNewVerificationRequest.qml
+++ /dev/null
@@ -1,49 +0,0 @@
-import QtQuick 2.3
-import QtQuick.Controls 2.10
-import QtQuick.Layouts 1.10
-
-import im.nheko 1.0
-
-Pane {
-	property string title: qsTr("Recieving Device Verification Request")
-	Component {
-		id: awaitingVerificationRequestAccept
-		AwaitingVerificationRequest {}
-	}
-	ColumnLayout {
-		spacing: 16
-		Label {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			text: qsTr("The device was requested to be verified")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		RowLayout {
-			Button {
-				Layout.alignment: Qt.AlignLeft
-				text: qsTr("Deny")
-
-				onClicked: { 
-					flow.cancelVerification(DeviceVerificationFlow.User);
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
-				}
-			}
-			Item {
-				Layout.fillWidth: true
-			}
-			Button {
-				Layout.alignment: Qt.AlignRight
-				text: qsTr("Accept")
-
-				onClicked: { 
-					stack.replace(awaitingVerificationRequestAccept); 
-					flow.sender ?flow.sendVerificationReady():flow.acceptVerificationRequest(); 
-				}
-			}
-		}
-	}
-}
diff --git a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
index aaebba6a..cd8ccfd9 100644
--- a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
+++ b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml
@@ -27,9 +27,8 @@ Pane {
 				text: qsTr("Cancel")
 
 				onClicked: { 
-					flow.cancelVerification(DeviceVerificationFlow.User); 
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
+					flow.cancel();
+					dialog.close();
 				}
 			}
 			Item {
diff --git a/resources/qml/device-verification/AwaitingVerificationRequest.qml b/resources/qml/device-verification/AwaitingVerificationRequest.qml
deleted file mode 100644
index b4b9178a..00000000
--- a/resources/qml/device-verification/AwaitingVerificationRequest.qml
+++ /dev/null
@@ -1,40 +0,0 @@
-import QtQuick 2.3
-import QtQuick.Controls 2.10
-import QtQuick.Layouts 1.10
-
-import im.nheko 1.0
-
-Pane {
-	property string title: qsTr("Waiting for other party")
-	ColumnLayout {
-		spacing: 16
-		Label {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			id: content
-			text: qsTr("Waiting for other side to accept the verification request.")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		BusyIndicator {
-			Layout.alignment: Qt.AlignHCenter
-		}
-		RowLayout {
-			Button {
-				Layout.alignment: Qt.AlignLeft
-				text: qsTr("Cancel")
-
-				onClicked: { 
-					flow.cancelVerification(DeviceVerificationFlow.User);
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
-				}
-			}
-			Item {
-				Layout.fillWidth: true
-			}
-		}
-	}
-}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index ca980987..4e93df06 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -18,36 +18,31 @@ ApplicationWindow {
 	height: stack.implicitHeight
 	width: stack.implicitWidth
 
-	Component{
-		id: newVerificationRequest
-		NewVerificationRequest {}
-	}
-
-	Component{
-		id: acceptNewVerificationRequest
-		AcceptNewVerificationRequest {}
-	}
-
 	StackView {
 		id: stack
-		initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest
+		initialItem: newVerificationRequest
 		implicitWidth: currentItem.implicitWidth
 		implicitHeight: currentItem.implicitHeight
 	}
 
+	Component{
+		id: newVerificationRequest
+		NewVerificationRequest {}
+	}
+
 	Component {
-		id: partnerAborted
-		PartnerAborted {}
+		id: waiting
+		Waiting {}
 	}
 
 	Component {
-		id: timedout
-		TimedOut {}
+		id: success
+		Success {}
 	}
 
 	Component {
-		id: verificationSuccess
-		VerificationSuccess {}
+		id: failed
+		Failed {}
 	}
 
 	Component {
@@ -60,19 +55,42 @@ ApplicationWindow {
 		EmojiVerification {}
 	}
 
-	Connections {
-		target: flow
-		onVerificationCanceled: stack.replace(partnerAborted)
-		onTimedout: stack.replace(timedout)
-		onDeviceVerified: stack.replace(verificationSuccess)
-
-		onVerificationRequestAccepted: switch(method) {
-			case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break;
-			case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break;
+	Item {
+	state: flow.state
+
+	states: [
+		State {
+			name: "PromptStartVerification"
+			StateChangeScript { script: stack.replace(newVerificationRequest) }
+		},
+		State {
+			name: "CompareEmoji"
+			StateChangeScript { script: stack.replace(emojiVerification) }
+		},
+		State {
+			name: "CompareNumber"
+			StateChangeScript { script: stack.replace(digitVerification) }
+		},
+		State {
+			name: "WaitingForKeys"
+			StateChangeScript { script: stack.replace(waiting) }
+		},
+		State {
+			name: "WaitingForOtherToAccept"
+			StateChangeScript { script: stack.replace(waiting) }
+		},
+		State {
+			name: "WaitingForMac"
+			StateChangeScript { script: stack.replace(waiting) }
+		},
+		State {
+			name: "Success"
+			StateChangeScript { script: stack.replace(success) }
+		},
+		State {
+			name: "Failed"
+			StateChangeScript { script: stack.replace(failed); }
 		}
-
-		onRefreshProfile: {
-			deviceVerificationList.updateProfile(flow.userId);
-		}
-	}
+	]
+}
 }
diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml
index f3b1f5cf..ff878a50 100644
--- a/resources/qml/device-verification/DigitVerification.qml
+++ b/resources/qml/device-verification/DigitVerification.qml
@@ -6,10 +6,7 @@ import im.nheko 1.0
 
 Pane {
 	property string title: qsTr("Verification Code")
-	Component {
-		id: awaitingVerificationConfirmation
-		AwaitingVerificationConfirmation {}
-	}
+
 	ColumnLayout {
 		spacing: 16
 		Label {
@@ -45,9 +42,8 @@ Pane {
 				text: qsTr("They do not match!")
 
 				onClicked: {
-					flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
+					flow.cancel();
+					dialog.close();
 				}
 			}
 			Item {
@@ -57,7 +53,7 @@ Pane {
 				Layout.alignment: Qt.AlignRight
 				text: qsTr("They match!")
 
-				onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); }
+				onClicked: flow.next();
 			}
 		}
 	}
diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml
index 19faf1b7..ed7727aa 100644
--- a/resources/qml/device-verification/EmojiVerification.qml
+++ b/resources/qml/device-verification/EmojiVerification.qml
@@ -6,10 +6,7 @@ import im.nheko 1.0
 
 Pane {
 	property string title: qsTr("Verification Code")
-	Component {
-		id: awaitingVerificationConfirmation
-		AwaitingVerificationConfirmation{}
-	}
+
 	ColumnLayout {
 		spacing: 16
 		Label {
@@ -125,9 +122,8 @@ Pane {
 				text: qsTr("They do not match!")
 
 				onClicked: {  
-					flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS);
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
+					flow.cancel();
+					dialog.close();
 				}
 			}
 			Item {
@@ -137,7 +133,7 @@ Pane {
 				Layout.alignment: Qt.AlignRight
 				text: qsTr("They match!")
 
-				onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); }
+				onClicked: flow.next()
 			}
 		}
 	}
diff --git a/resources/qml/device-verification/Failed.qml b/resources/qml/device-verification/Failed.qml
new file mode 100644
index 00000000..6b5d57ef
--- /dev/null
+++ b/resources/qml/device-verification/Failed.qml
@@ -0,0 +1,42 @@
+import QtQuick 2.3
+import QtQuick.Controls 2.10
+import QtQuick.Layouts 1.10
+
+Pane {
+	property string title: qsTr("Verification timed out")
+	ColumnLayout {
+		spacing: 16
+		Text {
+			Layout.maximumWidth: 400
+			Layout.fillHeight: true
+			Layout.fillWidth: true
+			wrapMode: Text.Wrap
+			id: content
+			text: switch (flow.error) {
+				case VerificationStatus.UnknownMethod: return qsTr("Device verification timed out.")
+				case VerificationStatus.MismatchedCommitment: return qsTr("Device verification timed out.")
+				case VerificationStatus.MismatchedSAS: return qsTr("Device verification timed out.")
+				case VerificationStatus.KeyMismatch: return qsTr("Device verification timed out.")
+				case VerificationStatus.Timeout: return qsTr("Device verification timed out.")
+				case VerificationStatus.OutOfOrder: return qsTr("Device verification timed out.")
+			}
+			color:colors.text
+			verticalAlignment: Text.AlignVCenter
+		}
+		RowLayout {
+			Item {
+				Layout.fillWidth: true
+			}
+			Button {
+				Layout.alignment: Qt.AlignRight
+				text: qsTr("Close")
+
+				onClicked: {
+					deviceVerificationList.remove(tran_id);
+					flow.deleteFlow();
+					dialog.close()
+				}
+			}
+		}
+	}
+}
diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml
index ef730b13..bd25bb90 100644
--- a/resources/qml/device-verification/NewVerificationRequest.qml
+++ b/resources/qml/device-verification/NewVerificationRequest.qml
@@ -2,12 +2,11 @@ import QtQuick 2.3
 import QtQuick.Controls 2.10
 import QtQuick.Layouts 1.10
 
+import im.nheko 1.0
+
 Pane {
-	property string title: qsTr("Sending Device Verification Request")
-	Component {
-		id: awaitingVerificationRequestAccept
-		AwaitingVerificationRequest {}
-	}
+	property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request")
+
 	ColumnLayout {
 		spacing: 16
 		Label {
@@ -15,28 +14,20 @@ Pane {
 			Layout.fillHeight: true
 			Layout.fillWidth: true
 			wrapMode: Text.Wrap
-			text: qsTr("A new device was added.")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		Label {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			text: qsTr("The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device.")
+			text: flow.sender ?
+			    qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.")
+			    : qsTr("The device was requested to be verified")
 			color:colors.text
 			verticalAlignment: Text.AlignVCenter
 		}
 		RowLayout {
 			Button {
 				Layout.alignment: Qt.AlignLeft
-				text: qsTr("Cancel")
+				text: flow.sender ? qsTr("Cancel") : qsTr("Deny")
 
 				onClicked: { 
-					deviceVerificationList.remove(tran_id);
-					flow.deleteFlow();
-					dialog.destroy();  
+					flow.cancel();
+					dialog.close();
 				}
 			}
 			Item {
@@ -44,12 +35,10 @@ Pane {
 			}
 			Button {
 				Layout.alignment: Qt.AlignRight
-				text: qsTr("Start verification")
+				text: flow.sender ? qsTr("Start verification") : qsTr("Accept")
 
-				onClicked: { 
-					stack.replace(awaitingVerificationRequestAccept); 
-					flow.sender ?flow.sendVerificationRequest():flow.startVerificationRequest(); }
-				}
+				onClicked: flow.next();
 			}
 		}
 	}
+}
diff --git a/resources/qml/device-verification/PartnerAborted.qml b/resources/qml/device-verification/PartnerAborted.qml
deleted file mode 100644
index 6174477d..00000000
--- a/resources/qml/device-verification/PartnerAborted.qml
+++ /dev/null
@@ -1,34 +0,0 @@
-import QtQuick 2.3
-import QtQuick.Controls 2.10
-import QtQuick.Layouts 1.10
-
-Pane {
-	property string title: qsTr("Verification aborted!")
-	ColumnLayout {
-		spacing: 16
-		Label {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			id: content
-			text: qsTr("Verification canceled by the other party!")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		RowLayout {
-			Item {
-				Layout.fillWidth: true
-			}
-			Button {
-				Layout.alignment: Qt.AlignRight
-				text: qsTr("Close")
-
-				onClicked: {
-					deviceVerificationList.remove(tran_id);
-					dialog.destroy();
-				}
-			}
-		}
-	}
-}
diff --git a/resources/qml/device-verification/Success.qml b/resources/qml/device-verification/Success.qml
new file mode 100644
index 00000000..b17b293c
--- /dev/null
+++ b/resources/qml/device-verification/Success.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.3
+import QtQuick.Controls 2.10
+import QtQuick.Layouts 1.10
+
+Pane {
+	property string title: qsTr("Successful Verification")
+	ColumnLayout {
+		spacing: 16
+		Label {
+			Layout.maximumWidth: 400
+			Layout.fillHeight: true
+			Layout.fillWidth: true
+			wrapMode: Text.Wrap
+			id: content
+			text: qsTr("Verification successful! Both sides verified their devices!")
+			color:colors.text
+			verticalAlignment: Text.AlignVCenter
+		}
+		RowLayout {
+			Item {
+				Layout.fillWidth: true
+			}
+			Button {
+				Layout.alignment: Qt.AlignRight
+				text: qsTr("Close")
+
+				onClicked: dialog.close();
+			}
+		}
+	}
+}
diff --git a/resources/qml/device-verification/TimedOut.qml b/resources/qml/device-verification/TimedOut.qml
deleted file mode 100644
index 7dd0ab69..00000000
--- a/resources/qml/device-verification/TimedOut.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-import QtQuick 2.3
-import QtQuick.Controls 2.10
-import QtQuick.Layouts 1.10
-
-Pane {
-	property string title: qsTr("Verification timed out")
-	ColumnLayout {
-		spacing: 16
-		Text {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			id: content
-			text: qsTr("Device verification timed out.")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		RowLayout {
-			Item {
-				Layout.fillWidth: true
-			}
-			Button {
-				Layout.alignment: Qt.AlignRight
-				text: qsTr("Close")
-
-				onClicked: {
-					deviceVerificationList.remove(tran_id);
-					flow.deleteFlow();
-					dialog.destroy()
-				}
-			}
-		}
-	}
-}
diff --git a/resources/qml/device-verification/VerificationSuccess.qml b/resources/qml/device-verification/VerificationSuccess.qml
deleted file mode 100644
index bc1e64f7..00000000
--- a/resources/qml/device-verification/VerificationSuccess.qml
+++ /dev/null
@@ -1,35 +0,0 @@
-import QtQuick 2.3
-import QtQuick.Controls 2.10
-import QtQuick.Layouts 1.10
-
-Pane {
-	property string title: qsTr("Successful Verification")
-	ColumnLayout {
-		spacing: 16
-		Label {
-			Layout.maximumWidth: 400
-			Layout.fillHeight: true
-			Layout.fillWidth: true
-			wrapMode: Text.Wrap
-			id: content
-			text: qsTr("Verification successful! Both sides verified their devices!")
-			color:colors.text
-			verticalAlignment: Text.AlignVCenter
-		}
-		RowLayout {
-			Item {
-				Layout.fillWidth: true
-			}
-			Button {
-				Layout.alignment: Qt.AlignRight
-				text: qsTr("Close")
-
-				onClicked: {
-					deviceVerificationList.remove(tran_id);
-					if(flow) flow.deleteFlow();
-					dialog.destroy();
-				}
-			}
-		}
-	}
-}
diff --git a/resources/qml/device-verification/Waiting.qml b/resources/qml/device-verification/Waiting.qml
new file mode 100644
index 00000000..f36910e7
--- /dev/null
+++ b/resources/qml/device-verification/Waiting.qml
@@ -0,0 +1,45 @@
+import QtQuick 2.3
+import QtQuick.Controls 2.10
+import QtQuick.Layouts 1.10
+
+import im.nheko 1.0
+
+Pane {
+	property string title: qsTr("Waiting for other party")
+	ColumnLayout {
+		spacing: 16
+		Label {
+			Layout.maximumWidth: 400
+			Layout.fillHeight: true
+			Layout.fillWidth: true
+			wrapMode: Text.Wrap
+			id: content
+			text: switch (flow.state) {
+				case "WaitingForOtherToAccept": return qsTr("Waiting for other side to accept the verification request.")
+				case "WaitingForKeys": return qsTr("Waiting for other side to continue the verification request.")
+				case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.")
+			}
+
+			color:colors.text
+			verticalAlignment: Text.AlignVCenter
+		}
+		BusyIndicator {
+			Layout.alignment: Qt.AlignHCenter
+			palette: color
+		}
+		RowLayout {
+			Button {
+				Layout.alignment: Qt.AlignLeft
+				text: qsTr("Cancel")
+
+				onClicked: { 
+					flow.cancel();
+					dialog.close();
+				}
+			}
+			Item {
+				Layout.fillWidth: true
+			}
+		}
+	}
+}
diff --git a/resources/res.qrc b/resources/res.qrc
index 7ef7ecf9..64e5b084 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -141,16 +141,13 @@
         qml/delegates/Pill.qml
         qml/delegates/Placeholder.qml
         qml/delegates/Reply.qml
-        qml/device-verification/AcceptNewVerificationRequest.qml
-        qml/device-verification/AwaitingVerificationConfirmation.qml
-        qml/device-verification/AwaitingVerificationRequest.qml
+        qml/device-verification/Waiting.qml
         qml/device-verification/DeviceVerification.qml
         qml/device-verification/DigitVerification.qml
         qml/device-verification/EmojiVerification.qml
         qml/device-verification/NewVerificationRequest.qml
-        qml/device-verification/PartnerAborted.qml
-        qml/device-verification/TimedOut.qml
-        qml/device-verification/VerificationSuccess.qml
+        qml/device-verification/Failed.qml
+        qml/device-verification/Success.qml
     
     
         media/ring.ogg
diff --git a/src/ChatPage.h b/src/ChatPage.h
index f363c4fe..c37aa915 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -169,22 +169,22 @@ signals:
         void decryptSidebarChanged();
 
         //! Signals for device verificaiton
-        void recievedDeviceVerificationAccept(
+        void receivedDeviceVerificationAccept(
           const mtx::events::msg::KeyVerificationAccept &message);
-        void recievedDeviceVerificationRequest(
+        void receivedDeviceVerificationRequest(
           const mtx::events::msg::KeyVerificationRequest &message,
           std::string sender);
-        void recievedRoomDeviceVerificationRequest(
+        void receivedRoomDeviceVerificationRequest(
           const mtx::events::RoomEvent &message,
           TimelineModel *model);
-        void recievedDeviceVerificationCancel(
+        void receivedDeviceVerificationCancel(
           const mtx::events::msg::KeyVerificationCancel &message);
-        void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
-        void recievedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message);
-        void recievedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message,
+        void receivedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
+        void receivedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message);
+        void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message,
                                              std::string sender);
-        void recievedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message);
-        void recievedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message);
+        void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message);
+        void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message);
 
 private slots:
         void showUnreadMessageNotification(int count);
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 7b367de9..99fd7bed 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -15,8 +15,12 @@ namespace msgs = mtx::events::msg;
 
 DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                                DeviceVerificationFlow::Type flow_type,
-                                               TimelineModel *model)
-  : type(flow_type)
+                                               TimelineModel *model,
+                                               QString userID,
+                                               QString deviceId_)
+  : sender(false)
+  , type(flow_type)
+  , deviceId(deviceId_)
   , model_(model)
 {
         timeout = new QTimer(this);
@@ -24,6 +28,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
         this->sas           = olm::client()->sas_init();
         this->isMacVerified = false;
 
+        auto user_id   = userID.toStdString();
+        this->toClient = mtx::identifiers::parse(user_id);
+        ChatPage::instance()->query_keys(
+          user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) {
+                  if (err) {
+                          nhlog::net()->warn("failed to query device keys: {},{}",
+                                             err->matrix_error.errcode,
+                                             static_cast(err->status_code));
+                          return;
+                  }
+
+                  if (!this->deviceId.isEmpty() &&
+                      (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) {
+                          nhlog::net()->warn("no devices retrieved {}", user_id);
+                          return;
+                  }
+
+                  for (const auto &[algorithm, key] :
+                       res.device_keys.at(deviceId.toStdString()).keys) {
+                          // TODO: Verify Signatures
+                          this->device_keys[algorithm] = key;
+                  }
+          });
+
         if (model) {
                 connect(this->model_,
                         &TimelineModel::updateFlowEventId,
@@ -36,64 +64,15 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
         }
 
         connect(timeout, &QTimer::timeout, this, [this]() {
-                emit timedout();
                 this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
-                this->deleteLater();
         });
 
-        connect(this, &DeviceVerificationFlow::deleteFlow, this, [this]() { this->deleteLater(); });
-
-        connect(
-          ChatPage::instance(),
-          &ChatPage::recievedDeviceVerificationStart,
-          this,
-          [this](const mtx::events::msg::KeyVerificationStart &msg, std::string) {
-                  if (msg.transaction_id.has_value()) {
-                          if (msg.transaction_id.value() != this->transaction_id)
-                                  return;
-                  } else if (msg.relates_to.has_value()) {
-                          if (msg.relates_to.value().event_id != this->relation.event_id)
-                                  return;
-                  }
-                  if ((std::find(msg.key_agreement_protocols.begin(),
-                                 msg.key_agreement_protocols.end(),
-                                 "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) &&
-                      (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") !=
-                       msg.hashes.end()) &&
-                      (std::find(msg.message_authentication_codes.begin(),
-                                 msg.message_authentication_codes.end(),
-                                 "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) {
-                          if (std::find(msg.short_authentication_string.begin(),
-                                        msg.short_authentication_string.end(),
-                                        mtx::events::msg::SASMethods::Decimal) !=
-                              msg.short_authentication_string.end()) {
-                                  this->method = DeviceVerificationFlow::Method::Emoji;
-                          } else if (std::find(msg.short_authentication_string.begin(),
-                                               msg.short_authentication_string.end(),
-                                               mtx::events::msg::SASMethods::Emoji) !=
-                                     msg.short_authentication_string.end()) {
-                                  this->method = DeviceVerificationFlow::Method::Decimal;
-                          } else {
-                                  this->cancelVerification(
-                                    DeviceVerificationFlow::Error::UnknownMethod);
-                                  return;
-                          }
-                          if (!sender)
-                                  this->canonical_json = nlohmann::json(msg);
-                          else {
-                                  if (utils::localUser().toStdString() <
-                                      this->toClient.to_string()) {
-                                          this->canonical_json = nlohmann::json(msg);
-                                  }
-                          }
-                          this->acceptVerificationRequest();
-                  } else {
-                          this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod);
-                  }
-          });
-
         connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationAccept,
+                &ChatPage::receivedDeviceVerificationStart,
+                this,
+                &DeviceVerificationFlow::handleStartMessage);
+        connect(ChatPage::instance(),
+                &ChatPage::receivedDeviceVerificationAccept,
                 this,
                 [this](const mtx::events::msg::KeyVerificationAccept &msg) {
                         if (msg.transaction_id.has_value()) {
@@ -111,9 +90,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                               msg.short_authentication_string.end(),
                                               mtx::events::msg::SASMethods::Emoji) !=
                                     msg.short_authentication_string.end()) {
-                                        this->method = DeviceVerificationFlow::Method::Emoji;
+                                        this->method = mtx::events::msg::SASMethods::Emoji;
                                 } else {
-                                        this->method = DeviceVerificationFlow::Method::Decimal;
+                                        this->method = mtx::events::msg::SASMethods::Decimal;
                                 }
                                 this->mac_method = msg.message_authentication_code;
                                 this->sendVerificationKey();
@@ -124,7 +103,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                 });
 
         connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationCancel,
+                &ChatPage::receivedDeviceVerificationCancel,
                 this,
                 [this](const mtx::events::msg::KeyVerificationCancel &msg) {
                         if (msg.transaction_id.has_value()) {
@@ -134,12 +113,13 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                 if (msg.relates_to.value().event_id != this->relation.event_id)
                                         return;
                         }
-                        this->deleteLater();
-                        emit verificationCanceled();
+                        error_ = User;
+                        emit errorChanged();
+                        setState(Failed);
                 });
 
         connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationKey,
+                &ChatPage::receivedDeviceVerificationKey,
                 this,
                 [this](const mtx::events::msg::KeyVerificationKey &msg) {
                         if (msg.transaction_id.has_value()) {
@@ -149,6 +129,19 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                 if (msg.relates_to.value().event_id != this->relation.event_id)
                                         return;
                         }
+
+                        if (sender) {
+                                if (state_ != WaitingForOtherToAccept) {
+                                        this->cancelVerification(OutOfOrder);
+                                        return;
+                                }
+                        } else {
+                                if (state_ != WaitingForKeys) {
+                                        this->cancelVerification(OutOfOrder);
+                                        return;
+                                }
+                        }
+
                         this->sas->set_their_key(msg.key);
                         std::string info;
                         if (this->sender == true) {
@@ -166,31 +159,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                        "|" + this->transaction_id;
                         }
 
-                        if (this->method == DeviceVerificationFlow::Method::Emoji) {
-                                std::cout << info << std::endl;
-                                this->sasList = this->sas->generate_bytes_emoji(info);
-                        } else if (this->method == DeviceVerificationFlow::Method::Decimal) {
-                                this->sasList = this->sas->generate_bytes_decimal(info);
-                        }
-
                         if (this->sender == false) {
-                                emit this->verificationRequestAccepted(this->method);
                                 this->sendVerificationKey();
                         } else {
-                                if (this->commitment ==
+                                if (this->commitment !=
                                     mtx::crypto::bin2base64_unpadded(
                                       mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) {
-                                        emit this->verificationRequestAccepted(this->method);
-                                } else {
                                         this->cancelVerification(
                                           DeviceVerificationFlow::Error::MismatchedCommitment);
+                                        return;
                                 }
                         }
+
+                        if (this->method == mtx::events::msg::SASMethods::Emoji) {
+                                this->sasList = this->sas->generate_bytes_emoji(info);
+                                setState(CompareEmoji);
+                        } else if (this->method == mtx::events::msg::SASMethods::Decimal) {
+                                this->sasList = this->sas->generate_bytes_decimal(info);
+                                setState(CompareNumber);
+                        }
                 });
 
         connect(
           ChatPage::instance(),
-          &ChatPage::recievedDeviceVerificationMac,
+          &ChatPage::receivedDeviceVerificationMac,
           this,
           [this](const mtx::events::msg::KeyVerificationMac &msg) {
                   if (msg.transaction_id.has_value()) {
@@ -222,26 +214,22 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                   }
                   key_string = key_string.substr(0, key_string.length() - 1);
                   if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
-                          // uncomment this in future to be compatible with the
-                          // MSC2366 this->sendVerificationDone(); and remove the
-                          // below line
-                          if (this->isMacVerified == true) {
-                                  this->acceptDevice();
-                          } else
-                                  this->isMacVerified = true;
+                          this->isMacVerified = true;
+                          this->acceptDevice();
                   } else {
                           this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch);
                   }
           });
 
         connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationReady,
+                &ChatPage::receivedDeviceVerificationReady,
                 this,
                 [this](const mtx::events::msg::KeyVerificationReady &msg) {
                         if (!sender) {
                                 if (msg.from_device != http::client()->device_id()) {
-                                        this->deleteLater();
-                                        emit verificationCanceled();
+                                        error_ = User;
+                                        emit errorChanged();
+                                        setState(Failed);
                                 }
 
                                 return;
@@ -261,7 +249,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                 });
 
         connect(ChatPage::instance(),
-                &ChatPage::recievedDeviceVerificationDone,
+                &ChatPage::receivedDeviceVerificationDone,
                 this,
                 [this](const mtx::events::msg::KeyVerificationDone &msg) {
                         if (msg.transaction_id.has_value()) {
@@ -271,22 +259,85 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                 if (msg.relates_to.value().event_id != this->relation.event_id)
                                         return;
                         }
-                        this->acceptDevice();
+                        nhlog::ui()->info("Flow done on other side");
                 });
 
         timeout->start(TIMEOUT);
 }
 
 QString
-DeviceVerificationFlow::getTransactionId()
+DeviceVerificationFlow::state()
 {
-        return QString::fromStdString(this->transaction_id);
+        switch (state_) {
+        case PromptStartVerification:
+                return "PromptStartVerification";
+        case CompareEmoji:
+                return "CompareEmoji";
+        case CompareNumber:
+                return "CompareNumber";
+        case WaitingForKeys:
+                return "WaitingForKeys";
+        case WaitingForOtherToAccept:
+                return "WaitingForOtherToAccept";
+        case WaitingForMac:
+                return "WaitingForMac";
+        case Success:
+                return "Success";
+        case Failed:
+                return "Failed";
+        default:
+                return "";
+        }
+}
+
+void
+DeviceVerificationFlow::next()
+{
+        if (sender) {
+                switch (state_) {
+                case PromptStartVerification:
+                        sendVerificationRequest();
+                        break;
+                case CompareEmoji:
+                case CompareNumber:
+                        sendVerificationMac();
+                        break;
+                case WaitingForKeys:
+                case WaitingForOtherToAccept:
+                case WaitingForMac:
+                case Success:
+                case Failed:
+                        nhlog::db()->error("verification: Invalid state transition!");
+                        break;
+                }
+        } else {
+                switch (state_) {
+                case PromptStartVerification:
+                        if (canonical_json.is_null())
+                                sendVerificationReady();
+                        else // legacy path without request and ready
+                                acceptVerificationRequest();
+                        break;
+                case CompareEmoji:
+                        [[fallthrough]];
+                case CompareNumber:
+                        sendVerificationMac();
+                        break;
+                case WaitingForKeys:
+                case WaitingForOtherToAccept:
+                case WaitingForMac:
+                case Success:
+                case Failed:
+                        nhlog::db()->error("verification: Invalid state transition!");
+                        break;
+                }
+        }
 }
 
 QString
 DeviceVerificationFlow::getUserId()
 {
-        return this->userId;
+        return QString::fromStdString(this->toClient.to_string());
 }
 
 QString
@@ -295,18 +346,6 @@ DeviceVerificationFlow::getDeviceId()
         return this->deviceId;
 }
 
-DeviceVerificationFlow::Method
-DeviceVerificationFlow::getMethod()
-{
-        return this->method;
-}
-
-DeviceVerificationFlow::Type
-DeviceVerificationFlow::getType()
-{
-        return this->type;
-}
-
 bool
 DeviceVerificationFlow::getSender()
 {
@@ -320,56 +359,58 @@ DeviceVerificationFlow::getSasList()
 }
 
 void
-DeviceVerificationFlow::setTransactionId(QString transaction_id_)
-{
-        this->transaction_id = transaction_id_.toStdString();
-}
-
-void
-DeviceVerificationFlow::setUserId(QString userID)
-{
-        this->userId   = userID;
-        this->toClient = mtx::identifiers::parse(userID.toStdString());
-
-        auto user_id = userID.toStdString();
-        ChatPage::instance()->query_keys(
-          user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) {
-                  this->callback_fn(res, err, user_id);
-          });
-}
-
-void
-DeviceVerificationFlow::setDeviceId(QString deviceID)
-{
-        this->deviceId = deviceID;
-}
-
-void
-DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_)
-{
-        this->method = method_;
-}
-
-void
-DeviceVerificationFlow::setType(Type type_)
+DeviceVerificationFlow::setEventId(std::string event_id_)
 {
-        this->type = type_;
+        this->relation.rel_type = mtx::common::RelationType::Reference;
+        this->relation.event_id = event_id_;
+        this->transaction_id    = event_id_;
 }
 
 void
-DeviceVerificationFlow::setSender(bool sender_)
+DeviceVerificationFlow::handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg,
+                                           std::string)
 {
-        this->sender = sender_;
-        if (this->sender)
-                this->transaction_id = http::client()->generate_txn_id();
-}
+        if (msg.transaction_id.has_value()) {
+                if (msg.transaction_id.value() != this->transaction_id)
+                        return;
+        } else if (msg.relates_to.has_value()) {
+                if (msg.relates_to.value().event_id != this->relation.event_id)
+                        return;
+        }
+        if ((std::find(msg.key_agreement_protocols.begin(),
+                       msg.key_agreement_protocols.end(),
+                       "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) &&
+            (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != msg.hashes.end()) &&
+            (std::find(msg.message_authentication_codes.begin(),
+                       msg.message_authentication_codes.end(),
+                       "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) {
+                if (std::find(msg.short_authentication_string.begin(),
+                              msg.short_authentication_string.end(),
+                              mtx::events::msg::SASMethods::Emoji) !=
+                    msg.short_authentication_string.end()) {
+                        this->method = mtx::events::msg::SASMethods::Emoji;
+                } else if (std::find(msg.short_authentication_string.begin(),
+                                     msg.short_authentication_string.end(),
+                                     mtx::events::msg::SASMethods::Decimal) !=
+                           msg.short_authentication_string.end()) {
+                        this->method = mtx::events::msg::SASMethods::Decimal;
+                } else {
+                        this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod);
+                        return;
+                }
+                if (!sender)
+                        this->canonical_json = nlohmann::json(msg);
+                else {
+                        if (utils::localUser().toStdString() < this->toClient.to_string()) {
+                                this->canonical_json = nlohmann::json(msg);
+                        }
+                }
 
-void
-DeviceVerificationFlow::setEventId(std::string event_id_)
-{
-        this->relation.rel_type = mtx::common::RelationType::Reference;
-        this->relation.event_id = event_id_;
-        this->transaction_id    = event_id_;
+                if (state_ != PromptStartVerification)
+                        this->acceptVerificationRequest();
+        } else {
+                this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod);
+        }
 }
 
 //! accepts a verification
@@ -382,30 +423,15 @@ DeviceVerificationFlow::acceptVerificationRequest()
         req.key_agreement_protocol      = "curve25519-hkdf-sha256";
         req.hash                        = "sha256";
         req.message_authentication_code = "hkdf-hmac-sha256";
-        if (this->method == DeviceVerificationFlow::Method::Emoji)
+        if (this->method == mtx::events::msg::SASMethods::Emoji)
                 req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji};
-        else if (this->method == DeviceVerificationFlow::Method::Decimal)
+        else if (this->method == mtx::events::msg::SASMethods::Decimal)
                 req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal};
         req.commitment = mtx::crypto::bin2base64_unpadded(
           mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump()));
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                mtx::requests::ToDeviceMessages body;
-                req.transaction_id = this->transaction_id;
-
-                body[this->toClient][this->deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to accept verification request: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationAccept);
-        }
+        send(req);
+        setState(WaitingForKeys);
 }
 //! responds verification request
 void
@@ -416,23 +442,8 @@ DeviceVerificationFlow::sendVerificationReady()
         req.from_device = http::client()->device_id();
         req.methods     = {mtx::events::msg::VerificationMethods::SASv1};
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                req.transaction_id = this->transaction_id;
-                mtx::requests::ToDeviceMessages body;
-
-                body[this->toClient][this->deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to send verification ready: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationReady);
-        }
+        send(req);
+        setState(WaitingForKeys);
 }
 //! accepts a verification
 void
@@ -440,23 +451,7 @@ DeviceVerificationFlow::sendVerificationDone()
 {
         mtx::events::msg::KeyVerificationDone req;
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                mtx::requests::ToDeviceMessages body;
-                req.transaction_id = this->transaction_id;
-
-                body[this->toClient][this->deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to send verification done: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationDone);
-        }
+        send(req);
 }
 //! starts the verification flow
 void
@@ -474,22 +469,14 @@ DeviceVerificationFlow::startVerificationRequest()
 
         if (this->type == DeviceVerificationFlow::Type::ToDevice) {
                 mtx::requests::ToDeviceMessages body;
-                req.transaction_id                                 = this->transaction_id;
-                this->canonical_json                               = nlohmann::json(req);
-                body[this->toClient][this->deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [body](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to start verification request: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
+                req.transaction_id   = this->transaction_id;
+                this->canonical_json = nlohmann::json(req);
         } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
                 req.relates_to       = this->relation;
                 this->canonical_json = nlohmann::json(req);
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationStart);
         }
+        send(req);
+        setState(WaitingForOtherToAccept);
 }
 //! sends a verification request
 void
@@ -503,28 +490,18 @@ DeviceVerificationFlow::sendVerificationRequest()
         if (this->type == DeviceVerificationFlow::Type::ToDevice) {
                 QDateTime currentTime = QDateTime::currentDateTimeUtc();
 
-                req.transaction_id = this->transaction_id;
-                req.timestamp      = (uint64_t)currentTime.toMSecsSinceEpoch();
-
-                mtx::requests::ToDeviceMessages body;
+                req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch();
 
-                body[this->toClient][this->deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to send verification request: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
         } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.to      = this->userId.toStdString();
+                req.to      = this->toClient.to_string();
                 req.msgtype = "m.key.verification.request";
                 req.body = "User is requesting to verify keys with you. However, your client does "
                            "not support this method, so you will need to use the legacy method of "
                            "key verification.";
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationRequest);
         }
+
+        send(req);
+        setState(WaitingForOtherToAccept);
 }
 //! cancels a verification flow
 void
@@ -534,7 +511,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
 
         if (error_code == DeviceVerificationFlow::Error::UnknownMethod) {
                 req.code   = "m.unknown_method";
-                req.reason = "unknown method recieved";
+                req.reason = "unknown method received";
         } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) {
                 req.code   = "m.mismatched_commitment";
                 req.reason = "commitment didn't match";
@@ -550,42 +527,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
         } else if (error_code == DeviceVerificationFlow::Error::User) {
                 req.code   = "m.user";
                 req.reason = "user cancelled the verification";
+        } else if (error_code == DeviceVerificationFlow::Error::OutOfOrder) {
+                req.code   = "m.unexpected_message";
+                req.reason = "received messages out of order";
         }
 
-        emit this->verificationCanceled();
+        this->error_ = error_code;
+        emit errorChanged();
+        this->setState(Failed);
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                req.transaction_id = this->transaction_id;
-                mtx::requests::ToDeviceMessages body;
-
-                body[this->toClient][deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [this](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to cancel verification request: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-
-                          this->deleteLater();
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationCancel);
-                this->deleteLater();
-        }
-
-        // TODO : Handle Blocking user better
-        // auto verified_cache = cache::getVerifiedCache(this->userId.toStdString());
-        //     if (verified_cache.has_value()) {
-        //             verified_cache->device_blocked.push_back(this->deviceId.toStdString());
-        //             cache::setVerifiedCache(this->userId.toStdString(),
-        //                                     verified_cache.value());
-        //     } else {
-        //             cache::setVerifiedCache(
-        //               this->userId.toStdString(),
-        //               DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}});
-        //     }
+        send(req);
 }
 //! sends the verification key
 void
@@ -595,23 +546,7 @@ DeviceVerificationFlow::sendVerificationKey()
 
         req.key = this->sas->public_key();
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                mtx::requests::ToDeviceMessages body;
-                req.transaction_id = this->transaction_id;
-
-                body[this->toClient][deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to send verification key: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey);
-        }
+        send(req);
 }
 
 mtx::events::msg::KeyVerificationMac
@@ -660,68 +595,102 @@ DeviceVerificationFlow::sendVerificationMac()
                                this->transaction_id,
                                key_list);
 
-        if (this->type == DeviceVerificationFlow::Type::ToDevice) {
-                mtx::requests::ToDeviceMessages body;
-                req.transaction_id                           = this->transaction_id;
-                body[this->toClient][deviceId.toStdString()] = req;
-
-                http::client()->send_to_device(
-                  this->transaction_id, body, [this](mtx::http::RequestErr err) {
-                          if (err)
-                                  nhlog::net()->warn("failed to send verification MAC: {} {}",
-                                                     err->matrix_error.error,
-                                                     static_cast(err->status_code));
-
-                          if (this->isMacVerified == true)
-                                  this->acceptDevice();
-                          else
-                                  this->isMacVerified = true;
-                  });
-        } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
-                req.relates_to = this->relation;
-                (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationMac);
-        }
+        send(req);
+
+        setState(WaitingForMac);
+        acceptDevice();
 }
 //! Completes the verification flow
 void
 DeviceVerificationFlow::acceptDevice()
 {
-        cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.toStdString());
+        if (!isMacVerified) {
+                setState(WaitingForMac);
+        } else if (state_ == WaitingForMac) {
+                cache::markDeviceVerified(this->toClient.to_string(), this->deviceId.toStdString());
+                this->sendVerificationDone();
+                setState(Success);
+        }
+}
+
+void
+DeviceVerificationFlow::unverify()
+{
+        cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString());
 
-        emit deviceVerified();
         emit refreshProfile();
-        this->deleteLater();
 }
 
-//! callback function to keep track of devices
-void
-DeviceVerificationFlow::callback_fn(const UserKeyCache &res,
-                                    mtx::http::RequestErr err,
-                                    std::string user_id)
+QSharedPointer
+DeviceVerificationFlow::NewInRoomVerification(QObject *parent_,
+                                              TimelineModel *timelineModel_,
+                                              const mtx::events::msg::KeyVerificationRequest &msg,
+                                              QString other_user_,
+                                              QString event_id_)
 {
-        if (err) {
-                nhlog::net()->warn("failed to query device keys: {},{}",
-                                   err->matrix_error.errcode,
-                                   static_cast(err->status_code));
-                return;
-        }
+        QSharedPointer flow(
+          new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, other_user_, ""));
+
+        flow->event_id = event_id_.toStdString();
 
-        if (res.device_keys.empty() ||
-            (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) {
-                nhlog::net()->warn("no devices retrieved {}", user_id);
-                return;
+        if (std::find(msg.methods.begin(),
+                      msg.methods.end(),
+                      mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) {
+                flow->cancelVerification(UnknownMethod);
         }
 
-        for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) {
-                // TODO: Verify Signatures
-                this->device_keys[algorithm] = key;
+        return flow;
+}
+QSharedPointer
+DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_,
+                                                const mtx::events::msg::KeyVerificationRequest &msg,
+                                                QString other_user_,
+                                                QString txn_id_)
+{
+        QSharedPointer flow(new DeviceVerificationFlow(
+          parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device)));
+        flow->transaction_id = txn_id_.toStdString();
+
+        if (std::find(msg.methods.begin(),
+                      msg.methods.end(),
+                      mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) {
+                flow->cancelVerification(UnknownMethod);
         }
+
+        return flow;
 }
+QSharedPointer
+DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_,
+                                                const mtx::events::msg::KeyVerificationStart &msg,
+                                                QString other_user_,
+                                                QString txn_id_)
+{
+        QSharedPointer flow(new DeviceVerificationFlow(
+          parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device)));
+        flow->transaction_id = txn_id_.toStdString();
 
-void
-DeviceVerificationFlow::unverify()
+        flow->handleStartMessage(msg, "");
+
+        return flow;
+}
+QSharedPointer
+DeviceVerificationFlow::InitiateUserVerification(QObject *parent_,
+                                                 TimelineModel *timelineModel_,
+                                                 QString userid)
 {
-        cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString());
+        QSharedPointer flow(
+          new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, userid, ""));
+        flow->sender = true;
+        return flow;
+}
+QSharedPointer
+DeviceVerificationFlow::InitiateDeviceVerification(QObject *parent_, QString userid, QString device)
+{
+        QSharedPointer flow(
+          new DeviceVerificationFlow(parent_, Type::ToDevice, nullptr, userid, device));
 
-        emit refreshProfile();
+        flow->sender         = true;
+        flow->transaction_id = http::client()->generate_txn_id();
+
+        return flow;
 }
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index de7a4567..1fe3919b 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -5,41 +5,84 @@
 #include 
 
 #include "CacheCryptoStructs.h"
+#include "Logging.h"
 #include "MatrixClient.h"
 #include "Olm.h"
+#include "timeline/TimelineModel.h"
 
 class QTimer;
 
 using sas_ptr = std::unique_ptr;
 
-class TimelineModel;
-
+// clang-format off
+/*
+ * Stolen from fluffy chat :D
+ *
+ *      State         |   +-------------+                    +-----------+                                  |
+ *                    |   | AliceDevice |                    | BobDevice |                                  |
+ *                    |   | (sender)    |                    |           |                                  |
+ *                    |   +-------------+                    +-----------+                                  |
+ * promptStartVerify  |         |                                 |                                         |
+ *                    |      o  | (m.key.verification.request)    |                                         |
+ *                    |      p  |-------------------------------->| (ASK FOR VERIFICATION REQUEST)          |
+ * waitForOtherAccept |      t  |                                 |                                         | promptStartVerify
+ * &&                 |      i  |      (m.key.verification.ready) |                                         |
+ * no commitment      |      o  |<--------------------------------|                                         |
+ * &&                 |      n  |                                 |                                         |
+ * no canonical_json  |      a  |      (m.key.verification.start) |                                         | waitingForKeys
+ *                    |      l  |<--------------------------------| Not sending to prevent the glare resolve| && no commitment
+ *                    |         |                                 |                                         | && no canonical_json
+ *                    |         | m.key.verification.start        |                                         |
+ * waitForOtherAccept |         |-------------------------------->| (IF NOT ALREADY ASKED,                  |
+ * &&                 |         |                                 |  ASK FOR VERIFICATION REQUEST)          | promptStartVerify, if not accepted
+ * canonical_json     |         |       m.key.verification.accept |                                         |
+ *                    |         |<--------------------------------|                                         |
+ * waitForOtherAccept |         |                                 |                                         | waitingForKeys
+ * &&                 |         | m.key.verification.key          |                                         | && canonical_json
+ * commitment         |         |-------------------------------->|                                         | && commitment
+ *                    |         |                                 |                                         |
+ *                    |         |          m.key.verification.key |                                         |
+ *                    |         |<--------------------------------|                                         |
+ * compareEmoji/Number|         |                                 |                                         | compareEmoji/Number
+ *                    |         |     COMPARE EMOJI / NUMBERS     |                                         |
+ *                    |         |                                 |                                         |
+ * waitingForMac      |         |     m.key.verification.mac      |                                         | waitingForMac
+ *                    | success |<------------------------------->|  success                                |
+ *                    |         |                                 |                                         |
+ * success/fail       |         |         m.key.verification.done |                                         | success/fail
+ *                    |         |<------------------------------->|                                         |
+ */
+// clang-format on
 class DeviceVerificationFlow : public QObject
 {
         Q_OBJECT
         // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
-        Q_PROPERTY(QString tranId READ getTransactionId WRITE setTransactionId)
-        Q_PROPERTY(bool sender READ getSender WRITE setSender)
-        Q_PROPERTY(QString userId READ getUserId WRITE setUserId)
-        Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId)
-        Q_PROPERTY(Method method READ getMethod WRITE setMethod)
-        Q_PROPERTY(Type type READ getType WRITE setType)
+        Q_PROPERTY(QString state READ state NOTIFY stateChanged)
+        Q_PROPERTY(Error error READ error CONSTANT)
+        Q_PROPERTY(QString userId READ getUserId CONSTANT)
+        Q_PROPERTY(QString deviceId READ getDeviceId CONSTANT)
+        Q_PROPERTY(bool sender READ getSender CONSTANT)
         Q_PROPERTY(std::vector sasList READ getSasList CONSTANT)
 
 public:
-        enum Type
+        enum State
         {
-                ToDevice,
-                RoomMsg
+                PromptStartVerification,
+                WaitingForOtherToAccept,
+                WaitingForKeys,
+                CompareEmoji,
+                CompareNumber,
+                WaitingForMac,
+                Success,
+                Failed,
         };
-        Q_ENUM(Type)
+        Q_ENUM(State)
 
-        enum Method
+        enum Type
         {
-                Decimal,
-                Emoji
+                ToDevice,
+                RoomMsg
         };
-        Q_ENUM(Method)
 
         enum Error
         {
@@ -48,36 +91,75 @@ public:
                 MismatchedSAS,
                 KeyMismatch,
                 Timeout,
-                User
+                User,
+                OutOfOrder,
         };
         Q_ENUM(Error)
 
-        DeviceVerificationFlow(
-          QObject *parent              = nullptr,
-          DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice,
-          TimelineModel *model         = nullptr);
+        static QSharedPointer NewInRoomVerification(
+          QObject *parent_,
+          TimelineModel *timelineModel_,
+          const mtx::events::msg::KeyVerificationRequest &msg,
+          QString other_user_,
+          QString event_id_);
+        static QSharedPointer NewToDeviceVerification(
+          QObject *parent_,
+          const mtx::events::msg::KeyVerificationRequest &msg,
+          QString other_user_,
+          QString txn_id_);
+        static QSharedPointer NewToDeviceVerification(
+          QObject *parent_,
+          const mtx::events::msg::KeyVerificationStart &msg,
+          QString other_user_,
+          QString txn_id_);
+        static QSharedPointer
+        InitiateUserVerification(QObject *parent_, TimelineModel *timelineModel_, QString userid);
+        static QSharedPointer InitiateDeviceVerification(QObject *parent,
+                                                                                 QString userid,
+                                                                                 QString device);
+
         // getters
-        QString getTransactionId();
+        QString state();
+        Error error() { return error_; }
         QString getUserId();
         QString getDeviceId();
-        Method getMethod();
-        Type getType();
-        std::vector getSasList();
         bool getSender();
+        std::vector getSasList();
+        QString transactionId() { return QString::fromStdString(this->transaction_id); }
         // setters
-        void setTransactionId(QString transaction_id_);
-        void setUserId(QString userID);
         void setDeviceId(QString deviceID);
-        void setMethod(Method method_);
-        void setType(Type type_);
-        void setSender(bool sender_);
         void setEventId(std::string event_id);
 
         void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id);
 
-        nlohmann::json canonical_json;
-
 public slots:
+        //! unverifies a device
+        void unverify();
+        //! Continues the flow
+        void next();
+        //! Cancel the flow
+        void cancel() { cancelVerification(User); }
+
+signals:
+        void refreshProfile();
+        void stateChanged();
+        void errorChanged();
+
+private:
+        DeviceVerificationFlow(QObject *,
+                               DeviceVerificationFlow::Type flow_type,
+                               TimelineModel *model,
+                               QString userID,
+                               QString deviceId_);
+        void setState(State state)
+        {
+                if (state != state_) {
+                        state_ = state;
+                        emit stateChanged();
+                }
+        }
+
+        void handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg, std::string);
         //! sends a verification request
         void sendVerificationRequest();
         //! accepts a verification request
@@ -96,37 +178,60 @@ public slots:
         void sendVerificationMac();
         //! Completes the verification flow
         void acceptDevice();
-        //! unverifies a device
-        void unverify();
 
-signals:
-        void verificationRequestAccepted(Method method);
-        void deviceVerified();
-        void timedout();
-        void verificationCanceled();
-        void refreshProfile();
-        void deleteFlow();
+        // for to_device messages
+        std::string transaction_id;
+        // for room messages
+        std::optional room_id;
+        std::optional event_id;
 
-private:
-        // general
-        QString userId;
-        QString deviceId;
-        Method method = Method::Emoji;
-        Type type;
         bool sender;
-        QTimer *timeout = nullptr;
+        Type type;
+        mtx::identifiers::User toClient;
+        QString deviceId;
+
+        mtx::events::msg::SASMethods method = mtx::events::msg::SASMethods::Emoji;
+        QTimer *timeout                     = nullptr;
         sas_ptr sas;
-        bool isMacVerified = false;
         std::string mac_method;
         std::string commitment;
-        mtx::identifiers::User toClient;
+        nlohmann::json canonical_json;
+
         std::vector sasList;
         std::map device_keys;
-        // for to_device messages
-        std::string transaction_id;
-        // for room messages
-        std::optional room_id;
-        std::optional event_id;
         TimelineModel *model_;
         mtx::common::RelatesTo relation;
+
+        State state_ = PromptStartVerification;
+        Error error_;
+
+        bool isMacVerified = false;
+
+        template
+        void send(T msg)
+        {
+                if (this->type == DeviceVerificationFlow::Type::ToDevice) {
+                        mtx::requests::ToDeviceMessages body;
+                        msg.transaction_id                           = this->transaction_id;
+                        body[this->toClient][deviceId.toStdString()] = msg;
+
+                        http::client()->send_to_device(
+                          this->transaction_id, body, [this](mtx::http::RequestErr err) {
+                                  if (err)
+                                          nhlog::net()->warn(
+                                            "failed to send verification to_device message: {} {}",
+                                            err->matrix_error.error,
+                                            static_cast(err->status_code));
+                          });
+                } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
+                        if constexpr (!std::is_same_v)
+                                msg.relates_to = this->relation;
+                        (model_)->sendMessageEvent(msg, mtx::events::to_device_content_to_type);
+                }
+
+                nhlog::net()->debug(
+                  "Sent verification step: {} in state: {}",
+                  mtx::events::to_string(mtx::events::to_device_content_to_type),
+                  state().toStdString());
+        }
 };
diff --git a/src/Olm.cpp b/src/Olm.cpp
index 8219ce4c..f4cb2209 100644
--- a/src/Olm.cpp
+++ b/src/Olm.cpp
@@ -80,40 +80,40 @@ handle_to_device_messages(const std::vector>(msg);
-                        ChatPage::instance()->recievedDeviceVerificationAccept(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationAccept(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) {
                         auto message = std::get<
                           mtx::events::DeviceEvent>(msg);
-                        ChatPage::instance()->recievedDeviceVerificationRequest(message.content,
+                        ChatPage::instance()->receivedDeviceVerificationRequest(message.content,
                                                                                 message.sender);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) {
                         auto message = std::get<
                           mtx::events::DeviceEvent>(msg);
-                        ChatPage::instance()->recievedDeviceVerificationCancel(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationCancel(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) {
                         auto message =
                           std::get>(
                             msg);
-                        ChatPage::instance()->recievedDeviceVerificationKey(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationKey(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) {
                         auto message =
                           std::get>(
                             msg);
-                        ChatPage::instance()->recievedDeviceVerificationMac(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationMac(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) {
                         auto message = std::get<
                           mtx::events::DeviceEvent>(msg);
-                        ChatPage::instance()->recievedDeviceVerificationStart(message.content,
+                        ChatPage::instance()->receivedDeviceVerificationStart(message.content,
                                                                               message.sender);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) {
                         auto message = std::get<
                           mtx::events::DeviceEvent>(msg);
-                        ChatPage::instance()->recievedDeviceVerificationReady(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationReady(message.content);
                 } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) {
                         auto message =
                           std::get>(
                             msg);
-                        ChatPage::instance()->recievedDeviceVerificationDone(message.content);
+                        ChatPage::instance()->receivedDeviceVerificationDone(message.content);
                 } else {
                         nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2));
                 }
@@ -153,42 +153,42 @@ handle_olm_message(const OlmMessage &msg)
                         std::string msg_type = payload["type"];
 
                         if (msg_type == to_string(mtx::events::EventType::KeyVerificationAccept)) {
-                                ChatPage::instance()->recievedDeviceVerificationAccept(
+                                ChatPage::instance()->receivedDeviceVerificationAccept(
                                   payload["content"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationRequest)) {
-                                ChatPage::instance()->recievedDeviceVerificationRequest(
+                                ChatPage::instance()->receivedDeviceVerificationRequest(
                                   payload["content"], payload["sender"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationCancel)) {
-                                ChatPage::instance()->recievedDeviceVerificationCancel(
+                                ChatPage::instance()->receivedDeviceVerificationCancel(
                                   payload["content"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationKey)) {
-                                ChatPage::instance()->recievedDeviceVerificationKey(
+                                ChatPage::instance()->receivedDeviceVerificationKey(
                                   payload["content"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationMac)) {
-                                ChatPage::instance()->recievedDeviceVerificationMac(
+                                ChatPage::instance()->receivedDeviceVerificationMac(
                                   payload["content"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationStart)) {
-                                ChatPage::instance()->recievedDeviceVerificationStart(
+                                ChatPage::instance()->receivedDeviceVerificationStart(
                                   payload["content"], payload["sender"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationReady)) {
-                                ChatPage::instance()->recievedDeviceVerificationReady(
+                                ChatPage::instance()->receivedDeviceVerificationReady(
                                   payload["content"]);
                                 return;
                         } else if (msg_type ==
                                    to_string(mtx::events::EventType::KeyVerificationDone)) {
-                                ChatPage::instance()->recievedDeviceVerificationDone(
+                                ChatPage::instance()->receivedDeviceVerificationDone(
                                   payload["content"]);
                                 return;
                         } else if (msg_type == to_string(mtx::events::EventType::RoomKey)) {
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 29b3c239..dc92a37f 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -299,7 +299,7 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
                                       mtx::events::msg::KeyVerificationReady>>(d_event)) {
                                         auto msg = std::get_if>(d_event);
-                                        ChatPage::instance()->recievedDeviceVerificationReady(
+                                        ChatPage::instance()->receivedDeviceVerificationReady(
                                           msg->content);
                                 }
                         }
@@ -328,43 +328,43 @@ EventStore::handle_room_verification(mtx::events::collections::TimelineEvents ev
                 auto msg =
                   std::get>(event);
                 last_verification_cancel_event = msg;
-                ChatPage::instance()->recievedDeviceVerificationCancel(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationCancel(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationAccept(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationAccept(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationKey(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationKey(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationMac(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationMac(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationReady(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationReady(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationDone(msg.content);
+                ChatPage::instance()->receivedDeviceVerificationDone(msg.content);
                 return;
         } else if (std::get_if>(
                      &event)) {
                 auto msg =
                   std::get>(event);
-                ChatPage::instance()->recievedDeviceVerificationStart(msg.content, msg.sender);
+                ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender);
                 return;
         }
 }
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 5e8952fc..e8d381df 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -254,7 +254,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
                 &EventStore::startDMVerification,
                 this,
                 [this](mtx::events::RoomEvent msg) {
-                        ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this);
+                        ChatPage::instance()->receivedRoomDeviceVerificationRequest(msg, this);
                 });
         connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) {
                 this->updateFlowEventId(event_id);
@@ -793,7 +793,7 @@ TimelineModel::viewDecryptedRawMessage(QString id) const
 void
 TimelineModel::openUserProfile(QString userid)
 {
-        emit openProfile(new UserProfile(room_id_, userid, this));
+        emit openProfile(new UserProfile(room_id_, userid, manager_, this));
 }
 
 void
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 03dd4773..250cd5f0 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -28,22 +28,6 @@ Q_DECLARE_METATYPE(std::vector)
 
 namespace msgs = mtx::events::msg;
 
-void
-DeviceVerificationList::add(QString tran_id)
-{
-        this->deviceVerificationList.push_back(tran_id);
-}
-void
-DeviceVerificationList::remove(QString tran_id)
-{
-        this->deviceVerificationList.removeOne(tran_id);
-}
-bool
-DeviceVerificationList::exist(QString tran_id)
-{
-        return this->deviceVerificationList.contains(tran_id);
-}
-
 void
 TimelineViewManager::updateEncryptedDescriptions()
 {
@@ -134,7 +118,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
 
         qmlRegisterType("im.nheko", 1, 0, "DelegateChoice");
         qmlRegisterType("im.nheko", 1, 0, "DelegateChooser");
-        qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow");
+        qmlRegisterUncreatableType(
+          "im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
         qmlRegisterUncreatableType(
           "im.nheko",
           1,
@@ -163,7 +148,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                                          0,
                                          "EmojiCategory",
                                          "Error: Only enums");
-        this->dvList = new DeviceVerificationList;
 
 #ifdef USE_QUICK_VIEW
         view      = new QQuickView();
@@ -183,7 +167,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
         });
 #endif
         container->setMinimumSize(200, 200);
-        view->rootContext()->setContextProperty("deviceVerificationList", this->dvList);
         updateColorPalette();
         view->engine()->addImageProvider("MxcImage", imgProvider);
         view->engine()->addImageProvider("colorimage", colorImgProvider);
@@ -197,102 +180,55 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin
                 &TimelineViewManager::updateEncryptedDescriptions);
         connect(
           dynamic_cast(parent),
-          &ChatPage::recievedRoomDeviceVerificationRequest,
+          &ChatPage::receivedRoomDeviceVerificationRequest,
           this,
           [this](const mtx::events::RoomEvent &message,
                  TimelineModel *model) {
-                  if (!(this->dvList->exist(QString::fromStdString(message.event_id)))) {
-                          auto flow = new DeviceVerificationFlow(
-                            this, DeviceVerificationFlow::Type::RoomMsg, model);
-                          if (std::find(message.content.methods.begin(),
-                                        message.content.methods.end(),
-                                        mtx::events::msg::VerificationMethods::SASv1) !=
-                              message.content.methods.end()) {
-                                  flow->setEventId(message.event_id);
-                                  emit newDeviceVerificationRequest(
-                                    std::move(flow),
-                                    QString::fromStdString(message.event_id),
-                                    QString::fromStdString(message.sender),
-                                    QString::fromStdString(message.content.from_device),
-                                    true);
-                          } else {
-                                  flow->cancelVerification(
-                                    DeviceVerificationFlow::Error::UnknownMethod);
-                          }
-                  }
-          });
-        connect(
-          dynamic_cast(parent),
-          &ChatPage::recievedDeviceVerificationRequest,
-          this,
-          [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
-                  if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) {
-                          auto flow = new DeviceVerificationFlow(this);
-                          if (std::find(msg.methods.begin(),
-                                        msg.methods.end(),
-                                        mtx::events::msg::VerificationMethods::SASv1) !=
-                              msg.methods.end()) {
-                                  emit newDeviceVerificationRequest(
-                                    std::move(flow),
-                                    QString::fromStdString(msg.transaction_id.value()),
-                                    QString::fromStdString(sender),
-                                    QString::fromStdString(msg.from_device));
-                          } else {
-                                  flow->cancelVerification(
-                                    DeviceVerificationFlow::Error::UnknownMethod);
-                          }
-                  }
-          });
-        connect(
-          dynamic_cast(parent),
-          &ChatPage::recievedDeviceVerificationStart,
-          this,
-          [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) {
-                  if (msg.transaction_id.has_value()) {
-                          if (!(this->dvList->exist(
-                                QString::fromStdString(msg.transaction_id.value())))) {
-                                  auto flow            = new DeviceVerificationFlow(this);
-                                  flow->canonical_json = nlohmann::json(msg);
-                                  if ((std::find(msg.key_agreement_protocols.begin(),
-                                                 msg.key_agreement_protocols.end(),
-                                                 "curve25519-hkdf-sha256") !=
-                                       msg.key_agreement_protocols.end()) &&
-                                      (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") !=
-                                       msg.hashes.end()) &&
-                                      (std::find(msg.message_authentication_codes.begin(),
-                                                 msg.message_authentication_codes.end(),
-                                                 "hmac-sha256") !=
-                                       msg.message_authentication_codes.end())) {
-                                          if (std::find(msg.short_authentication_string.begin(),
-                                                        msg.short_authentication_string.end(),
-                                                        mtx::events::msg::SASMethods::Emoji) !=
-                                              msg.short_authentication_string.end()) {
-                                                  flow->setMethod(
-                                                    DeviceVerificationFlow::Method::Emoji);
-                                          } else if (std::find(
-                                                       msg.short_authentication_string.begin(),
-                                                       msg.short_authentication_string.end(),
-                                                       mtx::events::msg::SASMethods::Decimal) !=
-                                                     msg.short_authentication_string.end()) {
-                                                  flow->setMethod(
-                                                    DeviceVerificationFlow::Method::Decimal);
-                                          } else {
-                                                  flow->cancelVerification(
-                                                    DeviceVerificationFlow::Error::UnknownMethod);
-                                                  return;
-                                          }
-                                          emit newDeviceVerificationRequest(
-                                            std::move(flow),
-                                            QString::fromStdString(msg.transaction_id.value()),
-                                            QString::fromStdString(sender),
-                                            QString::fromStdString(msg.from_device));
-                                  } else {
-                                          flow->cancelVerification(
-                                            DeviceVerificationFlow::Error::UnknownMethod);
-                                  }
+                  auto event_id = QString::fromStdString(message.event_id);
+                  if (!this->dvList.contains(event_id)) {
+                          if (auto flow = DeviceVerificationFlow::NewInRoomVerification(
+                                this,
+                                model,
+                                message.content,
+                                QString::fromStdString(message.sender),
+                                event_id)) {
+                                  dvList[event_id] = flow;
+                                  emit newDeviceVerificationRequest(flow.data());
                           }
                   }
           });
+        connect(dynamic_cast(parent),
+                &ChatPage::receivedDeviceVerificationRequest,
+                this,
+                [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) {
+                        if (!msg.transaction_id)
+                                return;
+
+                        auto txnid = QString::fromStdString(msg.transaction_id.value());
+                        if (!this->dvList.contains(txnid)) {
+                                if (auto flow = DeviceVerificationFlow::NewToDeviceVerification(
+                                      this, msg, QString::fromStdString(sender), txnid)) {
+                                        dvList[txnid] = flow;
+                                        emit newDeviceVerificationRequest(flow.data());
+                                }
+                        }
+                });
+        connect(dynamic_cast(parent),
+                &ChatPage::receivedDeviceVerificationStart,
+                this,
+                [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) {
+                        if (!msg.transaction_id)
+                                return;
+
+                        auto txnid = QString::fromStdString(msg.transaction_id.value());
+                        if (!this->dvList.contains(txnid)) {
+                                if (auto flow = DeviceVerificationFlow::NewToDeviceVerification(
+                                      this, msg, QString::fromStdString(sender), txnid)) {
+                                        dvList[txnid] = flow;
+                                        emit newDeviceVerificationRequest(flow.data());
+                                }
+                        }
+                });
         connect(parent, &ChatPage::loggedOut, this, [this]() {
                 isInitialSync_ = true;
                 emit initialSyncChanged(true);
@@ -428,6 +364,46 @@ TimelineViewManager::openRoomSettings() const
         MainWindow::instance()->openRoomSettings(timeline_->roomId());
 }
 
+void
+TimelineViewManager::verifyUser(QString userid)
+{
+        auto joined_rooms = cache::joinedRooms();
+        auto room_infos   = cache::getRoomInfo(joined_rooms);
+
+        for (std::string room_id : joined_rooms) {
+                if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
+                    cache::isRoomEncrypted(room_id)) {
+                        auto room_members = cache::roomMembers(room_id);
+                        if (std::find(room_members.begin(),
+                                      room_members.end(),
+                                      (userid).toStdString()) != room_members.end()) {
+                                auto model = models.value(QString::fromStdString(room_id));
+                                auto flow  = DeviceVerificationFlow::InitiateUserVerification(
+                                  this, model.data(), userid);
+                                connect(model.data(),
+                                        &TimelineModel::updateFlowEventId,
+                                        this,
+                                        [this, flow](std::string eventId) {
+                                                dvList[QString::fromStdString(eventId)] = flow;
+                                        });
+                                emit newDeviceVerificationRequest(flow.data());
+                                return;
+                        }
+                }
+        }
+
+        emit ChatPage::instance()->showNotification(
+          tr("No share room with this user found. Create an "
+             "encrypted room with this user and try again."));
+}
+void
+TimelineViewManager::verifyDevice(QString userid, QString deviceid)
+{
+        auto flow = DeviceVerificationFlow::InitiateDeviceVerification(this, userid, deviceid);
+        this->dvList[flow->transactionId()] = flow;
+        emit newDeviceVerificationRequest(flow.data());
+}
+
 void
 TimelineViewManager::updateReadReceipts(const QString &room_id,
                                         const std::vector &event_ids)
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 4779d3cd..12e49080 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -24,20 +24,6 @@ class ColorImageProvider;
 class UserSettings;
 class ChatPage;
 
-class DeviceVerificationList : public QObject
-{
-        Q_OBJECT
-public:
-        Q_INVOKABLE void add(QString tran_id);
-        Q_INVOKABLE void remove(QString tran_id);
-        Q_INVOKABLE bool exist(QString tran_id);
-signals:
-        void updateProfile(QString userId);
-
-private:
-        QVector deviceVerificationList;
-};
-
 class TimelineViewManager : public QObject
 {
         Q_OBJECT
@@ -77,6 +63,9 @@ public:
         Q_INVOKABLE void openLeaveRoomDialog() const;
         Q_INVOKABLE void openRoomSettings() const;
 
+        void verifyUser(QString userid);
+        void verifyDevice(QString userid, QString deviceid);
+
 signals:
         void clearRoomMessageCount(QString roomid);
         void updateRoomsLastMessage(QString roomid, const DescInfo &info);
@@ -84,11 +73,7 @@ signals:
         void initialSyncChanged(bool isInitialSync);
         void replyingEventChanged(QString replyingEvent);
         void replyClosed();
-        void newDeviceVerificationRequest(DeviceVerificationFlow *flow,
-                                          QString transactionId,
-                                          QString userId,
-                                          QString deviceId,
-                                          bool isRequest = false);
+        void newDeviceVerificationRequest(DeviceVerificationFlow *flow);
         void inviteUsers(QStringList users);
         void showRoomList();
         void narrowViewChanged();
@@ -180,7 +165,7 @@ private:
         QSharedPointer settings;
         QHash userColors;
 
-        DeviceVerificationList *dvList;
+        QHash> dvList;
 };
 Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept)
 Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel)
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 2ea3f7b6..2a1eecdf 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -6,13 +6,18 @@
 #include "Utils.h"
 #include "mtx/responses/crypto.hpp"
 #include "timeline/TimelineModel.h"
+#include "timeline/TimelineViewManager.h"
 
 #include  // only for debugging
 
-UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent)
+UserProfile::UserProfile(QString roomid,
+                         QString userid,
+                         TimelineViewManager *manager_,
+                         TimelineModel *parent)
   : QObject(parent)
   , roomid_(roomid)
   , userid_(userid)
+  , manager(manager_)
   , model(parent)
 {
         fetchDeviceList(this->userid_);
@@ -270,44 +275,18 @@ UserProfile::startChat()
         emit ChatPage::instance()->createRoom(req);
 }
 
-DeviceVerificationFlow *
-UserProfile::createFlow(bool isVerifyUser)
+void
+UserProfile::verify(QString device)
 {
-        if (!isVerifyUser)
-                return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
+        if (!device.isEmpty())
+                manager->verifyDevice(userid_, device);
         else {
-                std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION"
-                          << std::endl;
-                auto joined_rooms = cache::joinedRooms();
-                auto room_infos   = cache::getRoomInfo(joined_rooms);
-
-                for (std::string room_id : joined_rooms) {
-                        if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
-                            cache::isRoomEncrypted(room_id)) {
-                                auto room_members = cache::roomMembers(room_id);
-                                if (std::find(room_members.begin(),
-                                              room_members.end(),
-                                              (this->userid()).toStdString()) !=
-                                    room_members.end()) {
-                                        std::cout
-                                          << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id
-                                          << std::endl;
-                                        if (this->roomid_.toStdString() == room_id) {
-                                                auto newflow = new DeviceVerificationFlow(
-                                                  this,
-                                                  DeviceVerificationFlow::Type::RoomMsg,
-                                                  this->model);
-                                                return (std::move(newflow));
-                                        } else {
-                                                std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY "
-                                                             "NOT IN THAT ROOM : "
-                                                          << room_id << std::endl;
-                                        }
-                                }
-                        }
-                }
-
-                std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl;
-                return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
+                manager->verifyUser(userid_);
         }
 }
+
+void
+UserProfile::unverify(QString device)
+{
+        cache::markDeviceUnverified(userid_.toStdString(), device.toStdString());
+}
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index de55b6ab..18933727 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -21,6 +21,7 @@ Q_ENUM_NS(Status)
 
 class DeviceVerificationFlow;
 class TimelineModel;
+class TimelineViewManager;
 
 class DeviceInfo
 {
@@ -84,7 +85,10 @@ class UserProfile : public QObject
         Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
         Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
 public:
-        UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr);
+        UserProfile(QString roomid,
+                    QString userid,
+                    TimelineViewManager *manager_,
+                    TimelineModel *parent = nullptr);
 
         DeviceInfoModel *deviceList();
 
@@ -93,7 +97,8 @@ public:
         QString avatarUrl();
         bool getUserStatus();
 
-        Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser);
+        Q_INVOKABLE void verify(QString device = "");
+        Q_INVOKABLE void unverify(QString device = "");
         Q_INVOKABLE void fetchDeviceList(const QString &userID);
         Q_INVOKABLE void banUser();
         // Q_INVOKABLE void ignoreUser();
@@ -105,6 +110,7 @@ private:
         std::optional cross_verified;
         DeviceInfoModel deviceList_;
         bool isUserVerified = false;
+        TimelineViewManager *manager;
         TimelineModel *model;
 
         void callback_fn(const mtx::responses::QueryKeys &res,
-- 
cgit 1.5.1


From 8a4d85f801368137a0b1c17621947b334e280257 Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Mon, 5 Oct 2020 22:58:07 +0200
Subject: Show different verification errors

---
 .../qml/device-verification/DeviceVerification.qml |  1 -
 resources/qml/device-verification/Failed.qml       | 28 ++++++++++++----------
 resources/qml/device-verification/Waiting.qml      |  4 ++--
 src/DeviceVerificationFlow.cpp                     |  5 ++--
 src/DeviceVerificationFlow.h                       |  4 ++--
 src/timeline/TimelineViewManager.cpp               | 12 ++++++++++
 src/timeline/TimelineViewManager.h                 |  1 +
 7 files changed, 35 insertions(+), 20 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 4e93df06..64f10b35 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -6,7 +6,6 @@ import im.nheko 1.0
 
 ApplicationWindow {
 	property var flow
-	property var tran_id
 
 	title: stack.currentItem.title
 	id: dialog
diff --git a/resources/qml/device-verification/Failed.qml b/resources/qml/device-verification/Failed.qml
index 6b5d57ef..fcff7893 100644
--- a/resources/qml/device-verification/Failed.qml
+++ b/resources/qml/device-verification/Failed.qml
@@ -2,23 +2,29 @@ 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 timed out")
+	property string title: qsTr("Verification failed")
 	ColumnLayout {
 		spacing: 16
 		Text {
+			id: content
+
 			Layout.maximumWidth: 400
 			Layout.fillHeight: true
 			Layout.fillWidth: true
+
 			wrapMode: Text.Wrap
-			id: content
 			text: switch (flow.error) {
-				case VerificationStatus.UnknownMethod: return qsTr("Device verification timed out.")
-				case VerificationStatus.MismatchedCommitment: return qsTr("Device verification timed out.")
-				case VerificationStatus.MismatchedSAS: return qsTr("Device verification timed out.")
-				case VerificationStatus.KeyMismatch: return qsTr("Device verification timed out.")
-				case VerificationStatus.Timeout: return qsTr("Device verification timed out.")
-				case VerificationStatus.OutOfOrder: return qsTr("Device verification timed out.")
+				case DeviceVerificationFlow.UnknownMethod: return qsTr("Other client does not support our verification protocol.")
+				case DeviceVerificationFlow.MismatchedCommitment:
+				case DeviceVerificationFlow.MismatchedSAS:
+				case DeviceVerificationFlow.KeyMismatch: return qsTr("Key mismatch detected!")
+				case DeviceVerificationFlow.Timeout: return qsTr("Device verification timed out.")
+				case DeviceVerificationFlow.User: return qsTr("Other party canceled the verification.")
+				case DeviceVerificationFlow.OutOfOrder: return qsTr("Device verification timed out.")
+				default: return "Unknown verification error.";
 			}
 			color:colors.text
 			verticalAlignment: Text.AlignVCenter
@@ -31,11 +37,7 @@ Pane {
 				Layout.alignment: Qt.AlignRight
 				text: qsTr("Close")
 
-				onClicked: {
-					deviceVerificationList.remove(tran_id);
-					flow.deleteFlow();
-					dialog.close()
-				}
+				onClicked: dialog.close()
 			}
 		}
 	}
diff --git a/resources/qml/device-verification/Waiting.qml b/resources/qml/device-verification/Waiting.qml
index f36910e7..38abf767 100644
--- a/resources/qml/device-verification/Waiting.qml
+++ b/resources/qml/device-verification/Waiting.qml
@@ -20,12 +20,12 @@ Pane {
 				case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.")
 			}
 
-			color:colors.text
+			color: colors.text
 			verticalAlignment: Text.AlignVCenter
 		}
 		BusyIndicator {
 			Layout.alignment: Qt.AlignHCenter
-			palette: color
+			palette: colors
 		}
 		RowLayout {
 			Button {
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 99fd7bed..79f1de84 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -64,7 +64,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
         }
 
         connect(timeout, &QTimer::timeout, this, [this]() {
-                this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
+                if (state_ != Success && state_ != Failed)
+                        this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
         });
 
         connect(ChatPage::instance(),
@@ -114,7 +115,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                         return;
                         }
                         error_ = User;
-                        emit errorChanged();
+                        Emit errorChanged();
                         setState(Failed);
                 });
 
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 1fe3919b..a1ceaf80 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -58,7 +58,7 @@ class DeviceVerificationFlow : public QObject
         Q_OBJECT
         // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
         Q_PROPERTY(QString state READ state NOTIFY stateChanged)
-        Q_PROPERTY(Error error READ error CONSTANT)
+        Q_PROPERTY(Error error READ error NOTIFY errorChanged)
         Q_PROPERTY(QString userId READ getUserId CONSTANT)
         Q_PROPERTY(QString deviceId READ getDeviceId CONSTANT)
         Q_PROPERTY(bool sender READ getSender CONSTANT)
@@ -203,7 +203,7 @@ private:
         mtx::common::RelatesTo relation;
 
         State state_ = PromptStartVerification;
-        Error error_;
+        Error error_ = UnknownMethod;
 
         bool isMacVerified = false;
 
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 250cd5f0..ed720056 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -396,6 +396,18 @@ TimelineViewManager::verifyUser(QString userid)
           tr("No share room with this user found. Create an "
              "encrypted room with this user and try again."));
 }
+
+void
+TimelineViewManager::removeVerificationFlow(DeviceVerificationFlow *flow)
+{
+        for (auto it = dvList.keyValueBegin(); it != dvList.keyValueEnd(); ++it) {
+                if (it->second == flow) {
+                        dvList.remove(it->first);
+                        return;
+                }
+        }
+}
+
 void
 TimelineViewManager::verifyDevice(QString userid, QString deviceid)
 {
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 12e49080..a8bd2e06 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -62,6 +62,7 @@ public:
         Q_INVOKABLE void openMemberListDialog() const;
         Q_INVOKABLE void openLeaveRoomDialog() const;
         Q_INVOKABLE void openRoomSettings() const;
+        Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow);
 
         void verifyUser(QString userid);
         void verifyDevice(QString userid, QString deviceid);
-- 
cgit 1.5.1


From 64d5a193f1059cac8225df7b7c3c9eaedfcfa41a Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Tue, 6 Oct 2020 17:02:41 +0200
Subject: Fix in room verification

---
 .../qml/device-verification/DeviceVerification.qml |   2 +
 src/DeviceVerificationFlow.cpp                     |  75 ++++++++-----
 src/DeviceVerificationFlow.h                       |   7 +-
 src/timeline/EventStore.cpp                        | 118 +++++++++------------
 src/timeline/EventStore.h                          |   8 +-
 5 files changed, 103 insertions(+), 107 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index 64f10b35..d6185a01 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -7,6 +7,8 @@ import im.nheko 1.0
 ApplicationWindow {
 	property var flow
 
+	onClosing: TimelineManager.removeVerificationFlow(flow)
+
 	title: stack.currentItem.title
 	id: dialog
 
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 79f1de84..549569f4 100644
--- a/src/DeviceVerificationFlow.cpp
+++ b/src/DeviceVerificationFlow.cpp
@@ -13,6 +13,15 @@ static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes
 
 namespace msgs = mtx::events::msg;
 
+static 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);
+
 DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                                DeviceVerificationFlow::Type flow_type,
                                                TimelineModel *model,
@@ -45,11 +54,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                           return;
                   }
 
-                  for (const auto &[algorithm, key] :
-                       res.device_keys.at(deviceId.toStdString()).keys) {
-                          // TODO: Verify Signatures
-                          this->device_keys[algorithm] = key;
-                  }
+                  this->their_keys = res;
           });
 
         if (model) {
@@ -115,7 +120,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                         return;
                         }
                         error_ = User;
-                        Emit errorChanged();
+                        emit errorChanged();
                         setState(Failed);
                 });
 
@@ -160,6 +165,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                                        "|" + this->transaction_id;
                         }
 
+                        nhlog::ui()->info("Info is: '{}'", info);
+
                         if (this->sender == false) {
                                 this->sendVerificationKey();
                         } else {
@@ -193,28 +200,40 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
                           if (msg.relates_to.value().event_id != this->relation.event_id)
                                   return;
                   }
-                  std::string info = "MATRIX_KEY_VERIFICATION_MAC" + this->toClient.to_string() +
-                                     this->deviceId.toStdString() +
-                                     http::client()->user_id().to_string() +
-                                     http::client()->device_id() + this->transaction_id;
 
-                  std::vector key_list;
+                  std::map key_list;
                   std::string key_string;
-                  for (auto mac : msg.mac) {
-                          key_string += mac.first + ",";
-                          if (device_keys[mac.first] != "") {
-                                  if (mac.second ==
-                                      this->sas->calculate_mac(this->device_keys[mac.first],
-                                                               info + mac.first)) {
-                                  } else {
-                                          this->cancelVerification(
-                                            DeviceVerificationFlow::Error::KeyMismatch);
-                                          return;
-                                  }
+                  for (const auto &mac : msg.mac) {
+                          for (const auto &[deviceid, key] : their_keys.device_keys)
+                                  if (key.keys.count(mac.first))
+                                          key_list[mac.first] = key.keys.at(mac.first);
+
+                          if (their_keys.master_keys.keys.count(mac.first))
+                                  key_list[mac.first] = their_keys.master_keys.keys[mac.first];
+                          if (their_keys.user_signing_keys.keys.count(mac.first))
+                                  key_list[mac.first] =
+                                    their_keys.user_signing_keys.keys[mac.first];
+                          if (their_keys.self_signing_keys.keys.count(mac.first))
+                                  key_list[mac.first] =
+                                    their_keys.self_signing_keys.keys[mac.first];
+                  }
+                  auto macs = key_verification_mac(sas.get(),
+                                                   toClient,
+                                                   this->deviceId.toStdString(),
+                                                   http::client()->user_id(),
+                                                   http::client()->device_id(),
+                                                   this->transaction_id,
+                                                   key_list);
+
+                  for (const auto &[key, mac] : macs.mac) {
+                          if (mac != msg.mac.at(key)) {
+                                  this->cancelVerification(
+                                    DeviceVerificationFlow::Error::KeyMismatch);
+                                  return;
                           }
                   }
-                  key_string = key_string.substr(0, key_string.length() - 1);
-                  if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) {
+
+                  if (msg.keys == macs.keys) {
                           this->isMacVerified = true;
                           this->acceptDevice();
                   } else {
@@ -630,9 +649,13 @@ DeviceVerificationFlow::NewInRoomVerification(QObject *parent_,
                                               QString event_id_)
 {
         QSharedPointer flow(
-          new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, other_user_, ""));
+          new DeviceVerificationFlow(parent_,
+                                     Type::RoomMsg,
+                                     timelineModel_,
+                                     other_user_,
+                                     QString::fromStdString(msg.from_device)));
 
-        flow->event_id = event_id_.toStdString();
+        flow->setEventId(event_id_.toStdString());
 
         if (std::find(msg.methods.begin(),
                       msg.methods.end(),
diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index a1ceaf80..9ca45a94 100644
--- a/src/DeviceVerificationFlow.h
+++ b/src/DeviceVerificationFlow.h
@@ -56,7 +56,6 @@ using sas_ptr = std::unique_ptr;
 class DeviceVerificationFlow : public QObject
 {
         Q_OBJECT
-        // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
         Q_PROPERTY(QString state READ state NOTIFY stateChanged)
         Q_PROPERTY(Error error READ error NOTIFY errorChanged)
         Q_PROPERTY(QString userId READ getUserId CONSTANT)
@@ -179,11 +178,7 @@ private:
         //! Completes the verification flow
         void acceptDevice();
 
-        // for to_device messages
         std::string transaction_id;
-        // for room messages
-        std::optional room_id;
-        std::optional event_id;
 
         bool sender;
         Type type;
@@ -198,7 +193,7 @@ private:
         nlohmann::json canonical_json;
 
         std::vector sasList;
-        std::map device_keys;
+        UserKeyCache their_keys;
         TimelineModel *model_;
         mtx::common::RelatesTo relation;
 
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index dc92a37f..141d2ece 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -280,8 +280,6 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
                         }
                 }
 
-                handle_room_verification(event);
-
                 // decrypting and checking some encrypted messages
                 if (auto encrypted =
                       std::get_if>(
@@ -292,81 +290,65 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
                               [](auto e) { return (e.sender != utils::localUser().toStdString()); },
                               *d_event)) {
                                 handle_room_verification(*d_event);
-                        } 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()->receivedDeviceVerificationReady(
-                                          msg->content);
-                                }
                         }
+                        // 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()->receivedDeviceVerificationReady(
+                        //                  msg->content);
+                        //        }
+                        //}
                 }
         }
+}
 
-        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 = {};
-                }
-        }
+namespace {
+template
+struct overloaded : Ts...
+{
+        using Ts::operator()...;
+};
+template
+overloaded(Ts...) -> overloaded;
 }
 
 void
 EventStore::handle_room_verification(mtx::events::collections::TimelineEvents event)
 {
-        if (std::get_if>(&event)) {
-                auto msg =
-                  std::get>(event);
-                last_verification_request_event = msg;
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                last_verification_cancel_event = msg;
-                ChatPage::instance()->receivedDeviceVerificationCancel(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationAccept(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationKey(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationMac(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationReady(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationDone(msg.content);
-                return;
-        } else if (std::get_if>(
-                     &event)) {
-                auto msg =
-                  std::get>(event);
-                ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender);
-                return;
-        }
+        std::visit(
+          overloaded{
+            [this](const mtx::events::RoomEvent &msg) {
+                    emit startDMVerification(msg);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationCancel(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationAccept(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationKey(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationMac(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationReady(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationDone(msg.content);
+            },
+            [](const mtx::events::RoomEvent &msg) {
+                    ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender);
+            },
+            [](const auto &) {},
+          },
+          event);
 }
 
 QVariantList
diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 89a51477..954e271c 100644
--- a/src/timeline/EventStore.h
+++ b/src/timeline/EventStore.h
@@ -99,7 +99,7 @@ signals:
         void messageSent(std::string txn_id, std::string event_id);
         void messageFailed(std::string txn_id);
         void startDMVerification(
-          mtx::events::RoomEvent &msg);
+          const mtx::events::RoomEvent &msg);
         void updateFlowEventId(std::string event_id);
 
 public slots:
@@ -123,10 +123,4 @@ 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;
 };
-- 
cgit 1.5.1


From 7b6fab33731e369a860ab217709190e9457d6d76 Mon Sep 17 00:00:00 2001
From: Nicolas Werner 
Date: Wed, 7 Oct 2020 23:03:14 +0200
Subject: Calculate verification status from cross-signing sigs and update
 dynamically

---
 resources/qml/UserProfile.qml                      |   3 +-
 .../qml/device-verification/DeviceVerification.qml |   4 +-
 src/Cache.cpp                                      | 178 +++++++++++++++++----
 src/Cache.h                                        |   8 +-
 src/CacheCryptoStructs.h                           |  21 ++-
 src/Cache_p.h                                      |  11 +-
 src/timeline/TimelineModel.cpp                     |   2 +-
 src/ui/UserProfile.cpp                             | 145 +++++------------
 src/ui/UserProfile.h                               |   8 +-
 9 files changed, 223 insertions(+), 157 deletions(-)

(limited to 'resources/qml/device-verification/DeviceVerification.qml')

diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index e7dcc777..562dd4f9 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -56,7 +56,7 @@ ApplicationWindow{
 
 		Button {
 			id: verifyUserButton
-			text: "Verify"
+			text: qsTr("Verify")
 			Layout.alignment: Qt.AlignHCenter
 			enabled: !profile.isUserVerified
 			visible: !profile.isUserVerified
@@ -155,7 +155,6 @@ ApplicationWindow{
 					onClicked: {
 						if(model.verificationStatus == VerificationStatus.VERIFIED){
 							profile.unverify(model.deviceId)
-							deviceVerificationList.updateProfile(newFlow.userId);
 						}else{
 							profile.verify(model.deviceId);
 						}
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index d6185a01..2e8f7504 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -1,6 +1,6 @@
-import QtQuick 2.3
+import QtQuick 2.10
 import QtQuick.Controls 2.10
-import QtQuick.Window 2.2
+import QtQuick.Window 2.10
 
 import im.nheko 1.0
 
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 63f6e426..d6da03c6 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -3184,6 +3184,28 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query
         }
 
         txn.commit();
+
+        std::map tmp;
+        const auto local_user = utils::localUser().toStdString();
+
+        {
+                std::unique_lock lock(verification_storage.verification_storage_mtx);
+                for (auto &[user_id, update] : updates) {
+                        if (user_id == local_user) {
+                                std::swap(tmp, verification_storage.status);
+                        } else {
+                                verification_storage.status.erase(user_id);
+                        }
+                }
+        }
+        for (auto &[user_id, update] : updates) {
+                if (user_id == local_user) {
+                        for (const auto &[user, status] : tmp)
+                                emit verificationStatusChanged(user);
+                } else {
+                        emit verificationStatusChanged(user_id);
+                }
+        }
 }
 
 void
@@ -3236,23 +3258,19 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn,
 void
 to_json(json &j, const VerificationCache &info)
 {
-        j["verified_master_key"] = info.verified_master_key;
-        j["cross_verified"]      = info.cross_verified;
-        j["device_verified"]     = info.device_verified;
-        j["device_blocked"]      = info.device_blocked;
+        j["device_verified"] = info.device_verified;
+        j["device_blocked"]  = info.device_blocked;
 }
 
 void
 from_json(const json &j, VerificationCache &info)
 {
-        info.verified_master_key = j.at("verified_master_key");
-        info.cross_verified      = j.at("cross_verified").get>();
-        info.device_verified     = j.at("device_verified").get>();
-        info.device_blocked      = j.at("device_blocked").get>();
+        info.device_verified = j.at("device_verified").get>();
+        info.device_blocked  = j.at("device_blocked").get>();
 }
 
 std::optional
-Cache::verificationStatus(const std::string &user_id)
+Cache::verificationCache(const std::string &user_id)
 {
         lmdb::val verifiedVal;
 
@@ -3298,6 +3316,23 @@ Cache::markDeviceVerified(const std::string &user_id, const std::string &key)
                 txn.commit();
         } catch (std::exception &) {
         }
+
+        const auto local_user = utils::localUser().toStdString();
+        std::map tmp;
+        {
+                std::unique_lock lock(verification_storage.verification_storage_mtx);
+                if (user_id == local_user) {
+                        std::swap(tmp, verification_storage.status);
+                } else {
+                        verification_storage.status.erase(user_id);
+                }
+        }
+        if (user_id == local_user) {
+                for (const auto &[user, status] : tmp)
+                        emit verificationStatusChanged(user);
+        } else {
+                emit verificationStatusChanged(user_id);
+        }
 }
 
 void
@@ -3325,27 +3360,112 @@ Cache::markDeviceUnverified(const std::string &user_id, const std::string &key)
                 txn.commit();
         } catch (std::exception &) {
         }
+
+        const auto local_user = utils::localUser().toStdString();
+        std::map tmp;
+        {
+                std::unique_lock lock(verification_storage.verification_storage_mtx);
+                if (user_id == local_user) {
+                        std::swap(tmp, verification_storage.status);
+                } else {
+                        verification_storage.status.erase(user_id);
+                }
+        }
+        if (user_id == local_user) {
+                for (const auto &[user, status] : tmp)
+                        emit verificationStatusChanged(user);
+        } else {
+                emit verificationStatusChanged(user_id);
+        }
 }
 
-void
-Cache::markMasterKeyVerified(const std::string &user_id, const std::string &key)
+VerificationStatus
+Cache::verificationStatus(const std::string &user_id)
 {
-        lmdb::val val;
+        std::unique_lock lock(verification_storage.verification_storage_mtx);
+        if (verification_storage.status.count(user_id))
+                return verification_storage.status.at(user_id);
 
-        auto txn = lmdb::txn::begin(env_);
-        auto db  = getVerificationDb(txn);
+        VerificationStatus status;
+
+        if (auto verifCache = verificationCache(user_id)) {
+                status.verified_devices = verifCache->device_verified;
+        }
+
+        const auto local_user = utils::localUser().toStdString();
+
+        if (user_id == local_user)
+                status.verified_devices.push_back(http::client()->device_id());
+
+        verification_storage.status[user_id] = status;
+
+        auto verifyAtLeastOneSig = [](const auto &toVerif,
+                                      const std::map &keys,
+                                      const std::string &keyOwner) {
+                if (!toVerif.signatures.count(keyOwner))
+                        return false;
+
+                for (const auto &[key_id, signature] : toVerif.signatures.at(keyOwner)) {
+                        if (!keys.count(key_id))
+                                continue;
+
+                        if (mtx::crypto::ed25519_verify_signature(
+                              keys.at(key_id), json(toVerif), signature))
+                                return true;
+                }
+                return false;
+        };
 
         try {
-                VerificationCache verified_state;
-                auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val);
-                if (res) {
-                        verified_state = json::parse(std::string_view(val.data(), val.size()));
+                // for local user verify this device_key -> our master_key -> our self_signing_key
+                // -> our device_keys
+                //
+                // for other user verify this device_key -> our master_key -> our user_signing_key
+                // -> their master_key -> their self_signing_key -> their device_keys
+                //
+                // This means verifying the other user adds 2 extra steps,verifying our user_signing
+                // key and their master key
+                auto ourKeys   = userKeys(local_user);
+                auto theirKeys = userKeys(user_id);
+                if (!ourKeys || !theirKeys)
+                        return status;
+
+                if (!mtx::crypto::ed25519_verify_signature(
+                      olm::client()->identity_keys().ed25519,
+                      json(ourKeys->master_keys),
+                      ourKeys->master_keys.signatures.at(local_user)
+                        .at("ed25519:" + http::client()->device_id())))
+                        return status;
+
+                auto master_keys = ourKeys->master_keys.keys;
+
+                if (user_id != local_user) {
+                        if (!verifyAtLeastOneSig(
+                              ourKeys->user_signing_keys, master_keys, local_user))
+                                return status;
+
+                        if (!verifyAtLeastOneSig(
+                              theirKeys->master_keys, ourKeys->user_signing_keys.keys, local_user))
+                                return status;
+
+                        master_keys = theirKeys->master_keys.keys;
                 }
 
-                verified_state.verified_master_key = key;
-                lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump()));
-                txn.commit();
+                status.user_verified = true;
+
+                if (!verifyAtLeastOneSig(theirKeys->self_signing_keys, master_keys, user_id))
+                        return status;
+
+                for (const auto &[device, device_key] : theirKeys->device_keys) {
+                        if (verifyAtLeastOneSig(
+                              device_key, theirKeys->self_signing_keys.keys, user_id))
+                                status.verified_devices.push_back(device_key.device_id);
+                }
+
+                verification_storage.status[user_id] = status;
+                return status;
         } catch (std::exception &) {
+                return status;
         }
 }
 
@@ -3551,28 +3671,22 @@ updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &k
 }
 
 // device & user verification cache
-std::optional
+std::optional
 verificationStatus(const std::string &user_id)
 {
         return instance_->verificationStatus(user_id);
 }
 
 void
-markDeviceVerified(const std::string &user_id, const std::string &key)
-{
-        instance_->markDeviceVerified(user_id, key);
-}
-
-void
-markDeviceUnverified(const std::string &user_id, const std::string &key)
+markDeviceVerified(const std::string &user_id, const std::string &device)
 {
-        instance_->markDeviceUnverified(user_id, key);
+        instance_->markDeviceVerified(user_id, device);
 }
 
 void
-markMasterKeyVerified(const std::string &user_id, const std::string &key)
+markDeviceUnverified(const std::string &user_id, const std::string &device)
 {
-        instance_->markMasterKeyVerified(user_id, key);
+        instance_->markDeviceUnverified(user_id, device);
 }
 
 std::vector
diff --git a/src/Cache.h b/src/Cache.h
index fca80145..cd96708e 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -67,14 +67,12 @@ void
 updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery);
 
 // device & user verification cache
-std::optional
+std::optional
 verificationStatus(const std::string &user_id);
 void
-markDeviceVerified(const std::string &user_id, const std::string &key);
+markDeviceVerified(const std::string &user_id, const std::string &device);
 void
-markDeviceUnverified(const std::string &user_id, const std::string &key);
-void
-markMasterKeyVerified(const std::string &user_id, const std::string &key);
+markDeviceUnverified(const std::string &user_id, const std::string &device);
 
 //! Load saved data for the display names & avatars.
 void
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 10636ac6..935d6493 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -66,6 +66,23 @@ struct OlmSessionStorage
         std::mutex group_inbound_mtx;
 };
 
+//! Verification status of a single user
+struct VerificationStatus
+{
+        //! True, if the users master key is verified
+        bool user_verified = false;
+        //! List of all devices marked as verified
+        std::vector verified_devices;
+};
+
+//! In memory cache of verification status
+struct VerificationStorage
+{
+        //! mapping of user to verification status
+        std::map status;
+        std::mutex verification_storage_mtx;
+};
+
 // this will store the keys of the user with whom a encrypted room is shared with
 struct UserKeyCache
 {
@@ -90,12 +107,8 @@ struct VerificationCache
 {
         //! list of verified device_ids with device-verification
         std::vector device_verified;
-        //! list of verified device_ids with cross-signing, calculated from master key
-        std::vector cross_verified;
         //! list of devices the user blocks
         std::vector device_blocked;
-        //! The verified master key.
-        std::string verified_master_key;
 };
 
 void
diff --git a/src/Cache_p.h b/src/Cache_p.h
index b37eae58..b3f4c58c 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -67,10 +67,9 @@ public:
                             const std::vector &user_ids);
 
         // device & user verification cache
-        std::optional verificationStatus(const std::string &user_id);
-        void markDeviceVerified(const std::string &user_id, const std::string &key);
-        void markDeviceUnverified(const std::string &user_id, const std::string &key);
-        void markMasterKeyVerified(const std::string &user_id, const std::string &key);
+        VerificationStatus verificationStatus(const std::string &user_id);
+        void markDeviceVerified(const std::string &user_id, const std::string &device);
+        void markDeviceUnverified(const std::string &user_id, const std::string &device);
 
         static void removeDisplayName(const QString &room_id, const QString &user_id);
         static void removeAvatarUrl(const QString &room_id, const QString &user_id);
@@ -283,6 +282,7 @@ signals:
         void removeNotification(const QString &room_id, const QString &event_id);
         void userKeysUpdate(const std::string &sync_token,
                             const mtx::responses::QueryKeys &keyQuery);
+        void verificationStatusChanged(const std::string &userid);
 
 private:
         //! Save an invited room.
@@ -576,6 +576,8 @@ private:
                 return QString::fromStdString(event.state_key);
         }
 
+        std::optional verificationCache(const std::string &user_id);
+
         void setNextBatchToken(lmdb::txn &txn, const std::string &token);
         void setNextBatchToken(lmdb::txn &txn, const QString &token);
 
@@ -600,6 +602,7 @@ private:
         static QHash AvatarUrls;
 
         OlmSessionStorage session_storage;
+        VerificationStorage verification_storage;
 };
 
 namespace cache {
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index e8d381df..359e95bc 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -1031,7 +1031,7 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent msg, mtx::events::
 
                                           try {
                                                   if (!mtx::crypto::verify_identity_signature(
-                                                        json(dev.second), device_id, user_id)) {
+                                                        dev.second, device_id, user_id)) {
                                                           nhlog::crypto()->warn(
                                                             "failed to verify identity keys: {}",
                                                             json(dev.second).dump(2));
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 2a1eecdf..2bb0370f 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,5 +1,5 @@
 #include "UserProfile.h"
-#include "Cache.h"
+#include "Cache_p.h"
 #include "ChatPage.h"
 #include "DeviceVerificationFlow.h"
 #include "Logging.h"
@@ -8,8 +8,6 @@
 #include "timeline/TimelineModel.h"
 #include "timeline/TimelineViewManager.h"
 
-#include  // only for debugging
-
 UserProfile::UserProfile(QString roomid,
                          QString userid,
                          TimelineViewManager *manager_,
@@ -21,6 +19,31 @@ UserProfile::UserProfile(QString roomid,
   , model(parent)
 {
         fetchDeviceList(this->userid_);
+
+        connect(cache::client(),
+                &Cache::verificationStatusChanged,
+                this,
+                [this](const std::string &user_id) {
+                        if (user_id != this->userid_.toStdString())
+                                return;
+
+                        auto status = cache::verificationStatus(user_id);
+                        if (!status)
+                                return;
+                        this->isUserVerified = status->user_verified;
+                        emit userStatusChanged();
+
+                        for (auto &deviceInfo : deviceList_.deviceList_) {
+                                deviceInfo.verification_status =
+                                  std::find(status->verified_devices.begin(),
+                                            status->verified_devices.end(),
+                                            deviceInfo.device_id.toStdString()) ==
+                                      status->verified_devices.end()
+                                    ? verification::UNVERIFIED
+                                    : verification::VERIFIED;
+                        }
+                        deviceList_.reset(deviceList_.deviceList_);
+                });
 }
 
 QHash
@@ -126,107 +149,27 @@ UserProfile::fetchDeviceList(const QString &userID)
                             }
 
                             std::vector deviceInfo;
-                            auto devices         = other_user_keys.device_keys;
-                            auto device_verified = cache::verificationStatus(other_user_id);
-
-                            if (device_verified.has_value()) {
-                                    // TODO: properly check cross-signing signatures here
-                                    isUserVerified = !device_verified->verified_master_key.empty();
-                            }
-
-                            std::optional lmk, lsk, luk, mk, sk, uk;
+                            auto devices = other_user_keys.device_keys;
+                            auto verificationStatus =
+                              cache::client()->verificationStatus(other_user_id);
 
-                            lmk = res.master_keys;
-                            luk = res.user_signing_keys;
-                            lsk = res.self_signing_keys;
-                            mk  = other_user_keys.master_keys;
-                            uk  = other_user_keys.user_signing_keys;
-                            sk  = other_user_keys.self_signing_keys;
-
-                            // First checking if the user is verified
-                            if (luk.has_value() && mk.has_value()) {
-                                    // iterating through the public key of local user_signing keys
-                                    for (auto sign_key : luk.value().keys) {
-                                            // checking if the signatures are empty as "at" could
-                                            // cause exceptions
-                                            auto signs = mk->signatures;
-                                            if (!signs.empty() &&
-                                                signs.find(local_user_id) != signs.end()) {
-                                                    auto sign = signs.at(local_user_id);
-                                                    try {
-                                                            isUserVerified =
-                                                              isUserVerified ||
-                                                              (olm::client()->ed25519_verify_sig(
-                                                                sign_key.second,
-                                                                json(mk.value()),
-                                                                sign.at(sign_key.first)));
-                                                    } catch (std::out_of_range &) {
-                                                            isUserVerified =
-                                                              isUserVerified || false;
-                                                    }
-                                            }
-                                    }
-                            }
+                            isUserVerified = verificationStatus.user_verified;
+                            emit userStatusChanged();
 
                             for (const auto &d : devices) {
                                     auto device = d.second;
                                     verification::Status verified =
                                       verification::Status::UNVERIFIED;
 
-                                    if (device_verified.has_value()) {
-                                            if (std::find(device_verified->cross_verified.begin(),
-                                                          device_verified->cross_verified.end(),
-                                                          d.first) !=
-                                                device_verified->cross_verified.end())
-                                                    verified = verification::Status::VERIFIED;
-                                            if (std::find(device_verified->device_verified.begin(),
-                                                          device_verified->device_verified.end(),
-                                                          d.first) !=
-                                                device_verified->device_verified.end())
-                                                    verified = verification::Status::VERIFIED;
-                                            if (std::find(device_verified->device_blocked.begin(),
-                                                          device_verified->device_blocked.end(),
-                                                          d.first) !=
-                                                device_verified->device_blocked.end())
-                                                    verified = verification::Status::BLOCKED;
-                                    } else if (isUserVerified) {
-                                            device_verified = VerificationCache{};
-                                    }
-
-                                    // won't check for already verified devices
-                                    if (verified != verification::Status::VERIFIED &&
-                                        isUserVerified) {
-                                            if ((sk.has_value()) && (!device.signatures.empty())) {
-                                                    for (auto sign_key : sk.value().keys) {
-                                                            auto signs =
-                                                              device.signatures.at(other_user_id);
-                                                            try {
-                                                                    if (olm::client()
-                                                                          ->ed25519_verify_sig(
-                                                                            sign_key.second,
-                                                                            json(device),
-                                                                            signs.at(
-                                                                              sign_key.first))) {
-                                                                            verified =
-                                                                              verification::Status::
-                                                                                VERIFIED;
-                                                                            device_verified.value()
-                                                                              .cross_verified
-                                                                              .push_back(d.first);
-                                                                    }
-                                                            } catch (std::out_of_range &) {
-                                                            }
-                                                    }
-                                            }
-                                    }
-
-                                    // TODO(Nico): properly show cross-signing
-                                    // if (device_verified.has_value()) {
-                                    //        device_verified.value().is_user_verified =
-                                    //          isUserVerified;
-                                    //        cache::setVerifiedCache(user_id,
-                                    //                                device_verified.value());
-                                    //}
+                                    if (std::find(verificationStatus.verified_devices.begin(),
+                                                  verificationStatus.verified_devices.end(),
+                                                  device.device_id) !=
+                                          verificationStatus.verified_devices.end() &&
+                                        mtx::crypto::verify_identity_signature(
+                                          device,
+                                          DeviceId(device.device_id),
+                                          UserId(other_user_id)))
+                                            verified = verification::Status::VERIFIED;
 
                                     deviceInfo.push_back(
                                       {QString::fromStdString(d.first),
@@ -235,14 +178,6 @@ UserProfile::fetchDeviceList(const QString &userID)
                                        verified});
                             }
 
-                            std::cout << (isUserVerified ? "Yes" : "No") << std::endl;
-
-                            std::sort(deviceInfo.begin(),
-                                      deviceInfo.end(),
-                                      [](const DeviceInfo &a, const DeviceInfo &b) {
-                                              return a.device_id > b.device_id;
-                                      });
-
                             this->deviceList_.queueReset(std::move(deviceInfo));
                     });
           });
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 18933727..77b22323 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -74,6 +74,8 @@ public slots:
 
 private:
         std::vector deviceList_;
+
+        friend class UserProfile;
 };
 
 class UserProfile : public QObject
@@ -83,7 +85,7 @@ class UserProfile : public QObject
         Q_PROPERTY(QString userid READ userid CONSTANT)
         Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
         Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
-        Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
+        Q_PROPERTY(bool isUserVerified READ getUserStatus NOTIFY userStatusChanged)
 public:
         UserProfile(QString roomid,
                     QString userid,
@@ -105,9 +107,11 @@ public:
         Q_INVOKABLE void kickUser();
         Q_INVOKABLE void startChat();
 
+signals:
+        void userStatusChanged();
+
 private:
         QString roomid_, userid_;
-        std::optional cross_verified;
         DeviceInfoModel deviceList_;
         bool isUserVerified = false;
         TimelineViewManager *manager;
-- 
cgit 1.5.1