From 59ad4731ae891ec2295af392be240d147f730d8a Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Tue, 10 Mar 2020 21:59:15 -0700 Subject: [PATCH] modules/m_relation: Add hook to discover and fetch events from m_relates_to. --- matrix/matrix.cc | 1 + modules/Makefile.am | 2 + modules/m_relation.cc | 147 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 modules/m_relation.cc diff --git a/matrix/matrix.cc b/matrix/matrix.cc index 3f457879d..af54a28d3 100644 --- a/matrix/matrix.cc +++ b/matrix/matrix.cc @@ -71,6 +71,7 @@ ircd::m::matrix::module_names "m_presence", "m_profile", "m_receipt", + "m_relation", "m_room_aliases", "m_room_canonical_alias", "m_room_create", diff --git a/modules/Makefile.am b/modules/Makefile.am index d666f7b73..2fc63e993 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -101,6 +101,7 @@ m_noop_la_SOURCES = m_noop.cc m_presence_la_SOURCES = m_presence.cc m_profile_la_SOURCES = m_profile.cc m_receipt_la_SOURCES = m_receipt.cc +m_relation_la_SOURCES = m_relation.cc m_room_aliases_la_SOURCES = m_room_aliases.cc m_room_canonical_alias_la_SOURCES = m_room_canonical_alias.cc m_room_create_la_SOURCES = m_room_create.cc @@ -130,6 +131,7 @@ m_module_LTLIBRARIES = \ m_presence.la \ m_profile.la \ m_receipt.la \ + m_relation.la \ m_room_aliases.la \ m_room_canonical_alias.la \ m_room_create.la \ diff --git a/modules/m_relation.cc b/modules/m_relation.cc new file mode 100644 index 000000000..103942d8a --- /dev/null +++ b/modules/m_relation.cc @@ -0,0 +1,147 @@ +// The Construct +// +// Copyright (C) The Construct Developers, Authors & Contributors +// Copyright (C) 2016-2020 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +namespace ircd::m::relation +{ + static void handle_fetch(const event &, vm::eval &); + extern hookfn fetch_hook; + extern conf::item fetch_timeout; + extern conf::item fetch_enable; +} + +ircd::mapi::header +IRCD_MODULE +{ + "Matrix relations" +}; + +decltype(ircd::m::relation::fetch_enable) +ircd::m::relation::fetch_enable +{ + { "name", "ircd.m.relation.fetch.enable" }, + { "default", true }, +}; + +decltype(ircd::m::relation::fetch_timeout) +ircd::m::relation::fetch_timeout +{ + { "name", "ircd.m.relation.fetch.timeout" }, + { "default", 5L }, +}; + +decltype(ircd::m::relation::fetch_hook) +ircd::m::relation::fetch_hook +{ + handle_fetch, + { + { "_site", "vm.fetch" }, + } +}; + +void +ircd::m::relation::handle_fetch(const event &event, + vm::eval &eval) +try +{ + assert(eval.opts); + const auto &opts{*eval.opts}; + if(!opts.fetch_prev) + return; + + if(!fetch_enable) + return; + + if(my(event)) + return; + + // event must be in a room for now; we won't have context until dht + if(!json::get<"room_id"_>(event)) + return; + + const auto &content + { + json::get<"content"_>(event) + }; + + const json::object &m_relates_to + { + content["m.relates_to"] + }; + + if(!m_relates_to || json::type(m_relates_to) != json::OBJECT) + return; + + const event::id &event_id + { + m_relates_to["event_id"] + }; + + // If the relates_to is a prev_event then the vm::fetch unit will perform + // the fetch so this will just be redundant and we can bail. + const event::prev prev{event}; + if(prev.prev_events_has(event_id)) + return; + + if(likely(m::exists(event_id))) + return; + + log::dwarning + { + log, "%s in %s by %s relates to missing %s; fetching...", + string_view(event.event_id), + string_view(at<"room_id"_>(event)), + string_view(at<"sender"_>(event)), + string_view(event_id), + }; + + m::fetch::opts fetch_opts; + fetch_opts.op = m::fetch::op::event; + fetch_opts.room_id = at<"room_id"_>(event); + fetch_opts.event_id = event_id; + auto request + { + m::fetch::start(fetch_opts) + }; + + const auto response + { + request.get(seconds(fetch_timeout)) + }; + + const m::event result + { + json::object + { + response + } + }; + + auto eval_opts(opts); + eval_opts.fetch_prev = false; + eval_opts.fetch_state = false; + vm::eval + { + result, eval_opts + }; +} +catch(const ctx::interrupted &) +{ + throw; +} +catch(const std::exception &e) +{ + log::derror + { + log, "Failed to fetch relation for %s in %s :%s", + string_view(event.event_id), + string_view(json::get<"room_id"_>(event)), + e.what(), + }; +}