diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f83a865..4f6a6a38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,7 +340,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG ad5575bc24089dc385e97d9ace026414b618775c + GIT_TAG da9958e14e035fbf22d498074d381b2ea0092a9d ) FetchContent_MakeAvailable(MatrixClient) else() diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json index 59f4fa46..5f08dee0 100644 --- a/io.github.NhekoReborn.Nheko.json +++ b/io.github.NhekoReborn.Nheko.json @@ -146,7 +146,7 @@ "name": "mtxclient", "sources": [ { - "commit": "ad5575bc24089dc385e97d9ace026414b618775c", + "commit": "da9958e14e035fbf22d498074d381b2ea0092a9d", "type": "git", "url": "https://github.com/Nheko-Reborn/mtxclient.git" } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index e61df263..8b6f1123 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -1249,6 +1249,12 @@ ChatPage::unbanUser(QString userid, QString reason) reason.trimmed().toStdString()); } +void +ChatPage::receivedSessionKey(const std::string &room_id, const std::string &session_id) +{ + view_manager_->receivedSessionKey(room_id, session_id); +} + void ChatPage::sendTypingNotifications() { diff --git a/src/ChatPage.h b/src/ChatPage.h index f0e12ab5..bf649cc9 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -107,6 +107,8 @@ public slots: void banUser(QString userid, QString reason); void unbanUser(QString userid, QString reason); + void receivedSessionKey(const std::string &room_id, const std::string &session_id); + signals: void connectionLost(); void connectionRestored(); diff --git a/src/Olm.cpp b/src/Olm.cpp index 730a3ea5..79b00774 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -388,9 +388,10 @@ import_inbound_megolm_session( return; } - // TODO(Nico): Reload messages encrypted with this key. nhlog::crypto()->info( "established inbound megolm session ({}, {})", roomKey.content.room_id, roomKey.sender); + + ChatPage::instance()->receivedSessionKey(index.room_id, index.session_id); } void @@ -401,48 +402,24 @@ mark_keys_as_published() } void -request_keys(const std::string &room_id, const std::string &event_id) -{ - nhlog::crypto()->info("requesting keys for event {} at {}", event_id, room_id); - - http::client()->get_event( - room_id, - event_id, - [event_id, room_id](const mtx::events::collections::TimelineEvents &res, - mtx::http::RequestErr err) { - using namespace mtx::events; - - if (err) { - nhlog::net()->warn( - "failed to retrieve event {} from {}", event_id, room_id); - return; - } - - if (!std::holds_alternative>(res)) { - nhlog::net()->info( - "retrieved event is not encrypted: {} from {}", event_id, room_id); - return; - } - - olm::send_key_request_for(room_id, std::get>(res)); - }); -} - -void -send_key_request_for(const std::string &room_id, - const mtx::events::EncryptedEvent &e) +send_key_request_for(mtx::events::EncryptedEvent e, + const std::string &request_id, + bool cancel) { using namespace mtx::events; - nhlog::crypto()->debug("sending key request: {}", json(e).dump(2)); + nhlog::crypto()->debug("sending key request: sender_key {}, session_id {}", + e.content.sender_key, + e.content.session_id); mtx::events::msg::KeyRequest request; - request.action = mtx::events::msg::RequestAction::Request; + request.action = !cancel ? mtx::events::msg::RequestAction::Request + : mtx::events::msg::RequestAction::Cancellation; request.algorithm = MEGOLM_ALGO; - request.room_id = room_id; + request.room_id = e.room_id; request.sender_key = e.content.sender_key; request.session_id = e.content.session_id; - request.request_id = "key_request." + http::client()->generate_txn_id(); + request.request_id = request_id; request.requesting_device_id = http::client()->device_id(); nhlog::crypto()->debug("m.room_key_request: {}", json(request).dump(2)); diff --git a/src/Olm.h b/src/Olm.h index ce362e26..322affa1 100644 --- a/src/Olm.h +++ b/src/Olm.h @@ -96,11 +96,9 @@ mark_keys_as_published(); //! Request the encryption keys from sender's device for the given event. void -request_keys(const std::string &room_id, const std::string &event_id); - -void -send_key_request_for(const std::string &room_id, - const mtx::events::EncryptedEvent &); +send_key_request_for(mtx::events::EncryptedEvent e, + const std::string &request_id, + bool cancel = false); void handle_key_request_message(const mtx::events::DeviceEvent &); diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 3564ffc0..22809a20 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -212,6 +212,28 @@ EventStore::clearTimeline() emit endResetModel(); } +void +EventStore::receivedSessionKey(const std::string &session_id) +{ + if (!pending_key_requests.count(session_id)) + return; + + auto request = pending_key_requests.at(session_id); + pending_key_requests.erase(session_id); + + olm::send_key_request_for(request.events.front(), request.request_id, true); + + for (const auto &e : request.events) { + auto idx = idToIndex(e.event_id); + if (idx) { + decryptedEvents_.remove({room_id_, e.event_id}); + events_by_id_.remove({room_id_, e.event_id}); + events_.remove({room_id_, toInternalIdx(*idx)}); + emit dataChanged(*idx, *idx); + } + } +} + void EventStore::handleSync(const mtx::responses::Timeline &events) { @@ -294,18 +316,6 @@ EventStore::handleSync(const mtx::responses::Timeline &events) *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); - // } - //} } } } @@ -501,7 +511,7 @@ EventStore::decryptEvent(const IdIndex &idx, if (decryptionResult.error) { switch (*decryptionResult.error) { - case olm::DecryptionErrorCode::MissingSession: + case olm::DecryptionErrorCode::MissingSession: { dummy.content.body = tr("-- Encrypted Event (No keys found for decryption) --", "Placeholder, when the message was not decrypted yet or can't be " @@ -512,8 +522,21 @@ EventStore::decryptEvent(const IdIndex &idx, index.session_id, e.sender); // TODO: Check if this actually works and look in key backup - olm::send_key_request_for(room_id_, e); + auto copy = e; + copy.room_id = room_id_; + if (pending_key_requests.count(e.content.session_id)) { + pending_key_requests.at(e.content.session_id) + .events.push_back(copy); + } else { + PendingKeyRequests request; + request.request_id = + "key_request." + http::client()->generate_txn_id(); + request.events.push_back(copy); + olm::send_key_request_for(copy, request.request_id); + pending_key_requests[e.content.session_id] = request; + } break; + } case olm::DecryptionErrorCode::DbError: nhlog::db()->critical( "failed to retrieve megolm session with index ({}, {}, {})", diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h index 7f8e2396..2d5fb1be 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h @@ -104,6 +104,7 @@ signals: public slots: void addPending(mtx::events::collections::TimelineEvents event); + void receivedSessionKey(const std::string &session_id); void clearTimeline(); private: @@ -121,6 +122,13 @@ private: static QCache events_; static QCache events_by_id_; + struct PendingKeyRequests + { + std::string request_id; + std::vector> events; + }; + std::map pending_key_requests; + std::string current_txn; int current_txn_error_count = 0; bool noMoreMessages = false; diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 9f250b33..e1fb9196 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -264,6 +264,10 @@ public slots: } void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; } void clearTimeline() { events.clearTimeline(); } + void receivedSessionKey(const std::string &session_key) + { + events.receivedSessionKey(session_key); + } QString roomName() const; QString roomTopic() const; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 783584c9..1e8ed243 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -440,6 +440,15 @@ TimelineViewManager::updateReadReceipts(const QString &room_id, } } +void +TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::string &session_id) +{ + auto room = models.find(QString::fromStdString(room_id)); + if (room != models.end()) { + room.value()->receivedSessionKey(session_id); + } +} + void TimelineViewManager::initWithMessages(const std::map &msgs) { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 5e441562..b6f8b443 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -92,6 +92,7 @@ signals: public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); + void receivedSessionKey(const std::string &room_id, const std::string &session_id); void initWithMessages(const std::map &msgs); void setHistoryView(const QString &room_id);