diff --git a/charybdis/console.cc b/charybdis/console.cc index de421a894..d550507a2 100644 --- a/charybdis/console.cc +++ b/charybdis/console.cc @@ -945,7 +945,7 @@ try token_count(args, ' ') >= 2? lex_cast(token(args, ' ', 2)) : 0U }; - m::backfill(room, event_id, limit); + m::vm::backfill(room, event_id, limit); break; } @@ -1025,13 +1025,7 @@ try token(args, ' ', 1) }; - m::room room - { - room_id - }; - - json::iov content{}; - room.join(user_id, content); + m::join(room_id, user_id); break; } @@ -1052,12 +1046,12 @@ try token(args, ' ', 1) }; - m::state(room, event_id); + m::vm::state(room, event_id); break; } - case hash("fetch"): + case hash("get"): { const auto args { @@ -1074,6 +1068,92 @@ try break; } + case hash("fetch"): + { + const auto args + { + tokens_after(line, ' ', 0) + }; + + const m::event::id event_id + { + token(args, ' ', 0) + }; + + const net::remote remote + { + token(args, ' ', 1) + }; + + static char buf[65536]; + m::event::fetch fetch + { + event_id, buf, remote + }; + + std::cout << m::io::acquire(fetch) << std::endl; + break; + } + + case hash("mfetch"): + { + const auto args + { + tokens_after(line, ' ', 0) + }; + + const auto event_ids + { + tokens_after(line, ' ', 0) + }; + +// const net::remote remote +// { +// token(args, ' ', 0) +// }; + + string_view event_id[8]; + const auto count + { + tokens(event_ids, ' ', event_id) + }; + + static char buf[8][65536]; + m::event::fetch tab[count]; + for(size_t i(0); i < count; ++i) + { + tab[i].event_id = event_id[i]; + tab[i].buf = buf[i]; +// tab[i].hint = remote; + } + + m::io::acquire({tab, count}); + + for(size_t i(0); i < count; ++i) + { + std::cout << tab[i].event_id << ": size: " << string_view{tab[i].pdu}.size() << std::endl; + } + + break; + } + + case hash("acquire"): + { + const auto args + { + tokens_after(line, ' ', 0) + }; + + const m::event::id event_id + { + token(args, ' ', 0) + }; + + static char buf[65536]; + std::cout << m::vm::acquire(event_id, buf) << std::endl; + break; + } + case hash("trace"): { const auto args @@ -1086,7 +1166,7 @@ try token(args, ' ', 0) }; - m::trace(event_id, [] + m::vm::trace(event_id, [] (const auto &event, auto &next) { std::cout << event << std::endl << std::endl; diff --git a/include/ircd/m/event.h b/include/ircd/m/event.h index dea3be728..c4c7953b6 100644 --- a/include/ircd/m/event.h +++ b/include/ircd/m/event.h @@ -24,6 +24,8 @@ #pragma once #define HAVE_IRCD_M_EVENT_H +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsubobject-linkage" /////////////////////////////////////////////////////////////////////////////// // Protocol notes @@ -55,12 +57,12 @@ namespace ircd::m::name constexpr const char *const depth {"depth"}; constexpr const char *const event_id {"event_id"}; constexpr const char *const hashes {"hashes"}; + constexpr const char *const is_state {"is_state"}; constexpr const char *const membership {"membership"}; constexpr const char *const origin {"origin"}; constexpr const char *const origin_server_ts {"origin_server_ts"}; - constexpr const char *const prev_ids {"prev_ids"}; - constexpr const char *const prev_events {"prev_ids"}; - constexpr const char *const prev_state {"prev_ids"}; + constexpr const char *const prev_events {"prev_events"}; + constexpr const char *const prev_state {"prev_state"}; constexpr const char *const room_id {"room_id"}; constexpr const char *const sender {"sender"}; constexpr const char *const signatures {"signatures"}; @@ -75,22 +77,27 @@ struct ircd::m::event json::property, json::property, json::property, - json::property, - json::property, - json::property, - json::property, + json::property, + json::property, + json::property, + json::property, + json::property, json::property, - json::property, - json::property, - json::property, - json::property, - json::property, - json::property, - json::property, - json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, json::property > { + enum lineage : int; + enum temporality : int; + + struct fetch; + using id = m::id::event; static database *events; @@ -100,13 +107,36 @@ struct ircd::m::event using where = db::where; template using query = cursor::query_type; - // Queue of contexts waiting to see the next inserted event - static ctx::view inserted; - static id::buf head; - static const_iterator find(const id &); - static void insert(json::iov &); using super_type::tuple; using super_type::operator=; }; + +namespace ircd::m +{ + string_view reflect(const event::temporality &); + event::temporality temporality(const event &, const int64_t &rel); + + string_view reflect(const event::lineage &); + event::lineage lineage(const event &); +} + + +enum ircd::m::event::temporality +:int +{ + FUTURE = 1, ///< Event has a depth 1 or more into the future. + PRESENT = 0, ///< Event has a depth equal to the current depth. + PAST = -1, ///< Event has a depth less than the current depth. +}; + +enum ircd::m::event::lineage +:int +{ + ROOT = 0, ///< Event has no parents (must be m.room.create then) + FORWARD = 1, ///< Event has one parent at the previous depth + MERGE = 2, ///< Event has multiple parents at the previous depth +}; + +#pragma GCC diagnostic pop diff --git a/include/ircd/m/filter.h b/include/ircd/m/filter.h index 914c3a5a0..c83ec462a 100644 --- a/include/ircd/m/filter.h +++ b/include/ircd/m/filter.h @@ -24,6 +24,8 @@ #pragma once #define HAVE_IRCD_M_FILTER_H +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsubobject-linkage" namespace ircd::m { @@ -118,3 +120,5 @@ struct ircd::m::filter filter(const string_view &filter_id, const mutable_buffer &); }; + +#pragma GCC diagnostic pop diff --git a/include/ircd/m/id.h b/include/ircd/m/id.h index 1cb7712bd..02a86515d 100644 --- a/include/ircd/m/id.h +++ b/include/ircd/m/id.h @@ -95,10 +95,8 @@ namespace ircd::m const char *reflect(const enum id::sigil &); } -// -// ID object backed by an internal buffer. Useful for creating or composing -// a new ID. -// +/// ID object backed by an internal buffer of wost-case size. +/// template struct ircd::m::id::buf diff --git a/include/ircd/m/io.h b/include/ircd/m/io.h new file mode 100644 index 000000000..01553a24b --- /dev/null +++ b/include/ircd/m/io.h @@ -0,0 +1,158 @@ +/* + * charybdis: 21st Century IRC++d + * + * Copyright (C) 2016 Charybdis Development Team + * Copyright (C) 2016 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once +#define HAVE_IRCD_M_IO_H + +namespace ircd::m::io +{ + struct session; + struct request; + struct response; + + bool verify_x_matrix_authorization(const string_view &authorization, const string_view &method, const string_view &uri, const string_view &content); + + // Synchronous acquire + size_t acquire(vector_view); + json::object acquire(event::fetch &); + + size_t acquire(vector_view); + json::array acquire(room::fetch &); + + size_t acquire(vector_view); + json::array acquire(room::state::fetch &); + + // Synchronous acquire with user buffer + json::object get(const id::event &, const mutable_buffer &); +} + +namespace ircd::m +{ + using io::get; + using io::session; + using io::request; + using io::response; +} + +struct ircd::m::event::fetch +{ + // out + event::id event_id; + mutable_buffer buf; + net::remote hint; + + // in + json::object pdu; + std::exception_ptr error; +}; + +struct ircd::m::room::fetch +{ + // out + event::id event_id; + room::id room_id; + mutable_buffer buf; + net::remote hint; + uint64_t limit {128}; + + // in + json::array pdus; + json::array auth_chain; + std::exception_ptr error; +}; + +struct ircd::m::room::state::fetch +{ + // out + event::id event_id; + room::id room_id; + mutable_buffer buf; + net::remote hint; + uint64_t limit {128}; + + // in + json::array pdus; + json::array auth_chain; + std::exception_ptr error; +}; + +struct ircd::m::io::request +{ + struct authorization; + struct keys; + + string_view origin; + string_view destination; + string_view method; + string_view path; + string_view query; + string_view fragment; + std::string _content; + json::object content; + + string_view generate_authorization(const mutable_buffer &out) const; + void operator()(server &, const vector_view & = {}) const; + void operator()(const vector_view & = {}) const; + + request(const string_view &method, + const string_view &path, + const string_view &query = {}, + json::members body = {}) + :method{method} + ,path{path} + ,query{query} + ,_content{json::strung(body)} + ,content{_content} + {} + + request(const string_view &method, + const string_view &path, + const string_view &query, + const json::object &content) + :method{method} + ,path{path} + ,query{query} + ,content{content} + {} + + request() = default; +}; + +struct ircd::m::io::response +:json::object +{ + response(server &, parse::buffer &); +}; + +struct ircd::m::io::session +{ + ircd::server server; + std::string destination; + std::string access_token; + + json::object operator()(parse::buffer &pb, request &); + + session(const net::remote &); + session() = default; +}; diff --git a/include/ircd/m/m.h b/include/ircd/m/m.h index 61c7b4a33..83ad1ee9d 100644 --- a/include/ircd/m/m.h +++ b/include/ircd/m/m.h @@ -50,8 +50,6 @@ namespace ircd #include "filter.h" #include "keys.h" #include "txn.h" -#include "request.h" -#include "session.h" namespace ircd::m::dbs { diff --git a/include/ircd/m/request.h b/include/ircd/m/request.h deleted file mode 100644 index 77788b337..000000000 --- a/include/ircd/m/request.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * charybdis: 21st Century IRC++d - * - * Copyright (C) 2016 Charybdis Development Team - * Copyright (C) 2016 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#pragma once -#define HAVE_IRCD_M_REQUEST_H - -namespace ircd { -namespace m { - -struct request -{ - string_view method; - string_view path; - string_view query; - string_view access_token; - std::string _content; - json::object content; - - request(const string_view &method, - const string_view &path, - const string_view &query = {}, - json::members body = {}); - - request(const string_view &method, - const string_view &path, - const string_view &query, - const json::object &content); -}; - -} // namespace m -} // namespace ircd - -inline -ircd::m::request::request(const string_view &method, - const string_view &path, - const string_view &query, - json::members body) -:method{method} -,path{path} -,query{query} -,_content{json::string(body)} -,content{_content} -{ -} - -inline -ircd::m::request::request(const string_view &method, - const string_view &path, - const string_view &query, - const json::object &content) -:method{method} -,path{path} -,query{query} -,content{content} -{ -} diff --git a/include/ircd/m/room.h b/include/ircd/m/room.h index 90b5b0b73..4a78f2cc0 100644 --- a/include/ircd/m/room.h +++ b/include/ircd/m/room.h @@ -24,6 +24,8 @@ #pragma once #define HAVE_IRCD_M_ROOM_H +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsubobject-linkage" namespace ircd::m { @@ -32,27 +34,42 @@ namespace ircd::m IRCD_M_EXCEPTION(CONFLICT, ALREADY_MEMBER, http::CONFLICT); struct room; + + room create(const id::room &room_id, const id::user &creator, const id::room &parent, const string_view &type); + room create(const id::room &room_id, const id::user &creator, const string_view &type = {}); + + void membership(const id::room &room_id, const m::id::user &, const string_view &membership); + void leave(const id::room &room_id, const m::id::user &); + void join(const id::room &room_id, const m::id::user &); } struct ircd::m::room { struct alias; + struct state; struct members; + struct fetch; using id = m::id::room; id room_id; + operator const id &() const + { + return room_id; + } + bool membership(const m::id::user &, const string_view &membership = "join") const; - event::id head(event::id::buf &) const; + + std::vector barren(const int64_t &min_depth = 0) const; + uint64_t maxdepth(event::id::buf &) const; + uint64_t maxdepth() const; event::id::buf send(json::iov &event); event::id::buf send(const json::members &event); - void membership(const m::id::user &, json::iov &content); - void leave(const m::id::user &, json::iov &content); - void join(const m::id::user &, json::iov &content); - void create(const m::id::user &sender, const m::id::user &creator, json::iov &content); + void membership(json::iov &event, json::iov &content); + void create(json::iov &event, json::iov &content); room(const id &room_id) :room_id{room_id} @@ -62,3 +79,39 @@ struct ircd::m::room :room_id{} {} }; + +namespace ircd::m::name +{ + constexpr const char *const m_room_aliases {"m.room.aliases"}; + constexpr const char *const m_room_canonical_alias {"m.room.canonical_alias"}; + constexpr const char *const m_room_create {"m.room.create"}; + constexpr const char *const m_room_join_rules {"m.room.join_rules"}; + constexpr const char *const m_room_member {"m.room.member"}; + constexpr const char *const m_room_power_levels {"m.room.power_levels"}; + constexpr const char *const m_room_redaction {"m.room.redaction"}; + constexpr const char *const m_room_message {"m.room.message"}; + constexpr const char *const m_room_name {"m.room.name"}; + constexpr const char *const m_room_topic {"m.room.topic"}; + constexpr const char *const m_room_avatar {"m.room.avatar"}; +} + +struct ircd::m::room::state +:json::tuple +< + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property, + json::property +> +{ + struct fetch; +}; + +#pragma GCC diagnostic pop diff --git a/include/ircd/m/session.h b/include/ircd/m/sig.h similarity index 69% rename from include/ircd/m/session.h rename to include/ircd/m/sig.h index 3a8ac8eb1..840cd4b75 100644 --- a/include/ircd/m/session.h +++ b/include/ircd/m/sig.h @@ -1,8 +1,8 @@ /* * charybdis: 21st Century IRC++d * - * Copyright (C) 2016 Charybdis Development Team - * Copyright (C) 2016 Jason Volk + * Copyright (C) 2017 Charybdis Development Team + * Copyright (C) 2017 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 @@ -23,22 +23,8 @@ */ #pragma once -#define HAVE_IRCD_M_SESSION_H +#define HAVE_IRCD_M_SIG_H -namespace ircd { -namespace m { - -struct session +namespace ircd::m { - std::shared_ptr client; - std::string access_token; - std::deque tape; - std::multimap resource; - - json::object operator()(parse::buffer &pb, request &); - - session(const hostport &); -}; - -} // namespace m -} // namespace ircd +} diff --git a/include/ircd/m/events.h b/include/ircd/m/vm.h similarity index 66% rename from include/ircd/m/events.h rename to include/ircd/m/vm.h index 5cfd55d25..35150f196 100644 --- a/include/ircd/m/events.h +++ b/include/ircd/m/vm.h @@ -23,9 +23,9 @@ */ #pragma once -#define HAVE_IRCD_M_EVENTS_H +#define HAVE_IRCD_M_VM_H -namespace ircd::m::events +namespace ircd::m::vm { using closure = std::function; using closure_bool = std::function; @@ -42,3 +42,26 @@ namespace ircd::m::events bool test(const event::query<> &, const closure_bool &); bool test(const event::query<> &); }; + +namespace ircd::m::vm +{ + extern uint64_t current_sequence; + extern ctx::view inserted; + + // Synchronous fetch and eval + size_t acquire(const vector_view &, const vector_view &); + json::object acquire(const id::event &, const mutable_buffer &); + void state(const room::id &, const event::id &); + void backfill(const room::id &, const event::id &v, const size_t &limit); + + using tracer = std::function; + void trace(const id::event &, const tracer &); + + // Hypostasis + void eval(const vector_view &); + void eval(const json::array &); + void eval(const event &); + + event::id::buf commit(json::iov &event); + event::id::buf join(const room::id &, json::iov &iov); +} diff --git a/ircd/matrix.cc b/ircd/matrix.cc index ae8adac6f..1bc2de455 100644 --- a/ircd/matrix.cc +++ b/ircd/matrix.cc @@ -19,141 +19,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/////////////////////////////////////////////////////////////////////////////// -// -// m/session.h -// - -ircd::m::session::session(const hostport &host_port) -:client{std::make_shared(host_port)} -{ -} - -ircd::json::object -ircd::m::session::operator()(parse::buffer &pb, - request &r) -{ - const json::member origin - { - "origin", my_host() - }; - - const json::member destination - { - //host(remote_hostport(*client->sock)) - "destination", "zemos.net" - }; - - const json::member method - { - "method", r.method - }; - - const std::string uri - { - std::string {"/"} + std::string{r.path} + - (r.query? (std::string{"?"} + std::string{r.query}) : std::string{}) - }; - - json::iov iov; - const json::iov::push pushed[] - { - { iov, json::member { "uri", uri } }, - { iov, origin }, - { iov, method }, - { iov, destination }, - }; - - const json::iov::add_if content - { - iov, r.content.size() > 2, json::member - { - "content", r.content - } - }; - - size_t headers{2}; - http::line::header header[3] - { - { "Content-Type", "application/json" }, - { "User-Agent", "IRCd" } - }; - - char x_matrix_buf[2048]; - if(startswith(r.path, "_matrix/federation")) - { - // These buffers can be comfortably large if they're not on a stack and - // nothing in this procedure has a yield; the assertion is tripped if so - static char request_object_buffer[4096]; - static char signature_buffer[128]; - ctx::critical_assertion ca; - - const auto request_object - { - json::stringify(request_object_buffer, iov) - }; - - std::cout << request_object << std::endl; - - const ed25519::sig sig - { - my::secret_key.sign(request_object) - }; - - const auto signature - { - b64encode_unpadded(signature_buffer, sig) - }; - - const auto x_matrix_len - { - fmt::sprintf(x_matrix_buf, "X-Matrix origin=%s,key=\"ed25519:pk2\",sig=\"%s\"", - string_view{origin.second}, - signature) - }; - - const string_view x_matrix - { - x_matrix_buf, size_t(x_matrix_len) - }; - - header[headers++] = { "Authorization", x_matrix }; - } - - http::request - { - string_view{destination.second}, //host(remote(*client)), - r.method, - r.path, - r.query, - r.content, - write_closure(*client), - { header, headers } - }; - - http::code status; - json::object object; - parse::capstan pc - { - pb, read_closure(*client) - }; - - http::response - { - pc, - nullptr, - [&pc, &status, &object](const http::response::head &head) - { - status = http::status(head.status); - object = http::response::content{pc, head}; - } - }; - - if(status < 200 || status >= 300) - throw m::error(status, object); - - return object; -} /////////////////////////////////////////////////////////////////////////////// // @@ -162,6 +27,11 @@ ircd::m::session::operator()(parse::buffer &pb, namespace ircd::m { + struct log::log log + { + "matrix", 'm' + }; + std::map modules; ircd::net::listener *listener; @@ -171,10 +41,16 @@ namespace ircd::m static void bootstrap(); } +const ircd::m::room::id::buf +init_room_id +{ + "init", ircd::my_host() +}; + const ircd::m::user::id::buf ircd_user_id { - "ircd", "cdc.z" //TODO: hostname + "ircd", ircd::my_host() //TODO: hostname }; ircd::m::user @@ -195,6 +71,12 @@ ircd::m::my_room ircd_room_id }; +bool +ircd::m::my_host(const string_view &s) +{ + return s == my_host(); +} + ircd::string_view ircd::m::my_host() { @@ -208,7 +90,7 @@ try const string_view prefixes[] { - "client_", "key_", + "client_", "key_", "federation_", "media_" }; for(const auto &name : mods::available()) @@ -238,7 +120,7 @@ try } catch(const m::error &e) { - log::critical("%s %s", e.what(), e.content); + log.critical("%s %s", e.what(), e.content); } ircd::m::init::~init() @@ -251,7 +133,7 @@ noexcept try } catch(const m::error &e) { - log::critical("%s %s", e.what(), e.content); + log.critical("%s %s", e.what(), e.content); std::terminate(); } @@ -259,21 +141,17 @@ void ircd::m::join_ircd_room() try { - // ircd.start event - json::iov content; - my_room.join(me.user_id, content); + join(my_room, me.user_id); } catch(const m::ALREADY_MEMBER &e) { - log::warning("IRCd did not shut down correctly..."); + log.warning("IRCd did not shut down correctly..."); } void ircd::m::leave_ircd_room() { - // ircd.start event - json::iov content; - my_room.leave(me.user_id, content); + leave(my_room, me.user_id); } namespace ircd::m @@ -293,185 +171,16 @@ ircd::m::bootstrap() "database is empty. I will be bootstrapping it with initial events now..." ); - json::iov content; - my_room.create(me.user_id, me.user_id, content); - user::accounts.create(me.user_id, me.user_id, content); - user::accounts.join(me.user_id, content); - user::sessions.create(me.user_id, me.user_id, content); - filter::filters.create(me.user_id, me.user_id, content); + create(my_room, me.user_id); + create(user::accounts, me.user_id); + create(user::sessions, me.user_id); + create(filter::filters, me.user_id); + join(user::accounts, me.user_id); bootstrap_keys(); } -/////////////////////////////////////////////////////////////////////////////// // -// m/keys.h -// - -const ircd::m::room::id::buf -keys_room_id -{ - "keys", ircd::my_host() -}; - -ircd::m::room -ircd::m::key::keys -{ - keys_room_id -}; - -ircd::ed25519::sk -ircd::my::secret_key -{}; - -ircd::ed25519::pk -ircd::my::public_key -{}; - -std::string -ircd::my::public_key_b64 -{}; - -static void -ircd::m::init_keys(const std::string &sk_file) -{ - my::secret_key = ed25519::sk - { - sk_file, &my::public_key - }; - - my::public_key_b64 = b64encode_unpadded(my::public_key); - - log::info("My ed25519 public key is: %s", - my::public_key_b64); -} - -namespace ircd -{ - size_t certbytes(const mutable_raw_buffer &buf, const std::string &certfile); -} - -static void -ircd::m::bootstrap_keys() -{ - json::iov content; - key::keys.create(me.user_id, me.user_id, content); - - key my_key; - json::val(my_key) = my_host(); - json::val(my_key) = "{}"; - - const auto valid_until - { - ircd::time() + duration_cast(hours(2160)).count() - }; - json::val(my_key) = valid_until; - - static char verify_keys_buf[256]; - json::val(my_key) = json::stringify(verify_keys_buf, json::members - { - { "ed25519:pk2", json::members - { - { "key", my::public_key_b64 } - }} - }); - - //static unsigned char pembuf[4096]; - //const auto cbsz{ircd::certbytes(pembuf, "/home/jason/zemos.net.tls.crt")}; - - static std::array tls_hash; - a2u(tls_hash, "C259B83ABED34D81B31F773737574FBD966CE33BDED708BF502CA1D4CEC3D318"); - - static char tls_b64_buf[256]; - const json::members tlsfps - { - { "sha256", b64encode_unpadded(tls_b64_buf, tls_hash) } - }; - - const json::value tlsfp[1] - { - { tlsfps } - }; - - static char tls_fingerprints_buf[256]; - json::get<"tls_fingerprints"_>(my_key) = json::stringify(tls_fingerprints_buf, json::value - { - tlsfp, 1 - }); - - const std::string presig - { - json::string(my_key) - }; - - const ed25519::sig sig - { - my::secret_key.sign(const_raw_buffer{presig}) - }; - - static char signature[128], signatures[256]; - json::get<"signatures"_>(my_key) = json::stringify(signatures, json::members - { - { my_host(), json::members - { - { "ed25519:pk2", b64encode_unpadded(signature, sig) } - }} - }); - - keys::set(my_key); -} - -void -ircd::m::keys::set(const key &key) -{ - const auto &state_key - { - at<"server_name"_>(key) - }; - - const m::user::id::buf sender - { - "ircd", at<"server_name"_>(key) - }; - - const auto content - { - json::string(key) - }; - - json::iov event; - json::iov::push members[] - { - { event, json::member { "type", "ircd.key" }}, - { event, json::member { "state_key", state_key }}, - { event, json::member { "sender", sender }}, - { event, json::member { "content", content }} - }; - - key::keys.send(event); -} - -bool -ircd::m::keys::get(const string_view &server_name, - const closure &closure) -{ - const m::event::query query - { - { "room_id", key::keys.room_id }, - { "type", "ircd.key" }, - { "state_key", server_name }, - }; - - return m::events::test(query, [&closure] - (const auto &event) - { - closure(json::val(event)); - return true; - }); -} - -/////////////////////////////////////////////////////////////////////////////// -// -// m/db.h +// dbs // namespace ircd::m::dbs @@ -524,6 +233,614 @@ ircd::m::dbs::init_modules() modules.emplace(name, name); } +/////////////////////////////////////////////////////////////////////////////// +// +// m/session.h +// + +ircd::m::io::session::session(const net::remote &remote) +:server{remote} +,destination{remote.hostname} +{ +} + +ircd::json::object +ircd::m::io::session::operator()(parse::buffer &pb, + request &request) +{ + request.destination = destination; + request(server); + return response + { + server, pb + }; +} + +// +// response +// + +ircd::m::io::response::response(server &server, + parse::buffer &pb) +{ + http::code status; + json::object &object + { + static_cast(*this) + }; + + parse::capstan pc + { + pb, read_closure(server) + }; + + http::response + { + pc, + nullptr, + [&pc, &status, &object](const http::response::head &head) + { + status = http::status(head.status); + object = http::response::content{pc, head}; + }, + [](const auto &header) + { + //std::cout << header.first << " :" << header.second << std::endl; + } + }; + + if(status < 200 || status >= 300) + throw m::error(status, object); +} + +// +// request +// + +namespace ircd::m::name +{ +// constexpr const char *const content {"content"}; + constexpr const char *const destination {"destination"}; + constexpr const char *const method {"method"}; +// constexpr const char *const origin {"origin"}; + constexpr const char *const uri {"uri"}; +} + +struct ircd::m::io::request::authorization +:json::tuple +< + json::property, + json::property, + json::property, + json::property, + json::property +> +{ + string_view generate(const mutable_buffer &out); + + using super_type::tuple; +}; + +void +ircd::m::io::request::operator()(const vector_view &addl_headers) +const +{ +} + +void +ircd::m::io::request::operator()(server &server, + const vector_view &addl_headers) +const +{ + const size_t addl_headers_size + { + std::min(addl_headers.size(), size_t(64UL)) + }; + + size_t headers{2 + addl_headers_size}; + http::line::header header[headers + 1] + { + { "User-Agent", BRANDING_NAME " (IRCd " BRANDING_VERSION ")" }, + { "Content-Type", "application/json" }, + }; + + for(size_t i(0); i < addl_headers_size; ++i) + header[headers++] = addl_headers.at(i); + + char x_matrix[1024]; + if(startswith(path, "_matrix/federation")) + header[headers++] = + { + "Authorization", generate_authorization(x_matrix) + }; + + http::request + { + destination, + method, + path, + query, + content, + write_closure(server), + { header, headers } + }; +} + +ircd::string_view +ircd::m::io::request::generate_authorization(const mutable_buffer &out) +const +{ + const fmt::bsprintf<2048> uri + { + "/%s%s%s", lstrip(path, '/'), query? "?" : "", query + }; + + request::authorization authorization + { + json::members + { + { "destination", destination }, + { "method", method }, + { "origin", my_host() }, + { "uri", uri }, + } + }; + + if(string_view{content}.size() > 2) + json::get<"content"_>(authorization) = content; + + return authorization.generate(out); +} + +ircd::string_view +ircd::m::io::request::authorization::generate(const mutable_buffer &out) +{ + // Any buffers here can be comfortably large if they're not on a stack and + // nothing in this procedure has a yield which risks decohering static + // buffers; the assertion is tripped if so. + ctx::critical_assertion ca; + + static fixed_buffer request_object_buf; + const auto request_object + { + json::stringify(request_object_buf, *this) + }; + + const ed25519::sig sig + { + my::secret_key.sign(request_object) + }; + + static fixed_buffer signature_buf; + const auto x_matrix_len + { + fmt::sprintf(out, "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"", + unquote(string_view{at<"origin"_>(*this)}), + my::public_key_id, + b64encode_unpadded(signature_buf, sig)) + }; + + return + { + data(out), size_t(x_matrix_len) + }; +} + +bool +ircd::m::io::verify_x_matrix_authorization(const string_view &x_matrix, + const string_view &method, + const string_view &uri, + const string_view &content) +{ + string_view tokens[3], origin, key, sig; + if(ircd::tokens(split(x_matrix, ' ').second, ',', tokens) != 3) + return false; + + for(const auto &token : tokens) + { + const auto &key_value + { + split(token, '=') + }; + + switch(hash(key_value.first)) + { + case hash("origin"): origin = unquote(key_value.second); break; + case hash("key"): key = unquote(key_value.second); break; + case hash("sig"): sig = unquote(key_value.second); break; + } + } + + request::authorization authorization{json::members + { + { "destination", my_host() }, + { "method", method }, + { "origin", origin }, + { "uri", uri } + }}; + + if(content.size() > 2) + json::get<"content"_>(authorization) = content; + + //TODO: XXX + const auto request_object + { + json::strung(authorization) + }; + + const ed25519::sig _sig + { + [&sig](auto &buf) + { + b64decode(buf, sig); + } + }; + + const ed25519::pk pk + { + [&origin, &key](auto &buf) + { + m::keys::get(origin, [&key, &buf](const auto &keys) + { + const json::object vks{at<"verify_keys"_>(keys)}; + const json::object vkk{vks.at(key)}; + b64decode(buf, unquote(vkk.at("key"))); + }); + } + }; + + return pk.verify(const_raw_buffer{request_object}, _sig); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// m/keys.h +// + +const ircd::m::room::id::buf +keys_room_id +{ + "keys", ircd::my_host() +}; + +ircd::m::room +ircd::m::key::keys +{ + keys_room_id +}; + +ircd::ed25519::sk +ircd::my::secret_key +{}; + +ircd::ed25519::pk +ircd::my::public_key +{}; + +std::string +ircd::my::public_key_b64 +{}; + +std::string +ircd::my::public_key_id +{}; + +static void +ircd::m::init_keys(const std::string &sk_file) +{ + my::secret_key = ed25519::sk + { + sk_file, &my::public_key + }; + + my::public_key_b64 = b64encode_unpadded(my::public_key); + const fixed_buffer hash + { + sha256{const_raw_buffer{my::public_key}} + }; + + const auto public_key_hash_b64 + { + b64encode_unpadded(hash) + }; + + my::public_key_id = fmt::snstringf(BUFSIZE, "ed25519:%s", public_key_hash_b64); + + log.info("Current key is '%s' and the public key is: %s", + my::public_key_id, + my::public_key_b64); +} + +static void +ircd::m::bootstrap_keys() +{ + create(key::keys, me.user_id); + + const auto verify_keys{json::strung(json::members + {{ + string_view{my::public_key_id}, + { + { "key", my::public_key_b64 } + } + }})}; + + key my_key; + json::get<"verify_keys"_>(my_key) = verify_keys; + json::get<"server_name"_>(my_key) = my_host(); + json::get<"old_verify_keys"_>(my_key) = "{}"; + json::get<"valid_until_ts"_>(my_key) = ircd::time() + duration_cast(hours(2160)).count(); + + const fixed_buffer tls_hash + { + [](const auto &buffer) + { + a2u(buffer, "C259B83ABED34D81B31F773737574FBD966CE33BDED708BF502CA1D4CEC3D318"); + } + }; + + fixed_buffer tls_b64; + const json::members tlsfps + { + { "sha256", b64encode_unpadded(tls_b64, tls_hash) } + }; + + const json::value tlsfp[1] + { + { tlsfps } + }; + + const auto tls_fingerprints{json::strung(json::value + { + tlsfp, 1 + })}; + + json::get<"tls_fingerprints"_>(my_key) = tls_fingerprints; + + const auto presig + { + json::strung(my_key) + }; + + const ed25519::sig sig + { + my::secret_key.sign(const_raw_buffer{presig}) + }; + + static char signature[256]; + const auto signatures{json::strung(json::members + { + { my_host(), + { + { string_view{my::public_key_id}, b64encode_unpadded(signature, sig) } + }} + })}; + + json::get<"signatures"_>(my_key) = signatures; + keys::set(my_key); +} + +bool +ircd::m::keys::get(const string_view &server_name, + const closure &closure) +{ + return get(server_name, string_view{}, closure); +} + +bool +ircd::m::keys::get(const string_view &server_name, + const string_view &key_id, + const closure &closure) +{ + const m::event::query query + { + { "room_id", key::keys.room_id }, + { "type", "ircd.key" }, + { "state_key", server_name }, + }; + + const auto have + { + [&closure](const auto &event) + { + closure(json::get<"content"_>(event)); + return true; + } + }; + + if(m::vm::test(query, have)) + return true; + + if(server_name == my_host()) + throw m::NOT_FOUND + { + "key '%s' for '%s' not found", key_id, server_name + }; + + string_view server_name_encoded; + const fixed_buffer server_name_buf{[&server_name, &server_name_encoded] + (const auto &buf) + { + server_name_encoded = http::urlencode(server_name, buf); + }}; + + string_view key_id_encoded; + const fixed_buffer key_id_buf{[&key_id, &key_id_encoded] + (const auto &buf) + { + key_id_encoded = http::urlencode(key_id, buf); + }}; + + m::session session + { + server_name + }; + + char url[128]; const auto url_len + { +/* + fmt::snprintf(url, sizeof(url), "_matrix/key/v2/query/%s/%s", + server_name, + key_id): +*/ + fmt::snprintf(url, sizeof(url), "_matrix/key/v2/server/%s", + key_id) + }; + + char buf[4096]; + ircd::parse::buffer pb{buf}; + m::request request + { + "GET", url, {}, {} + }; + + const string_view response + { + session(pb, request) + }; + +/* + const json::array &keys + { + response.at("server_keys") + }; + + log::debug("Fetched %zu candidate keys from '%s' (%s)", + keys.size(), + server_name, + string(remote(*session.client))); + + if(keys.empty()) + throw m::NOT_FOUND + { + "Failed to get key '%s' for '%s'", key_id, server_name + }; + + const m::key &key + { + keys[0] + }; +*/ + + const m::key &key + { + response + }; + + if(!key.verify()) + throw m::error + { + http::UNAUTHORIZED, "M_INVALID_SIGNATURE", "Failed to verify key from '%s'", server_name + }; + + log.debug("Verified key from '%s'", + server_name); + + m::keys::set(key); + closure(key); + return true; +} + +void +ircd::m::keys::set(const key &key) +{ + const auto &state_key + { + unquote(at<"server_name"_>(key)) + }; + + const m::user::id::buf sender + { + "ircd", unquote(at<"server_name"_>(key)) + }; + + const json::strung content + { + key + }; + + json::iov event; + json::iov::push members[] + { + { event, json::member { "type", "ircd.key" }}, + { event, json::member { "state_key", state_key }}, + { event, json::member { "sender", sender }}, + { event, json::member { "content", content }} + }; + + key::keys.send(event); +} + +/// Verify this key data (with itself). +bool +ircd::m::key::verify() +const noexcept try +{ + const auto &valid_until_ts + { + at<"valid_until_ts"_>(*this) + }; + + if(valid_until_ts < ircd::time()) + throw ircd::error("Key was valid until %s", timestr(valid_until_ts)); + + const json::object &verify_keys + { + at<"verify_keys"_>(*this) + }; + + const string_view &key_id + { + begin(verify_keys)->first + }; + + const json::object &key + { + begin(verify_keys)->second + }; + + const ed25519::pk pk + { + [&key](auto &pk) + { + b64decode(pk, unquote(key.at("key"))); + } + }; + + const json::object &signatures + { + at<"signatures"_>(*this) + }; + + const string_view &server_name + { + unquote(at<"server_name"_>(*this)) + }; + + const json::object &server_signatures + { + signatures.at(server_name) + }; + + const ed25519::sig sig{[&server_signatures, &key_id](auto &sig) + { + b64decode(sig, unquote(server_signatures.at(key_id))); + }}; + + ///TODO: XXX + m::key copy{*this}; + at<"signatures"_>(copy) = string_view{}; + const std::string preimage{json::strung(copy)}; + return pk.verify(const_raw_buffer{preimage}, sig); +} +catch(const std::exception &e) +{ + log.error("key verification for '%s' failed: %s", + json::get<"server_name"_>(*this, ""_sv), + e.what()); + + return false; +} + /////////////////////////////////////////////////////////////////////////////// // // m/filter.h @@ -552,7 +869,7 @@ ircd::m::filter::filter(const string_view &filter_id, }; size_t len{0}; - m::events::test(query, [&buf, &len] + m::vm::test(query, [&buf, &len] (const auto &event) { len = copy(buf, json::get<"content"_>(event)); @@ -573,10 +890,15 @@ ircd::m::filter::size(const string_view &filter_id) }; size_t ret{0}; - m::events::test(query, [&ret] + m::vm::test(query, [&ret] (const auto &event) { - ret = json::get<"content"_>(event).size(); + const string_view content + { + json::get<"content"_>(event) + }; + + ret = content.size(); return true; }); @@ -588,62 +910,122 @@ ircd::m::filter::size(const string_view &filter_id) // m/room.h // -// -// m::room -// +ircd::m::room +ircd::m::create(const id::room &room_id, + const id::user &creator, + const string_view &type) +{ + return create(room_id, creator, init_room_id, type); +} + +ircd::m::room +ircd::m::create(const id::room &room_id, + const id::user &creator, + const id::room &parent, + const string_view &type) +{ + json::iov event; + json::iov content; + const json::iov::push push[] + { + { event, { "sender", creator }}, + { content, { "creator", creator }}, + }; + + const json::iov::add_if _parent + { + content, !parent.empty() && parent.local() != "init", + { + "parent", parent + } + }; + + const json::iov::add_if _type + { + content, !type.empty() && type != "room", + { + "type", type + } + }; + + room room + { + room_id + }; + + room.create(event, content); + return room; +} void -ircd::m::room::create(const m::id::user &sender, - const m::id::user &creator, +ircd::m::room::create(json::iov &event, json::iov &content) { - const json::iov::add _creator + const json::iov::defaults defaults[] { - content, { "creator", me.user_id } + { event, { "sender", me.user_id }}, + { content, { "creator", me.user_id }}, }; - const auto _content + const json::strung _content { - json::string(content) + content }; - send( + json::iov::set set[] { - { "type", "m.room.create" }, - { "sender", sender }, - { "state_key", "" }, - { "content", _content } - }); + { event, { "type", "m.room.create" }}, + { event, { "state_key", "" }}, + { event, { "content", _content }} + }; + + send(event); } void -ircd::m::room::join(const m::id::user &user_id, - json::iov &content) +ircd::m::join(const m::room::id &room_id, + const m::id::user &user_id) { - const json::iov::set membership_join - { - content, { "membership", "join" } - }; - - membership(user_id, content); + membership(room_id, user_id, "join"); } void -ircd::m::room::leave(const m::id::user &user_id, - json::iov &content) +ircd::m::leave(const m::room::id &room_id, + const m::id::user &user_id) { - const json::iov::set membership_leave - { - content, { "membership", "leave" } - }; - - membership(user_id, content); + membership(room_id, user_id, "leave"); } void -ircd::m::room::membership(const m::id::user &user_id, +ircd::m::membership(const m::id::room &room_id, + const m::id::user &user_id, + const string_view &membership) +{ + json::iov event; + json::iov content; + json::iov::push push[] + { + { event, { "sender", user_id }}, + { content, { "membership", membership }}, + }; + + room room + { + room_id + }; + + room.membership(event, content); +} + +void +ircd::m::room::membership(json::iov &event, json::iov &content) { + const user::id &user_id + { + event.at("sender") + }; + const string_view &membership { content.at("membership") @@ -655,19 +1037,17 @@ ircd::m::room::membership(const m::id::user &user_id, "Member '%s' is already '%s'.", string_view{user_id}, membership }; - //TODO: child iov - const std::string c + const json::strung c //TODO: child iov { - json::string(content) + content }; - json::iov event; - json::iov::push members[] + const json::iov::set _event[] { - { event, json::member { "type", "m.room.member" }}, - { event, json::member { "state_key", user_id }}, - { event, json::member { "sender", user_id }}, - { event, json::member { "content", string_view{c} }} + { event, { "type", "m.room.member" }}, + { event, { "state_key", user_id }}, + { event, { "membership", membership }}, + { event, { "content", string_view{c} }}, }; send(event); @@ -686,7 +1066,7 @@ const }; if(!membership) - return m::events::test(member_event); + return m::vm::test(member_event); const event::query membership_test{[&membership] (const auto &event) @@ -704,11 +1084,12 @@ const return membership == existing_membership; }}; - return m::events::test(member_event && membership_test); + return m::vm::test(member_event && membership_test); } -ircd::m::event::id -ircd::m::room::head(event::id::buf &buf) +/// academic search +std::vector +ircd::m::room::barren(const int64_t &min_depth) const { const event::query query @@ -716,14 +1097,64 @@ const { "room_id", room_id }, }; - events::test(query, [&buf] - (const auto &event) + std::set> ret; + vm::for_each(query, [&ret, &min_depth](const auto &event) { - buf = json::get<"event_id"_>(event); - return true; + if(json::get<"depth"_>(event) < min_depth) + return; + + const json::array prev_events + { + json::get<"prev_events"_>(event) + }; + + for(const json::array &prev_event : prev_events) + { + const event::id &event_id + { + unquote(prev_event[0]) + }; + + ret.erase(std::string{event_id}); + } + + ret.emplace(at<"event_id"_>(event)); }); - return buf; + return { std::begin(ret), std::end(ret) }; +} + +/// academic search +uint64_t +ircd::m::room::maxdepth() +const +{ + event::id::buf buf; + return maxdepth(buf); +} + +/// academic search +uint64_t +ircd::m::room::maxdepth(event::id::buf &buf) +const +{ + const event::query query + { + { "room_id", room_id }, + }; + + int64_t depth{0}; + vm::for_each(query, [&buf, &depth] + (const auto &event) + { + if(json::get<"depth"_>(event) > depth) + { + depth = json::get<"depth"_>(event); + buf = json::get<"event_id"_>(event); + } + }); + + return depth; } ircd::m::event::id::buf @@ -741,23 +1172,22 @@ ircd::m::room::send(const json::members &event) ircd::m::event::id::buf ircd::m::room::send(json::iov &event) { - const json::iov::add room_id + const json::iov::set room_id { event, { "room_id", this->room_id } }; - const id::event::buf generated_event_id + //std::cout << this->room_id << " at " << this->maxdepth() << std::endl; + + // TODO: XXX + // commitment to room here @ exclusive acquisition of depth + + const json::iov::defaults depth { - id::generate, this->room_id.host() + event, { "depth", int64_t(this->maxdepth()) } }; - const json::iov::add event_id - { - event, { "event_id", generated_event_id } - }; - - m::event::insert(event); - return generated_event_id; + return m::vm::commit(event); } /////////////////////////////////////////////////////////////////////////////// @@ -798,14 +1228,15 @@ void ircd::m::user::activate(const json::members &contents) try { + json::iov event; json::iov content; - json::iov::push members[contents.size()]; + json::iov::push push[] + { + { event, { "sender", user_id }}, + { content, { "membership", "join" }}, + }; - size_t i(0); - for(const auto &member : contents) - new (members + i++) json::iov::push(content, member); - - accounts.join(user_id, content); + accounts.membership(event, content); } catch(const m::ALREADY_MEMBER &e) { @@ -818,14 +1249,15 @@ catch(const m::ALREADY_MEMBER &e) void ircd::m::user::deactivate(const json::members &contents) { + json::iov event; json::iov content; - json::iov::push members[contents.size()]; + json::iov::push push[] + { + { event, { "sender", user_id }}, + { content, { "membership", "leave" }}, + }; - size_t i(0); - for(const auto &member : contents) - new (members + i++) json::iov::push(content, member); - - accounts.leave(user_id, content); + accounts.membership(event, content); } void @@ -835,10 +1267,10 @@ try json::iov event; json::iov::push members[] { - { event, json::member { "type", "ircd.password" }}, - { event, json::member { "state_key", user_id }}, - { event, json::member { "sender", user_id }}, - { event, json::member { "content", json::members + { event, { "type", "ircd.password" }}, + { event, { "state_key", user_id }}, + { event, { "sender", user_id }}, + { event, { "content", json::members { { "plaintext", password } }}}, @@ -886,7 +1318,7 @@ const member_event && correct_password }; - return m::events::test(member_event && correct_password); + return m::vm::test(member_event && correct_password); } bool @@ -898,227 +1330,11 @@ const /////////////////////////////////////////////////////////////////////////////// // -// query lib -// -/* -bool -ircd::m::room::members::_rquery_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for type,state_key in room_id", - &query - }; - - //TODO: ??? - static const size_t max_type_size - { - 256 - }; - - const auto key_max - { - room::id::buf::SIZE + max_type_size - }; - - size_t key_len; - char key[key_max]; key[0] = '\0'; - key_len = strlcat(key, room_id, sizeof(key)); - key_len = strlcat(key, "..m.room.member", sizeof(key)); //TODO: prefix protocol - for(auto it(cursor.rbegin(key)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::room::members::_query_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for type,state_key in room_id", - &query - }; - - //TODO: ??? - static const size_t max_type_size - { - 256 - }; - - const auto key_max - { - room::id::buf::SIZE + max_type_size - }; - - size_t key_len; - char key[key_max]; key[0] = '\0'; - key_len = strlcat(key, room_id, sizeof(key)); - key_len = strlcat(key, "..m.room.member", sizeof(key)); //TODO: prefix protocol - for(auto it(cursor.begin(key)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::room::state::_rquery_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for type,state_key in room_id", - &query - }; - - for(auto it(cursor.rbegin(room_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::room::state::_query_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for type,state_key in room_id", - &query - }; - - for(auto it(cursor.begin(room_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::room::events::_rquery_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id in room_id", - &query - }; - - for(auto it(cursor.rbegin(room_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::room::events::_query_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id in room_id", - &query - }; - - for(auto it(cursor.begin(room_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::user::rooms::_rquery_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for room_id in sender", - &query - }; - - for(auto it(cursor.rbegin(user_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::user::rooms::_query_(const event::query<> &query, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id for room_id in sender", - &query - }; - - for(auto it(cursor.begin(user_id)); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::events::_rquery_(const event::query<> &where, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id", - &where - }; - - for(auto it(cursor.rbegin()); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} - -bool -ircd::m::events::_query_(const event::query<> &where, - const event_closure_bool &closure) -const -{ - event::cursor cursor - { - "event_id", - &where - }; - - for(auto it(cursor.begin()); bool(it); ++it) - if(closure(*it)) - return true; - - return false; -} -*/ - -/////////////////////////////////////////////////////////////////////////////// -// -// m/events.h +// m/vm.h // bool -ircd::m::events::test(const event::query<> &where) +ircd::m::vm::test(const event::query<> &where) { return test(where, [](const auto &event) { @@ -1127,7 +1343,7 @@ ircd::m::events::test(const event::query<> &where) } bool -ircd::m::events::test(const event::query<> &where, +ircd::m::vm::test(const event::query<> &where, const closure_bool &closure) { bool ret{false}; @@ -1142,7 +1358,7 @@ ircd::m::events::test(const event::query<> &where, } size_t -ircd::m::events::count(const event::query<> &where) +ircd::m::vm::count(const event::query<> &where) { return count(where, [](const auto &event) { @@ -1151,7 +1367,7 @@ ircd::m::events::count(const event::query<> &where) } size_t -ircd::m::events::count(const event::query<> &where, +ircd::m::vm::count(const event::query<> &where, const closure_bool &closure) { size_t i(0); @@ -1165,14 +1381,14 @@ ircd::m::events::count(const event::query<> &where, /* void -ircd::m::events::rfor_each(const closure &closure) +ircd::m::vm::rfor_each(const closure &closure) { const event::query where{}; rfor_each(where, closure); } void -ircd::m::events::rfor_each(const event::query<> &where, +ircd::m::vm::rfor_each(const event::query<> &where, const closure &closure) { rquery(where, [&closure](const auto &event) @@ -1183,14 +1399,14 @@ ircd::m::events::rfor_each(const event::query<> &where, } bool -ircd::m::events::rquery(const closure_bool &closure) +ircd::m::vm::rquery(const closure_bool &closure) { const event::query where{}; return rquery(where, closure); } bool -ircd::m::events::rquery(const event::query<> &where, +ircd::m::vm::rquery(const event::query<> &where, const closure_bool &closure) { //return _rquery_(where, closure); @@ -1199,7 +1415,7 @@ ircd::m::events::rquery(const event::query<> &where, */ void -ircd::m::events::for_each(const event::query<> &where, +ircd::m::vm::for_each(const event::query<> &where, const closure &closure) { query(where, [&closure](const auto &event) @@ -1210,20 +1426,20 @@ ircd::m::events::for_each(const event::query<> &where, } void -ircd::m::events::for_each(const closure &closure) +ircd::m::vm::for_each(const closure &closure) { const event::query where{}; for_each(where, closure); } bool -ircd::m::events::query(const closure_bool &closure) +ircd::m::vm::query(const closure_bool &closure) { const event::query where{}; return query(where, closure); } -namespace ircd::m::events +namespace ircd::m::vm { bool _query_event_id(const event::query<> &, const closure_bool &); bool _query_in_room_id(const event::query<> &, const closure_bool &, const room::id &); @@ -1231,7 +1447,7 @@ namespace ircd::m::events } bool -ircd::m::events::query(const event::query<> &where, +ircd::m::vm::query(const event::query<> &where, const closure_bool &closure) { switch(where.type) @@ -1247,10 +1463,11 @@ ircd::m::events::query(const event::query<> &where, const auto &room_id{json::get<"room_id"_>(value)}; const auto &type{json::get<"type"_>(value)}; const auto &state_key{json::get<"state_key"_>(value)}; - if(room_id && type && state_key.defined()) + const bool is_state{json::get<"is_state"_>(value) == true}; + if(room_id && type && is_state) return _query_for_type_state_key_in_room_id(where, closure, room_id, type, state_key); - if(room_id && state_key.defined()) + if(room_id && is_state) return _query_for_type_state_key_in_room_id(where, closure, room_id, type, state_key); if(room_id) @@ -1276,7 +1493,7 @@ ircd::m::events::query(const event::query<> &where, return closure(event); }}; - return events::query(lhs, reclosure); + return vm::query(lhs, reclosure); } default: @@ -1287,7 +1504,7 @@ ircd::m::events::query(const event::query<> &where, } bool -ircd::m::events::_query_event_id(const event::query<> &where, +ircd::m::vm::_query_event_id(const event::query<> &where, const closure_bool &closure) { event::cursor cursor @@ -1304,7 +1521,7 @@ ircd::m::events::_query_event_id(const event::query<> &where, } bool -ircd::m::events::_query_in_room_id(const event::query<> &query, +ircd::m::vm::_query_in_room_id(const event::query<> &query, const closure_bool &closure, const room::id &room_id) { @@ -1322,7 +1539,7 @@ ircd::m::events::_query_in_room_id(const event::query<> &query, } bool -ircd::m::events::_query_for_type_state_key_in_room_id(const event::query<> &query, +ircd::m::vm::_query_for_type_state_key_in_room_id(const event::query<> &query, const closure_bool &closure, const room::id &room_id, const string_view &type, @@ -1362,16 +1579,1298 @@ ircd::m::events::_query_for_type_state_key_in_room_id(const event::query<> &quer return false; } -/////////////////////////////////////////////////////////////////////////////// -// -// m/event.h -// - namespace ircd::m { void append_indexes(const event &, db::iov &); } +namespace ircd::m::vm +{ + struct head; +} + +struct ircd::m::vm::head +{ + struct leaf; + + int64_t depth {-1}; + std::set>> heads; +}; + +struct ircd::m::vm::head::leaf +:std::pair +{ + using std::pair::pair; +}; + +namespace ircd::m::vm +{ + std::map> heads; +} + +namespace ircd::m::vm +{ + void eval_future(head &, const event &); + void eval_present(head &, const event &); + void eval_past(head &, const event &); +} + +ircd::ctx::view +ircd::m::vm::inserted +{}; + +uint64_t +ircd::m::vm::current_sequence +{}; + +ircd::m::event::id::buf +ircd::m::vm::join(const room::id &room_id, + json::iov &iov) +{ + const user::id user_id + { + iov.at("sender") + }; + + const auto &hostname{room_id.hostname()}; + const auto &hostport{room_id.hostport()}; + m::log.debug("%s make_join %s to %s from %s:%u", + my_host(), + string_view{user_id}, + string_view{room_id}, + hostname, + hostport); + + char room_id_urle_buf[768]; + const auto room_id_urle + { + urlencode(room_id, room_id_urle_buf), + }; + + char user_id_urle_buf[768]; + const auto user_id_urle + { + urlencode(user_id, user_id_urle_buf) + }; + + const fmt::snstringf url + { + 1024, "_matrix/federation/v1/make_join/%s/%s", room_id_urle, user_id_urle + }; + + m::request request + { + "GET", url, {}, {} + }; + + struct session session + { + { std::string(hostname), hostport } + }; + + unique_buffer buf + { + 64_KiB + }; + + ircd::parse::buffer pb{buf}; + const json::object response + { + session(pb, request) + }; + + const m::event proto + { + response.at("event") + }; + + //TODO: hash prototype + //at<"hashes"_>(proto) + + //TODO: verify prototype + //at<"signatures"_>(proto) + + m::log.debug("%s make_join %s to %s responded. depth: %ld prev: %s auth: %s", + room_id.host(), + string_view{user_id}, + string_view{room_id}, + json::get<"depth"_>(proto), + json::get<"prev_events"_>(proto), + json::get<"auth_events"_>(proto)); + + const json::strung content + { + json::members {{ "membership", "join" }} + }; + + json::iov event; + const json::iov::push push[] + { + { event, { "type", "m.room.member" }}, + { event, { "membership", "join" }}, + { event, { "room_id", room_id }}, + { event, { "origin", my_host() }}, + { event, { "sender", user_id }}, + { event, { "state_key", user_id }}, + { event, { "is_state", true }}, + { event, { "origin_server_ts", ircd::time() }}, + { event, { "depth", at<"depth"_>(proto) }}, + { event, { "content", content }}, + // { event, { "auth_events", at<"auth_events"_>(proto) }}, + // { event, { "prev_events", at<"prev_events"_>(proto) }}, + // { event, { "prev_state", at<"prev_state"_>(proto) }}, + }; + + //TODO: XXX + auto replaced_auth_events{replace(at<"auth_events"_>(proto), '\\', "")}; + auto replaced_prev_events{replace(at<"prev_events"_>(proto), '\\', "")}; + auto replaced_prev_state{replace(at<"prev_state"_>(proto), '\\', "")}; + const json::iov::push replacements[] + { + { event, { "auth_events", replaced_auth_events }}, + { event, { "prev_events", replaced_prev_events }}, + { event, { "prev_state", replaced_prev_state }}, + }; + + const json::strung hash_preimage + { + event + }; + + const fixed_buffer hash + { + sha256{const_buffer{hash_preimage}} + }; + + char hashb64[uint(hash.size() * 1.34) + 2]; + const json::strung hashes + { + json::members + { + { "sha256", b64encode_unpadded(hashb64, hash) } + } + }; + + const json::strung event_id_preimage + { + event + }; + + const fixed_buffer event_id_hash + { + sha256{const_buffer{event_id_preimage}} + }; + + char event_id_hash_b64[uint(event_id_hash.size() * 1.34) + 2]; + const event::id::buf event_id_buf + { + b64encode_unpadded(event_id_hash_b64, event_id_hash), my_host() + }; + + const json::iov::push _event_id + { + event, { "event_id", event_id_buf } + }; + + const json::strung signature_preimage + { + event + }; + + const ed25519::sig sig + { + my::secret_key.sign(const_buffer{signature_preimage}) + }; + + char signature_buffer[128]; + const json::strung signatures{json::members + {{ + my_host(), json::members + { + json::member { my::public_key_id, b64encode_unpadded(signature_buffer, sig) } + } + }}}; + + const json::iov::push _signatures + { + event, { "signatures", signatures } + }; + + char event_id_urle_buf[768]; + const auto event_id_urle + { + urlencode(event.at("event_id"), event_id_urle_buf) + }; + + const fmt::bsprintf<1024> send_join_url + { + "_matrix/federation/v1/send_join/%s/%s", room_id_urle, event_id_urle + }; + + const auto join_event + { + json::strung(event) + }; + + m::log.debug("%s send_join %s to %s sending: %s membership: %s %s", + my_host(), + string_view{user_id}, + string_view{room_id}, + string_view{event.at("type")}, + string_view{event.at("membership")}, + string_view{event.at("event_id")}); + + m::request send_join_request + { + "PUT", send_join_url, {}, join_event + }; + + unique_buffer send_join_buf + { + 64_KiB + }; + + ircd::parse::buffer sjpb{send_join_buf}; + const json::array send_join_response + { + session(sjpb, send_join_request) + }; + + const auto status + { + send_join_response.at(0) + }; + + const json::object data + { + send_join_response.at(1) + }; + + const json::array state + { + data.at("state") + }; + + const json::array auth_chain + { + data.at("auth_chain") + }; + + m::log.debug("%s %u send_join %s to %s responded with %zu state and %zu auth_chain events", + room_id.host(), + status, + string_view{user_id}, + string_view{room_id}, + state.count(), + auth_chain.count()); + + vm::eval(state); + vm::eval(event); + return event_id_buf; +} + +namespace ircd::m::vm +{ + //void mtrace(const id::event &event_id, const tracer &closure); +} + +void +ircd::m::vm::trace(const id::event &event_id, + const tracer &closure) +{ + id::event::buf id{event_id}; + unique_buffer buf[8]; + + while(1) + { + buf[0] = { 64_KiB }; + const event event + { + get(id, buf[0]) + }; + + const json::array &prev_events + { + json::get<"prev_events"_>(event) + }; + + const auto count + { + prev_events.count() + }; + + if(!count) + break; + + event::fetch tab[count]; + for(size_t i(0); i < count; ++i) + { + const json::array &prev_event + { + prev_events[i] + }; + + tab[i].event_id = unquote(prev_event[0]); + buf[i + 1] = { 64_KiB }; + tab[i].buf = buf[i + 1]; + } + + size_t j(0); + m::io::acquire({tab, count}); + for(size_t i(0); i < count; ++i) + { + if(tab[i].error) + continue; + else + ++j; + + const auto &event + { + tab[i].pdu + }; + + if(!closure(event, id)) + return; + } + + if(!j) + break; + } +} + +void +ircd::m::vm::state(const room::id &room_id, + const event::id &event_id) +try +{ + const unique_buffer buf + { + 16_MiB //TODO: XXX + }; + + room::state::fetch tab + { + event_id, room_id, buf + }; + + io::acquire(tab); + + const json::array &auth_chain + { + tab.auth_chain + }; + + const json::array &pdus + { + tab.pdus + }; + + std::vector events(pdus.count()); + std::transform(begin(pdus), end(pdus), begin(events), [] + (const json::object &event) -> m::event + { + return event; + }); + + vm::eval(events); +} +catch(const std::exception &e) +{ + log.error("Acquiring state for %s at %s: %s", + string_view{room_id}, + string_view{event_id}, + e.what()); + throw; +} + +void +ircd::m::vm::backfill(const room::id &room_id, + const event::id &event_id, + const size_t &limit) +try +{ + const unique_buffer buf + { + 16_MiB //TODO: XXX + }; + + room::fetch tab + { + event_id, room_id, buf + }; + + io::acquire(tab); + + const json::array &auth_chain + { + tab.auth_chain + }; + + const json::array &pdus + { + tab.pdus + }; + + std::vector events(pdus.count()); + std::transform(begin(pdus), end(pdus), begin(events), [] + (const json::object &event) -> m::event + { + return event; + }); + + vm::eval(events); +} +catch(const std::exception &e) +{ + log.error("Acquiring backfill for %s at %s: %s", + string_view{room_id}, + string_view{event_id}, + e.what()); + throw; +} + +ircd::json::object +ircd::m::vm::acquire(const id::event &event_id, + const mutable_buffer &buf) +{ + const auto event + { + get(event_id, buf) + }; + + vm::eval(m::event{event}); + return event; +} + +size_t +ircd::m::vm::acquire(const vector_view &event_id, + const vector_view &buf) +{ + std::vector tabs(event_id.size()); + for(size_t i(0); i < event_id.size(); ++i) + tabs[i] = event::fetch { event_id[i], buf[i] }; + + size_t i(0); + io::acquire(vector_view(tabs)); + for(const auto &fetch : tabs) + if(fetch.pdu) + { + i++; + vm::eval(m::event{fetch.pdu}); + } + + return i; +} + +/// Insert a new event originating from this server. +/// +/// Figure 1: +/// in . +/// ___:::::::__V <-- this function +/// | ||||||| // +/// | \\|// //| +/// | ||| // | +/// | ||// | +/// | !!! | +/// | * | <----- IRCd core +/// | |//|||\\| | +/// |/|/|/|\|\|\| <---- release commitment propagation cone +/// out +/// +/// This function takes an event object vector and adds our origin and event_id +/// and hashes and signature and attempts to inject the event into the core. +/// The caller is expected to have done their best to check if this event will +/// succeed once it hits the core because failures blow all this effort. The +/// caller's ircd::ctx will obviously yield for evaluation, which may involve +/// requests over the internet in the worst case. Nevertheless, the evaluation, +/// write and release sequence of the core commitment is designed to allow the +/// caller to service a usual HTTP request conveying success or error without +/// hanging their client too much. +/// +ircd::m::event::id::buf +ircd::m::vm::commit(json::iov &iov) +{ + const json::iov::set set[] + { + { iov, { "origin_server_ts", ircd::time() }}, + { iov, { "origin", my_host() }}, + { iov, { "is_state", iov.has("state_key") }}, + }; + + if(iov.has("room_id")) + { + const room::id &room_id + { + iov.at("room_id") + }; + + if(room_id.host() != my_host()) + return join(room_id, iov); + } + + // Need this for now + const unique_buffer scratch + { + 64_KiB + }; + + auto preimage + { + json::stringify(mutable_buffer{scratch}, iov) + }; + + const fixed_buffer event_id_hash + { + sha256{const_buffer{preimage}} + }; + + char event_id_hash_b64[uint(event_id_hash.size() * 1.34) + 2]; + const event::id::buf event_id_buf + { + b64encode_unpadded(event_id_hash_b64, event_id_hash), my_host() + }; + + const json::iov::set event_id + { + iov, { "event_id", event_id_buf } + }; + + preimage = + { + json::stringify(mutable_buffer{scratch}, iov) + }; + + const fixed_buffer hash + { + sha256{const_buffer{preimage}} + }; + + fixed_buffer hashb64; + const json::iov::set hashes + { + iov, json::member + { + "hashes", json::members + { + { "sha256", b64encode_unpadded(hashb64, hash) } + } + } + }; + + preimage = + { + json::stringify(mutable_buffer{scratch}, iov) + }; + + const ed25519::sig sig + { + my::secret_key.sign(const_buffer{preimage}) + }; + + char signature_buffer[128]; + const json::iov::set signatures + { + iov, json::member + { + "signatures", json::members + {{ + my_host(), json::members + { + json::member { my::public_key_id, b64encode_unpadded(signature_buffer, sig) } + } + }} + } + }; + + const m::event event + { + iov + }; + + if(!json::get<"type"_>(event)) + throw BAD_JSON("Required event field: type"); + + if(!json::get<"sender"_>(event)) + throw BAD_JSON("Required event field: sender"); + + log.debug("injecting event %s '%s' from %s :%s %s (mark: %ld)", + at<"event_id"_>(event), + at<"type"_>(event), + at<"sender"_>(event), + at<"origin"_>(event), + json::get<"is_state"_>(event)? "state" : "", + vm::current_sequence); + + ircd::timer timer; + + vm::eval(event); + + log.debug("committed event %s '%s' from %s :%s %s (mark: %ld time: %ld$ms)", + at<"event_id"_>(event), + at<"type"_>(event), + at<"sender"_>(event), + at<"origin"_>(event), + json::get<"is_state"_>(event)? "state" : "", + vm::current_sequence, + timer.at().count()); + + return event_id_buf; +} + +namespace ircd::m::vm +{ + void fetch_head(const room::id &, head &, const event &); + head &dome(const room::id &room_id, const event &); + void eval_future(head &, const event &); + void write(const event &, db::iov &txn); +} + +/// Processes a bulk set of events similar to eval(event) except with +/// some transactionality and optimization over the set (i.e if two events +/// in the set cancel each other out their effects might be ignored rather +/// than invoking those operations with IRCd etc). +/// +/// TEMP: Only calls single eval() in a loop for now. +void +ircd::m::vm::eval(const vector_view &events) +{ + for(const auto &event : events) + eval(m::event{event}); +} + +void +ircd::m::vm::eval(const json::array &events) +{ + for(const auto &event : events) + eval(m::event{event}); +} + +/// Processes any event from any place from any time and does whatever is +/// necessary to validate, reject, learn from new information, ignore old +/// information and advance the state of IRCd as best as possible. +/// +void +ircd::m::vm::eval(const event &event) +{ + std::cout << event << std::endl; + + const auto &event_id + { + at<"event_id"_>(event) + }; + + const std::string &room_id + { + at<"room_id"_>(event) + }; + + // Head fetch phase. If the head is not loaded into RAM then it + // may have to be requested with IO and recursive evaluation which + // could be a massive suspension for this stack. This usually only + // occurs on the first eval for a room after IRCd startup or after + // some other cache eviction. + auto &head + { + vm::dome(room_id, event) + }; + + log.debug("evaluating event %s '%s' from %s :%s %s %s to %ld", + at<"event_id"_>(event), + at<"type"_>(event), + at<"sender"_>(event), + at<"origin"_>(event), + json::get<"is_state"_>(event)? "state" : "", + reflect(temporality(event, head.depth)), + head.depth); + + switch(temporality(event, head.depth)) + { + case event.temporality::FUTURE: + eval_future(head, event); + break; + + case event.temporality::PRESENT: + eval_present(head, event); + break; + + case event.temporality::PAST: + eval_past(head, event); + break; + } + + log.debug("committing event %s '%s' from %s :%s %s", + at<"event_id"_>(event), + at<"type"_>(event), + at<"sender"_>(event), + at<"origin"_>(event), + json::get<"is_state"_>(event)? "state" : ""); + + db::iov txn + { + *event::events + }; + + write(event, txn); + vm::current_sequence++; + + log.debug("release sequence %s '%s' from %s :%s %s", + at<"event_id"_>(event), + at<"type"_>(event), + at<"sender"_>(event), + at<"origin"_>(event), + json::get<"is_state"_>(event)? "state" : ""); + + vm::inserted.notify(event); +} + +void +ircd::m::vm::write(const event &event, + db::iov &txn) +{ + db::iov::append + { + txn, at<"event_id"_>(event), event + }; + + if(!txn.has(db::op::SET, "is_state")) + db::iov::append + { + txn, db::delta + { + "is_state", at<"event_id"_>(event), byte_view + { + defined(json::get<"state_key"_>(event)) + } + } + }; + + append_indexes(event, txn); + txn(); +} + +void +ircd::m::vm::eval_past(head &head, + const event &event) +{ +} + +void +ircd::m::vm::eval_present(head &head, + const event &event) +{ + const auto &depth + { + json::get<"depth"_>(event) + }; + + switch(depth) + { + case 0: + //TODO: XXX + //std::cout << "NO DEPTH?" << std::endl; + break; + + case 1: + std::cout << "CREATE?" << std::endl; + return; + + default: + break; + } + + const json::array &prev_events + { + json::get<"prev_events"_>(event) + }; + + for(const json::array &prev_event : prev_events) + { + std::string prev_event_id + { + replace(unquote(prev_event[0]), '\\', "") + }; + + const unique_buffer buf + { + 64_KiB + }; + + try + { + acquire(prev_event_id, buf); + } + catch(const std::exception &e) + { + log.error("Failed to acquire %s: %s", + string_view{prev_event_id}, + e.what()); + } + } +} + +void +ircd::m::vm::eval_future(head &head, + const event &event) +{ + const auto push_prev{[&head](const json::array &prev_events) + { + for(const json::array &prev_event : prev_events) + { + const std::string &prev_event_id + { + unquote(prev_event[0]) + }; + + auto it + { + head.heads.lower_bound({prev_event_id, 0}) + }; + + if(it == head.heads.end()) try + { + const unique_buffer buf + { + 64_KiB + }; + + acquire(prev_event_id, buf); + } + catch(const std::exception &e) + { + log.error("Failed to acquire %s: %s", + string_view{prev_event_id}, + e.what()); + } + + it = head.heads.lower_bound({prev_event_id, 0}); + if(it != head.heads.end() && it->first == prev_event_id) + head.heads.erase(it); + } + }}; + + push_prev(json::get<"prev_events"_>(event)); + push_prev(json::get<"prev_state"_>(event)); + push_prev(json::get<"auth_events"_>(event)); + head.depth = json::get<"depth"_>(event); + head.heads.emplace(head::leaf + { + at<"event_id"_>(event), json::get<"depth"_>(event) + }); +} + +ircd::m::vm::head & +ircd::m::vm::dome(const room::id &room_id, + const event &event) +{ + head &ret + { + heads[std::string(room_id)] + }; + + if(ret.depth < 0) + fetch_head(room_id, ret, event); + + return ret; +} + +void +ircd::m::vm::fetch_head(const room::id &room_id, + head &head, + const event &event) +{ + const room room + { + room_id + }; + + const auto event_ids + { + room.barren() + }; + + if(event_ids.empty()) + { + const event::id &event_id + { + at<"event_id"_>(event) + }; + + log.debug("No heads available for %s using %s", + string_view{room_id}, + string_view{event_id}); + + if(!my_host(room_id.host())) + state(room_id, event_id); + + head.depth = json::get<"depth"_>(event); + head.heads.emplace(head::leaf + { + at<"event_id"_>(event), json::get<"depth"_>(event), + }); + + if(!my_host(room_id.host())) + backfill(room_id, event_id, 64); + return; + } + + std::vector> buf(event_ids.size()); + std::vector tab(event_ids.size()); + for(size_t i(0); i < event_ids.size(); ++i) + { + buf[i] = unique_buffer + { + 64_KiB + }; + + tab[i] = event::fetch + { + event_ids[i], buf[i] + }; + } + + io::acquire(vector_view(tab)); + + for(const auto &t : tab) + { + const m::event &event + { + t.pdu + }; + + const auto &event_id + { + at<"event_id"_>(event) + }; + + const auto &depth + { + at<"depth"_>(event) + }; + + if(head.depth < depth) + head.depth = depth; + + head.heads.emplace(head::leaf{event_id, depth}); + log.debug("Found %s head for %s depth: %ld", + string_view{event_id}, + string_view{room.room_id}, + depth); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// m/io.h +// + +ircd::json::object +ircd::m::io::get(const id::event &event_id, + const mutable_buffer &buf) +{ + const m::event::query query + { + { "event_id", event_id }, + }; + + json::object ret; + const auto test{[&buf, &ret](const auto &event) + { + ret = stringify(mutable_buffer{buf}, event); + return true; + }}; + + // Attempt to find the event in the local database and return it + if(m::vm::test(query, test)) + return ret; + + // Prevents any queries to the network going to own host. + if(my_host(event_id.host())) + throw m::NOT_FOUND + { + "Event '%s' not found", string_view{event_id} + }; + + event::fetch tab + { + event_id, buf + }; + + ret = io::acquire(tab); + return ret; +} + +ircd::json::array +ircd::m::io::acquire(room::state::fetch &tab) +{ + acquire({&tab, 1}); + if(unlikely(bool(tab.error))) + std::rethrow_exception(tab.error); + + return tab.pdus; +} + +size_t +ircd::m::io::acquire(vector_view tab) +{ + const auto count + { + tab.size() + }; + + std::string url[count]; + std::string query[count]; + m::io::request request[count]; + struct session session[count]; + for(size_t i(0); i < count; ++i) try + { + static char tmp[768]; + url[i] = fmt::snstringf + { + 1024, "_matrix/federation/v1/state/%s/", urlencode(tab[i].room_id, tmp) + }; + + query[i] = fmt::snstringf + { + 1024, "event_id=%s", urlencode(tab[i].event_id, tmp) + }; + + request[i] = + { + "GET", url[i], query[i], {} + }; + + session[i] = + { + tab[i].hint? tab[i].hint : tab[i].event_id.hostname() + }; + + request[i].destination = session[i].destination; + request[i](session[i].server); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s in room_id %s will not succeed: %s", + string_view{tab[i].event_id}, + string_view{tab[i].room_id}, + e.what()); + } + + size_t ret(0); + json::object response[count]; + for(size_t i(0); i < count; ++i) try + { + if(bool(tab[i].error)) + continue; + + ircd::parse::buffer pb{tab[i].buf}; + response[i] = m::io::response(session[i].server, pb); + tab[i].auth_chain = response[i]["auth_chain"]; + tab[i].pdus = response[i]["pdus"]; + //TODO: check event id + //TODO: check hashes + //TODO: check signatures + ret += !tab[i].error; + + log.debug("%s sent us event %s in room %s pdus: %zu (size: %zu) error: %d", + string(net::remote{session[i].server}), + string_view{tab[i].event_id}, + string_view{tab[i].room_id}, + json::array{response[i]["pdus"]}.count(), + string_view{response[i]}.size(), + bool(tab[i].error)); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s failed: %s", + string_view{tab[i].event_id}, + e.what()); + } + + return ret; +} + +ircd::json::array +ircd::m::io::acquire(room::fetch &tab) +{ + acquire({&tab, 1}); + if(unlikely(bool(tab.error))) + std::rethrow_exception(tab.error); + + return tab.pdus; +} + +size_t +ircd::m::io::acquire(vector_view tab) +{ + const auto count + { + tab.size() + }; + + std::string url[count]; + std::string query[count]; + m::io::request request[count]; + struct session session[count]; + for(size_t i(0); i < count; ++i) try + { + static char tmp[768]; + url[i] = fmt::snstringf + { + 1024, "_matrix/federation/v1/backfill/%s/", urlencode(tab[i].room_id, tmp) + }; + + query[i] = fmt::snstringf + { + 1024, "limit=%zu&v=%s", tab[i].limit, urlencode(tab[i].event_id, tmp) + }; + + session[i] = + { + tab[i].hint? tab[i].hint : tab[i].event_id.hostname() + }; + + request[i] = + { + "GET", url[i], query[i], {} + }; + + request[i].destination = session[i].destination; + request[i](session[i].server); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s in room_id %s will not succeed: %s", + string_view{tab[i].event_id}, + string_view{tab[i].room_id}, + e.what()); + } + + size_t ret(0); + json::object response[count]; + for(size_t i(0); i < count; ++i) try + { + if(bool(tab[i].error)) + continue; + + ircd::parse::buffer pb{tab[i].buf}; + response[i] = m::io::response(session[i].server, pb); + tab[i].auth_chain = response[i]["auth_chain"]; + tab[i].pdus = response[i]["pdus"]; + //TODO: check event id + //TODO: check hashes + //TODO: check signatures + ret += !tab[i].error; + + log.debug("%s sent us event %s in room %s pdus: %zu (size: %zu) error: %d", + string(net::remote{session[i].server}), + string_view{tab[i].event_id}, + string_view{tab[i].room_id}, + json::array{response[i]["pdus"]}.count(), + string_view{response[i]}.size(), + bool(tab[i].error)); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s failed: %s", + string_view{tab[i].event_id}, + e.what()); + } + + return ret; +} + +ircd::json::object +ircd::m::io::acquire(event::fetch &tab) +{ + acquire({&tab, 1}); + if(unlikely(bool(tab.error))) + std::rethrow_exception(tab.error); + + return tab.pdu; +} + +size_t +ircd::m::io::acquire(vector_view tab) +{ + const auto count + { + tab.size() + }; + + std::string url[count]; + m::io::request request[count]; + struct session session[count]; + for(size_t i(0); i < count; ++i) try + { + static char tmp[768]; + url[i] = fmt::snstringf + { + 1024, "_matrix/federation/v1/event/%s/", urlencode(tab[i].event_id, tmp) + }; + + session[i] = + { + tab[i].hint? tab[i].hint : tab[i].event_id.hostname() + }; + + request[i] = + { + "GET", url[i], {}, {} + }; + + request[i].destination = session[i].destination; + request[i](session[i].server); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s will not succeed: %s", + string_view{tab[i].event_id}, + e.what()); + } + + size_t ret(0); + json::object response[count]; + for(size_t i(0); i < count; ++i) try + { + if(bool(tab[i].error)) + continue; + + ircd::parse::buffer pb{tab[i].buf}; + response[i] = m::io::response{session[i].server, pb}; + tab[i].pdu = json::array{response[i]["pdus"]}[0]; + //TODO: check event id + //TODO: check hashes + //TODO: check signatures + ret += !tab[i].error; + + log.debug("%s sent us event %s pdus: %zu (size: %zu) error: %d", + string(net::remote{session[i].server}), + string_view{tab[i].event_id}, + json::array{response[i]["pdus"]}.count(), + string_view{response[i]}.size(), + bool(tab[i].error)); + } + catch(const std::exception &e) + { + tab[i].error = std::make_exception_ptr(e); + log.warning("request for event_id %s failed: %s", + string_view{tab[i].event_id}, + e.what()); + } + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// m/event.h +// + // vtable for the db::query partially specialized to m::event as its tuple template ircd::db::query::~query(); @@ -1379,59 +2878,6 @@ ircd::database * ircd::m::event::events {}; -ircd::ctx::view -ircd::m::event::inserted -{}; - -ircd::m::event::id::buf -ircd::m::event::head -{}; - -void -ircd::m::event::insert(json::iov &iov) -{ - const id::event::buf generated_event_id - { - iov.has("event_id")? id::event::buf{} : id::event::buf{id::generate, my_host()} - }; - - const json::iov::add_if event_id - { - iov, !iov.has("event_id"), { "event_id", generated_event_id } - }; - - const json::iov::set origin_server_ts - { - iov, { "origin_server_ts", ircd::time() } - }; - - const m::event event - { - iov - }; - - if(!json::at<"type"_>(event)) - throw BAD_JSON("Required event field: '%s'", name::type); - - if(!json::at<"sender"_>(event)) - throw BAD_JSON("Required event field: '%s'", name::sender); - - db::iov txn - { - *event::events - }; - - db::iov::append - { - txn, json::at<"event_id"_>(event), iov - }; - - append_indexes(event, txn); - txn(*event::events); - event::head = json::at<"event_id"_>(event); - event::inserted.notify(event); -} - ircd::m::event::const_iterator ircd::m::event::find(const event::id &id) { @@ -1439,6 +2885,67 @@ ircd::m::event::find(const event::id &id) return c.begin(string_view{id}); } +ircd::m::event::temporality +ircd::m::temporality(const event &event, + const int64_t &rel) +{ + const auto &depth + { + json::get<"depth"_>(event) + }; + + return depth > rel? event::temporality::FUTURE: + depth == rel? event::temporality::PRESENT: + event::temporality::PAST; +} + +ircd::m::event::lineage +ircd::m::lineage(const event &event) +{ + const json::array prev[] + { + json::get<"prev_events"_>(event), + json::get<"auth_events"_>(event), + json::get<"prev_state"_>(event), + }; + + const auto count{std::accumulate(begin(prev), end(prev), size_t(0), [] + (auto ret, const auto &array) + { + return ret += array.count(); + })}; + + return count > 1? event::lineage::MERGE: + count == 1? event::lineage::FORWARD: + event::lineage::ROOT; +} + +ircd::string_view +ircd::m::reflect(const event::lineage &lineage) +{ + switch(lineage) + { + case event::lineage::MERGE: return "MERGE"; + case event::lineage::FORWARD: return "FORWARD"; + case event::lineage::ROOT: return "ROOT"; + } + + return "?????"; +} + +ircd::string_view +ircd::m::reflect(const event::temporality &temporality) +{ + switch(temporality) + { + case event::temporality::FUTURE: return "FUTURE"; + case event::temporality::PRESENT: return "PRESENT"; + case event::temporality::PAST: return "PAST"; + } + + return "?????"; +} + namespace ircd::m { struct indexer; @@ -1515,7 +3022,7 @@ const index[0] = '\0'; const auto function { - [&index](const auto &val) + [&index](auto &val) { strlcat(index, byte_view{val}, buf_max); } @@ -1523,6 +3030,7 @@ const at(event, col_b, function); at(event, col_a, function); + db::iov::append { iov, db::delta @@ -1559,7 +3067,7 @@ ircd::m::indexer::concat_v::operator()(const event &event, db::iov &iov) const { - if(!iov.has(db::op::SET, col_c) || !iov.has(db::op::SET, col_b)) + if(!iov.has(db::op::SET, col_c) || !iov.has(db::op::SET, col_b) || !iov.has(db::op::SET, col_a)) return; static const size_t buf_max @@ -1571,7 +3079,7 @@ const index[0] = '\0'; const auto concat { - [&index](const auto &val) + [&index](auto &val) { strlcat(index, byte_view{val}, buf_max); } @@ -1581,7 +3089,7 @@ const at(event, col_b, concat); string_view val; - at(event, col_a, [&val](const auto &_val) + at(event, col_a, [&val](auto &_val) { val = byte_view{_val}; }); @@ -1636,7 +3144,7 @@ const index[0] = '\0'; const auto concat { - [&index](const auto &val) + [&index](auto &val) { strlcat(index, byte_view{val}, buf_max); } @@ -1648,7 +3156,7 @@ const at(event, col_b1, concat); string_view val; - at(event, col_a, [&val](const auto &_val) + at(event, col_a, [&val](auto &_val) { val = byte_view{_val}; }); diff --git a/ircd/resource.cc b/ircd/resource.cc index 3dbeb6bb4..56b0514d7 100644 --- a/ircd/resource.cc +++ b/ircd/resource.cc @@ -131,7 +131,7 @@ try const bool result { - m::events::test(query, [&request, &access_token](const m::event &event) + m::vm::test(query, [&request, &access_token](const m::event &event) { // Checks if the access token has expired. Tokens are expired when // an m.room.redaction event is issued for the ircd.access_token @@ -187,7 +187,7 @@ ircd::verify_origin(client &client, const auto verified { - m::verify_x_matrix_authorization(authorization, method.name, uri, request.content) + m::io::verify_x_matrix_authorization(authorization, method.name, uri, request.content) }; if(!verified) diff --git a/modules/client/createroom.cc b/modules/client/createroom.cc index be2bcc7e1..486695ec4 100644 --- a/modules/client/createroom.cc +++ b/modules/client/createroom.cc @@ -41,38 +41,31 @@ try unquote(request["visibility"]) }; + const m::id::user sender_id + { + request.user_id + }; + const m::id::room::buf room_id { - m::id::generate, "localhost" + m::id::generate, my_host() }; - const m::id::user::buf sender_id + json::iov event; + json::iov content; + const json::iov::push push[] { - m::id::generate, "localhost" + { event, { "sender", sender_id }}, + { content, { "creator", sender_id }}, }; - const m::id::event::buf create_event_id + m::room room { - m::id::generate, "localhost", + room_id }; - const time_t origin_server_ts - { - time(NULL) - }; -/* - db::object event - { - create_event_id - }; + room.create(event, content); - db::write - ({ - { event["type"], "m.room.create" }, - { event["room_id"], room_id }, - { event["origin_server_ts"], binary_view(origin_server_ts) }, - }); -*/ return resource::response { client, http::CREATED, diff --git a/modules/client/events.cc b/modules/client/events.cc index bf369fecb..8bf83930f 100644 --- a/modules/client/events.cc +++ b/modules/client/events.cc @@ -41,14 +41,14 @@ get_events(client &client, const resource::request &request) }; size_t i(0); - m::events::for_each(query, [&i](const auto &event) + m::vm::for_each(query, [&i](const auto &event) { ++i; }); size_t j(0); json::value ret[i]; - m::events::for_each(query, [&i, &j, &ret](const m::event &event) + m::vm::for_each(query, [&i, &j, &ret](const m::event &event) { if(j < i) ret[j++] = event; diff --git a/modules/client/rooms.cc b/modules/client/rooms.cc index a9bee2f02..9a15dd91c 100644 --- a/modules/client/rooms.cc +++ b/modules/client/rooms.cc @@ -45,27 +45,15 @@ get_messages(client &client, const resource::request &request, const m::room::id &room_id) { - const m::event::query event_in_room + const m::event::query query { - { "room_id", room_id } - }; - - const m::event::query event_not_state - { - [](const auto &event) - { - return !defined(json::get<"state_key"_>(event)); - } - }; - - const auto query - { - event_in_room && event_not_state + { "room_id", room_id }, + { "is_state", false }, }; const size_t count { - std::min(m::events::count(query), 128UL) + std::min(m::vm::count(query), 128UL) }; if(!count) @@ -75,8 +63,8 @@ get_messages(client &client, }; size_t j(0); - json::value ret[count]; - m::events::for_each(query, [&count, &j, &ret] + std::vector ret(count); + m::vm::for_each(query, [&count, &j, &ret] (const auto &event) { if(j < count) @@ -87,7 +75,7 @@ get_messages(client &client, { client, json::members { - { "chunk", json::value { ret, j } } + { "chunk", json::value { ret.data(), j } } } }; } @@ -102,12 +90,11 @@ get_members(client &client, { { "room_id", room_id }, { "type", "m.room.member" }, - { "state_key", "" }, }; const auto count { - m::events::count(query) + m::vm::count(query) }; if(!count) @@ -117,8 +104,8 @@ get_members(client &client, }; size_t j(0); - json::value ret[count]; - m::events::for_each(query, [&count, &j, &ret] + std::vector ret(count); + m::vm::for_each(query, [&count, &j, &ret] (const auto &event) { if(j < count) @@ -129,7 +116,7 @@ get_members(client &client, { client, json::members { - { "chunk", json::value { ret, j } } + { "chunk", json::value { ret.data(), j } } } }; } @@ -141,7 +128,7 @@ get_state(client &client, { const auto count { - m::events::count(query) + m::vm::count(query) }; if(!count) @@ -151,8 +138,8 @@ get_state(client &client, }; size_t j(0); - json::value ret[count]; - m::events::for_each(query, [&count, &j, &ret] + std::vector ret(count); + m::vm::for_each(query, [&count, &j, &ret] (const auto &event) { if(j < count) @@ -163,7 +150,7 @@ get_state(client &client, { client, json::value { - ret, j + ret.data(), j } }; } @@ -180,6 +167,7 @@ get_state(client &client, { "room_id", room_id }, { "type", type }, { "state_key", state_key }, + { "is_state", true }, }; return get_state(client, request, query); @@ -194,7 +182,8 @@ get_state(client &client, const m::event::query query { { "room_id", room_id }, - { "type", type } + { "type", type }, + { "is_state", true }, }; return get_state(client, request, query); @@ -205,14 +194,16 @@ get_state(client &client, const resource::request &request, const m::room::id &room_id) { + char type_buf[uint(256 * 1.34 + 1)]; const string_view &type { - request.parv[2] + urldecode(request.parv[2], type_buf) }; + char skey_buf[uint(256 * 1.34 + 1)]; const string_view &state_key { - request.parv[3] + urldecode(request.parv[3], skey_buf) }; if(type && state_key) @@ -224,7 +215,7 @@ get_state(client &client, const m::event::query query { { "room_id", room_id }, - { "state_key", "" }, + { "is_state", true }, }; return get_state(client, request, query); @@ -235,29 +226,36 @@ get_context(client &client, const resource::request &request, const m::room::id &room_id) { - const m::event::id &event_id + m::event::id::buf event_id { - request.parv[2] + urldecode(request.parv[2], event_id) }; - const auto it + const m::event::query query { - m::event::find(event_id) + { "room_id", room_id }, + { "event_id", event_id }, }; - if(!it) + std::string ret; + const bool found + { + m::vm::test(query, [&ret] + (const m::event &event) + { + ret = json::strung{event}; + return true; + }) + }; + + if(!found) throw m::NOT_FOUND{"event not found"}; - const auto event - { - json::string(*it) - }; - return resource::response { client, json::members { - { "event", event } + { "event", ret } } }; } @@ -265,10 +263,10 @@ get_context(client &client, resource::response get_rooms(client &client, const resource::request &request) { - if(request.parv.size() != 2) + if(request.parv.size() < 2) throw m::error { - http::MULTIPLE_CHOICES, "/rooms command required" + http::MULTIPLE_CHOICES, "M_NOT_FOUND", "/rooms command required" }; m::room::id::buf room_id @@ -276,7 +274,10 @@ get_rooms(client &client, const resource::request &request) urldecode(request.parv[0], room_id) }; - const string_view &cmd{request.parv[1]}; + const string_view &cmd + { + request.parv[1] + }; if(cmd == "context") return get_context(client, request, room_id); @@ -306,11 +307,17 @@ put_send(client &client, const resource::request &request, const m::room::id &room_id) { + if(request.parv.size() < 3) + throw m::BAD_REQUEST{"type parameter missing"}; + const string_view &type { request.parv[2] }; + if(request.parv.size() < 4) + throw m::BAD_REQUEST{"txnid parameter missing"}; + const string_view &txnid { request.parv[3] @@ -320,17 +327,17 @@ put_send(client &client, const json::iov::push _type { - event, "type", type + event, { "type", type } }; const json::iov::push _sender { - event, "sender", request.user_id + event, { "sender", request.user_id } }; const json::iov::push _content { - event, "content", json::object{request} + event, { "content", json::object{request} } }; m::room room @@ -352,10 +359,49 @@ put_send(client &client, }; } +resource::response +put_typing(client &client, + const resource::request &request, + const m::room::id &room_id) +{ + if(request.parv.size() < 3) + throw m::BAD_REQUEST{"user_id parameter missing"}; + + m::user::id::buf user_id + { + urldecode(request.parv[2], user_id) + }; + + static const milliseconds timeout_default + { + 30 * 1000 + }; + + const auto timeout + { + request.get("timeout", timeout_default) + }; + + const auto typing + { + request.at("typing") + }; + + log::debug("%s typing: %d timeout: %ld", + user_id, + typing, + timeout.count()); + + return resource::response + { + client, http::OK + }; +} + resource::response put_rooms(client &client, const resource::request &request) { - if(request.parv.size() != 2) + if(request.parv.size() < 2) throw m::BAD_REQUEST{"/rooms command required"}; m::room::id::buf room_id @@ -371,6 +417,9 @@ put_rooms(client &client, const resource::request &request) if(cmd == "send") return put_send(client, request, room_id); + if(cmd == "typing") + return put_typing(client, request, room_id); + throw m::NOT_FOUND{"/rooms command not found"}; } @@ -387,19 +436,20 @@ post_receipt(client &client, const resource::request &request, const m::room::id &room_id) { - if(request.parv.size() != 4) + if(request.parv.size() < 4) throw m::BAD_REQUEST{"receipt type and event_id required"}; const string_view &receipt_type{request.parv[2]}; const string_view &event_id{request.parv[3]}; - std::cout << "type: " << receipt_type << " eid: " << event_id << std::endl; + //std::cout << "type: " << receipt_type << " eid: " << event_id << std::endl; + return {}; } resource::response post_rooms(client &client, const resource::request &request) { - if(request.parv.size() != 2) + if(request.parv.size() < 2) throw m::BAD_REQUEST{"/rooms command required"}; m::room::id::buf room_id @@ -414,6 +464,11 @@ post_rooms(client &client, if(cmd == "receipt") return post_receipt(client, request, room_id); + + throw m::error + { + http::MULTIPLE_CHOICES, "M_NOT_FOUND", "/rooms command required" + }; } resource::method method_post diff --git a/modules/client/sync.cc b/modules/client/sync.cc index ca93596f5..e6afe6422 100644 --- a/modules/client/sync.cc +++ b/modules/client/sync.cc @@ -133,15 +133,15 @@ sync(client &client, const resource::request &request) { "state_key", request.query.at("access_token") }, }; - m::event::id::buf head; - if(!m::events::test(query, [&head](const auto &event) + int64_t sequence{0}; + if(!m::vm::test(query, [&sequence](const auto &event) { const json::object &content { at<"content"_>(event) }; - head = unquote(content.at("event_id")); + sequence = content.at("sequence"); return true; })) throw m::NOT_FOUND{"since parameter invalid"}; @@ -300,15 +300,15 @@ try { while(1) try { - std::unique_lock lock + std::unique_lock lock { - m::event::inserted + m::vm::inserted }; // reference to the event on the inserter's stack const auto &event { - m::event::inserted.wait(lock) + m::vm::inserted.wait(lock) }; if(!syncpoll::polling.empty()) @@ -485,7 +485,7 @@ initial_sync_room(client &client, { "is_state", true }, }; - m::events::for_each(state_query, [&state](const auto &event) + m::vm::for_each(state_query, [&state](const auto &event) { state.emplace_back(json::strung(event)); }); @@ -503,7 +503,7 @@ initial_sync_room(client &client, { "room_id", room.room_id }, }; - m::events::query(timeline_query, [&timeline](const auto &event) + m::vm::query(timeline_query, [&timeline](const auto &event) { if(timeline.size() > 10) return true; @@ -543,7 +543,7 @@ initial_sync_rooms(client &client, std::array, 3> r; std::array, 3> m; - m::events::for_each(query, [&r, &m, &client, &request, &full_state](const auto &event) + m::vm::for_each(query, [&r, &m, &client, &request, &full_state](const auto &event) { const auto &content{json::get<"content"_>(event)}; const auto &membership{unquote(content["membership"])}; @@ -588,14 +588,14 @@ initial_sync(client &client, "{}" }; - const string_view next_batch + const int64_t &next_batch { - m::event::head + m::vm::current_sequence }; const json::members content { - { "event_id", next_batch } + { "sequence", next_batch } }; m::user::sessions.send( diff --git a/modules/client/user.cc b/modules/client/user.cc index 40bd278f5..5544ecded 100644 --- a/modules/client/user.cc +++ b/modules/client/user.cc @@ -35,12 +35,12 @@ get_filter(client &client, const resource::request &request) { m::user::id::buf user_id { - urldecode(request.parv[4], user_id) + urldecode(request.parv[0], user_id) }; const auto &filter_id { - request.parv[6] + request.parv[2] }; const m::event::query query @@ -67,7 +67,7 @@ get_filter(client &client, const resource::request &request) return true; }}; - if(!m::events::test(query, result)) + if(!m::vm::test(query, result)) throw m::NOT_FOUND("No matching filter with that ID"); // Response already made @@ -91,7 +91,7 @@ post_filter(client &client, const resource::request::object &re // token must be authorized to make requests for this user id. m::user::id::buf user_id { - urldecode(request.parv[4], user_id) + urldecode(request.parv[0], user_id) }; user_id.validate(); diff --git a/modules/db/events.cc b/modules/db/events.cc index 59dc1b58d..ace7cd713 100644 --- a/modules/db/events.cc +++ b/modules/db/events.cc @@ -169,6 +169,24 @@ const database::descriptor events_state_key_descriptor } }; +const database::descriptor events_is_state_descriptor +{ + // name + "is_state", + + // explanation + R"(### protocol note: + + ### developer note: + key is event_id + )", + + // typing (key, value) + { + typeid(string_view), typeid(bool) + } +}; + const database::descriptor events_origin_descriptor { // name @@ -215,31 +233,6 @@ const database::descriptor events_origin_server_ts_descriptor } }; -const database::descriptor events_prev_ids_descriptor -{ - // name - "prev_ids", - - // explanation - R"(### protocol note: - - FEDERATION 4.1 (INCONSISTENT) - List of (String, String, Object) Triplets - The originating homeserver, PDU ids and hashes of the most recent PDUs the homeserver was - aware of for the room when it made this PDU. ["blue.example.com","99d16afbc8", {"sha256": - "abase64encodedsha256hashshouldbe43byteslong"}] - - ### developer note: - key is event_id - - )", - - // typing (key, value) - { - typeid(string_view), typeid(string_view) - } -}; - const database::descriptor events_unsigned_descriptor { // name @@ -457,7 +450,7 @@ const database::descriptor event_id_in_room_id {}, // comparator - sorts from highest to lowest - ircd::db::reverse_cmp_string_view{}, + {}, //ircd::db::reverse_cmp_string_view{}, // prefix transform event_id_in, @@ -585,6 +578,7 @@ const database::description events_description events_room_id_descriptor, events_sender_descriptor, events_state_key_descriptor, + events_is_state_descriptor, events_origin_descriptor, events_origin_server_ts_descriptor, events_prev_ids_descriptor, diff --git a/modules/key/server.cc b/modules/key/server.cc index 473c03cb6..c9bc0504a 100644 --- a/modules/key/server.cc +++ b/modules/key/server.cc @@ -53,7 +53,7 @@ handle_get(client &client, std::string my_key; m::keys::get(my_host(), key_id, [&my_key](const auto &key) { - my_key = json::string(key); + my_key = json::strung(key); }); return resource::response @@ -116,7 +116,7 @@ void foop() << std::endl; std::cout << - test(json::string(json::members + test(json::strung(json::members { { "one", 1 }, { "two", "Two" } diff --git a/modules/root.cc b/modules/root.cc index e40566c7b..fbdeb4fe7 100644 --- a/modules/root.cc +++ b/modules/root.cc @@ -50,7 +50,9 @@ get_root(client &client, const resource::request &request) { const auto &path { - request.head.path?: "index.html" + !request.head.path? "index.html": + request.head.path == "/"? "index.html": + request.head.path }; auto it(files.find(lstrip(path, '/')));