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 --- src/timeline/EventStore.cpp | 221 ++++++++++++++++----------- src/timeline/EventStore.h | 8 + src/timeline/TimelineModel.cpp | 333 +++++++++++------------------------------ src/timeline/TimelineModel.h | 16 +- 4 files changed, 235 insertions(+), 343 deletions(-) (limited to 'src/timeline') 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; }; -- cgit 1.5.1