// 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::bridge { static thread_local char tmp[2][512]; } decltype(ircd::m::bridge::log) ircd::m::bridge::log { "m.bridge" }; ircd::string_view ircd::m::bridge::make_uri(const mutable_buffer &buf, const config &config, const string_view &path) { const rfc3986::uri base_url { at<"url"_>(config) }; char hs_token[256]; return fmt::sprintf { buf, "%s/_matrix/app/v1/%s?access_token=%s", base_url.path, path, url::encode(hs_token, at<"hs_token"_>(config)), }; } // // query // decltype(ircd::m::bridge::query::timeout) ircd::m::bridge::query::timeout { { "name", "ircd.m.bridge.query.timeout" }, { "default", 5L }, }; ircd::m::bridge::query::query(const config &config, const m::room::alias &alias) :base_url { at<"url"_>(config) } ,buf { 8_KiB } ,uri { make_uri(buf, config, fmt::sprintf { tmp[0], "rooms/%s", string_view{alias} }) } ,wb { buf + size(uri) } ,hypertext { wb, base_url.remote, "GET", uri, } ,request { net::hostport { base_url.remote }, server::out { wb.completed(), {} }, server::in { wb.remains(), wb.remains() }, } ,code { request.get(seconds(timeout)) } { } ircd::m::bridge::query::query(const config &config, const m::user::id &user_id) :base_url { at<"url"_>(config) } ,buf { 8_KiB } ,uri { make_uri(buf, config, fmt::sprintf { tmp[0], "users/%s", string_view{user_id} }) } ,wb { buf + size(uri) } ,hypertext { wb, base_url.remote, "GET", uri, } ,request { net::hostport { base_url.remote }, server::out { wb.completed(), {} }, server::in { wb.remains(), wb.remains() }, } ,code { request.get(seconds(timeout)) } { } // // config // bool ircd::m::bridge::config::exists(const string_view &id) { return get(std::nothrow, id, [] (const auto &, const auto &, const auto &) { }); } void ircd::m::bridge::config::get(const string_view &id, const closure &closure) { if(unlikely(!get(std::nothrow, id, closure))) throw m::NOT_FOUND { "No bridge config found for '%s'", id, }; } bool ircd::m::bridge::config::get(std::nothrow_t, const string_view &id, const closure &closure) { return !config::for_each([&id, &closure] (const auto &event_idx, const auto &event, const config &config) { if(json::get<"id"_>(config) != id) return true; closure(event_idx, event, config); return false; }); } bool ircd::m::bridge::config::for_each(const closure_bool &closure) { return events::type::for_each_in("ircd.bridge", [&closure] (const string_view &, const event::idx &event_idx) { const m::event::fetch event { std::nothrow, event_idx }; if(unlikely(!event.valid)) return true; if(!my(event)) return true; if(!defined(json::get<"state_key"_>(event))) return true; const bridge::config config { json::get<"content"_>(event) }; if(!json::get<"id"_>(config)) return true; // the state_key has to match the id for now if(json::get<"id"_>(config) != json::get<"state_key"_>(event)) return true; // filter replaced state if(room::state::next(event_idx)) return true; // filter redacted state if(redacted(event_idx)) return true; const user::id sender { json::get<"sender"_>(event) }; if(!is_oper(sender)) return true; if(likely(closure(event_idx, event, config))) return true; return false; }); }