mirror of
https://github.com/matrix-construct/construct
synced 2024-05-29 00:03:45 +02:00
matrix: Split matrix base unit.
This commit is contained in:
parent
7b4dcbc488
commit
f6992ca3e1
|
@ -41,3 +41,25 @@ ircd::m::redacted::redacted(const event::id &event_id)
|
|||
index(event_id, std::nothrow)
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::redacted::redacted(const event::idx &event_idx)
|
||||
:boolean
|
||||
{
|
||||
event_idx?
|
||||
event::refs(event_idx).has(dbs::ref::M_ROOM_REDACTION):
|
||||
false
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::redacted::prefetch(const event::idx &event_idx)
|
||||
{
|
||||
const event::refs refs
|
||||
{
|
||||
event_idx
|
||||
};
|
||||
|
||||
return refs.prefetch(dbs::ref::M_ROOM_REDACTION);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ libircd_matrix_la_SOURCES += name.cc
|
|||
libircd_matrix_la_SOURCES += id.cc
|
||||
libircd_matrix_la_SOURCES += dbs.cc
|
||||
libircd_matrix_la_SOURCES += self.cc
|
||||
libircd_matrix_la_SOURCES += matrix.cc
|
||||
libircd_matrix_la_SOURCES += hook.cc
|
||||
libircd_matrix_la_SOURCES += event.cc
|
||||
libircd_matrix_la_SOURCES += event_cached.cc
|
||||
libircd_matrix_la_SOURCES += event_conforms.cc
|
||||
|
@ -85,6 +85,7 @@ libircd_matrix_la_SOURCES += room_power.cc
|
|||
libircd_matrix_la_SOURCES += room_state.cc
|
||||
libircd_matrix_la_SOURCES += room_state_history.cc
|
||||
libircd_matrix_la_SOURCES += room_state_space.cc
|
||||
libircd_matrix_la_SOURCES += room_server_acl.cc
|
||||
libircd_matrix_la_SOURCES += room_stats.cc
|
||||
libircd_matrix_la_SOURCES += user.cc
|
||||
libircd_matrix_la_SOURCES += user_account_data.cc
|
||||
|
@ -107,20 +108,27 @@ libircd_matrix_la_SOURCES += events.cc
|
|||
libircd_matrix_la_SOURCES += fed.cc
|
||||
libircd_matrix_la_SOURCES += feds.cc
|
||||
libircd_matrix_la_SOURCES += fetch.cc
|
||||
libircd_matrix_la_SOURCES += init_backfill.cc
|
||||
libircd_matrix_la_SOURCES += init_bootstrap.cc
|
||||
libircd_matrix_la_SOURCES += request.cc
|
||||
libircd_matrix_la_SOURCES += keys.cc
|
||||
libircd_matrix_la_SOURCES += node.cc
|
||||
libircd_matrix_la_SOURCES += presence.cc
|
||||
libircd_matrix_la_SOURCES += pretty.cc
|
||||
libircd_matrix_la_SOURCES += receipt.cc
|
||||
libircd_matrix_la_SOURCES += rooms.cc
|
||||
libircd_matrix_la_SOURCES += room_server_acl.cc
|
||||
libircd_matrix_la_SOURCES += membership.cc
|
||||
libircd_matrix_la_SOURCES += rooms_summary.cc
|
||||
libircd_matrix_la_SOURCES += sync.cc
|
||||
libircd_matrix_la_SOURCES += typing.cc
|
||||
libircd_matrix_la_SOURCES += users.cc
|
||||
libircd_matrix_la_SOURCES += users_servers.cc
|
||||
libircd_matrix_la_SOURCES += conf.cc
|
||||
libircd_matrix_la_SOURCES += error.cc
|
||||
libircd_matrix_la_SOURCES += filter.cc
|
||||
libircd_matrix_la_SOURCES += txn.cc
|
||||
libircd_matrix_la_SOURCES += vm.cc
|
||||
libircd_matrix_la_SOURCES += init_backfill.cc
|
||||
libircd_matrix_la_SOURCES += init_bootstrap.cc
|
||||
libircd_matrix_la_SOURCES += matrix.cc
|
||||
|
||||
#
|
||||
# Unit configurations
|
||||
|
|
|
@ -248,26 +248,6 @@ init_conf_item(conf::item<> &item)
|
|||
conf_updated(event_idx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_init_conf_items(const m::event &,
|
||||
m::vm::eval &eval)
|
||||
{
|
||||
init_conf_items();
|
||||
}
|
||||
|
||||
m::hookfn<m::vm::eval &>
|
||||
init_conf_items_hook
|
||||
{
|
||||
handle_init_conf_items,
|
||||
{
|
||||
{ "_site", "vm.effect" },
|
||||
{ "room_id", "!ircd" },
|
||||
{ "type", "m.room.member" },
|
||||
{ "membership", "join" },
|
||||
{ "state_key", "@ircd" },
|
||||
}
|
||||
};
|
||||
|
||||
static m::event::id::buf
|
||||
create_conf_item(const string_view &key,
|
||||
const conf::item<> &item)
|
||||
|
|
|
@ -582,13 +582,7 @@ ircd::m::dbs::_index_event_refs_auth(db::txn &txn,
|
|||
assert(opts.appendix.test(appendix::EVENT_REFS));
|
||||
assert(opts.event_refs.test(uint(ref::NEXT_AUTH)));
|
||||
|
||||
//TODO: XXX module dep
|
||||
static mods::import<bool (const m::event &)> is_power_event
|
||||
{
|
||||
"m_room_auth", "ircd::m::room::auth::is_power_event"
|
||||
};
|
||||
|
||||
if(!is_power_event(event))
|
||||
if(!m::room::auth::is_power_event(event))
|
||||
return;
|
||||
|
||||
const event::prev prev{event};
|
||||
|
|
|
@ -177,6 +177,30 @@ ircd::m::device::has(const m::user &user,
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::device::get(const m::user &user,
|
||||
const string_view &id,
|
||||
const string_view &prop,
|
||||
const closure &c)
|
||||
{
|
||||
const bool ret
|
||||
{
|
||||
get(std::nothrow, user, id, prop, c)
|
||||
};
|
||||
|
||||
if(!ret)
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"Property '%s' for device '%s' for user %s not found",
|
||||
id,
|
||||
prop,
|
||||
string_view{user.user_id}
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::device::get(std::nothrow_t,
|
||||
|
|
112
matrix/error.cc
Normal file
112
matrix/error.cc
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
const std::array<http::header, 1> _error_headers
|
||||
{{
|
||||
{ "Content-Type", "application/json; charset=utf-8" },
|
||||
}};
|
||||
}
|
||||
|
||||
thread_local
|
||||
decltype(ircd::m::error::fmtbuf)
|
||||
ircd::m::error::fmtbuf
|
||||
{};
|
||||
|
||||
//
|
||||
// error::error
|
||||
//
|
||||
|
||||
ircd::m::error::error()
|
||||
:error
|
||||
{
|
||||
internal, http::INTERNAL_SERVER_ERROR, std::string{},
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(std::string c)
|
||||
:error
|
||||
{
|
||||
internal, http::INTERNAL_SERVER_ERROR, std::move(c),
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(const http::code &c)
|
||||
:error
|
||||
{
|
||||
internal, c, std::string{},
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(const http::code &c,
|
||||
const json::members &members)
|
||||
:error
|
||||
{
|
||||
internal, c, json::strung{members},
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(const http::code &c,
|
||||
const json::iov &iov)
|
||||
:error
|
||||
{
|
||||
internal, c, json::strung{iov},
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(const http::code &c,
|
||||
const json::object &object)
|
||||
:error
|
||||
{
|
||||
internal, c, json::strung{object},
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::error::error(internal_t,
|
||||
const http::code &c,
|
||||
std::string object)
|
||||
:http::error
|
||||
{
|
||||
c, std::move(object), vector_view<const http::header>{_error_headers}
|
||||
}
|
||||
{
|
||||
if(!content.empty())
|
||||
{
|
||||
strlcat(ircd::exception::buf, " ");
|
||||
strlcat(ircd::exception::buf, errcode());
|
||||
strlcat(ircd::exception::buf, " :");
|
||||
strlcat(ircd::exception::buf, errstr());
|
||||
}
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::error::errstr()
|
||||
const noexcept
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
this->http::error::content
|
||||
};
|
||||
|
||||
return unquote(content.get("error"));
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::error::errcode()
|
||||
const noexcept
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
this->http::error::content
|
||||
};
|
||||
|
||||
return unquote(content.get("errcode", "M_UNKNOWN"_sv));
|
||||
}
|
|
@ -46,8 +46,8 @@ namespace ircd::m::fetch
|
|||
static size_t request_cleanup();
|
||||
static void request_worker();
|
||||
|
||||
static void init();
|
||||
static void fini();
|
||||
void init();
|
||||
void fini();
|
||||
}
|
||||
|
||||
decltype(ircd::m::fetch::log)
|
||||
|
|
191
matrix/filter.cc
Normal file
191
matrix/filter.cc
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/filter.h
|
||||
//
|
||||
|
||||
//TODO: globular expression
|
||||
//TODO: tribool for contains_url; we currently ignore the false value.
|
||||
bool
|
||||
ircd::m::match(const room_event_filter &filter,
|
||||
const event &event)
|
||||
{
|
||||
if(json::get<"contains_url"_>(filter) == true)
|
||||
if(!at<"content"_>(event).has("url"))
|
||||
return false;
|
||||
|
||||
for(const auto &room_id : json::get<"not_rooms"_>(filter))
|
||||
if(at<"room_id"_>(event) == unquote(room_id))
|
||||
return false;
|
||||
|
||||
if(empty(json::get<"rooms"_>(filter)))
|
||||
return match(event_filter{filter}, event);
|
||||
|
||||
for(const auto &room_id : json::get<"rooms"_>(filter))
|
||||
if(at<"room_id"_>(event) == unquote(room_id))
|
||||
return match(event_filter{filter}, event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: globular expression
|
||||
bool
|
||||
ircd::m::match(const event_filter &filter,
|
||||
const event &event)
|
||||
{
|
||||
for(const auto &type : json::get<"not_types"_>(filter))
|
||||
if(at<"type"_>(event) == unquote(type))
|
||||
return false;
|
||||
|
||||
for(const auto &sender : json::get<"not_senders"_>(filter))
|
||||
if(at<"sender"_>(event) == unquote(sender))
|
||||
return false;
|
||||
|
||||
if(empty(json::get<"senders"_>(filter)) && empty(json::get<"types"_>(filter)))
|
||||
return true;
|
||||
|
||||
if(empty(json::get<"senders"_>(filter)))
|
||||
{
|
||||
for(const auto &type : json::get<"types"_>(filter))
|
||||
if(at<"type"_>(event) == unquote(type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(empty(json::get<"types"_>(filter)))
|
||||
{
|
||||
for(const auto &sender : json::get<"senders"_>(filter))
|
||||
if(at<"sender"_>(event) == unquote(sender))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// filter
|
||||
//
|
||||
|
||||
/// Convenience interface for filters out of common `?filter=` query string
|
||||
/// arguments. This function expects a raw urlencoded value of the filter
|
||||
/// query parameter. It detects if the value is an "inline" filter by being
|
||||
/// a valid JSON object; otherwise it considers the value an ID and fetches
|
||||
/// the filter stored previously by the user.
|
||||
std::string
|
||||
ircd::m::filter::get(const string_view &val,
|
||||
const m::user &user)
|
||||
{
|
||||
if(!val)
|
||||
return {};
|
||||
|
||||
const bool is_inline
|
||||
{
|
||||
startswith(val, "{") || startswith(val, "%7B")
|
||||
};
|
||||
|
||||
if(is_inline)
|
||||
return util::string(val.size(), [&val]
|
||||
(const mutable_buffer &buf)
|
||||
{
|
||||
return url::decode(buf, val);
|
||||
});
|
||||
|
||||
if(!user.user_id)
|
||||
return {};
|
||||
|
||||
char idbuf[m::event::STATE_KEY_MAX_SIZE];
|
||||
const string_view &id
|
||||
{
|
||||
url::decode(idbuf, val)
|
||||
};
|
||||
|
||||
const m::user::filter filter
|
||||
{
|
||||
user
|
||||
};
|
||||
|
||||
return filter.get(id);
|
||||
}
|
||||
|
||||
//
|
||||
// filter::filter
|
||||
//
|
||||
|
||||
ircd::m::filter::filter(const user &user,
|
||||
const string_view &filter_id,
|
||||
const mutable_buffer &buf)
|
||||
{
|
||||
const json::object &obj
|
||||
{
|
||||
user::filter(user).get(buf, filter_id)
|
||||
};
|
||||
|
||||
new (this) m::filter
|
||||
{
|
||||
obj
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// room_filter
|
||||
//
|
||||
|
||||
ircd::m::room_filter::room_filter(const mutable_buffer &buf,
|
||||
const json::members &members)
|
||||
:super_type::tuple
|
||||
{
|
||||
json::stringify(mutable_buffer{buf}, members)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// state_filter
|
||||
//
|
||||
|
||||
ircd::m::state_filter::state_filter(const mutable_buffer &buf,
|
||||
const json::members &members)
|
||||
:super_type::tuple
|
||||
{
|
||||
json::stringify(mutable_buffer{buf}, members)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// room_event_filter
|
||||
//
|
||||
|
||||
ircd::m::room_event_filter::room_event_filter(const mutable_buffer &buf,
|
||||
const json::members &members)
|
||||
:super_type::tuple
|
||||
{
|
||||
json::stringify(mutable_buffer{buf}, members)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// event_filter
|
||||
//
|
||||
|
||||
ircd::m::event_filter::event_filter(const mutable_buffer &buf,
|
||||
const json::members &members)
|
||||
:super_type::tuple
|
||||
{
|
||||
json::stringify(mutable_buffer{buf}, members)
|
||||
}
|
||||
{
|
||||
}
|
640
matrix/hook.cc
Normal file
640
matrix/hook.cc
Normal file
|
@ -0,0 +1,640 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
// Internal utils
|
||||
namespace ircd::m
|
||||
{
|
||||
static bool _hook_match(const m::event &matching, const m::event &);
|
||||
static void _hook_fix_state_key(const json::members &, json::member &);
|
||||
static void _hook_fix_room_id(const json::members &, json::member &);
|
||||
static void _hook_fix_sender(const json::members &, json::member &);
|
||||
static json::strung _hook_make_feature(const json::members &);
|
||||
}
|
||||
|
||||
/// Instance list linkage for all hook sites
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::hook::base::site>::allocator)
|
||||
ircd::util::instance_list<ircd::m::hook::base::site>::allocator
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::hook::base::site>::list)
|
||||
ircd::util::instance_list<ircd::m::hook::base::site>::list
|
||||
{
|
||||
allocator
|
||||
};
|
||||
|
||||
/// Instance list linkage for all hooks
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::hook::base>::allocator)
|
||||
ircd::util::instance_list<ircd::m::hook::base>::allocator
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::hook::base>::list)
|
||||
ircd::util::instance_list<ircd::m::hook::base>::list
|
||||
{
|
||||
allocator
|
||||
};
|
||||
|
||||
//
|
||||
// hook::maps
|
||||
//
|
||||
|
||||
struct ircd::m::hook::maps
|
||||
{
|
||||
std::multimap<string_view, base *> origin;
|
||||
std::multimap<string_view, base *> room_id;
|
||||
std::multimap<string_view, base *> sender;
|
||||
std::multimap<string_view, base *> state_key;
|
||||
std::multimap<string_view, base *> type;
|
||||
std::vector<base *> always;
|
||||
|
||||
size_t match(const event &match, const std::function<bool (base &)> &) const;
|
||||
size_t add(base &hook, const event &matching);
|
||||
size_t del(base &hook, const event &matching);
|
||||
|
||||
maps();
|
||||
~maps() noexcept;
|
||||
};
|
||||
|
||||
ircd::m::hook::maps::maps()
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::hook::maps::~maps()
|
||||
noexcept
|
||||
{
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::hook::maps::add(base &hook,
|
||||
const event &matching)
|
||||
{
|
||||
size_t ret{0};
|
||||
const auto map{[&hook, &ret]
|
||||
(auto &map, const string_view &value)
|
||||
{
|
||||
map.emplace(value, &hook);
|
||||
++ret;
|
||||
}};
|
||||
|
||||
if(json::get<"origin"_>(matching))
|
||||
map(origin, at<"origin"_>(matching));
|
||||
|
||||
if(json::get<"room_id"_>(matching))
|
||||
map(room_id, at<"room_id"_>(matching));
|
||||
|
||||
if(json::get<"sender"_>(matching))
|
||||
map(sender, at<"sender"_>(matching));
|
||||
|
||||
if(json::get<"state_key"_>(matching))
|
||||
map(state_key, at<"state_key"_>(matching));
|
||||
|
||||
if(json::get<"type"_>(matching))
|
||||
map(type, at<"type"_>(matching));
|
||||
|
||||
// Hook had no mappings which means it will match everything.
|
||||
// We don't increment the matcher count for this case.
|
||||
if(!ret)
|
||||
always.emplace_back(&hook);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::hook::maps::del(base &hook,
|
||||
const event &matching)
|
||||
{
|
||||
size_t ret{0};
|
||||
const auto unmap{[&hook, &ret]
|
||||
(auto &map, const string_view &value)
|
||||
{
|
||||
auto pit{map.equal_range(value)};
|
||||
while(pit.first != pit.second)
|
||||
if(pit.first->second == &hook)
|
||||
{
|
||||
pit.first = map.erase(pit.first);
|
||||
++ret;
|
||||
}
|
||||
else ++pit.first;
|
||||
}};
|
||||
|
||||
// Unconditional attempt to remove from always.
|
||||
std::remove(begin(always), end(always), &hook);
|
||||
|
||||
if(json::get<"origin"_>(matching))
|
||||
unmap(origin, at<"origin"_>(matching));
|
||||
|
||||
if(json::get<"room_id"_>(matching))
|
||||
unmap(room_id, at<"room_id"_>(matching));
|
||||
|
||||
if(json::get<"sender"_>(matching))
|
||||
unmap(sender, at<"sender"_>(matching));
|
||||
|
||||
if(json::get<"state_key"_>(matching))
|
||||
unmap(state_key, at<"state_key"_>(matching));
|
||||
|
||||
if(json::get<"type"_>(matching))
|
||||
unmap(type, at<"type"_>(matching));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::hook::maps::match(const event &event,
|
||||
const std::function<bool (base &)> &callback)
|
||||
const
|
||||
{
|
||||
std::set<base *> matching
|
||||
{
|
||||
begin(always), end(always)
|
||||
};
|
||||
|
||||
const auto site_match{[&matching]
|
||||
(auto &map, const string_view &key)
|
||||
{
|
||||
auto pit{map.equal_range(key)};
|
||||
for(; pit.first != pit.second; ++pit.first)
|
||||
matching.emplace(pit.first->second);
|
||||
}};
|
||||
|
||||
if(json::get<"origin"_>(event))
|
||||
site_match(origin, at<"origin"_>(event));
|
||||
|
||||
if(json::get<"room_id"_>(event))
|
||||
site_match(room_id, at<"room_id"_>(event));
|
||||
|
||||
if(json::get<"sender"_>(event))
|
||||
site_match(sender, at<"sender"_>(event));
|
||||
|
||||
if(json::get<"type"_>(event))
|
||||
site_match(type, at<"type"_>(event));
|
||||
|
||||
if(json::get<"state_key"_>(event))
|
||||
site_match(state_key, at<"state_key"_>(event));
|
||||
|
||||
auto it(begin(matching));
|
||||
while(it != end(matching))
|
||||
{
|
||||
const base &hook(**it);
|
||||
if(!_hook_match(hook.matching, event))
|
||||
it = matching.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
size_t ret{0};
|
||||
for(auto it(begin(matching)); it != end(matching); ++it, ++ret)
|
||||
if(!callback(**it))
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// hook::base
|
||||
//
|
||||
|
||||
/// Primary hook ctor
|
||||
ircd::m::hook::base::base(const json::members &members)
|
||||
:_feature
|
||||
{
|
||||
_hook_make_feature(members)
|
||||
}
|
||||
,feature
|
||||
{
|
||||
_feature
|
||||
}
|
||||
,matching
|
||||
{
|
||||
feature
|
||||
}
|
||||
{
|
||||
site *site; try
|
||||
{
|
||||
if((site = find_site()))
|
||||
site->add(*this);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
if(registered)
|
||||
{
|
||||
auto *const site(find_site());
|
||||
assert(site != nullptr);
|
||||
site->del(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ircd::m::hook::base::~base()
|
||||
noexcept
|
||||
{
|
||||
if(!registered)
|
||||
return;
|
||||
|
||||
auto *const site
|
||||
{
|
||||
find_site()
|
||||
};
|
||||
|
||||
// should be non-null if !registered
|
||||
assert(site != nullptr);
|
||||
|
||||
// if someone is calling and inside this hook we shouldn't be destructing
|
||||
assert(calling == 0);
|
||||
|
||||
// if someone is calling the hook::site but inside some other hook, we can
|
||||
// still remove this hook from the site.
|
||||
//assert(site->calling == 0);
|
||||
|
||||
site->del(*this);
|
||||
}
|
||||
|
||||
ircd::m::hook::base::site *
|
||||
ircd::m::hook::base::find_site()
|
||||
const
|
||||
{
|
||||
const auto &site_name
|
||||
{
|
||||
this->site_name()
|
||||
};
|
||||
|
||||
if(!site_name)
|
||||
return nullptr;
|
||||
|
||||
for(auto *const &site : m::hook::base::site::list)
|
||||
if(site->name() == site_name)
|
||||
return site;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::hook::base::site_name()
|
||||
const try
|
||||
{
|
||||
return unquote(feature.at("_site"));
|
||||
}
|
||||
catch(const std::out_of_range &e)
|
||||
{
|
||||
throw panic
|
||||
{
|
||||
"Hook %p must name a '_site' to register with.", this
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// hook::site
|
||||
//
|
||||
|
||||
//
|
||||
// hook::site::site
|
||||
//
|
||||
|
||||
ircd::m::hook::base::site::site(const json::members &members)
|
||||
:_feature
|
||||
{
|
||||
members
|
||||
}
|
||||
,feature
|
||||
{
|
||||
_feature
|
||||
}
|
||||
,maps
|
||||
{
|
||||
std::make_unique<struct maps>()
|
||||
}
|
||||
,exceptions
|
||||
{
|
||||
feature.get<bool>("exceptions", true)
|
||||
}
|
||||
{
|
||||
for(const auto &site : list)
|
||||
if(site->name() == name() && site != this)
|
||||
throw error
|
||||
{
|
||||
"Hook site '%s' already registered at %p",
|
||||
name(),
|
||||
site
|
||||
};
|
||||
|
||||
// Find and register all of the orphan hooks which were constructed before
|
||||
// this site was constructed.
|
||||
for(auto *const &hook : m::hook::base::list)
|
||||
if(hook->site_name() == name())
|
||||
add(*hook);
|
||||
}
|
||||
|
||||
ircd::m::hook::base::site::~site()
|
||||
noexcept
|
||||
{
|
||||
assert(!calling);
|
||||
const std::vector<base *> hooks
|
||||
{
|
||||
begin(this->hooks), end(this->hooks)
|
||||
};
|
||||
|
||||
for(auto *const hook : hooks)
|
||||
del(*hook);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::hook::base::site::match(const event &event,
|
||||
const std::function<bool (base &)> &callback)
|
||||
{
|
||||
maps->match(event, callback);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::hook::base::site::add(base &hook)
|
||||
{
|
||||
assert(!hook.registered);
|
||||
assert(hook.site_name() == name());
|
||||
assert(hook.matchers == 0);
|
||||
|
||||
if(!hooks.emplace(&hook).second)
|
||||
{
|
||||
log::warning
|
||||
{
|
||||
log, "Hook %p already registered to site %s",
|
||||
&hook,
|
||||
name()
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(maps);
|
||||
const size_t matched
|
||||
{
|
||||
maps->add(hook, hook.matching)
|
||||
};
|
||||
|
||||
hook.matchers = matched;
|
||||
hook.registered = true;
|
||||
matchers += matched;
|
||||
++count;
|
||||
|
||||
log::debug
|
||||
{
|
||||
log, "Registered hook %p to site %s",
|
||||
&hook,
|
||||
name()
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::hook::base::site::del(base &hook)
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Removing hook %p from site %s",
|
||||
&hook,
|
||||
name()
|
||||
};
|
||||
|
||||
assert(hook.registered);
|
||||
assert(hook.site_name() == name());
|
||||
|
||||
const size_t matched
|
||||
{
|
||||
maps->del(hook, hook.matching)
|
||||
};
|
||||
|
||||
const auto erased
|
||||
{
|
||||
hooks.erase(&hook)
|
||||
};
|
||||
|
||||
hook.matchers -= matched;
|
||||
hook.registered = false;
|
||||
matchers -= matched;
|
||||
--count;
|
||||
assert(hook.matchers == 0);
|
||||
assert(erased);
|
||||
return true;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::hook::base::site::name()
|
||||
const try
|
||||
{
|
||||
return unquote(feature.at("name"));
|
||||
}
|
||||
catch(const std::out_of_range &e)
|
||||
{
|
||||
throw panic
|
||||
{
|
||||
"Hook site %p requires a name", this
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// hook<void>
|
||||
//
|
||||
|
||||
ircd::m::hook::hook<void>::hook(const json::members &feature,
|
||||
decltype(function) function)
|
||||
:base{feature}
|
||||
,function{std::move(function)}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::hook::hook<void>::hook(decltype(function) function,
|
||||
const json::members &feature)
|
||||
:base{feature}
|
||||
,function{std::move(function)}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::hook::site<void>::site(const json::members &feature)
|
||||
:base::site{feature}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::hook::site<void>::operator()(const event &event)
|
||||
{
|
||||
match(event, [this, &event]
|
||||
(base &base)
|
||||
{
|
||||
call(dynamic_cast<hook<void> &>(base), event);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::hook::site<void>::call(hook<void> &hfn,
|
||||
const event &event)
|
||||
try
|
||||
{
|
||||
// stats for site
|
||||
++calls;
|
||||
const scope_count site_calling
|
||||
{
|
||||
calling
|
||||
};
|
||||
|
||||
// stats for hook
|
||||
++hfn.calls;
|
||||
const scope_count hook_calling
|
||||
{
|
||||
hfn.calling
|
||||
};
|
||||
|
||||
// call hook
|
||||
hfn.function(event);
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
if(exceptions)
|
||||
throw;
|
||||
|
||||
log::critical
|
||||
{
|
||||
log, "Unhandled hookfn(%p) %s error :%s",
|
||||
&hfn,
|
||||
string_view{hfn.feature},
|
||||
e.what()
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// hook internal
|
||||
//
|
||||
|
||||
/// Internal interface which manipulates the initializer supplied by the
|
||||
/// developer to the hook to create the proper JSON output. i.e They supply
|
||||
/// a "room_id" of "!config" which has no hostname, that is added here
|
||||
/// depending on my_host() in the deployment runtime...
|
||||
///
|
||||
ircd::json::strung
|
||||
ircd::m::_hook_make_feature(const json::members &members)
|
||||
{
|
||||
const ctx::critical_assertion ca;
|
||||
std::vector<json::member> copy
|
||||
{
|
||||
begin(members), end(members)
|
||||
};
|
||||
|
||||
for(auto &member : copy) switch(hash(member.first))
|
||||
{
|
||||
case hash("room_id"):
|
||||
_hook_fix_room_id(members, member);
|
||||
continue;
|
||||
|
||||
case hash("sender"):
|
||||
_hook_fix_sender(members, member);
|
||||
continue;
|
||||
|
||||
case hash("state_key"):
|
||||
_hook_fix_state_key(members, member);
|
||||
continue;
|
||||
}
|
||||
|
||||
return { copy.data(), copy.data() + copy.size() };
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::_hook_fix_sender(const json::members &members,
|
||||
json::member &member)
|
||||
{
|
||||
// Rewrite the sender if the supplied input has no hostname
|
||||
if(valid_local_only(id::USER, member.second))
|
||||
{
|
||||
assert(my_host());
|
||||
thread_local char buf[256];
|
||||
member.second = id::user { buf, member.second, my_host() };
|
||||
}
|
||||
|
||||
validate(id::USER, member.second);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::_hook_fix_room_id(const json::members &members,
|
||||
json::member &member)
|
||||
{
|
||||
// Rewrite the room_id if the supplied input has no hostname
|
||||
if(valid_local_only(id::ROOM, member.second))
|
||||
{
|
||||
assert(my_host());
|
||||
thread_local char buf[256];
|
||||
member.second = id::room { buf, member.second, my_host() };
|
||||
}
|
||||
|
||||
validate(id::ROOM, member.second);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::_hook_fix_state_key(const json::members &members,
|
||||
json::member &member)
|
||||
{
|
||||
const bool is_member_event
|
||||
{
|
||||
end(members) != std::find_if(begin(members), end(members), []
|
||||
(const auto &member)
|
||||
{
|
||||
return member.first == "type" && member.second == "m.room.member";
|
||||
})
|
||||
};
|
||||
|
||||
if(!is_member_event)
|
||||
return;
|
||||
|
||||
// Rewrite the sender if the supplied input has no hostname
|
||||
if(valid_local_only(id::USER, member.second))
|
||||
{
|
||||
assert(my_host());
|
||||
thread_local char buf[256];
|
||||
member.second = id::user { buf, member.second, my_host() };
|
||||
}
|
||||
|
||||
validate(id::USER, member.second);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::_hook_match(const m::event &matching,
|
||||
const m::event &event)
|
||||
{
|
||||
if(json::get<"origin"_>(matching))
|
||||
if(at<"origin"_>(matching) != json::get<"origin"_>(event))
|
||||
return false;
|
||||
|
||||
if(json::get<"room_id"_>(matching))
|
||||
if(at<"room_id"_>(matching) != json::get<"room_id"_>(event))
|
||||
return false;
|
||||
|
||||
if(json::get<"sender"_>(matching))
|
||||
if(at<"sender"_>(matching) != json::get<"sender"_>(event))
|
||||
return false;
|
||||
|
||||
if(json::get<"type"_>(matching))
|
||||
if(at<"type"_>(matching) != json::get<"type"_>(event))
|
||||
return false;
|
||||
|
||||
if(json::get<"state_key"_>(matching))
|
||||
if(at<"state_key"_>(matching) != json::get<"state_key"_>(event))
|
||||
return false;
|
||||
|
||||
if(membership(matching))
|
||||
if(membership(matching) != membership(event))
|
||||
return false;
|
||||
|
||||
if(json::get<"content"_>(matching))
|
||||
if(json::get<"type"_>(event) == "m.room.message")
|
||||
if(at<"content"_>(matching).has("msgtype"))
|
||||
if(at<"content"_>(matching).get("msgtype") != json::get<"content"_>(event).get("msgtype"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
3082
matrix/matrix.cc
3082
matrix/matrix.cc
File diff suppressed because it is too large
Load diff
144
matrix/membership.cc
Normal file
144
matrix/membership.cc
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
decltype(ircd::m::membership_positive)
|
||||
ircd::m::membership_positive
|
||||
{
|
||||
"join"_sv,
|
||||
"invite"_sv,
|
||||
};
|
||||
|
||||
decltype(ircd::m::membership_negative)
|
||||
ircd::m::membership_negative
|
||||
{
|
||||
"leave"_sv,
|
||||
"ban"_sv,
|
||||
""_sv,
|
||||
};
|
||||
|
||||
bool
|
||||
ircd::m::membership(const room &room,
|
||||
const user::id &user_id,
|
||||
const vector_view<const string_view> &membership)
|
||||
{
|
||||
const room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
{
|
||||
state.get(std::nothrow, "m.room.member", user_id)
|
||||
};
|
||||
|
||||
return m::membership(event_idx, membership);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::membership(const event::idx &event_idx,
|
||||
const vector_view<const string_view> &membership)
|
||||
{
|
||||
bool ret;
|
||||
const auto closure
|
||||
{
|
||||
[&membership, &ret](const json::object &content)
|
||||
{
|
||||
const json::string &content_membership
|
||||
{
|
||||
content["membership"]
|
||||
};
|
||||
|
||||
const auto it
|
||||
{
|
||||
std::find(begin(membership), end(membership), content_membership)
|
||||
};
|
||||
|
||||
ret = content_membership && it != end(membership);
|
||||
}
|
||||
};
|
||||
|
||||
// If the query was successful a membership state exists (even if the
|
||||
// string found was illegally empty) thus we must return the value of ret.
|
||||
if(m::get(std::nothrow, event_idx, "content", closure))
|
||||
return ret;
|
||||
|
||||
// If the user included an empty string in the vector, they want us to
|
||||
// return true for non-membership (i.e a failed query).
|
||||
static const auto &empty{[](auto&& s) { return !s; }};
|
||||
if(std::any_of(begin(membership), end(membership), empty))
|
||||
return true;
|
||||
|
||||
// If the membership vector itself was empty that's also a non-membership.
|
||||
if(membership.empty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::membership(const mutable_buffer &out,
|
||||
const room &room,
|
||||
const user::id &user_id)
|
||||
{
|
||||
const room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
{
|
||||
state.get(std::nothrow, "m.room.member", user_id)
|
||||
};
|
||||
|
||||
return m::membership(out, event_idx);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::membership(const mutable_buffer &out,
|
||||
const event::idx &event_idx)
|
||||
{
|
||||
return m::query(std::nothrow, event_idx, "content", [&out]
|
||||
(const json::object &content) -> string_view
|
||||
{
|
||||
const json::string &content_membership
|
||||
{
|
||||
content["membership"]
|
||||
};
|
||||
|
||||
return strlcpy
|
||||
{
|
||||
out, content_membership
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::membership(const event &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::get<"content"_>(event)
|
||||
};
|
||||
|
||||
const string_view &membership
|
||||
{
|
||||
json::get<"membership"_>(event)
|
||||
};
|
||||
|
||||
if(membership)
|
||||
return membership;
|
||||
|
||||
const json::string &content_membership
|
||||
{
|
||||
content.get("membership")
|
||||
};
|
||||
|
||||
return content_membership;
|
||||
}
|
131
matrix/node.cc
131
matrix/node.cc
|
@ -8,6 +8,110 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::node(const string_view &node_id)
|
||||
:node_id{node_id}
|
||||
{
|
||||
rfc3986::valid_remote(node_id);
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::key(const string_view &key_id,
|
||||
const ed25519_closure &closure)
|
||||
const
|
||||
{
|
||||
key(key_id, key_closure{[&closure]
|
||||
(const string_view &keyb64)
|
||||
{
|
||||
const ed25519::pk pk
|
||||
{
|
||||
[&keyb64](auto &buf)
|
||||
{
|
||||
b64decode(buf, unquote(keyb64));
|
||||
}
|
||||
};
|
||||
|
||||
closure(pk);
|
||||
}});
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::key(const string_view &key_id,
|
||||
const key_closure &closure)
|
||||
const
|
||||
{
|
||||
m::keys::get(node_id, key_id, [&closure, &key_id]
|
||||
(const json::object &keys)
|
||||
{
|
||||
const json::object &vks
|
||||
{
|
||||
keys.at("verify_keys")
|
||||
};
|
||||
|
||||
const json::object &vkk
|
||||
{
|
||||
vks.at(key_id)
|
||||
};
|
||||
|
||||
const string_view &key
|
||||
{
|
||||
vkk.at("key")
|
||||
};
|
||||
|
||||
closure(key);
|
||||
});
|
||||
}
|
||||
|
||||
/// Generates a node-room ID into buffer; see room_id() overload.
|
||||
ircd::m::id::room::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::room_id()
|
||||
const
|
||||
{
|
||||
ircd::m::id::room::buf buf;
|
||||
return buf.assigned(room_id(buf));
|
||||
}
|
||||
|
||||
/// This generates a room mxid for the "node's room" essentially serving as
|
||||
/// a database mechanism for this specific node. This room_id is a hash of
|
||||
/// the node's full mxid.
|
||||
///
|
||||
ircd::m::id::room
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::room_id(const mutable_buffer &buf)
|
||||
const
|
||||
{
|
||||
assert(!empty(this->node_id));
|
||||
|
||||
// for compatibility with hashing legacy node_id's
|
||||
thread_local char node_id_buf[m::id::MAX_SIZE];
|
||||
mutable_buffer mb{node_id_buf};
|
||||
consume(mb, copy(mb, "::"_sv));
|
||||
consume(mb, copy(mb, this->node_id));
|
||||
const string_view &node_id
|
||||
{
|
||||
node_id_buf, data(mb)
|
||||
};
|
||||
|
||||
const sha256::buf hash
|
||||
{
|
||||
sha256{node_id}
|
||||
};
|
||||
|
||||
thread_local char b58_buf[b58encode_size(size(hash))];
|
||||
const string_view b58
|
||||
{
|
||||
b58encode(b58_buf, hash)
|
||||
};
|
||||
|
||||
return id::room
|
||||
{
|
||||
buf, b58, my_host()
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::node
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::create(const node &node,
|
||||
|
@ -41,3 +145,30 @@ ircd::m::my(const node &node)
|
|||
{
|
||||
return my_host(node.node_id);
|
||||
}
|
||||
|
||||
//
|
||||
// node::room
|
||||
//
|
||||
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::room::room(const string_view &node_id)
|
||||
:room
|
||||
{
|
||||
m::node{node_id}
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::node::room::room(const m::node &node)
|
||||
:node
|
||||
{
|
||||
node
|
||||
}
|
||||
,room_id
|
||||
{
|
||||
node.room_id()
|
||||
}
|
||||
{
|
||||
this->m::room::room_id = this->room_id;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,91 @@ ircd::m::presence_valid_states
|
|||
"unavailable",
|
||||
};
|
||||
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::presence(const user &user,
|
||||
const mutable_buffer &buf)
|
||||
:edu::m_presence{[&user, &buf]
|
||||
{
|
||||
json::object ret;
|
||||
get(user, [&ret, &buf]
|
||||
(const json::object &content)
|
||||
{
|
||||
ret =
|
||||
{
|
||||
data(buf), copy(buf, string_view{content})
|
||||
};
|
||||
});
|
||||
|
||||
return ret;
|
||||
}()}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::set(const user &user,
|
||||
const string_view &presence,
|
||||
const string_view &status_msg)
|
||||
{
|
||||
return set(m::presence
|
||||
{
|
||||
{ "user_id", user.user_id },
|
||||
{ "presence", presence },
|
||||
{ "status_msg", status_msg },
|
||||
{ "currently_active", presence == "online" },
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::get(const user &user,
|
||||
const closure &closure)
|
||||
{
|
||||
if(!get(std::nothrow, user, closure))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"No presence found for %s", string_view{user.user_id}
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::get(std::nothrow_t,
|
||||
const user &user,
|
||||
const closure &closure)
|
||||
{
|
||||
static const m::event::fetch::opts fopts
|
||||
{
|
||||
m::event::keys::include {"content"}
|
||||
};
|
||||
|
||||
const auto reclosure{[&closure]
|
||||
(const m::event &event)
|
||||
{
|
||||
closure(json::get<"content"_>(event));
|
||||
}};
|
||||
|
||||
return get(std::nothrow, user, reclosure, &fopts);
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::get(const user &user)
|
||||
{
|
||||
const event::idx ret
|
||||
{
|
||||
get(std::nothrow, user)
|
||||
};
|
||||
|
||||
if(!ret)
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"No presence found for %s", string_view{user.user_id}
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::presence::get(const std::nothrow_t,
|
||||
|
|
324
matrix/request.cc
Normal file
324
matrix/request.cc
Normal file
|
@ -0,0 +1,324 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
ircd::m::request::request(const string_view &method,
|
||||
const string_view &uri,
|
||||
const mutable_buffer &body_buf,
|
||||
const json::members &body)
|
||||
:request
|
||||
{
|
||||
my_host(),
|
||||
string_view{},
|
||||
method,
|
||||
uri,
|
||||
json::stringify(mutable_buffer{body_buf}, body)
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::request::request(const string_view &method,
|
||||
const string_view &uri)
|
||||
:request
|
||||
{
|
||||
my_host(),
|
||||
string_view{},
|
||||
method,
|
||||
uri,
|
||||
json::object{}
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::request::request(const string_view &method,
|
||||
const string_view &uri,
|
||||
const json::object &content)
|
||||
:request
|
||||
{
|
||||
my_host(),
|
||||
string_view{},
|
||||
method,
|
||||
uri,
|
||||
content
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::m::request::request(const string_view &origin,
|
||||
const string_view &destination,
|
||||
const string_view &method,
|
||||
const string_view &uri,
|
||||
const json::object &content)
|
||||
{
|
||||
json::get<"origin"_>(*this) = origin;
|
||||
json::get<"destination"_>(*this) = destination;
|
||||
json::get<"method"_>(*this) = method;
|
||||
json::get<"uri"_>(*this) = uri;
|
||||
json::get<"content"_>(*this) = content;
|
||||
|
||||
if(unlikely(origin && !rfc3986::valid_remote(std::nothrow, origin)))
|
||||
throw m::error
|
||||
{
|
||||
http::BAD_REQUEST, "M_REQUEST_INVALID_ORIGIN",
|
||||
"This origin string '%s' is not a valid remote.",
|
||||
origin
|
||||
};
|
||||
|
||||
if(unlikely(destination && !rfc3986::valid_remote(std::nothrow, destination)))
|
||||
throw m::error
|
||||
{
|
||||
http::BAD_REQUEST, "M_REQUEST_INVALID_DESTINATION",
|
||||
"This destination string '%s' is not a valid remote.",
|
||||
destination
|
||||
};
|
||||
}
|
||||
|
||||
decltype(ircd::m::request::headers_max)
|
||||
ircd::m::request::headers_max
|
||||
{
|
||||
32UL
|
||||
};
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::request::operator()(const mutable_buffer &out,
|
||||
const vector_view<const http::header> &addl_headers)
|
||||
const
|
||||
{
|
||||
thread_local http::header header[headers_max];
|
||||
const ctx::critical_assertion ca;
|
||||
size_t headers{0};
|
||||
|
||||
header[headers++] =
|
||||
{
|
||||
"User-Agent", info::user_agent
|
||||
};
|
||||
|
||||
thread_local char x_matrix[2_KiB];
|
||||
if(startswith(json::at<"uri"_>(*this), "/_matrix/federation"))
|
||||
{
|
||||
const auto &sk{self::secret_key};
|
||||
const auto &pkid{self::public_key_id};
|
||||
header[headers++] =
|
||||
{
|
||||
"Authorization", generate(x_matrix, sk, pkid)
|
||||
};
|
||||
}
|
||||
|
||||
assert(headers <= headers_max);
|
||||
assert(headers + addl_headers.size() <= headers_max);
|
||||
for(size_t i(0); i < addl_headers.size() && headers < headers_max; ++i)
|
||||
header[headers++] = addl_headers.at(i);
|
||||
|
||||
static const string_view content_type
|
||||
{
|
||||
"application/json; charset=utf-8"_sv
|
||||
};
|
||||
|
||||
const auto content_length
|
||||
{
|
||||
string_view(json::get<"content"_>(*this)).size()
|
||||
};
|
||||
|
||||
window_buffer sb{out};
|
||||
http::request
|
||||
{
|
||||
sb,
|
||||
json::at<"destination"_>(*this),
|
||||
json::at<"method"_>(*this),
|
||||
json::at<"uri"_>(*this),
|
||||
content_length,
|
||||
content_type,
|
||||
{ header, headers }
|
||||
};
|
||||
|
||||
return sb.completed();
|
||||
}
|
||||
|
||||
decltype(ircd::m::request::generate_content_max)
|
||||
ircd::m::request::generate_content_max
|
||||
{
|
||||
{ "name", "ircd.m.request.generate.content_max" },
|
||||
{ "default", long(1_MiB) },
|
||||
};
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::request::generate(const mutable_buffer &out,
|
||||
const ed25519::sk &sk,
|
||||
const string_view &pkid)
|
||||
const
|
||||
{
|
||||
const ctx::critical_assertion ca;
|
||||
thread_local unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
size_t(generate_content_max)
|
||||
};
|
||||
|
||||
if(unlikely(json::serialized(*this) > buffer::size(buf)))
|
||||
throw m::error
|
||||
{
|
||||
"M_REQUEST_TOO_LARGE", "This server generated a request of %zu bytes; limit is %zu",
|
||||
json::serialized(*this),
|
||||
buffer::size(buf)
|
||||
};
|
||||
|
||||
const json::object object
|
||||
{
|
||||
stringify(mutable_buffer{buf}, *this)
|
||||
};
|
||||
|
||||
const ed25519::sig sig
|
||||
{
|
||||
self::secret_key.sign(object)
|
||||
};
|
||||
|
||||
const auto &origin
|
||||
{
|
||||
unquote(string_view{json::at<"origin"_>(*this)})
|
||||
};
|
||||
|
||||
thread_local char sigb64[1_KiB];
|
||||
return fmt::sprintf
|
||||
{
|
||||
out, "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"",
|
||||
origin,
|
||||
pkid,
|
||||
b64encode_unpadded(sigb64, sig)
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::request::verify(const string_view &key,
|
||||
const string_view &sig_)
|
||||
const
|
||||
{
|
||||
const ed25519::sig sig
|
||||
{
|
||||
[&sig_](auto &buf)
|
||||
{
|
||||
b64decode(buf, sig_);
|
||||
}
|
||||
};
|
||||
|
||||
const json::string &origin
|
||||
{
|
||||
json::at<"origin"_>(*this)
|
||||
};
|
||||
|
||||
const m::node node
|
||||
{
|
||||
origin
|
||||
};
|
||||
|
||||
bool verified{false};
|
||||
node.key(key, [this, &verified, &sig]
|
||||
(const ed25519::pk &pk)
|
||||
{
|
||||
verified = verify(pk, sig);
|
||||
});
|
||||
|
||||
return verified;
|
||||
}
|
||||
|
||||
decltype(ircd::m::request::verify_content_max)
|
||||
ircd::m::request::verify_content_max
|
||||
{
|
||||
{ "name", "ircd.m.request.verify.content_max" },
|
||||
{ "default", long(1_MiB) },
|
||||
};
|
||||
|
||||
bool
|
||||
ircd::m::request::verify(const ed25519::pk &pk,
|
||||
const ed25519::sig &sig)
|
||||
const
|
||||
{
|
||||
// Matrix spec sez that an empty content object {} is excluded entirely
|
||||
// from the verification. Our JSON only excludes members if they evaluate
|
||||
// to undefined i.e json::object{}/string_view{} but not json::object{"{}"}
|
||||
// or even json::object{""}; rather than burdening the caller with ensuring
|
||||
// their assignment conforms perfectly, we ensure correctness manually.
|
||||
auto _this(*this);
|
||||
if(empty(json::get<"content"_>(*this)))
|
||||
json::get<"content"_>(_this) = json::object{};
|
||||
|
||||
const ctx::critical_assertion ca;
|
||||
thread_local unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
size_t(verify_content_max)
|
||||
};
|
||||
|
||||
const size_t request_size
|
||||
{
|
||||
json::serialized(_this)
|
||||
};
|
||||
|
||||
if(unlikely(request_size > buffer::size(buf)))
|
||||
throw m::error
|
||||
{
|
||||
http::PAYLOAD_TOO_LARGE, "M_REQUEST_TOO_LARGE",
|
||||
"The request size %zu bytes exceeds maximum of %zu bytes",
|
||||
request_size,
|
||||
buffer::size(buf)
|
||||
};
|
||||
|
||||
const json::object object
|
||||
{
|
||||
stringify(mutable_buffer{buf}, _this)
|
||||
};
|
||||
|
||||
return verify(pk, sig, object);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::request::verify(const ed25519::pk &pk,
|
||||
const ed25519::sig &sig,
|
||||
const json::object &object)
|
||||
{
|
||||
return pk.verify(object, sig);
|
||||
}
|
||||
|
||||
//
|
||||
// x_matrix
|
||||
//
|
||||
|
||||
ircd::m::request::x_matrix::x_matrix(const string_view &input)
|
||||
{
|
||||
string_view tokens[3];
|
||||
if(ircd::tokens(split(input, ' ').second, ',', tokens) != 3)
|
||||
throw std::out_of_range
|
||||
{
|
||||
"The x_matrix header is malformed"
|
||||
};
|
||||
|
||||
for(const auto &token : tokens)
|
||||
{
|
||||
const auto &kv{split(token, '=')};
|
||||
const auto &val{unquote(kv.second)};
|
||||
switch(hash(kv.first))
|
||||
{
|
||||
case hash("origin"): origin = val; break;
|
||||
case hash("key"): key = val; break;
|
||||
case hash("sig"): sig = val; break;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty(origin))
|
||||
throw std::out_of_range
|
||||
{
|
||||
"The x_matrix header is missing 'origin='"
|
||||
};
|
||||
|
||||
if(empty(key))
|
||||
throw std::out_of_range
|
||||
{
|
||||
"The x_matrix header is missing 'key='"
|
||||
};
|
||||
|
||||
if(empty(sig))
|
||||
throw std::out_of_range
|
||||
{
|
||||
"The x_matrix header is missing 'sig='"
|
||||
};
|
||||
}
|
|
@ -111,7 +111,7 @@ const
|
|||
return for_each(room, server, closure);
|
||||
}
|
||||
|
||||
ebool
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::for_each(const m::room &room,
|
||||
const string_view &server,
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
412
matrix/sync.cc
Normal file
412
matrix/sync.cc
Normal file
|
@ -0,0 +1,412 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
namespace ircd::m::sync
|
||||
{
|
||||
extern const ctx::pool::opts pool_opts;
|
||||
}
|
||||
|
||||
decltype(ircd::m::sync::stats_info)
|
||||
ircd::m::sync::stats_info
|
||||
{
|
||||
{ "name", "ircd.m.sync.stats.info" },
|
||||
{ "default", false },
|
||||
};
|
||||
|
||||
decltype(ircd::m::sync::log)
|
||||
ircd::m::sync::log
|
||||
{
|
||||
"m.sync", 's'
|
||||
};
|
||||
|
||||
decltype(ircd::m::sync::pool_opts)
|
||||
ircd::m::sync::pool_opts
|
||||
{
|
||||
ctx::DEFAULT_STACK_SIZE, 0, -1, 0
|
||||
};
|
||||
|
||||
decltype(ircd::m::sync::pool)
|
||||
ircd::m::sync::pool
|
||||
{
|
||||
"m.sync", pool_opts
|
||||
};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_multimap<std::string, ircd::m::sync::item, std::less<>>::map)
|
||||
ircd::util::instance_multimap<std::string, ircd::m::sync::item, std::less<>>::map
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::sync::data>::allocator)
|
||||
ircd::util::instance_list<ircd::m::sync::data>::allocator
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::sync::data>::list)
|
||||
ircd::util::instance_list<ircd::m::sync::data>::list
|
||||
{
|
||||
allocator
|
||||
};
|
||||
|
||||
bool
|
||||
ircd::m::sync::for_each(const item_closure_bool &closure)
|
||||
{
|
||||
auto it(begin(item::map));
|
||||
for(; it != end(item::map); ++it)
|
||||
if(!closure(*it->second))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::for_each(const string_view &prefix,
|
||||
const item_closure_bool &closure)
|
||||
{
|
||||
const auto depth
|
||||
{
|
||||
token_count(prefix, '.')
|
||||
};
|
||||
|
||||
auto it
|
||||
{
|
||||
item::map.lower_bound(prefix)
|
||||
};
|
||||
|
||||
for(; it != end(item::map); ++it)
|
||||
{
|
||||
const auto item_depth
|
||||
{
|
||||
token_count(it->first, '.')
|
||||
};
|
||||
|
||||
if(item_depth > depth + 1)
|
||||
continue;
|
||||
|
||||
if(it->first == prefix)
|
||||
continue;
|
||||
|
||||
if(item_depth < depth + 1)
|
||||
break;
|
||||
|
||||
if(!closure(*it->second))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::apropos(const data &d,
|
||||
const event &event)
|
||||
{
|
||||
return apropos(d, index(event, std::nothrow));
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::apropos(const data &d,
|
||||
const event::id &event_id)
|
||||
{
|
||||
return apropos(d, index(event_id, std::nothrow));
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::apropos(const data &d,
|
||||
const event::idx &event_idx)
|
||||
{
|
||||
return d.phased ||
|
||||
(event_idx >= d.range.first && event_idx < d.range.second);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::sync::loghead(const data &data)
|
||||
{
|
||||
thread_local char headbuf[256], rembuf[128], iecbuf[2][64], tmbuf[32];
|
||||
|
||||
const auto remstr
|
||||
{
|
||||
data.client?
|
||||
string(rembuf, ircd::remote(*data.client)):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
const auto flush_bytes
|
||||
{
|
||||
data.stats?
|
||||
data.stats->flush_bytes:
|
||||
0U
|
||||
};
|
||||
|
||||
const auto flush_count
|
||||
{
|
||||
data.stats?
|
||||
data.stats->flush_count:
|
||||
0U
|
||||
};
|
||||
|
||||
const auto tmstr
|
||||
{
|
||||
data.stats?
|
||||
ircd::pretty(tmbuf, data.stats->timer.at<milliseconds>(), true):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
return fmt::sprintf
|
||||
{
|
||||
headbuf, "%s %s %ld:%lu|%lu%s chunk:%zu sent:%s of %s in %s",
|
||||
remstr,
|
||||
string_view{data.user.user_id},
|
||||
data.range.first,
|
||||
data.range.second,
|
||||
vm::sequence::retired,
|
||||
data.phased?
|
||||
"|P"_sv : ""_sv,
|
||||
flush_count,
|
||||
ircd::pretty(iecbuf[1], iec(flush_bytes)),
|
||||
data.out?
|
||||
ircd::pretty(iecbuf[0], iec(flush_bytes + size(data.out->completed()))):
|
||||
string_view{},
|
||||
tmstr
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// item
|
||||
//
|
||||
|
||||
//
|
||||
// item::item
|
||||
//
|
||||
|
||||
ircd::m::sync::item::item(std::string name,
|
||||
handle polylog,
|
||||
handle linear,
|
||||
const json::members &feature)
|
||||
:instance_multimap
|
||||
{
|
||||
std::move(name)
|
||||
}
|
||||
,conf_name
|
||||
{
|
||||
fmt::snstringf{128, "ircd.m.sync.%s.enable", this->name()},
|
||||
fmt::snstringf{128, "ircd.m.sync.%s.stats.debug", this->name()},
|
||||
}
|
||||
,enable
|
||||
{
|
||||
{ "name", conf_name[0] },
|
||||
{ "default", true },
|
||||
}
|
||||
,stats_debug
|
||||
{
|
||||
{ "name", conf_name[1] },
|
||||
{ "default", false },
|
||||
}
|
||||
,_polylog
|
||||
{
|
||||
std::move(polylog)
|
||||
}
|
||||
,_linear
|
||||
{
|
||||
std::move(linear)
|
||||
}
|
||||
,feature
|
||||
{
|
||||
feature
|
||||
}
|
||||
,opts
|
||||
{
|
||||
this->feature
|
||||
}
|
||||
,phased
|
||||
{
|
||||
opts.get<bool>("phased", false)
|
||||
}
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Registered sync item(%p) '%s' (%zu features)",
|
||||
this,
|
||||
this->name(),
|
||||
opts.size(),
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::sync::item::~item()
|
||||
noexcept
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Unregistered sync item(%p) '%s'",
|
||||
this,
|
||||
this->name()
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::item::polylog(data &data)
|
||||
try
|
||||
{
|
||||
// Skip the item if disabled by configuration
|
||||
if(!enable)
|
||||
return false;
|
||||
|
||||
if(data.phased && !phased && int64_t(data.range.first) < 0)
|
||||
return false;
|
||||
|
||||
#ifdef RB_DEBUG
|
||||
sync::stats stats
|
||||
{
|
||||
data.stats && (stats_info || stats_debug)?
|
||||
*data.stats:
|
||||
sync::stats{}
|
||||
};
|
||||
|
||||
if(data.stats && (stats_info || stats_debug))
|
||||
stats.timer = {};
|
||||
#endif
|
||||
|
||||
const bool ret
|
||||
{
|
||||
_polylog(data)
|
||||
};
|
||||
|
||||
#ifdef RB_DEBUG
|
||||
if(data.stats && (stats_info || stats_debug))
|
||||
{
|
||||
//data.out.flush();
|
||||
thread_local char tmbuf[32];
|
||||
log::debug
|
||||
{
|
||||
log, "polylog %s commit:%b '%s' %s",
|
||||
loghead(data),
|
||||
ret,
|
||||
name(),
|
||||
ircd::pretty(tmbuf, stats.timer.at<microseconds>(), true)
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
this_ctx::interruption_point();
|
||||
return ret;
|
||||
}
|
||||
catch(const std::bad_function_call &e)
|
||||
{
|
||||
log::dwarning
|
||||
{
|
||||
log, "polylog %s '%s' missing handler :%s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what()
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(const m::error &e)
|
||||
{
|
||||
log::derror
|
||||
{
|
||||
log, "polylog %s '%s' :%s %s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what(),
|
||||
e.content
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::derror
|
||||
{
|
||||
log, "polylog %s '%s' :%s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what()
|
||||
};
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::item::linear(data &data)
|
||||
try
|
||||
{
|
||||
if(!enable)
|
||||
return false;
|
||||
|
||||
return _linear(data);
|
||||
}
|
||||
catch(const std::bad_function_call &e)
|
||||
{
|
||||
thread_local char rembuf[128];
|
||||
log::dwarning
|
||||
{
|
||||
log, "linear %s '%s' missing handler :%s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what()
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(const m::error &e)
|
||||
{
|
||||
log::derror
|
||||
{
|
||||
log, "linear %s '%s' :%s %s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what(),
|
||||
e.content
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::derror
|
||||
{
|
||||
log, "linear %s '%s' :%s",
|
||||
loghead(data),
|
||||
name(),
|
||||
e.what()
|
||||
};
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::sync::item::children()
|
||||
const
|
||||
{
|
||||
size_t ret(0);
|
||||
sync::for_each(this->name(), [&ret]
|
||||
(auto &item)
|
||||
{
|
||||
++ret;
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::sync::item::member_name()
|
||||
const
|
||||
{
|
||||
return token_last(name(), '.');
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::sync::item::name()
|
||||
const
|
||||
{
|
||||
return this->instance_multimap::it->first;
|
||||
}
|
139
matrix/txn.cc
Normal file
139
matrix/txn.cc
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
/// Returns the serial size of the JSON this txn would consume. Note: this
|
||||
/// creates a json::iov involving a timestamp to figure out the total size
|
||||
/// of the txn. When the user creates the actual txn a different timestamp
|
||||
/// is created which may be a different size. Consider using the lower-level
|
||||
/// create(closure) or add some pad to be sure.
|
||||
///
|
||||
size_t
|
||||
ircd::m::txn::serialized(const array &pdu,
|
||||
const array &edu,
|
||||
const array &pdu_failure)
|
||||
{
|
||||
size_t ret;
|
||||
const auto closure{[&ret]
|
||||
(const json::iov &iov)
|
||||
{
|
||||
ret = json::serialized(iov);
|
||||
}};
|
||||
|
||||
create(closure, pdu, edu, pdu_failure);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Stringifies a txn from the inputs into the returned std::string
|
||||
///
|
||||
std::string
|
||||
ircd::m::txn::create(const array &pdu,
|
||||
const array &edu,
|
||||
const array &pdu_failure)
|
||||
{
|
||||
std::string ret;
|
||||
const auto closure{[&ret]
|
||||
(const json::iov &iov)
|
||||
{
|
||||
ret = json::strung(iov);
|
||||
}};
|
||||
|
||||
create(closure, pdu, edu, pdu_failure);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Stringifies a txn from the inputs into the buffer
|
||||
///
|
||||
ircd::string_view
|
||||
ircd::m::txn::create(const mutable_buffer &buf,
|
||||
const array &pdu,
|
||||
const array &edu,
|
||||
const array &pdu_failure)
|
||||
{
|
||||
string_view ret;
|
||||
const auto closure{[&buf, &ret]
|
||||
(const json::iov &iov)
|
||||
{
|
||||
ret = json::stringify(mutable_buffer{buf}, iov);
|
||||
}};
|
||||
|
||||
create(closure, pdu, edu, pdu_failure);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Forms a txn from the inputs into a json::iov and presents that iov
|
||||
/// to the user's closure.
|
||||
///
|
||||
void
|
||||
ircd::m::txn::create(const closure &closure,
|
||||
const array &pdu,
|
||||
const array &edu,
|
||||
const array &pdu_failure)
|
||||
{
|
||||
using ircd::size;
|
||||
|
||||
json::iov iov;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ iov, { "origin", my_host() }},
|
||||
{ iov, { "origin_server_ts", ircd::time<milliseconds>() }},
|
||||
};
|
||||
|
||||
const json::iov::add _pdus
|
||||
{
|
||||
iov, !empty(pdu),
|
||||
{
|
||||
"pdus", [&pdu]() -> json::value
|
||||
{
|
||||
return { data(pdu), size(pdu) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const json::iov::add _edus
|
||||
{
|
||||
iov, !empty(edu),
|
||||
{
|
||||
"edus", [&edu]() -> json::value
|
||||
{
|
||||
return { data(edu), size(edu) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const json::iov::add _pdu_failures
|
||||
{
|
||||
iov, !empty(pdu_failure),
|
||||
{
|
||||
"pdu_failures", [&pdu_failure]() -> json::value
|
||||
{
|
||||
return { data(pdu_failure), size(pdu_failure) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
closure(iov);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::txn::create_id(const mutable_buffer &out,
|
||||
const string_view &txn)
|
||||
{
|
||||
const sha256::buf hash
|
||||
{
|
||||
sha256{txn}
|
||||
};
|
||||
|
||||
const string_view txnid
|
||||
{
|
||||
b58encode(out, hash)
|
||||
};
|
||||
|
||||
return txnid;
|
||||
}
|
160
matrix/user.cc
160
matrix/user.cc
|
@ -14,6 +14,30 @@ namespace ircd::m
|
|||
static room create_user_room(const user::id &, const room::id &, const json::members &contents);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::exists(const user::id &user_id)
|
||||
{
|
||||
// The way we know a user exists is testing if their room exists.
|
||||
const m::user::room user_room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
return m::exists(user_room);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::exists(const user &user)
|
||||
{
|
||||
return exists(user.user_id);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::my(const user &user)
|
||||
{
|
||||
return my(user.user_id);
|
||||
}
|
||||
|
||||
ircd::m::user
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::create(const m::user::id &user_id,
|
||||
|
@ -65,6 +89,108 @@ catch(const std::exception &e)
|
|||
// user::user
|
||||
//
|
||||
|
||||
/// Generates a user-room ID into buffer; see room_id() overload.
|
||||
ircd::m::id::room::buf
|
||||
ircd::m::user::room_id()
|
||||
const
|
||||
{
|
||||
ircd::m::id::room::buf buf;
|
||||
return buf.assigned(room_id(buf));
|
||||
}
|
||||
|
||||
/// This generates a room mxid for the "user's room" essentially serving as
|
||||
/// a database mechanism for this specific user. This room_id is a hash of
|
||||
/// the user's full mxid.
|
||||
///
|
||||
ircd::m::id::room
|
||||
ircd::m::user::room_id(const mutable_buffer &buf)
|
||||
const
|
||||
{
|
||||
assert(!empty(user_id));
|
||||
const ripemd160::buf hash
|
||||
{
|
||||
ripemd160{user_id}
|
||||
};
|
||||
|
||||
char b58[size(hash) * 2];
|
||||
return
|
||||
{
|
||||
buf, b58encode(b58, hash), my_host()
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::device::id::buf
|
||||
ircd::m::user::get_device_from_access_token(const string_view &token)
|
||||
{
|
||||
const event::idx event_idx
|
||||
{
|
||||
user::tokens.get("ircd.access_token", token)
|
||||
};
|
||||
|
||||
device::id::buf ret;
|
||||
m::get(event_idx, "content", [&ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
ret = unquote(content.at("device_id"));
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::user::gen_access_token(const mutable_buffer &buf)
|
||||
{
|
||||
static const size_t token_max{32};
|
||||
static const auto &token_dict{rand::dict::alpha};
|
||||
|
||||
const mutable_buffer out
|
||||
{
|
||||
data(buf), std::min(token_max, size(buf))
|
||||
};
|
||||
|
||||
return rand::string(token_dict, out);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::user::activate()
|
||||
{
|
||||
using prototype = event::id::buf (const m::user &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"client_account", "activate__user"
|
||||
};
|
||||
|
||||
return function(*this);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::user::deactivate()
|
||||
{
|
||||
using prototype = event::id::buf (const m::user &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"client_account", "deactivate__user"
|
||||
};
|
||||
|
||||
return function(*this);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::is_active()
|
||||
const
|
||||
{
|
||||
using prototype = bool (const m::user &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"client_account", "is_active__user"
|
||||
};
|
||||
|
||||
return function(*this);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::user::password(const string_view &password)
|
||||
|
@ -150,3 +276,37 @@ ircd::m::gen_password_hash(const mutable_buffer &out,
|
|||
|
||||
return b64encode_unpadded(out, hash);
|
||||
}
|
||||
|
||||
//
|
||||
// user::room
|
||||
//
|
||||
|
||||
ircd::m::user::room::room(const m::user::id &user_id,
|
||||
const vm::copts *const &copts,
|
||||
const event::fetch::opts *const &fopts)
|
||||
:room
|
||||
{
|
||||
m::user{user_id}, copts, fopts
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::user::room::room(const m::user &user,
|
||||
const vm::copts *const &copts,
|
||||
const event::fetch::opts *const &fopts)
|
||||
:user{user}
|
||||
,room_id{user.room_id()}
|
||||
{
|
||||
static_cast<m::room &>(*this) =
|
||||
{
|
||||
room_id, copts, fopts
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::room::is(const room::id &room_id,
|
||||
const user::id &user_id)
|
||||
{
|
||||
const user::room user_room{user_id};
|
||||
return user_room.room_id == room_id;
|
||||
}
|
||||
|
|
600
matrix/vm.cc
Normal file
600
matrix/vm.cc
Normal file
|
@ -0,0 +1,600 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
decltype(ircd::m::vm::default_opts)
|
||||
ircd::m::vm::default_opts;
|
||||
|
||||
decltype(ircd::m::vm::default_copts)
|
||||
ircd::m::vm::default_copts;
|
||||
|
||||
decltype(ircd::m::vm::log)
|
||||
ircd::m::vm::log
|
||||
{
|
||||
"m.vm", 'v'
|
||||
};
|
||||
|
||||
decltype(ircd::m::vm::dock)
|
||||
ircd::m::vm::dock;
|
||||
|
||||
decltype(ircd::m::vm::ready)
|
||||
ircd::m::vm::ready;
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::vm::loghead(const eval &eval)
|
||||
{
|
||||
thread_local char buf[128];
|
||||
return loghead(buf, eval);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::vm::loghead(const mutable_buffer &buf,
|
||||
const eval &eval)
|
||||
{
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "vm:%lu:%lu:%lu eval:%lu seq:%lu share:%lu:%lu %s",
|
||||
sequence::uncommitted,
|
||||
sequence::committed,
|
||||
sequence::retired,
|
||||
eval.id,
|
||||
sequence::get(eval),
|
||||
eval.sequence_shared[0],
|
||||
eval.sequence_shared[1],
|
||||
eval.event_?
|
||||
string_view{eval.event_->event_id}:
|
||||
"<unidentified>"_sv,
|
||||
};
|
||||
}
|
||||
|
||||
ircd::http::code
|
||||
ircd::m::vm::http_code(const fault &code)
|
||||
{
|
||||
switch(code)
|
||||
{
|
||||
case fault::ACCEPT: return http::OK;
|
||||
case fault::EXISTS: return http::CONFLICT;
|
||||
case fault::INVALID: return http::BAD_REQUEST;
|
||||
case fault::GENERAL: return http::UNAUTHORIZED;
|
||||
case fault::AUTH: return http::FORBIDDEN;
|
||||
case fault::STATE: return http::NOT_FOUND;
|
||||
case fault::EVENT: return http::NOT_FOUND;
|
||||
default: return http::INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::vm::reflect(const enum fault &code)
|
||||
{
|
||||
switch(code)
|
||||
{
|
||||
case fault::ACCEPT: return "#ACCEPT";
|
||||
case fault::EXISTS: return "#EXISTS";
|
||||
case fault::GENERAL: return "#GENERAL";
|
||||
case fault::INVALID: return "#INVALID";
|
||||
case fault::AUTH: return "#AUTH";
|
||||
case fault::EVENT: return "#EVENT";
|
||||
case fault::STATE: return "#STATE";
|
||||
case fault::INTERRUPT: return "#INTERRUPT";
|
||||
}
|
||||
|
||||
return "??????";
|
||||
}
|
||||
|
||||
//
|
||||
// Eval
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// Instance list linkage for all of the evaluations.
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::vm::eval>::allocator)
|
||||
ircd::util::instance_list<ircd::m::vm::eval>::allocator
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::m::vm::eval>::list)
|
||||
ircd::util::instance_list<ircd::m::vm::eval>::list
|
||||
{
|
||||
allocator
|
||||
};
|
||||
|
||||
decltype(ircd::m::vm::eval::id_ctr)
|
||||
ircd::m::vm::eval::id_ctr;
|
||||
|
||||
decltype(ircd::m::vm::eval::executing)
|
||||
ircd::m::vm::eval::executing;
|
||||
|
||||
decltype(ircd::m::vm::eval::injecting)
|
||||
ircd::m::vm::eval::injecting;
|
||||
|
||||
void
|
||||
ircd::m::vm::eval::seqsort()
|
||||
{
|
||||
eval::list.sort([]
|
||||
(const auto *const &a, const auto *const &b)
|
||||
{
|
||||
if(sequence::get(*a) == 0)
|
||||
return false;
|
||||
|
||||
if(sequence::get(*b) == 0)
|
||||
return true;
|
||||
|
||||
return sequence::get(*a) < sequence::get(*b);
|
||||
});
|
||||
}
|
||||
|
||||
ircd::m::vm::eval *
|
||||
ircd::m::vm::eval::seqmin()
|
||||
{
|
||||
const auto it
|
||||
{
|
||||
std::min_element(begin(eval::list), end(eval::list), []
|
||||
(const auto *const &a, const auto *const &b)
|
||||
{
|
||||
if(sequence::get(*a) == 0)
|
||||
return false;
|
||||
|
||||
if(sequence::get(*b) == 0)
|
||||
return true;
|
||||
|
||||
return sequence::get(*a) < sequence::get(*b);
|
||||
})
|
||||
};
|
||||
|
||||
if(it == end(eval::list))
|
||||
return nullptr;
|
||||
|
||||
if(sequence::get(**it) == 0)
|
||||
return nullptr;
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
ircd::m::vm::eval *
|
||||
ircd::m::vm::eval::seqmax()
|
||||
{
|
||||
const auto it
|
||||
{
|
||||
std::max_element(begin(eval::list), end(eval::list), []
|
||||
(const auto *const &a, const auto *const &b)
|
||||
{
|
||||
return sequence::get(*a) < sequence::get(*b);
|
||||
})
|
||||
};
|
||||
|
||||
if(it == end(eval::list))
|
||||
return nullptr;
|
||||
|
||||
if(sequence::get(**it) == 0)
|
||||
return nullptr;
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
ircd::m::vm::eval *
|
||||
ircd::m::vm::eval::seqnext(const uint64_t &seq)
|
||||
{
|
||||
eval *ret{nullptr};
|
||||
for(auto *const &eval : eval::list)
|
||||
{
|
||||
if(sequence::get(*eval) <= seq)
|
||||
continue;
|
||||
|
||||
if(!ret || sequence::get(*eval) < sequence::get(*ret))
|
||||
ret = eval;
|
||||
}
|
||||
|
||||
assert(!ret || sequence::get(*ret) > seq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::vm::eval::sequnique(const uint64_t &seq)
|
||||
{
|
||||
return 1 == std::count_if(begin(eval::list), end(eval::list), [&seq]
|
||||
(const auto *const &eval)
|
||||
{
|
||||
return sequence::get(*eval) == seq;
|
||||
});
|
||||
}
|
||||
|
||||
ircd::m::vm::eval &
|
||||
ircd::m::vm::eval::get(const event::id &event_id)
|
||||
{
|
||||
auto *const ret
|
||||
{
|
||||
find(event_id)
|
||||
};
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw std::out_of_range
|
||||
{
|
||||
"eval::get(): event_id not being evaluated."
|
||||
};
|
||||
|
||||
return *ret;
|
||||
}
|
||||
|
||||
ircd::m::vm::eval *
|
||||
ircd::m::vm::eval::find(const event::id &event_id)
|
||||
{
|
||||
eval *ret{nullptr};
|
||||
for_each([&event_id, &ret](eval &e)
|
||||
{
|
||||
if(e.event_)
|
||||
{
|
||||
if(e.event_->event_id == event_id)
|
||||
ret = &e;
|
||||
}
|
||||
else if(e.issue)
|
||||
{
|
||||
if(e.issue->has("event_id"))
|
||||
if(string_view{e.issue->at("event_id")} == event_id)
|
||||
ret = &e;
|
||||
}
|
||||
else if(e.event_id == event_id)
|
||||
ret = &e;
|
||||
|
||||
return ret == nullptr;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::vm::eval::count(const event::id &event_id)
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each([&event_id, &ret](eval &e)
|
||||
{
|
||||
if(e.event_)
|
||||
{
|
||||
if(e.event_->event_id == event_id)
|
||||
++ret;
|
||||
}
|
||||
else if(e.issue)
|
||||
{
|
||||
if(e.issue->has("event_id"))
|
||||
if(string_view{e.issue->at("event_id")} == event_id)
|
||||
++ret;
|
||||
}
|
||||
else if(e.event_id == event_id)
|
||||
++ret;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::vm::eval::count(const ctx::ctx *const &c)
|
||||
{
|
||||
return std::count_if(begin(eval::list), end(eval::list), [&c]
|
||||
(const eval *const &eval)
|
||||
{
|
||||
return eval->ctx == c;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::vm::eval::for_each(const std::function<bool (eval &)> &closure)
|
||||
{
|
||||
for(eval *const &eval : eval::list)
|
||||
if(!closure(*eval))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::vm::eval::for_each(const ctx::ctx *const &c,
|
||||
const std::function<bool (eval &)> &closure)
|
||||
{
|
||||
for(eval *const &eval : eval::list)
|
||||
if(eval->ctx == c)
|
||||
if(!closure(*eval))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// eval::eval
|
||||
//
|
||||
|
||||
ircd::m::vm::eval::eval(json::iov &event,
|
||||
const json::iov &content,
|
||||
const vm::copts &opts)
|
||||
:eval{opts}
|
||||
{
|
||||
operator()(event, content);
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::eval(const event &event,
|
||||
const vm::opts &opts)
|
||||
:eval{opts}
|
||||
{
|
||||
operator()(event);
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::eval(const json::array &pdus,
|
||||
const vm::opts &opts)
|
||||
:opts{&opts}
|
||||
{
|
||||
// Sort the events first to avoid complicating the evals; the events might
|
||||
// be from different rooms but it doesn't matter.
|
||||
std::vector<m::event> events(begin(pdus), end(pdus));
|
||||
std::sort(begin(events), end(events));
|
||||
this->pdus = events;
|
||||
|
||||
// Conduct each eval without letting any one exception ruin things for the
|
||||
// others, including an interrupt. The only exception is a termination.
|
||||
for(auto it(begin(events)); it != end(events); ++it) try
|
||||
{
|
||||
auto &event{*it};
|
||||
|
||||
// If we set the event_id in the event instance we have to unset
|
||||
// it so other contexts don't see an invalid reference.
|
||||
const unwind event_id{[&event]
|
||||
{
|
||||
event.event_id = json::get<"event_id"_>(event)?
|
||||
event.event_id:
|
||||
m::event::id{};
|
||||
}};
|
||||
|
||||
// We have to set the event_id in the event instance if it didn't come
|
||||
// with the event JSON.
|
||||
if(!opts.edu && !event.event_id)
|
||||
event.event_id = opts.room_version == "3"?
|
||||
event::id{event::id::v3{this->event_id, event}}:
|
||||
event::id{event::id::v4{this->event_id, event}};
|
||||
|
||||
// When a fault::EXISTS would not actually be revealed to the user in
|
||||
// any way we can elide a lot of grief by checking this here first and
|
||||
// skipping the event. The query path will be adequately cached anyway.
|
||||
if(~(opts.warnlog | opts.errorlog) & fault::EXISTS)
|
||||
if(event.event_id && m::exists(event.event_id))
|
||||
continue;
|
||||
|
||||
operator()(event);
|
||||
}
|
||||
catch(const ctx::interrupted &e)
|
||||
{
|
||||
if(opts.nothrows & fault::INTERRUPT)
|
||||
continue;
|
||||
else
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
catch(const ctx::terminated &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::eval(const vm::copts &opts)
|
||||
:opts{&opts}
|
||||
,copts{&opts}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::eval(const vm::opts &opts)
|
||||
:opts{&opts}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::~eval()
|
||||
noexcept
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::vm::eval::operator
|
||||
const event::id::buf &()
|
||||
const
|
||||
{
|
||||
return event_id;
|
||||
}
|
||||
|
||||
const ircd::m::event *
|
||||
ircd::m::vm::eval::find_pdu(const event::id &event_id)
|
||||
{
|
||||
const m::event *ret{nullptr};
|
||||
for_each_pdu([&ret, &event_id]
|
||||
(const m::event &event)
|
||||
{
|
||||
if(event.event_id != event_id)
|
||||
return true;
|
||||
|
||||
ret = std::addressof(event);
|
||||
return false;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const ircd::m::event *
|
||||
ircd::m::vm::eval::find_pdu(const eval &eval,
|
||||
const event::id &event_id)
|
||||
{
|
||||
const m::event *ret{nullptr};
|
||||
for(const auto &event : eval.pdus)
|
||||
{
|
||||
if(event.event_id != event_id)
|
||||
continue;
|
||||
|
||||
ret = std::addressof(event);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::vm::eval::for_each_pdu(const std::function<bool (const event &)> &closure)
|
||||
{
|
||||
return for_each([&closure](eval &e)
|
||||
{
|
||||
if(!empty(e.pdus))
|
||||
{
|
||||
for(const auto &pdu : e.pdus)
|
||||
if(!closure(pdu))
|
||||
return false;
|
||||
}
|
||||
else if(e.event_)
|
||||
{
|
||||
if(!closure(*e.event_))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// Figure 1:
|
||||
/// in . <-- injection
|
||||
/// ===:::::::==//
|
||||
/// | ||||||| // <-- these functions
|
||||
/// | \\|// //|
|
||||
/// | ||| // | | acceleration
|
||||
/// | |||// | |
|
||||
/// | |||/ | |
|
||||
/// | ||| | V
|
||||
/// | !!! |
|
||||
/// | * | <----- nozzle
|
||||
/// | ///|||\\\ |
|
||||
/// |/|/|/|\|\|\| <---- propagation cone
|
||||
/// _/|/|/|/|\|\|\|\_
|
||||
/// out
|
||||
///
|
||||
|
||||
/// Inject a new event originating from this server.
|
||||
///
|
||||
enum ircd::m::vm::fault
|
||||
ircd::m::vm::eval::operator()(json::iov &event,
|
||||
const json::iov &contents)
|
||||
{
|
||||
using prototype = fault (eval &, json::iov &, const json::iov &);
|
||||
|
||||
static mods::import<prototype> call
|
||||
{
|
||||
"m_vm", "ircd::m::vm::inject"
|
||||
};
|
||||
|
||||
vm::dock.wait([]
|
||||
{
|
||||
return vm::ready;
|
||||
});
|
||||
|
||||
return call(*this, event, contents);
|
||||
}
|
||||
|
||||
enum ircd::m::vm::fault
|
||||
ircd::m::vm::eval::operator()(const event &event)
|
||||
{
|
||||
using prototype = fault (eval &, const m::event &);
|
||||
|
||||
static mods::import<prototype> call
|
||||
{
|
||||
"m_vm", "ircd::m::vm::execute"
|
||||
};
|
||||
|
||||
vm::dock.wait([]
|
||||
{
|
||||
return vm::ready;
|
||||
});
|
||||
|
||||
return call(*this, event);
|
||||
}
|
||||
|
||||
//
|
||||
// sequence
|
||||
//
|
||||
|
||||
decltype(ircd::m::vm::sequence::dock)
|
||||
ircd::m::vm::sequence::dock;
|
||||
|
||||
decltype(ircd::m::vm::sequence::retired)
|
||||
ircd::m::vm::sequence::retired;
|
||||
|
||||
decltype(ircd::m::vm::sequence::committed)
|
||||
ircd::m::vm::sequence::committed;
|
||||
|
||||
decltype(ircd::m::vm::sequence::uncommitted)
|
||||
ircd::m::vm::sequence::uncommitted;
|
||||
|
||||
uint64_t
|
||||
ircd::m::vm::sequence::min()
|
||||
{
|
||||
const auto *const e
|
||||
{
|
||||
eval::seqmin()
|
||||
};
|
||||
|
||||
return e? get(*e) : 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ircd::m::vm::sequence::max()
|
||||
{
|
||||
const auto *const e
|
||||
{
|
||||
eval::seqmax()
|
||||
};
|
||||
|
||||
return e? get(*e) : 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ircd::m::vm::sequence::get(id::event::buf &event_id)
|
||||
{
|
||||
static constexpr auto column_idx
|
||||
{
|
||||
json::indexof<event, "event_id"_>()
|
||||
};
|
||||
|
||||
auto &column
|
||||
{
|
||||
dbs::event_column.at(column_idx)
|
||||
};
|
||||
|
||||
const auto it
|
||||
{
|
||||
column.rbegin()
|
||||
};
|
||||
|
||||
if(!it)
|
||||
{
|
||||
// If this iterator is invalid the events db should
|
||||
// be completely fresh.
|
||||
assert(db::sequence(*dbs::events) == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto &ret
|
||||
{
|
||||
byte_view<uint64_t>(it->first)
|
||||
};
|
||||
|
||||
event_id = it->second;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const uint64_t &
|
||||
ircd::m::vm::sequence::get(const eval &eval)
|
||||
{
|
||||
return eval.sequence;
|
||||
}
|
|
@ -1663,6 +1663,11 @@ console_cmd__conf(opt &out, const string_view &line)
|
|||
return console_cmd__conf__list(out, line);
|
||||
}
|
||||
|
||||
extern "C" ircd::m::event::id::buf
|
||||
set_conf_item(const ircd::m::user::id &,
|
||||
const ircd::string_view &key,
|
||||
const ircd::string_view &val);
|
||||
|
||||
bool
|
||||
console_cmd__conf__set(opt &out, const string_view &line)
|
||||
{
|
||||
|
@ -1681,15 +1686,6 @@ console_cmd__conf__set(opt &out, const string_view &line)
|
|||
tokens_after(line, ' ', 0)
|
||||
};
|
||||
|
||||
using prototype = m::event::id::buf (const m::user::id &,
|
||||
const string_view &key,
|
||||
const string_view &val);
|
||||
|
||||
static mods::import<prototype> set_conf_item
|
||||
{
|
||||
"conf", "set_conf_item"
|
||||
};
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
set_conf_item(m::me, key, val)
|
||||
|
|
|
@ -14,6 +14,12 @@ namespace ircd::m
|
|||
extern hookfn<room::auth::hookdata &> user_highlight_auth_hook;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Matrix @room highlight authentication"
|
||||
};
|
||||
|
||||
decltype(ircd::m::user_highlight_auth_hook)
|
||||
ircd::m::user_highlight_auth_hook
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue