From 2e77a1554f1572b7c7e59f8177a48e5dffa16c23 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 26 Jan 2021 22:36:35 +0100 Subject: [PATCH] Switch to new relations format --- CMakeLists.txt | 2 +- io.github.NhekoReborn.Nheko.json | 3 +- src/Cache.cpp | 72 ++++++++++++---------------- src/DeviceVerificationFlow.cpp | 34 ++++++------- src/DeviceVerificationFlow.h | 10 ++-- src/EventAccessors.cpp | 35 ++++---------- src/EventAccessors.h | 6 +-- src/Olm.cpp | 27 ++++------- src/timeline/EventStore.cpp | 37 +++++++------- src/timeline/InputBar.cpp | 18 ++++--- src/timeline/TimelineModel.cpp | 4 +- src/timeline/TimelineViewManager.cpp | 8 ++-- 12 files changed, 112 insertions(+), 144 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5245ef8..cf2b5959 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,7 +356,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG v0.4.1 + GIT_TAG 70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json index e6eeb123..98ab9629 100644 --- a/io.github.NhekoReborn.Nheko.json +++ b/io.github.NhekoReborn.Nheko.json @@ -220,8 +220,7 @@ "name": "mtxclient", "sources": [ { - "commit": "4951190c938740defa0988d98d5e861038622936", - "tag": "v0.4.1", + "commit": "70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b", "type": "git", "url": "https://github.com/Nheko-Reborn/mtxclient.git" } diff --git a/src/Cache.cpp b/src/Cache.cpp index 3f2bf73a..94b9a6a6 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2713,23 +2713,19 @@ Cache::saveTimelineMessages(lmdb::txn &txn, lmdb::dbi_put(txn, evToOrderDb, event_id, txn_order); lmdb::dbi_del(txn, evToOrderDb, lmdb::val(txn_id)); - if (event.contains("content") && - event["content"].contains("m.relates_to")) { - auto temp = event["content"]["m.relates_to"]; - json relates_to_j = temp.contains("m.in_reply_to") && - temp["m.in_reply_to"].is_object() - ? temp["m.in_reply_to"]["event_id"] - : temp["event_id"]; - std::string relates_to = - relates_to_j.is_string() ? relates_to_j.get() : ""; - - if (!relates_to.empty()) { - lmdb::dbi_del(txn, - relationsDb, - lmdb::val(relates_to), - lmdb::val(txn_id)); - lmdb::dbi_put( - txn, relationsDb, lmdb::val(relates_to), event_id); + auto relations = mtx::accessors::relations(e); + if (!relations.relations.empty()) { + for (const auto &r : relations.relations) { + if (!r.event_id.empty()) { + lmdb::dbi_del(txn, + relationsDb, + lmdb::val(r.event_id), + lmdb::val(txn_id)); + lmdb::dbi_put(txn, + relationsDb, + lmdb::val(r.event_id), + event_id); + } } } @@ -2808,19 +2804,16 @@ Cache::saveTimelineMessages(lmdb::txn &txn, lmdb::val(&msgIndex, sizeof(msgIndex))); } - if (event.contains("content") && - event["content"].contains("m.relates_to")) { - auto temp = event["content"]["m.relates_to"]; - json relates_to_j = temp.contains("m.in_reply_to") && - temp["m.in_reply_to"].is_object() - ? temp["m.in_reply_to"]["event_id"] - : temp["event_id"]; - std::string relates_to = - relates_to_j.is_string() ? relates_to_j.get() : ""; - - if (!relates_to.empty()) - lmdb::dbi_put( - txn, relationsDb, lmdb::val(relates_to), event_id); + auto relations = mtx::accessors::relations(e); + if (!relations.relations.empty()) { + for (const auto &r : relations.relations) { + if (!r.event_id.empty()) { + lmdb::dbi_put(txn, + relationsDb, + lmdb::val(r.event_id), + event_id); + } + } } } } @@ -2901,17 +2894,14 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message txn, msg2orderDb, event_id, lmdb::val(&msgIndex, sizeof(msgIndex))); } - if (event.contains("content") && event["content"].contains("m.relates_to")) { - auto temp = event["content"]["m.relates_to"]; - json relates_to_j = - temp.contains("m.in_reply_to") && temp["m.in_reply_to"].is_object() - ? temp["m.in_reply_to"]["event_id"] - : temp["event_id"]; - std::string relates_to = - relates_to_j.is_string() ? relates_to_j.get() : ""; - - if (!relates_to.empty()) - lmdb::dbi_put(txn, relationsDb, lmdb::val(relates_to), event_id); + auto relations = mtx::accessors::relations(e); + if (!relations.relations.empty()) { + for (const auto &r : relations.relations) { + if (!r.event_id.empty()) { + lmdb::dbi_put( + txn, relationsDb, lmdb::val(r.event_id), event_id); + } + } } } diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 51ef79fd..c6277a9d 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -105,8 +105,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") && @@ -136,8 +136,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } error_ = User; @@ -152,8 +152,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } @@ -217,8 +217,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } @@ -385,8 +385,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.transaction_id.has_value()) { if (msg.transaction_id.value() != this->transaction_id) return; - } else if ((msg.relates_to.has_value() && sender)) { - if (msg.relates_to.value().event_id != this->relation.event_id) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; else { this->deviceId = QString::fromStdString(msg.from_device); @@ -402,8 +402,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } nhlog::ui()->info("Flow done on other side"); @@ -526,8 +526,8 @@ DeviceVerificationFlow::handleStartMessage(const mtx::events::msg::KeyVerificati 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) + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) return; } if ((std::find(msg.key_agreement_protocols.begin(), @@ -625,8 +625,10 @@ DeviceVerificationFlow::startVerificationRequest() 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); + req.relations.relations.push_back(this->relation); + // Set synthesized to surpress the nheko relation extensions + req.relations.synthesized = true; + this->canonical_json = nlohmann::json(req); } send(req); setState(WaitingForOtherToAccept); diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index 34b78962..6c613545 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -206,7 +206,7 @@ private: std::vector sasList; UserKeyCache their_keys; TimelineModel *model_; - mtx::common::RelatesTo relation; + mtx::common::Relation relation; State state_ = PromptStartVerification; Error error_ = UnknownMethod; @@ -230,8 +230,12 @@ private: static_cast(err->status_code)); }); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - if constexpr (!std::is_same_v) - msg.relates_to = this->relation; + if constexpr (!std::is_same_v) { + msg.relations.relations.push_back(this->relation); + // Set synthesized to surpress the nheko relation extensions + msg.relations.synthesized = true; + } (model_)->sendMessageEvent(msg, mtx::events::to_device_content_to_type); } diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp index 3ae781f0..4218f491 100644 --- a/src/EventAccessors.cpp +++ b/src/EventAccessors.cpp @@ -250,31 +250,17 @@ struct EventFilesize } }; -struct EventInReplyTo +struct EventRelations { template - using related_ev_id_t = decltype(Content::relates_to.in_reply_to.event_id); + using related_ev_id_t = decltype(Content::relations); template - std::string operator()(const mtx::events::Event &e) + mtx::common::Relations operator()(const mtx::events::Event &e) { if constexpr (is_detected::value) { - return e.content.relates_to.in_reply_to.event_id; + return e.content.relations; } - return ""; - } -}; - -struct EventRelatesTo -{ - template - using related_ev_id_t = decltype(Content::relates_to.event_id); - template - std::string operator()(const mtx::events::Event &e) - { - if constexpr (is_detected::value) { - return e.content.relates_to.event_id; - } - return ""; + return {}; } }; @@ -434,15 +420,10 @@ mtx::accessors::mimetype(const mtx::events::collections::TimelineEvents &event) { return std::visit(EventMimeType{}, event); } -std::string -mtx::accessors::in_reply_to_event(const mtx::events::collections::TimelineEvents &event) +mtx::common::Relations +mtx::accessors::relations(const mtx::events::collections::TimelineEvents &event) { - return std::visit(EventInReplyTo{}, event); -} -std::string -mtx::accessors::relates_to_event_id(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit(EventRelatesTo{}, event); + return std::visit(EventRelations{}, event); } std::string diff --git a/src/EventAccessors.h b/src/EventAccessors.h index 0cdc5f89..60912497 100644 --- a/src/EventAccessors.h +++ b/src/EventAccessors.h @@ -53,10 +53,8 @@ std::string blurhash(const mtx::events::collections::TimelineEvents &event); std::string mimetype(const mtx::events::collections::TimelineEvents &event); -std::string -in_reply_to_event(const mtx::events::collections::TimelineEvents &event); -std::string -relates_to_event_id(const mtx::events::collections::TimelineEvents &event); +mtx::common::Relations +relations(const mtx::events::collections::TimelineEvents &event); std::string transaction_id(const mtx::events::collections::TimelineEvents &event); diff --git a/src/Olm.cpp b/src/Olm.cpp index 4ccf8ab9..54be4751 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -575,29 +575,19 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id, if (!sendSessionTo.empty()) olm::send_encrypted_to_device_messages(sendSessionTo, megolm_payload); - mtx::common::ReplyRelatesTo relation; - mtx::common::RelatesTo r_relation; - // relations shouldn't be encrypted... - if (body["content"].contains("m.relates_to")) { - if (body["content"]["m.relates_to"].contains("m.in_reply_to")) { - relation = body["content"]["m.relates_to"]; - } else if (body["content"]["m.relates_to"].contains("event_id")) { - r_relation = body["content"]["m.relates_to"]; - } - } + mtx::common::Relations relations = mtx::common::parse_relations(body["content"]); auto payload = olm::client()->encrypt_group_message(session.get(), body.dump()); // Prepare the m.room.encrypted event. msg::Encrypted data; - data.ciphertext = std::string((char *)payload.data(), payload.size()); - data.sender_key = olm::client()->identity_keys().curve25519; - data.session_id = mtx::crypto::session_id(session.get()); - data.device_id = device_id; - data.algorithm = MEGOLM_ALGO; - data.relates_to = relation; - data.r_relates_to = r_relation; + data.ciphertext = std::string((char *)payload.data(), payload.size()); + data.sender_key = olm::client()->identity_keys().curve25519; + data.session_id = mtx::crypto::session_id(session.get()); + data.device_id = device_id; + data.algorithm = MEGOLM_ALGO; + data.relations = relations; group_session_data.message_index = olm_outbound_group_session_message_index(session.get()); nhlog::crypto()->debug("next message_index {}", group_session_data.message_index); @@ -910,8 +900,7 @@ decryptEvent(const MegolmSessionIndex &index, body["unsigned"] = event.unsigned_data; // relations are unencrypted in content... - if (json old_ev = event; old_ev["content"].count("m.relates_to") != 0) - body["content"]["m.relates_to"] = old_ev["content"]["m.relates_to"]; + mtx::common::add_relations(body["content"], event.content.relations); mtx::events::collections::TimelineEvent te; try { diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index be4bc09e..4a90222f 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -293,16 +293,16 @@ EventStore::handleSync(const mtx::responses::Timeline &events) } for (const auto &event : events.events) { - std::string relates_to; + std::set relates_to; if (auto redaction = std::get_if>( &event)) { // fixup reactions auto redacted = events_by_id_.object({room_id_, redaction->redacts}); if (redacted) { - auto id = mtx::accessors::relates_to_event_id(*redacted); - if (!id.empty()) { - auto idx = idToIndex(id); + auto id = mtx::accessors::relations(*redacted); + if (id.annotates()) { + auto idx = idToIndex(id.annotates()->event_id); if (idx) { events_by_id_.remove( {room_id_, redaction->redacts}); @@ -312,20 +312,17 @@ EventStore::handleSync(const mtx::responses::Timeline &events) } } - relates_to = redaction->redacts; - } else if (auto reaction = - std::get_if>( - &event)) { - relates_to = reaction->content.relates_to.event_id; + relates_to.insert(redaction->redacts); } else { - relates_to = mtx::accessors::in_reply_to_event(event); + for (const auto &r : mtx::accessors::relations(event).relations) + relates_to.insert(r.event_id); } - if (!relates_to.empty()) { - auto idx = cache::client()->getTimelineIndex(room_id_, relates_to); + for (const auto &relates_to_id : relates_to) { + auto idx = cache::client()->getTimelineIndex(room_id_, relates_to_id); if (idx) { - events_by_id_.remove({room_id_, relates_to}); - decryptedEvents_.remove({room_id_, relates_to}); + events_by_id_.remove({room_id_, relates_to_id}); + decryptedEvents_.remove({room_id_, relates_to_id}); events_.remove({room_id_, *idx}); emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx)); } @@ -430,13 +427,14 @@ EventStore::reactions(const std::string &event_id) if (auto reaction = std::get_if>( related_event); - reaction && reaction->content.relates_to.key) { - auto &agg = aggregation[reaction->content.relates_to.key.value()]; + reaction && reaction->content.relations.annotates() && + reaction->content.relations.annotates()->key) { + auto key = reaction->content.relations.annotates()->key.value(); + auto &agg = aggregation[key]; if (agg.count == 0) { Reaction temp{}; - temp.key_ = - QString::fromStdString(reaction->content.relates_to.key.value()); + temp.key_ = QString::fromStdString(key); reactions.push_back(temp); } @@ -691,8 +689,7 @@ EventStore::decryptEvent(const IdIndex &idx, body["unsigned"] = e.unsigned_data; // relations are unencrypted in content... - if (json old_ev = e; old_ev["content"].count("m.relates_to") != 0) - body["content"]["m.relates_to"] = old_ev["content"]["m.relates_to"]; + mtx::common::add_relations(body["content"], e.content.relations); json event_array = json::array(); event_array.push_back(body); diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index b31c1f76..738fb37c 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -294,7 +294,8 @@ InputBar::message(QString msg, MarkdownOverride useMarkdown) text.formatted_body = utils::getFormattedQuoteBody(related, msg.toHtmlEscaped()).toStdString(); - text.relates_to.in_reply_to.event_id = related.related_event; + text.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, related.related_event}); room->resetReply(); } @@ -316,7 +317,8 @@ InputBar::emote(QString msg) } if (!room->reply().isEmpty()) { - emote.relates_to.in_reply_to.event_id = room->reply().toStdString(); + emote.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); room->resetReply(); } @@ -346,7 +348,8 @@ InputBar::image(const QString &filename, image.url = url.toStdString(); if (!room->reply().isEmpty()) { - image.relates_to.in_reply_to.event_id = room->reply().toStdString(); + image.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); room->resetReply(); } @@ -371,7 +374,8 @@ InputBar::file(const QString &filename, file.url = url.toStdString(); if (!room->reply().isEmpty()) { - file.relates_to.in_reply_to.event_id = room->reply().toStdString(); + file.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); room->resetReply(); } @@ -397,7 +401,8 @@ InputBar::audio(const QString &filename, audio.url = url.toStdString(); if (!room->reply().isEmpty()) { - audio.relates_to.in_reply_to.event_id = room->reply().toStdString(); + audio.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); room->resetReply(); } @@ -422,7 +427,8 @@ InputBar::video(const QString &filename, video.url = url.toStdString(); if (!room->reply().isEmpty()) { - video.relates_to.in_reply_to.event_id = room->reply().toStdString(); + video.relations.relations.push_back( + {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); room->resetReply(); } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 968ec3c7..c47194f5 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -360,7 +360,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r const static QRegularExpression replyFallback( ".*", QRegularExpression::DotMatchesEverythingOption); - bool isReply = !in_reply_to_event(event).empty(); + bool isReply = relations(event).reply_to().has_value(); auto formattedBody_ = QString::fromStdString(formatted_body(event)); if (formattedBody_.isEmpty()) { @@ -442,7 +442,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r return cache::isRoomEncrypted(room_id_.toStdString()); } case ReplyTo: - return QVariant(QString::fromStdString(in_reply_to_event(event))); + return QVariant(QString::fromStdString(relations(event).reply_to().value_or(""))); case Reactions: { auto id = event_id(event); return QVariant::fromValue(events.reactions(id)); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 9e045e83..e1e2b681 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -503,9 +503,11 @@ TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QSt // If selfReactedEvent is empty, that means we haven't previously reacted if (selfReactedEvent.isEmpty()) { mtx::events::msg::Reaction reaction; - reaction.relates_to.rel_type = mtx::common::RelationType::Annotation; - reaction.relates_to.event_id = reactedEvent.toStdString(); - reaction.relates_to.key = reactionKey.toStdString(); + mtx::common::Relation rel; + rel.rel_type = mtx::common::RelationType::Annotation; + rel.event_id = reactedEvent.toStdString(); + rel.key = reactionKey.toStdString(); + reaction.relations.relations.push_back(rel); timeline_->sendMessageEvent(reaction, mtx::events::EventType::Reaction); // Otherwise, we have previously reacted and the reaction should be redacted