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: [PATCH] [WIP] Room-Verification Messages --- resources/qml/Reactions.qml | 7 +- resources/qml/TimelineRow.qml | 2 +- resources/qml/TimelineView.qml | 5 - resources/qml/UserProfile.qml | 15 +- .../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(-) diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml index 11109d7..9fc30f6 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 db58eb2..b464b76 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 c6fc385..86b78a1 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 c7dbc9a..9b53ff3 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 8e74d1c..f40a7b8 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 aba1f75..b97b6b3 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 0f521f9..5069ff9 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 bec9f1e..1ad3b1d 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 869687f..24e2f35 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 639cae0..208b20e 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 b5c17d1..28d46e9 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 adf207a..809fe38 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 1b6f999..fb9921d 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 3499384..1eaa9d2 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 3f9cbe6..3d0d298 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,