0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-20 03:43:47 +02:00

modules: Distill all API's from modules up to matrix lib.

This commit is contained in:
Jason Volk 2019-09-26 12:16:22 -07:00
parent d3809d43e5
commit 183e44e0b6
62 changed files with 1893 additions and 2478 deletions

View file

@ -45,6 +45,7 @@ namespace ircd::m::feds
/// the user can distinguish different requests in their options vector.
///
struct ircd::m::feds::execute
:boolean
{
execute(const vector_view<const opts> &, const closure &);
execute(const opts &, const closure &);
@ -142,3 +143,13 @@ struct ircd::m::feds::opts
/// typical use case.
bool exclude_myself {false};
};
inline
ircd::m::feds::execute::execute(const opts &opts,
const closure &closure)
:execute
{
vector_view<const feds::opts>(std::addressof(opts), 1),
closure
}
{}

View file

@ -32,6 +32,8 @@ namespace ircd::m::receipt
// [SET] Indicate that the user has read the event in the room.
id::event::buf read(const id::room &, const id::user &, const id::event &, const json::object & = {});
extern log::log log;
};
struct ircd::m::edu::m_receipt

View file

@ -24,5 +24,7 @@ struct ircd::m::user::events
bool for_each(const closure_bool &) const;
size_t count() const;
events(const m::user &user);
events(const m::user &user)
:user{user}
{}
};

View file

@ -32,5 +32,7 @@ struct ircd::m::user::mitsein
// Existential convenience (does `user` and `other` share any common room).
bool has(const m::user &other, const string_view &membership = {}) const;
mitsein(const m::user &user);
mitsein(const m::user &user)
:user{user}
{}
};

View file

@ -33,5 +33,7 @@ struct ircd::m::user::rooms
size_t count(const string_view &membership) const;
size_t count() const;
rooms(const m::user &user);
rooms(const m::user &user)
:user_room{user}
{}
};

View file

@ -29,5 +29,7 @@ struct ircd::m::user::servers
// Existential convenience (does `user` and `other` share any common room).
bool has(const string_view &server, const string_view &membership = {}) const;
servers(const m::user &user);
servers(const m::user &user)
:user{user}
{}
};

View file

@ -26,15 +26,18 @@ AM_CPPFLAGS = \
AM_LDFLAGS = \
-version-info 0:1:0 \
-Wl,--no-undefined-version \
-Wl,--unresolved-symbols=report-all \
-Wl,--allow-shlib-undefined \
-Wl,--weak-unresolved-symbols \
-Wl,--unresolved-symbols=ignore-in-shared-libs \
-Wl,-z,now \
-L$(top_srcdir)/ircd \
$(PLATFORM_LDFLAGS) \
@EXTRA_LDFLAGS@ \
###
AM_LIBS = \
-lircd \
###
if MINGW
AM_LDFLAGS += \
-Wl,--enable-runtime-pseudo-reloc \
@ -46,10 +49,6 @@ libircd_matrix_LTLIBRARIES = \
libircd_matrix.la \
###
libircd_matrix_la_LIBADD = \
-lircd \
###
#
# Unit list
#
@ -58,11 +57,53 @@ libircd_matrix_la_SOURCES =#
libircd_matrix_la_SOURCES += name.cc
libircd_matrix_la_SOURCES += id.cc
libircd_matrix_la_SOURCES += dbs.cc
libircd_matrix_la_SOURCES += room.cc
libircd_matrix_la_SOURCES += fed.cc
libircd_matrix_la_SOURCES += init_bootstrap.cc
libircd_matrix_la_SOURCES += matrix.cc
libircd_matrix_la_SOURCES += event.cc
libircd_matrix_la_SOURCES += room.cc
libircd_matrix_la_SOURCES += user.cc
libircd_matrix_la_SOURCES += breadcrumb_rooms.cc
libircd_matrix_la_SOURCES += create.cc
libircd_matrix_la_SOURCES += device.cc
libircd_matrix_la_SOURCES += display_name.cc
libircd_matrix_la_SOURCES += event_append.cc
libircd_matrix_la_SOURCES += event_horizon.cc
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 += ignored_user_list.cc
libircd_matrix_la_SOURCES += init_backfill.cc
libircd_matrix_la_SOURCES += init_bootstrap.cc
libircd_matrix_la_SOURCES += join.cc
libircd_matrix_la_SOURCES += keys.cc
libircd_matrix_la_SOURCES += leave.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 += room_aliases.cc
libircd_matrix_la_SOURCES += room_auth.cc
libircd_matrix_la_SOURCES += room_bootstrap.cc
libircd_matrix_la_SOURCES += room_events.cc
libircd_matrix_la_SOURCES += room_head.cc
libircd_matrix_la_SOURCES += room_history_visibility.cc
libircd_matrix_la_SOURCES += rooms.cc
libircd_matrix_la_SOURCES += room_server_acl.cc
libircd_matrix_la_SOURCES += rooms_summary.cc
libircd_matrix_la_SOURCES += typing.cc
libircd_matrix_la_SOURCES += user_account_data.cc
libircd_matrix_la_SOURCES += user_events.cc
libircd_matrix_la_SOURCES += user_filter.cc
libircd_matrix_la_SOURCES += user_highlight.cc
libircd_matrix_la_SOURCES += user_mitsein.cc
libircd_matrix_la_SOURCES += user_profile.cc
libircd_matrix_la_SOURCES += user_register.cc
libircd_matrix_la_SOURCES += user_room_account_data.cc
libircd_matrix_la_SOURCES += user_rooms.cc
libircd_matrix_la_SOURCES += user_room_tags.cc
libircd_matrix_la_SOURCES += users.cc
libircd_matrix_la_SOURCES += users_servers.cc
libircd_matrix_la_SOURCES += conf.cc
#
# Unit configurations

View file

@ -0,0 +1,75 @@
// 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.
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::set(const json::array &rooms)
const
{
const json::strung object
{
json::members
{
{ "rooms", rooms }
}
};
return account_data.set("im.vector.riot.breadcrumb_rooms", object);
}
bool
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::for_each(const closure_bool &closure)
const
{
bool ret{true};
get(std::nothrow, [&closure, &ret]
(const json::array &rooms)
{
for(const json::string &room : rooms)
if(!closure(room))
{
ret = false;
break;
}
});
return ret;
}
void
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::get(const closure &closure)
const
{
if(!get(std::nothrow, closure))
throw m::NOT_FOUND
{
"User has no breadcrumb_rooms set in their account_data."
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::get(std::nothrow_t,
const closure &closure)
const
{
return account_data.get(std::nothrow, "im.vector.riot.breadcrumb_rooms", [&closure]
(const string_view &key, const json::object &object)
{
const json::array &rooms
{
object["rooms"]
};
closure(rooms);
});
}

View file

@ -25,19 +25,17 @@ conf_on_init_iter
end(conf::on_init)
};
mapi::header
IRCD_MODULE
void conf_on_init()
{
"Server Configuration", []
{
conf_on_init_iter = conf::on_init.emplace(end(conf::on_init), init_conf_item);
reload_conf();
}, []
{
assert(conf_on_init_iter != end(conf::on_init));
conf::on_init.erase(conf_on_init_iter);
}
};
conf_on_init_iter = conf::on_init.emplace(end(conf::on_init), init_conf_item);
reload_conf();
}
void conf_on_fini()
{
assert(conf_on_init_iter != end(conf::on_init));
conf::on_init.erase(conf_on_init_iter);
}
/// Set to false to quiet errors from a conf item failing to set
bool

View file

@ -27,12 +27,6 @@ struct ircd::m::report_error
args&&... a);
};
ircd::mapi::header
IRCD_MODULE
{
"Matrix create room"
};
decltype(ircd::m::createroom::version_default)
IRCD_MODULE_EXPORT_DATA
ircd::m::createroom::version_default

310
matrix/device.cc Normal file
View file

@ -0,0 +1,310 @@
// 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.
std::map<std::string, long>
IRCD_MODULE_EXPORT
ircd::m::device::count_one_time_keys(const user &user,
const string_view &device_id)
{
std::map<std::string, long> ret;
for_each(user, device_id, [&ret]
(const string_view &type)
{
if(!startswith(type, "one_time_key|"))
return true;
const auto &[prefix, ident]
{
split(type, '|')
};
const auto &[algorithm, name]
{
split(ident, ':')
};
assert(prefix == "one_time_key");
assert(!empty(algorithm));
assert(!empty(ident));
assert(!empty(name));
auto it(ret.lower_bound(algorithm));
if(it == end(ret) || it->first != algorithm)
it = ret.emplace_hint(it, algorithm, 0L);
auto &count(it->second);
++count;
return true;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::set(const m::user &user,
const device &device)
{
const user::room user_room{user};
const string_view &device_id
{
json::at<"device_id"_>(device)
};
json::for_each(device, [&user, &user_room, &device_id]
(const auto &prop, auto &&val)
{
if(!json::defined(json::value(val)))
return;
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
m::send(user_room, user, type, device_id, json::members
{
{ "", val }
});
});
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::set(const m::user &user,
const string_view &id,
const string_view &prop,
const string_view &val)
{
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
const user::room user_room{user};
m::send(user_room, user, type, id, json::members
{
{ "", val }
});
return true;
}
/// To delete a device we iterate the user's room state for all types matching
/// ircd.device.* (and ircd.device) which have a state_key of the device_id.
/// Those events are redacted which removes them from appearing in the state.
bool
IRCD_MODULE_EXPORT
ircd::m::device::del(const m::user &user,
const string_view &id)
{
const user::room user_room{user};
const room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
state.for_each(type, [&user, &id, &user_room, &state]
(const string_view &type, const string_view &, const event::idx &)
{
const auto event_idx
{
state.get(std::nothrow, type, id)
};
const auto event_id
{
m::event_id(event_idx, std::nothrow)
};
if(event_id)
m::redact(user_room, user, event_id, "deleted");
return true;
});
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::has(const m::user &user,
const string_view &id)
{
const user::room user_room{user};
const room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
bool ret(false);
state.for_each(type, [&state, &id, &ret]
(const string_view &type, const string_view &, const event::idx &)
{
ret = state.has(type, id);
return !ret;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::has(const m::user &user,
const string_view &id,
const string_view &prop)
{
bool ret{false};
get(std::nothrow, user, id, prop, [&ret]
(const string_view &value)
{
ret = !empty(value);
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::get(std::nothrow_t,
const m::user &user,
const string_view &id,
const string_view &prop,
const closure &closure)
{
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
const m::user::room user_room{user};
const m::room::state state{user_room};
const auto event_idx
{
state.get(std::nothrow, type, id)
};
return m::get(std::nothrow, event_idx, "content", [&closure]
(const json::object &content)
{
const string_view &value
{
content.get("")
};
closure(value);
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::for_each(const m::user &user,
const string_view &device_id,
const closure_bool &closure)
{
const m::user::room user_room{user};
const m::room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
return state.for_each(type, [&state, &device_id, &closure]
(const string_view &type, const string_view &, const event::idx &)
{
const string_view &prop
{
lstrip(type, "ircd.device.")
};
return state.has(type, device_id)?
closure(prop):
true;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::for_each(const m::user &user,
const closure_bool &closure)
{
const m::user::room user_room
{
user
};
const m::room::state state
{
user_room
};
return state.for_each("ircd.device.device_id", [&closure]
(const string_view &, const string_view &state_key, const event::idx &)
{
return closure(state_key);
});
}
ircd::m::device::id::buf
IRCD_MODULE_EXPORT
ircd::m::device::access_token_to_id(const string_view &token)
{
id::buf ret;
access_token_to_id(token, [&ret]
(const string_view &device_id)
{
ret = device_id;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::access_token_to_id(const string_view &token,
const closure &closure)
{
const m::room::state &state{m::user::tokens};
const m::event::idx &event_idx
{
state.get(std::nothrow, "ircd.access_token", token)
};
bool ret{false};
const auto device_id{[&closure, &ret]
(const json::object &content)
{
const json::string &device_id
{
content["device_id"]
};
if(likely(device_id))
{
closure(device_id);
ret = true;
}
}};
if(!event_idx)
return ret;
if(!m::get(std::nothrow, event_idx, "content", device_id))
return ret;
return ret;
}

View file

@ -8,17 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
// NOTE: !!!
// Definitions re currently split between libircd and modules until the API
// and dependency graph has stabilized. Eventually most/all of ircd::m::
// should migrate out of libircd into modules.
ircd::mapi::header
IRCD_MODULE
{
"Matrix room library"
};
ircd::string_view
IRCD_MODULE_EXPORT
ircd::m::display_name(const mutable_buffer &out,

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix Event Library :Streaming tools"
};
namespace ircd::m
{
extern const event::keys::exclude event_append_exclude_keys;

View file

@ -14,12 +14,6 @@ namespace ircd::m::dbs
void _index_event_horizon(db::txn &, const event &, const write_opts &, const m::event::id &);
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix event horizon library"
};
size_t
ircd::m::event::horizon::rebuild()
{

View file

@ -13,12 +13,6 @@ namespace ircd::m::events
extern conf::item<size_t> dump_buffer_size;
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix events library"
};
decltype(ircd::m::events::dump_buffer_size)
ircd::m::events::dump_buffer_size
{

View file

@ -8,14 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
using namespace ircd;
mapi::header
IRCD_MODULE
{
"Federation :General Library and Utils"
};
namespace ircd::m::feds
{
struct request_base;
@ -37,8 +29,6 @@ namespace ircd::m::feds
static request_list version(const opts &, const closure &);
static request_list keys(const opts &, const closure &);
static request_list send(const opts &, const closure &);
bool execute(const vector_view<const opts> &opts, const closure &closure);
}
//
@ -101,10 +91,10 @@ noexcept
// m/feds.h
//
bool
IRCD_MODULE_EXPORT
ircd::m::feds::execute(const vector_view<const opts> &optsv,
const closure &closure)
ircd::m::feds::execute::execute(const vector_view<const opts> &optsv,
const closure &closure)
:boolean{true}
{
request_list list;
for(const auto &opts : optsv) switch(opts.op)
@ -150,7 +140,7 @@ ircd::m::feds::execute(const vector_view<const opts> &optsv,
if(opts.timeout > timeout)
timeout = opts.timeout;
return handler(list, timeout, closure);
this->boolean::val = handler(list, timeout, closure);
}
ircd::m::feds::request_list

View file

@ -50,12 +50,6 @@ namespace ircd::m::fetch
static void fini();
}
ircd::mapi::header
IRCD_MODULE
{
"Event Fetch Unit", ircd::m::fetch::init, ircd::m::fetch::fini
};
decltype(ircd::m::fetch::log)
ircd::m::fetch::log
{

View file

@ -0,0 +1,91 @@
// 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
{
extern conf::item<bool> ignored_user_list_enforce_invites;
extern conf::item<bool> ignored_user_list_enforce_events;
}
decltype(ircd::m::ignored_user_list_enforce_invites)
ircd::m::ignored_user_list_enforce_invites
{
{ "name", "ircd.m.ignored_user_list.enforce.invites" },
{ "default", true }
};
decltype(ircd::m::ignored_user_list_enforce_events)
ircd::m::ignored_user_list_enforce_events
{
{ "name", "ircd.m.ignored_user_list.enforce.events" },
{ "default", false }
};
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::has(const m::user::id &other)
const
{
return !for_each([&other]
(const m::user::id &user_id, const json::object &)
{
return user_id != other;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::for_each(const closure_bool &closure)
const try
{
const m::user::account_data account_data
{
user
};
bool ret{true};
account_data.get(std::nothrow, "m.ignored_user_list", [&closure, &ret]
(const string_view &key, const json::object &content)
{
const json::object &ignored_users
{
content.get("ignored_users")
};
for(const auto &[user_id, object] : ignored_users)
if(!(ret = closure(user_id, object)))
return;
});
return ret;
}
catch(const std::exception &e)
{
log::derror
{
m::log, "Error in ignore list for %s",
string_view{user.user_id}
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::enforce(const string_view &type)
{
if(type == "events")
return bool(ignored_user_list_enforce_events);
if(type == "invites")
return bool(ignored_user_list_enforce_invites);
return false;
}

View file

@ -26,14 +26,6 @@ struct ircd::m::init::backfill
static log::log log;
};
ircd::mapi::header
IRCD_MODULE
{
"Matrix resynchronization backfilling",
ircd::m::init::backfill::init,
ircd::m::init::backfill::fini,
};
decltype(ircd::m::init::backfill::log)
ircd::m::init::backfill::log
{

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix room join"
};
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::join(const room::alias &room_alias,

View file

@ -8,13 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Server keys"
,ircd::m::self::init::keys
};
std::ostream &
IRCD_MODULE_EXPORT
ircd::m::pretty_oneline(std::ostream &s,

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix room leave"
};
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::leave(const room &room,

View file

@ -198,69 +198,34 @@ noexcept
decltype(ircd::m::module_names)
ircd::m::module_names
{
"m_node",
"m_keys",
"m_event",
"m_fetch",
"m_feds",
"m_room",
"m_room_events",
"m_room_auth",
"m_room_head",
"m_room_bootstrap",
"m_room_join",
"m_room_leave",
"m_noop",
"m_breadcrumb_rooms",
"m_command",
"m_control",
"m_device",
"m_device_list_update",
"m_direct",
"m_direct_to_device",
"m_ignored_user_list",
"m_init_backfill",
"m_listen",
"m_presence",
"m_profile",
"m_receipt",
"m_room_aliases",
"m_room_canonical_alias",
"m_room_create",
"m_room_history_visibility",
"m_room_join_rules",
"m_room_member",
"m_room_name",
"m_room_third_party_invite",
"m_room_message",
"m_room_name",
"m_room_power_levels",
"m_room_server_acl",
"m_room_redaction",
"m_room_server_acl",
"m_room_third_party_invite",
"m_user_highlight_auth",
"m_user",
"m_user_filter",
"m_user_rooms",
"m_user_mitsein",
"m_user_servers",
"m_user_events",
"m_user_highlight",
"m_user_profile",
"m_user_account_data",
"m_user_room_account_data",
"m_user_room_tags",
"m_user_register",
"m_events",
"m_rooms",
"m_rooms_summary",
"m_users",
"m_presence",
"m_receipt",
"m_typing",
"m_device_list_update",
"m_device",
"m_direct",
"m_direct_to_device",
"m_breadcrumb_rooms",
"m_ignored_user_list",
"m_command",
"m_control",
"m_create",
"m_profile",
"m_noop",
"m_event_append",
"m_event_horizon",
"m_event_pretty",
"conf",
"net_dns",
"key_query",
"key_server",
@ -350,7 +315,6 @@ ircd::m::module_names
"stats",
"m_vm_fetch",
"m_vm",
"m_init_backfill",
"m_listen",
};
@ -453,7 +417,7 @@ ircd::m::self::host(const string_view &other)
// port() is 0 when the origin has no port (and implies 8448)
const auto port
{
me.user_id.port()
net::port(hostport(origin))
};
// If my_host has a port number, then the argument must also have the
@ -478,7 +442,7 @@ ircd::m::self::host(const string_view &other)
ircd::string_view
ircd::m::self::host()
{
return me.user_id.host();
return m::self::origin;
}
//
@ -525,7 +489,7 @@ try
// inits of m::self::globals. Calling the inits directly from
// here makes the module dependent on libircd and unloadable.
assert(ircd::run::level == run::level::START);
mods::imports.emplace("m_keys"s, "m_keys"s);
m::self::init::keys();
}
catch(const std::exception &e)
{
@ -1054,33 +1018,6 @@ ircd::m::app::exists(const string_view &id)
return call(id);
}
///////////////////////////////////////////////////////////////////////////////
//
// m/feds.h
//
ircd::m::feds::execute::execute(const opts &o,
const closure &c)
:execute
{
vector_view<const opts>{&o, 1}, c
}
{
}
ircd::m::feds::execute::execute(const vector_view<const opts> &o,
const closure &c)
{
using prototype = bool (const vector_view<const opts> &, const closure &);
static mods::import<prototype> call
{
"m_feds", "ircd::m::feds::execute"
};
call(o, c);
}
///////////////////////////////////////////////////////////////////////////////
//
// m/vm.h
@ -1741,19 +1678,6 @@ ircd::m::presence::set(const user &user,
});
}
ircd::m::event::id::buf
ircd::m::presence::set(const presence &object)
{
using prototype = event::id::buf (const presence &);
static mods::import<prototype> function
{
"m_presence", "ircd::m::presence::set"
};
return function(object);
}
void
ircd::m::presence::get(const user &user,
const closure &closure)
@ -1784,22 +1708,6 @@ ircd::m::presence::get(std::nothrow_t,
return get(std::nothrow, user, reclosure, &fopts);
}
bool
ircd::m::presence::get(std::nothrow_t,
const user &user,
const closure_event &closure,
const event::fetch::opts *const &opts)
{
using prototype = bool (std::nothrow_t, const m::user &, const closure_event &, const event::fetch::opts *const &);
static mods::import<prototype> function
{
"m_presence", "ircd::m::presence::get"
};
return function(std::nothrow, user, closure, opts);
}
ircd::m::event::idx
ircd::m::presence::get(const user &user)
{
@ -1817,111 +1725,11 @@ ircd::m::presence::get(const user &user)
return ret;
}
ircd::m::event::idx
ircd::m::presence::get(std::nothrow_t,
const user &user)
{
using prototype = event::idx (std::nothrow_t, const m::user &);
static mods::import<prototype> function
{
"m_presence", "ircd::m::presence::get"
};
return function(std::nothrow, user);
}
bool
ircd::m::presence::valid_state(const string_view &state)
{
using prototype = bool (const string_view &);
static mods::import<prototype> function
{
"m_presence", "ircd::m::presence::valid_state"
};
return function(state);
}
///////////////////////////////////////////////////////////////////////////////
//
// m/device.h
//
bool
ircd::m::device::set(const m::user &user,
const device &device_)
{
using prototype = bool (const m::user &, const device &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::set"
};
return function(user, device_);
}
bool
ircd::m::device::set(const m::user &user,
const string_view &id,
const string_view &prop,
const string_view &val)
{
using prototype = bool (const m::user &, const string_view &, const string_view &, const string_view &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::set"
};
return function(user, id, prop, val);
}
bool
ircd::m::device::del(const m::user &user,
const string_view &id)
{
using prototype = bool (const m::user &, const string_view &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::del"
};
return function(user, id);
}
bool
ircd::m::device::has(const m::user &user,
const string_view &id)
{
using prototype = bool (const m::user &, const string_view &id);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::has"
};
return function(user, id);
}
bool
ircd::m::device::has(const m::user &user,
const string_view &id,
const string_view &prop)
{
using prototype = bool (const m::user &, const string_view &id, const string_view &prop);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::has"
};
return function(user, id, prop);
}
bool
ircd::m::device::get(const m::user &user,
const string_view &id,
@ -1945,56 +1753,6 @@ ircd::m::device::get(const m::user &user,
return ret;
}
bool
ircd::m::device::get(std::nothrow_t,
const m::user &user,
const string_view &id,
const string_view &prop,
const closure &c)
{
using prototype = bool (std::nothrow_t,
const m::user &,
const string_view &,
const string_view &,
const closure &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::get"
};
return function(std::nothrow, user, id, prop, c);
}
bool
ircd::m::device::for_each(const m::user &user,
const string_view &id,
const closure_bool &c)
{
using prototype = bool (const m::user &, const string_view &id, const closure_bool &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::for_each"
};
return function(user, id, c);
}
bool
ircd::m::device::for_each(const m::user &user,
const closure_bool &c)
{
using prototype = bool (const m::user &, const closure_bool &);
static mods::import<prototype> function
{
"m_device", "ircd::m::device::for_each"
};
return function(user, c);
}
///////////////////////////////////////////////////////////////////////////////
//
// m/node.h
@ -2035,15 +1793,7 @@ ircd::m::node::key(const string_view &key_id,
const key_closure &closure)
const
{
using prototype = void (const string_view &, const string_view &, const keys::closure &);
//TODO: Remove this import once this callsite is outside of libircd.
static mods::import<prototype> call
{
"m_keys", "ircd::m::keys::get"
};
call(node_id, key_id, [&closure, &key_id]
m::keys::get(node_id, key_id, [&closure, &key_id]
(const json::object &keys)
{
const json::object &vks

View file

@ -8,16 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Server Nodes"
};
//
// node
//
ircd::m::node
IRCD_MODULE_EXPORT
ircd::m::create(const node &node,

105
matrix/presence.cc Normal file
View file

@ -0,0 +1,105 @@
// 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
{
extern const string_view presence_valid_states[];
}
decltype(ircd::m::presence_valid_states)
ircd::m::presence_valid_states
{
"online",
"offline",
"unavailable",
};
bool
IRCD_MODULE_EXPORT
ircd::m::presence::get(const std::nothrow_t,
const m::user &user,
const m::presence::closure_event &closure,
const m::event::fetch::opts *const &fopts_p)
{
const m::event::idx event_idx
{
m::presence::get(std::nothrow, user)
};
if(!event_idx)
return false;
const auto &fopts
{
fopts_p? *fopts_p : event::fetch::default_opts
};
const m::event::fetch event
{
event_idx, std::nothrow, fopts
};
if(event.valid)
closure(event);
return event.valid;
}
ircd::m::event::idx
IRCD_MODULE_EXPORT
ircd::m::presence::get(const std::nothrow_t,
const m::user &user)
{
const m::user::room user_room
{
user
};
const m::room::state state
{
user_room
};
return state.get(std::nothrow, "ircd.presence", "");
}
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::presence::set(const m::presence &content)
{
const m::user user
{
json::at<"user_id"_>(content)
};
//TODO: ABA
if(!exists(user))
create(user.user_id);
m::vm::copts copts;
const m::user::room user_room
{
user, &copts
};
//TODO: ABA
return send(user_room, user.user_id, "ircd.presence", "", json::strung{content});
}
bool
IRCD_MODULE_EXPORT
ircd::m::presence::valid_state(const string_view &state)
{
return std::any_of(begin(presence_valid_states), end(presence_valid_states), [&state]
(const string_view &valid)
{
return state == valid;
});
}

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix event pretty printing library"
};
std::ostream &
IRCD_MODULE_EXPORT
ircd::m::pretty_stateline(std::ostream &out,

194
matrix/receipt.cc Normal file
View file

@ -0,0 +1,194 @@
// 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::receipt::log)
IRCD_MODULE_EXPORT_DATA
ircd::m::receipt::log
{
"m.receipt"
};
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::receipt::read(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id,
const json::object &options)
{
const m::user::room user_room
{
user_id
};
const auto evid
{
send(user_room, user_id, "ircd.read", room_id,
{
{ "event_id", event_id },
{ "ts", options.get("ts", ircd::time<milliseconds>()) },
{ "m.hidden", options.get("m.hidden", false) },
})
};
log::info
{
log, "%s read by %s in %s options:%s",
string_view{event_id},
string_view{user_id},
string_view{room_id},
string_view{options},
};
return evid;
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::get(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id::closure &closure)
{
const m::user::room user_room
{
user_id
};
const auto event_idx
{
user_room.get(std::nothrow, "ircd.read", room_id)
};
return m::get(std::nothrow, event_idx, "content", [&closure]
(const json::object &content)
{
const json::string &event_id
{
content["event_id"]
};
closure(event_id);
});
}
/// Does the user wish to not send receipts for events sent by its specific
/// sender?
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::ignoring(const m::user &user,
const m::event::id &event_id)
{
bool ret{false};
m::get(std::nothrow, event_id, "sender", [&ret, &user]
(const string_view &sender)
{
const m::user::room user_room{user};
ret = user_room.has("ircd.read.ignore", sender);
});
return ret;
}
/// Does the user wish to not send receipts for events for this entire room?
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::ignoring(const m::user &user,
const m::room::id &room_id)
{
const m::user::room user_room{user};
return user_room.has("ircd.read.ignore", room_id);
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::freshest(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id)
try
{
const m::user::room user_room
{
user_id
};
bool ret{true};
user_room.get("ircd.read", room_id, [&ret, &event_id]
(const m::event &event)
{
const auto &content
{
at<"content"_>(event)
};
const m::event::id &previous_id
{
unquote(content.get("event_id"))
};
if(event_id == previous_id)
{
ret = false;
return;
}
const m::event::idx &previous_idx
{
index(previous_id)
};
const m::event::idx &event_idx
{
index(event_id)
};
ret = event_idx > previous_idx;
});
return ret;
}
catch(const std::exception &e)
{
log::derror
{
log, "Freshness of receipt in %s from %s for %s :%s",
string_view{room_id},
string_view{user_id},
string_view{event_id},
e.what()
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::exists(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id)
{
const m::user::room user_room
{
user_id
};
bool ret{false};
user_room.get(std::nothrow, "ircd.read", room_id, [&ret, &event_id]
(const m::event &event)
{
const auto &content
{
at<"content"_>(event)
};
ret = unquote(content.get("event_id")) == event_id;
});
return ret;
}

View file

@ -312,48 +312,6 @@ ircd::m::create(const id::room &room_id,
});
}
ircd::m::event::id::buf
ircd::m::join(const id::room_alias &room_alias,
const id::user &user_id)
{
using prototype = event::id::buf (const id::room_alias &, const id::user &);
static mods::import<prototype> function
{
"m_room_join", "ircd::m::join"
};
return function(room_alias, user_id);
}
ircd::m::event::id::buf
ircd::m::join(const room &room,
const id::user &user_id)
{
using prototype = event::id::buf (const m::room &, const id::user &);
static mods::import<prototype> function
{
"m_room_join", "ircd::m::join"
};
return function(room, user_id);
}
ircd::m::event::id::buf
ircd::m::leave(const room &room,
const id::user &user_id)
{
using prototype = event::id::buf (const m::room &, const id::user &);
static mods::import<prototype> function
{
"m_room_leave", "ircd::m::leave"
};
return function(room, user_id);
}
ircd::m::event::id::buf
ircd::m::invite(const room &room,
const id::user &target,
@ -3548,52 +3506,10 @@ const
return for_each(room, server, closure);
}
bool
ircd::m::room::aliases::for_each(const m::room &room,
const string_view &server,
const closure_bool &closure)
{
using prototype = bool (const m::room &, const string_view &, const closure_bool &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::for_each"
};
return call(room, server, closure);
}
//
// room::aliases::cache
//
bool
ircd::m::room::aliases::cache::del(const alias &a)
{
using prototype = bool (const alias &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::del"
};
return call(a);
}
bool
ircd::m::room::aliases::cache::set(const alias &a,
const id &i)
{
using prototype = bool (const alias &, const id &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::set"
};
return call(a, i);
}
bool
ircd::m::room::aliases::cache::fetch(std::nothrow_t,
const alias &a,
@ -3617,20 +3533,6 @@ catch(const std::exception &e)
return false;
}
void
ircd::m::room::aliases::cache::fetch(const alias &a,
const net::hostport &hp)
{
using prototype = void (const alias &, const net::hostport &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::fetch"
};
return call(a, hp);
}
ircd::m::room::id::buf
ircd::m::room::aliases::cache::get(const alias &a)
{
@ -3670,54 +3572,12 @@ ircd::m::room::aliases::cache::get(const alias &a,
};
}
bool
ircd::m::room::aliases::cache::get(std::nothrow_t,
const alias &a,
const id::closure &c)
{
using prototype = bool (std::nothrow_t, const alias &, const id::closure &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::get"
};
return call(std::nothrow, a, c);
}
bool
ircd::m::room::aliases::cache::has(const alias &a)
{
using prototype = bool (const alias &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::has"
};
return call(a);
}
bool
ircd::m::room::aliases::cache::for_each(const closure_bool &c)
{
return for_each(string_view{}, c);
}
bool
ircd::m::room::aliases::cache::for_each(const string_view &s,
const closure_bool &c)
{
using prototype = bool (const string_view &, const closure_bool &);
static mods::import<prototype> call
{
"m_room_aliases", "ircd::m::room::aliases::cache::for_each"
};
return call(s, c);
}
//
// room::power
//

465
matrix/room_aliases.cc Normal file
View file

@ -0,0 +1,465 @@
// 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
{
extern conf::item<seconds> alias_fetch_timeout;
extern conf::item<seconds> alias_cache_ttl;
extern const room::id::buf alias_room_id;
extern const room alias_room;
}
decltype(ircd::m::alias_room_id)
ircd::m::alias_room_id
{
"alias", ircd::my_host()
};
decltype(ircd::m::alias_room)
ircd::m::alias_room
{
alias_room_id
};
decltype(ircd::m::alias_cache_ttl)
ircd::m::alias_cache_ttl
{
{ "name", "ircd.m.room.aliases.cache.ttl" },
{ "default", 604800L },
};
decltype(ircd::m::alias_fetch_timeout)
ircd::m::alias_fetch_timeout
{
{ "name", "ircd.m.room.aliases.fetch.timeout" },
{ "default", 10L },
};
//
// m::room::aliases
//
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::for_each(const m::room &room,
const string_view &server,
const closure_bool &closure)
{
const room::state state
{
room
};
assert(server);
const event::idx &event_idx
{
state.get(std::nothrow, "m.room.aliases", server)
};
if(!event_idx)
return true;
bool ret{true};
m::get(std::nothrow, event_idx, "content", [&closure, &ret]
(const json::object &content)
{
const json::array &aliases
{
content["aliases"]
};
for(auto it(begin(aliases)); it != end(aliases) && ret; ++it)
{
const json::string &alias(*it);
if(!valid(m::id::ROOM_ALIAS, alias))
continue;
if(!closure(alias))
ret = false;
}
});
return ret;
}
//
// m::room::aliases::cache
//
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::del(const alias &alias)
{
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
make_key(buf, alias)
};
const auto &event_idx
{
alias_room.get(std::nothrow, "ircd.room.alias", key)
};
if(!event_idx)
return false;
const auto event_id
{
m::event_id(event_idx, std::nothrow)
};
if(!event_id)
return false;
const auto ret
{
redact(alias_room, m::me.user_id, event_id, "deleted")
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::set(const alias &alias,
const id &id)
{
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
make_key(buf, alias)
};
const auto ret
{
send(alias_room, m::me.user_id, "ircd.room.alias", key,
{
{ "room_id", id }
})
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::get(std::nothrow_t,
const alias &alias,
const id::closure &closure)
{
m::event::idx event_idx
{
getidx(alias)
};
if(!event_idx)
{
if(my_host(alias.host()))
return false;
if(!fetch(std::nothrow, alias, alias.host()))
return false;
event_idx = getidx(alias);
}
const bool expired
{
!my_host(alias.host()) && cache::expired(event_idx)
};
if(expired)
{
log::dwarning
{
log, "Cached alias %s expired age:%ld ttl:%ld",
string_view{alias},
cache::age(event_idx).count(),
milliseconds(seconds(alias_cache_ttl)).count(),
};
fetch(std::nothrow, alias, alias.host());
event_idx = getidx(alias);
}
if(!event_idx)
return false;
bool ret{false};
m::get(std::nothrow, event_idx, "content", [&closure, &ret]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
if(!empty(room_id))
{
ret = true;
closure(room_id);
}
});
return ret;
}
namespace ircd::m
{
thread_local char room_aliases_cache_fetch_hpbuf[384];
}
void
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::fetch(const alias &alias,
const net::hostport &hp)
try
{
const unique_buffer<mutable_buffer> buf
{
16_KiB
};
m::v1::query::opts opts;
opts.remote = hp;
opts.dynamic = true;
m::v1::query::directory request
{
alias, buf, std::move(opts)
};
request.wait(seconds(alias_fetch_timeout));
const http::code &code
{
request.get()
};
const json::object response
{
request
};
if(!response.has("room_id"))
throw m::NOT_FOUND
{
"Server '%s' does not know room_id for %s",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
const m::room::id &room_id
{
unquote(response["room_id"])
};
set(alias, room_id);
}
catch(const ctx::timeout &e)
{
throw m::error
{
http::GATEWAY_TIMEOUT, "M_ROOM_ALIAS_TIMEOUT",
"Server '%s' did not respond with a room_id for %s in time",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
}
catch(const server::unavailable &e)
{
throw m::error
{
http::BAD_GATEWAY, "M_ROOM_ALIAS_UNAVAILABLE",
"Server '%s' is not available to query a room_id for %s",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::for_each(const string_view &server,
const closure_bool &closure)
{
const m::room::state state
{
alias_room
};
bool ret{true};
const m::room::state::closure_bool reclosure{[&server, &closure, &ret]
(const string_view &type, const string_view &state_key, const m::event::idx &event_idx)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE];
const alias &alias
{
m::id::unswap(state_key, swapbuf)
};
if(server && alias.host() != server)
return false;
if(expired(event_idx))
return true;
m::get(std::nothrow, event_idx, "content", [&closure, &ret, &alias]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
if(!empty(room_id))
ret = closure(alias, room_id);
});
return ret;
}};
state.for_each("ircd.room.alias", server, reclosure);
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::has(const alias &alias)
{
const auto &event_idx
{
getidx(alias)
};
if(!event_idx)
return false;
if(expired(event_idx))
return false;
bool ret{false};
m::get(std::nothrow, event_idx, "content", [&ret]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
ret = !empty(room_id);
});
return ret;
}
ircd::system_point
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::expires(const alias &alias)
{
const auto event_idx
{
getidx(alias)
};
if(!event_idx)
return system_point::min();
const milliseconds age
{
cache::age(event_idx)
};
const seconds ttl
{
alias_cache_ttl
};
return now<system_point>() + (ttl - age);
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::expired(const event::idx &event_idx)
{
const milliseconds age
{
cache::age(event_idx)
};
const seconds ttl
{
alias_cache_ttl
};
return age > ttl;
}
ircd::milliseconds
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::age(const event::idx &event_idx)
{
time_t ts;
if(!m::get(event_idx, "origin_server_ts", ts))
return milliseconds::max();
const time_t now
{
ircd::time<milliseconds>()
};
return milliseconds
{
now - ts
};
}
ircd::m::event::idx
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::getidx(const alias &alias)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE];
const string_view &swapped
{
alias.swap(swapbuf)
};
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
tolower(buf, swapped)
};
const auto &event_idx
{
alias_room.get(std::nothrow, "ircd.room.alias", key)
};
return event_idx;
}
ircd::string_view
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::make_key(const mutable_buffer &out,
const alias &alias)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE] alignas(16);
const string_view &swapped
{
alias.swap(swapbuf)
};
const string_view &key
{
tolower(out, swapped)
};
return key;
}

View file

@ -19,12 +19,6 @@ namespace ircd::m
extern hook::site<room::auth::hookdata &> room_auth_hook;
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix room event authentication support."
};
decltype(ircd::m::room_auth_hook)
ircd::m::room_auth_hook
{

View file

@ -36,12 +36,6 @@ struct ircd::m::bootstrap::pkg
std::string host;
};
ircd::mapi::header
IRCD_MODULE
{
"Matrix room bootstrap."
};
decltype(ircd::m::bootstrap::log)
ircd::m::bootstrap::log
{

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix room library"
};
decltype(ircd::m::room::events::viewport_size)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::events::viewport_size

View file

@ -8,16 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix room head"
};
//
// room::head
//
std::pair<ircd::json::array, int64_t>
IRCD_MODULE_EXPORT
ircd::m::room::head::generate(const mutable_buffer &buf,

View file

@ -0,0 +1,150 @@
// 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
{
static bool visible_to_node(const room &, const string_view &node_id, const event &);
static bool visible_to_user(const room &, const string_view &history_visibility, const m::user::id &, const event &);
}
bool
IRCD_MODULE_EXPORT
ircd::m::visible(const m::event &event,
const string_view &mxid)
{
const m::room room
{
at<"room_id"_>(event), event.event_id
};
const m::room::state state
{
room
};
const event::idx visibility_event_idx
{
state.get(std::nothrow, "m.room.history_visibility", "")
};
char buf[32];
string_view history_visibility{"shared"};
m::get(std::nothrow, visibility_event_idx, "content", [&buf, &history_visibility]
(const json::object &content)
{
const json::string &_history_visibility
{
content.get("history_visibility", "shared")
};
history_visibility = strncpy
{
buf, _history_visibility
};
});
if(history_visibility == "world_readable")
return true;
if(empty(mxid))
return false;
if(m::valid(m::id::USER, mxid))
return visible_to_user(room, history_visibility, mxid, event);
if(rfc3986::valid_remote(std::nothrow, mxid))
return visible_to_node(room, mxid, event);
throw m::UNSUPPORTED
{
"Cannot determine visibility of %s for '%s'",
string_view{room.room_id},
mxid,
};
}
bool
ircd::m::visible_to_user(const m::room &room,
const string_view &history_visibility,
const m::user::id &user_id,
const m::event &event)
{
assert(history_visibility != "world_readable");
// Allow any member event where the state_key string is a user mxid.
if(json::get<"type"_>(event) == "m.room.member")
if(at<"state_key"_>(event) == user_id)
return true;
// Get the membership of the user in the room at the event.
char buf[m::room::MEMBERSHIP_MAX_SIZE];
const string_view membership
{
m::membership(buf, room, user_id)
};
if(membership == "join")
return true;
if(history_visibility == "joined")
return false;
if(membership == "invite")
return true;
if(history_visibility == "invited")
return false;
// The history_visibility is now likely "shared"; though we cannot assert
// that in case some other string is used for any non-spec customization
// or for graceful forward compatibility. We default to "shared" here.
//assert(history_visibility == "shared");
// An m::room instance with no event_id is used to query the room at the
// present state.
const m::room present
{
room.room_id
};
// If the room is not at the present event then we have to run another
// test for membership here. Otherwise the "join" test already failed.
if(!room.event_id)
return false;
return m::membership(present, user_id, m::membership_positive); // join || invite
}
bool
ircd::m::visible_to_node(const m::room &room,
const string_view &node_id,
const m::event &event)
{
// Allow auth chain events XXX: this is too broad
if(m::room::auth::is_power_event(event))
return true;
// Allow any event where the state_key string is a user mxid and the server
// is the host of that user. Note that applies to any type of event.
if(m::valid(m::id::USER, json::get<"state_key"_>(event)))
if(m::user::id(at<"state_key"_>(event)).host() == node_id)
return true;
const m::room::origins origins
{
room
};
// Allow joined servers
if(origins.has(node_id))
return true;
return false;
}

332
matrix/room_server_acl.cc Normal file
View file

@ -0,0 +1,332 @@
// 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.
/// Coarse control over whether ACL's are considered during the vm::eval of an
/// event, ACL's will be checked against the event's origin during processing
/// of the event, regardless of how the event was received, fetched, etc. The
/// m::vm options may dictate further detailed behavior (hard-fail, soft-
/// fail, auth integration, etc). This is the principal configuration option
/// for effecting the server access control list functionality. Though this
/// conf item is independent of other conf items in this module, setting it
/// to false denudes the core functionality.
///
/// Setting this to true is *stricter* than the official specification and
/// fixes several vulnerabilities for bypassing ACL's. This also applies to
/// both PDU's and EDU's, and is agnostic to the method or endpoint by which
/// this server obtained the event. This departs from the specification.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_write)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_write
{
{ "name", "ircd.m.room.server_acl.enable.write" },
{ "default", true },
};
/// Coarse control over whether ACL's apply to endpoints considered
/// non-modifying/passive to the room. If false, ACL's are not checked on
/// endpoints which have no visible effects to the federation; this can
/// increase performance.
///
/// Setting this option to false relaxes the list of endpoints covered by ACL's
/// and departs from the official specification.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_read)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_read
{
{ "name", "ircd.m.room.server_acl.enable.read" },
{ "default", false },
};
/// Coarse control over whether ACL's are considered for event fetching. If
/// true, events originating from an ACL'ed server will not be fetched, nor
/// will an ACL'ed server be queried by the fetch unit for any event. Note that
/// this cannot fully apply for newer event_id's without hostparts, but the
/// fetch unit may discard such events for an ACL'ed server after receiving.
///
/// Setting this to true is *stricter* than the official specification, which
/// is vulnerable to "bouncing" around ACL's.
/// (see: https://github.com/maubot/bouncybot)
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_fetch)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_fetch
{
{ "name", "ircd.m.room.server_acl.enable.fetch" },
{ "default", true },
};
/// Coarse control over whether ACL's are considered when this server
/// transmits transactions to the participants in a room. If true, transactions
/// with all contained PDU's and EDU's will not be sent to ACL'ed servers.
///
/// Setting this to true is *stricter* than the official specification, which
/// leaks all transmissions to ACL'ed servers.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_send)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_send
{
{ "name", "ircd.m.room.server_acl.enable.send" },
{ "default", true },
};
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::check(const m::room::id &room_id,
const net::hostport &server)
try
{
const server_acl server_acl
{
room_id
};
return server_acl(server);
}
catch(const std::exception &e)
{
log::critical
{
log, "Failed to check server_acl for '%s' in %s :%s",
string(server),
string_view{room_id},
e.what()
};
return false;
}
//
// server_acl::server_acl
//
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::server_acl(const m::room &room,
const event::idx &event_idx)
:room
{
room
}
,event_idx
{
!event_idx?
room.get(std::nothrow, "m.room.server_acl", ""):
event_idx
}
{
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::operator()(const net::hostport &server)
const
{
bool ret;
const auto closure{[this, &server, &ret]
(const json::object &content)
{
// Set the content reference here so only one actual IO is made to
// fetch the m.room.server_acl content for all queries.
const scope_restore this_content
{
this->content, content
};
ret = this->check(server);
}};
return !view(closure) || ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::match(const string_view &prop,
const net::hostport &remote)
const
{
// Spec sez when comparing against the server ACLs, the suspect server's
// port number must not be considered.
const string_view &server
{
net::host(remote)
};
return !for_each(prop, [&server]
(const string_view &expression)
{
const globular_match match
{
expression
};
// return false to break on match.
return match(server)? false : true;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::has(const string_view &prop,
const string_view &expr)
const
{
return !for_each(prop, [&expr]
(const string_view &_expr)
{
// false to break on match
return _expr == expr? false : true;
});
}
int
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::getbool(const string_view &prop)
const
{
int ret(-1);
view([&ret, &prop]
(const json::object &object)
{
const string_view &value
{
object[prop]
};
if(value == json::literal_true)
ret = 1;
else if(value == json::literal_false)
ret = 0;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::has(const string_view &prop)
const
{
bool ret{false};
view([&ret, &prop]
(const json::object &object)
{
ret = object.has(prop);
});
return ret;
}
size_t
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::count(const string_view &prop)
const
{
size_t ret(0);
for_each(prop, [&ret]
(const string_view &)
{
++ret;
return true;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::for_each(const string_view &prop,
const closure_bool &closure)
const
{
bool ret{true};
view([&ret, &closure, &prop]
(const json::object &content)
{
const json::array &list
{
content[prop]
};
if(!list || json::type(list, std::nothrow) != json::ARRAY)
return;
for(auto it(begin(list)); it != end(list) && ret; ++it)
{
if(json::type(*it, json::strict, std::nothrow) != json::STRING)
continue;
if(!closure(json::string(*it)))
ret = false;
}
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::exists()
const
{
return content || event_idx;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::check(const net::hostport &server)
const
{
// c2s 13.29.1 rules
// 1. If there is no m.room.server_acl event in the room state, allow.
if(!exists())
return true;
// 2. If the server name is an IP address (v4 or v6) literal, and
// allow_ip_literals is present and false, deny.
if(getbool("allow_ip_literals") == false)
if(rfc3986::valid(std::nothrow, rfc3986::parser::ip_address, net::host(server)))
return false;
// 3. If the server name matches an entry in the deny list, deny.
if(match("deny", server))
return false;
// 4. If the server name matches an entry in the allow list, allow.
if(match("allow", server))
return true;
// 5. Otherwise, deny.
return false;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::view(const view_closure &closure)
const
{
if(content)
{
closure(content);
return true;
}
return event_idx && m::get(std::nothrow, event_idx, "content", closure);
}

View file

@ -8,13 +8,8 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix rooms interface; modular components"
};
decltype(ircd::m::rooms::opts_default)
IRCD_MODULE_EXPORT_DATA
ircd::m::rooms::opts_default;
bool

View file

@ -17,12 +17,6 @@ namespace ircd::m::rooms::summary
extern hookfn<vm::eval &> create_public_room;
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix rooms summary"
};
decltype(ircd::m::rooms::summary::public_room_id)
ircd::m::rooms::summary::public_room_id
{

View file

@ -10,12 +10,6 @@
using namespace ircd;
mapi::header
IRCD_MODULE
{
"Matrix Typing"
};
log::log
typing_log
{

View file

@ -10,15 +10,10 @@
namespace ircd::m
{
static string_view gen_password_hash(const mutable_buffer &, const string_view &);
static room create_user_room(const user::id &, const room::id &, const json::members &contents);
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix user library; modular components."
};
ircd::m::user
IRCD_MODULE_EXPORT
ircd::m::create(const m::user::id &user_id,
@ -70,11 +65,6 @@ catch(const std::exception &e)
// user::user
//
namespace ircd::m
{
static string_view gen_password_hash(const mutable_buffer &, const string_view &);
}
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::user::password(const string_view &password)

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix user account data."
};
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::user::account_data::set(const string_view &type,

View file

@ -8,18 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix events for a user."
};
IRCD_MODULE_EXPORT
ircd::m::user::events::events(const m::user &user)
:user{user}
{
}
size_t
IRCD_MODULE_EXPORT
ircd::m::user::events::count()

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix user filter."
};
ircd::string_view
IRCD_MODULE_EXPORT
ircd::m::user::filter::set(const mutable_buffer &buf,

View file

@ -8,13 +8,13 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
namespace ircd::m
{
"Matrix user library; highlight notification support"
};
static bool user_highlight_match(const string_view &text, const string_view &arg, const size_t &pos);
}
decltype(ircd::m::user::highlight::enable_count)
IRCD_MODULE_EXPORT_DATA
ircd::m::user::highlight::enable_count
{
{ "name", "ircd.m.user.highlight.enable.count" },
@ -22,6 +22,7 @@ ircd::m::user::highlight::enable_count
};
decltype(ircd::m::user::highlight::match_mxid_full)
IRCD_MODULE_EXPORT_DATA
ircd::m::user::highlight::match_mxid_full
{
{ "name", "ircd.m.user.highlight.match.mxid.full" },
@ -29,6 +30,7 @@ ircd::m::user::highlight::match_mxid_full
};
decltype(ircd::m::user::highlight::match_mxid_local_cs)
IRCD_MODULE_EXPORT_DATA
ircd::m::user::highlight::match_mxid_local_cs
{
{ "name", "ircd.m.user.highlight.match.mxid.local.cs" },
@ -36,6 +38,7 @@ ircd::m::user::highlight::match_mxid_local_cs
};
decltype(ircd::m::user::highlight::match_mxid_local_cs)
IRCD_MODULE_EXPORT_DATA
ircd::m::user::highlight::match_mxid_local_ci
{
{ "name", "ircd.m.user.highlight.match.mxid.local.ci" },
@ -43,6 +46,7 @@ ircd::m::user::highlight::match_mxid_local_ci
};
decltype(ircd::m::user::highlight::match_at_room)
IRCD_MODULE_EXPORT_DATA
ircd::m::user::highlight::match_at_room
{
{ "name", "ircd.m.user.highlight.match.at.room" },
@ -244,11 +248,6 @@ const
return false;
}
namespace ircd::m
{
static bool user_highlight_match(const string_view &text, const string_view &arg, const size_t &pos);
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::highlight::match(const string_view &text,

View file

@ -8,18 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix rooms in common between users."
};
IRCD_MODULE_EXPORT
ircd::m::user::mitsein::mitsein(const m::user &user)
:user{user}
{
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::mitsein::has(const m::user &other,

View file

@ -13,12 +13,6 @@ namespace ircd::m
extern conf::item<seconds> remote_request_timeout;
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix user profiles."
};
decltype(ircd::m::remote_request_timeout)
ircd::m::remote_request_timeout
{

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix user register"
};
ircd::json::object
IRCD_MODULE_EXPORT
ircd::m::user::registar::operator()(const mutable_buffer &out,

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix user room account data."
};
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::user::room_account_data::set(const string_view &user_type,

View file

@ -8,12 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix user room tags."
};
bool
IRCD_MODULE_EXPORT
ircd::m::user::room_tags::del(const string_view &user_type)

View file

@ -8,18 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix rooms for a user."
};
IRCD_MODULE_EXPORT
ircd::m::user::rooms::rooms(const m::user &user)
:user_room{user}
{
}
size_t
IRCD_MODULE_EXPORT
ircd::m::user::rooms::count()

View file

@ -14,12 +14,6 @@ namespace ircd::m::users
static bool for_each_in_host(const opts &, const user::closure_bool &);
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix users interface"
};
decltype(ircd::m::users::opts_default)
ircd::m::users::opts_default;

View file

@ -8,18 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
ircd::mapi::header
IRCD_MODULE
{
"Matrix servers visible to a user from all their rooms."
};
IRCD_MODULE_EXPORT
ircd::m::user::servers::servers(const m::user &user)
:user{user}
{
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::servers::has(const string_view &server,

View file

@ -39,7 +39,6 @@ AM_LDFLAGS = \
moduledir = @moduledir@
conf_la_SOURCES = conf.cc
stats_la_SOURCES = stats.cc
net_dns_la_SOURCES = net_dns.cc net_dns_cache.cc net_dns_resolver.cc
net_dns_la_CPPFLAGS = -include $(top_srcdir)/include/ircd/asio.h
@ -52,7 +51,6 @@ web_hook_la_SOURCES = web_hook.cc
well_known_la_SOURCES = well_known.cc
module_LTLIBRARIES = \
conf.la \
stats.la \
net_dns.la \
console.la \
@ -81,135 +79,62 @@ endif IMAGEMAGICK
m_moduledir = @moduledir@
m_node_la_SOURCES = m_node.cc
m_keys_la_SOURCES = m_keys.cc
m_event_la_SOURCES = m_event.cc
m_feds_la_SOURCES = m_feds.cc
m_fetch_la_SOURCES = m_fetch.cc
m_room_la_SOURCES = m_room.cc
m_room_events_la_SOURCES = m_room_events.cc
m_room_auth_la_SOURCES = m_room_auth.cc
m_room_head_la_SOURCES = m_room_head.cc
m_room_create_la_SOURCES = m_room_create.cc
m_room_member_la_SOURCES = m_room_member.cc
m_room_join_la_SOURCES = m_room_join.cc
m_room_leave_la_SOURCES = m_room_leave.cc
m_room_join_rules_la_SOURCES = m_room_join_rules.cc
m_room_history_visibility_la_SOURCES = m_room_history_visibility.cc
m_room_canonical_alias_la_SOURCES = m_room_canonical_alias.cc
m_room_aliases_la_SOURCES = m_room_aliases.cc
m_room_message_la_SOURCES = m_room_message.cc
m_room_power_levels_la_SOURCES = m_room_power_levels.cc
m_room_server_acl_la_SOURCES = m_room_server_acl.cc
m_room_third_party_invite_la_SOURCES = m_room_third_party_invite.cc
m_room_redaction_la_SOURCES = m_room_redaction.cc
m_room_bootstrap_la_SOURCES = m_room_bootstrap.cc
m_room_name_la_SOURCES = m_room_name.cc
m_user_la_SOURCES = m_user.cc
m_user_events_la_SOURCES = m_user_events.cc
m_user_rooms_la_SOURCES = m_user_rooms.cc
m_user_filter_la_SOURCES = m_user_filter.cc
m_user_register_la_SOURCES = m_user_register.cc
m_user_mitsein_la_SOURCES = m_user_mitsein.cc
m_user_servers_la_SOURCES = m_user_servers.cc
m_user_highlight_la_SOURCES = m_user_highlight.cc m_user_highlight_auth.cc
m_user_profile_la_SOURCES = m_user_profile.cc
m_user_account_data_la_SOURCES = m_user_account_data.cc
m_user_room_account_data_la_SOURCES = m_user_room_account_data.cc
m_user_room_tags_la_SOURCES = m_user_room_tags.cc
m_events_la_SOURCES = m_events.cc
m_rooms_la_SOURCES = m_rooms.cc
m_rooms_summary_la_SOURCES = m_rooms_summary.cc
m_users_la_SOURCES = m_users.cc
m_presence_la_SOURCES = m_presence.cc
m_receipt_la_SOURCES = m_receipt.cc
m_typing_la_SOURCES = m_typing.cc
m_device_list_update_la_SOURCES = m_device_list_update.cc
m_device_la_SOURCES = m_device.cc
m_direct_la_SOURCES = m_direct.cc
m_direct_to_device_la_SOURCES = m_direct_to_device.cc
m_breadcrumb_rooms_la_SOURCES = m_breadcrumb_rooms.cc
m_ignored_user_list_la_SOURCES = m_ignored_user_list.cc
m_command_la_SOURCES = m_command.cc
m_control_la_SOURCES = m_control.cc
m_create_la_SOURCES = m_create.cc
m_profile_la_SOURCES = m_profile.cc
m_noop_la_SOURCES = m_noop.cc
m_event_append_la_SOURCES = m_event_append.cc
m_event_horizon_la_SOURCES = m_event_pretty.cc
m_event_pretty_la_SOURCES = m_event_horizon.cc
m_init_backfill_la_SOURCES = m_init_backfill.cc
m_device_la_SOURCES = m_device.cc
m_device_list_update_la_SOURCES = m_device_list_update.cc
m_direct_la_SOURCES = m_direct.cc
m_direct_to_device_la_SOURCES = m_direct_to_device.cc
m_ignored_user_list_la_SOURCES = m_ignored_user_list.cc
m_listen_la_SOURCES = m_listen.cc
m_noop_la_SOURCES = m_noop.cc
m_presence_la_SOURCES = m_presence.cc
m_profile_la_SOURCES = m_profile.cc
m_receipt_la_SOURCES = m_receipt.cc
m_room_aliases_la_SOURCES = m_room_aliases.cc
m_room_canonical_alias_la_SOURCES = m_room_canonical_alias.cc
m_room_create_la_SOURCES = m_room_create.cc
m_room_history_visibility_la_SOURCES = m_room_history_visibility.cc
m_room_join_rules_la_SOURCES = m_room_join_rules.cc
m_room_member_la_SOURCES = m_room_member.cc
m_room_message_la_SOURCES = m_room_message.cc
m_room_name_la_SOURCES = m_room_name.cc
m_room_power_levels_la_SOURCES = m_room_power_levels.cc
m_room_redaction_la_SOURCES = m_room_redaction.cc
m_room_server_acl_la_SOURCES = m_room_server_acl.cc
m_room_third_party_invite_la_SOURCES = m_room_third_party_invite.cc
m_user_highlight_auth_la_SOURCES = m_user_highlight_auth.cc
m_vm_la_SOURCES = m_vm.cc
m_vm_fetch_la_SOURCES = m_vm_fetch.cc
m_module_LTLIBRARIES = \
m_noop.la \
m_event.la \
m_event_append.la \
m_event_horizon.la \
m_event_pretty.la \
m_user.la \
m_user_events.la \
m_user_rooms.la \
m_user_filter.la \
m_user_register.la \
m_user_mitsein.la \
m_user_servers.la \
m_user_highlight.la \
m_user_profile.la \
m_user_account_data.la \
m_user_room_account_data.la \
m_user_room_tags.la \
m_node.la \
m_keys.la \
m_feds.la \
m_fetch.la \
m_breadcrumb_rooms.la \
m_command.la \
m_control.la \
m_create.la \
m_profile.la \
m_device.la \
m_direct.la \
m_typing.la \
m_receipt.la \
m_presence.la \
m_direct_to_device.la \
m_device_list_update.la \
m_direct.la \
m_direct_to_device.la \
m_ignored_user_list.la \
m_breadcrumb_rooms.la \
m_events.la \
m_rooms.la \
m_rooms_summary.la \
m_room.la \
m_room_events.la \
m_room_auth.la \
m_room_head.la \
m_room_create.la \
m_room_member.la \
m_room_join.la \
m_room_leave.la \
m_room_join_rules.la \
m_room_history_visibility.la \
m_room_canonical_alias.la \
m_listen.la \
m_noop.la \
m_presence.la \
m_profile.la \
m_receipt.la \
m_room_aliases.la \
m_room_canonical_alias.la \
m_room_create.la \
m_room_history_visibility.la \
m_room_join_rules.la \
m_room_member.la \
m_room_message.la \
m_room_name.la \
m_room_power_levels.la \
m_room_third_party_invite.la \
m_room_redaction.la \
m_room_server_acl.la \
m_room_bootstrap.la \
m_room_name.la \
m_init_backfill.la \
m_listen.la \
m_users.la \
m_room_third_party_invite.la \
m_user_highlight_auth.la \
m_vm.la \
m_vm_fetch.la \
###

View file

@ -129,69 +129,3 @@ ircd::m::handle_breadcrumb_rooms_focus_out(const event &event,
at<"sender"_>(event),
};
}
ircd::m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::set(const json::array &rooms)
const
{
const json::strung object
{
json::members
{
{ "rooms", rooms }
}
};
return account_data.set("im.vector.riot.breadcrumb_rooms", object);
}
bool
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::for_each(const closure_bool &closure)
const
{
bool ret{true};
get(std::nothrow, [&closure, &ret]
(const json::array &rooms)
{
for(const json::string &room : rooms)
if(!closure(room))
{
ret = false;
break;
}
});
return ret;
}
void
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::get(const closure &closure)
const
{
if(!get(std::nothrow, closure))
throw m::NOT_FOUND
{
"User has no breadcrumb_rooms set in their account_data."
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::breadcrumb_rooms::get(std::nothrow_t,
const closure &closure)
const
{
return account_data.get(std::nothrow, "im.vector.riot.breadcrumb_rooms", [&closure]
(const string_view &key, const json::object &object)
{
const json::array &rooms
{
object["rooms"]
};
closure(rooms);
});
}

View file

@ -20,136 +20,6 @@ IRCD_MODULE
"Matrix device library; modular components."
};
std::map<std::string, long>
IRCD_MODULE_EXPORT
ircd::m::device::count_one_time_keys(const user &user,
const string_view &device_id)
{
std::map<std::string, long> ret;
for_each(user, device_id, [&ret]
(const string_view &type)
{
if(!startswith(type, "one_time_key|"))
return true;
const auto &[prefix, ident]
{
split(type, '|')
};
const auto &[algorithm, name]
{
split(ident, ':')
};
assert(prefix == "one_time_key");
assert(!empty(algorithm));
assert(!empty(ident));
assert(!empty(name));
auto it(ret.lower_bound(algorithm));
if(it == end(ret) || it->first != algorithm)
it = ret.emplace_hint(it, algorithm, 0L);
auto &count(it->second);
++count;
return true;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::set(const m::user &user,
const device &device)
{
const user::room user_room{user};
const string_view &device_id
{
json::at<"device_id"_>(device)
};
json::for_each(device, [&user, &user_room, &device_id]
(const auto &prop, auto &&val)
{
if(!json::defined(json::value(val)))
return;
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
m::send(user_room, user, type, device_id, json::members
{
{ "", val }
});
});
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::set(const m::user &user,
const string_view &id,
const string_view &prop,
const string_view &val)
{
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
const user::room user_room{user};
m::send(user_room, user, type, id, json::members
{
{ "", val }
});
return true;
}
/// To delete a device we iterate the user's room state for all types matching
/// ircd.device.* (and ircd.device) which have a state_key of the device_id.
/// Those events are redacted which removes them from appearing in the state.
bool
IRCD_MODULE_EXPORT
ircd::m::device::del(const m::user &user,
const string_view &id)
{
const user::room user_room{user};
const room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
state.for_each(type, [&user, &id, &user_room, &state]
(const string_view &type, const string_view &, const event::idx &)
{
const auto event_idx
{
state.get(std::nothrow, type, id)
};
const auto event_id
{
m::event_id(event_idx, std::nothrow)
};
if(event_id)
m::redact(user_room, user, event_id, "deleted");
return true;
});
return true;
}
/// Deletes the access_token associated with a device when the device
/// (specifically the access_token_id property of that device) is deleted.
decltype(ircd::m::_access_token_delete_hook)
@ -189,174 +59,3 @@ ircd::m::_access_token_delete(const m::event &event,
m::redact(m::user::tokens, at<"sender"_>(event), token_event_id, "device deleted");
});
};
bool
IRCD_MODULE_EXPORT
ircd::m::device::has(const m::user &user,
const string_view &id)
{
const user::room user_room{user};
const room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
bool ret(false);
state.for_each(type, [&state, &id, &ret]
(const string_view &type, const string_view &, const event::idx &)
{
ret = state.has(type, id);
return !ret;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::has(const m::user &user,
const string_view &id,
const string_view &prop)
{
bool ret{false};
get(std::nothrow, user, id, prop, [&ret]
(const string_view &value)
{
ret = !empty(value);
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::get(std::nothrow_t,
const m::user &user,
const string_view &id,
const string_view &prop,
const closure &closure)
{
char buf[m::event::TYPE_MAX_SIZE];
const string_view type{fmt::sprintf
{
buf, "ircd.device.%s", prop
}};
const m::user::room user_room{user};
const m::room::state state{user_room};
const auto event_idx
{
state.get(std::nothrow, type, id)
};
return m::get(std::nothrow, event_idx, "content", [&closure]
(const json::object &content)
{
const string_view &value
{
content.get("")
};
closure(value);
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::for_each(const m::user &user,
const string_view &device_id,
const closure_bool &closure)
{
const m::user::room user_room{user};
const m::room::state state{user_room};
const room::state::type_prefix type
{
"ircd.device."
};
return state.for_each(type, [&state, &device_id, &closure]
(const string_view &type, const string_view &, const event::idx &)
{
const string_view &prop
{
lstrip(type, "ircd.device.")
};
return state.has(type, device_id)?
closure(prop):
true;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::for_each(const m::user &user,
const closure_bool &closure)
{
const m::user::room user_room
{
user
};
const m::room::state state
{
user_room
};
return state.for_each("ircd.device.device_id", [&closure]
(const string_view &, const string_view &state_key, const event::idx &)
{
return closure(state_key);
});
}
ircd::m::device::id::buf
IRCD_MODULE_EXPORT
ircd::m::device::access_token_to_id(const string_view &token)
{
id::buf ret;
access_token_to_id(token, [&ret]
(const string_view &device_id)
{
ret = device_id;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::device::access_token_to_id(const string_view &token,
const closure &closure)
{
const m::room::state &state{m::user::tokens};
const m::event::idx &event_idx
{
state.get(std::nothrow, "ircd.access_token", token)
};
bool ret{false};
const auto device_id{[&closure, &ret]
(const json::object &content)
{
const json::string &device_id
{
content["device_id"]
};
if(likely(device_id))
{
closure(device_id);
ret = true;
}
}};
if(!event_idx)
return ret;
if(!m::get(std::nothrow, event_idx, "content", device_id))
return ret;
return ret;
}

View file

@ -10,16 +10,16 @@
using namespace ircd;
static void
handle_edu_m_device_list_update(const m::event &,
m::vm::eval &);
mapi::header
IRCD_MODULE
{
"Matrix Device List Update"
};
static void
handle_edu_m_device_list_update(const m::event &,
m::vm::eval &);
m::hookfn<m::vm::eval &>
_m_device_list_update_eval
{

View file

@ -1,20 +0,0 @@
// 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.
// NOTE: !!!
// Definitions re currently split between libircd and modules until the API
// and dependency graph has stabilized. Eventually most/all of ircd::m::
// should migrate out of libircd into modules.
ircd::mapi::header
IRCD_MODULE
{
"Matrix event library"
};

View file

@ -19,20 +19,6 @@ IRCD_MODULE
"14.24 :Ignoring Users"
};
conf::item<bool>
enforce_invites
{
{ "name", "ircd.m.ignored_user_list.enforce.invites" },
{ "default", true }
};
conf::item<bool>
enforce_events
{
{ "name", "ircd.m.ignored_user_list.enforce.events" },
{ "default", false }
};
/*
m::hookfn<m::vm::eval &>
_m_ignored_user_list
@ -96,65 +82,3 @@ handle_m_ignored_user(const m::event &event,
string_view{user_id},
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::has(const m::user::id &other)
const
{
return !for_each([&other]
(const m::user::id &user_id, const json::object &)
{
return user_id != other;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::for_each(const closure_bool &closure)
const try
{
const m::user::account_data account_data
{
user
};
bool ret{true};
account_data.get(std::nothrow, "m.ignored_user_list", [&closure, &ret]
(const string_view &key, const json::object &content)
{
const json::object &ignored_users
{
content.get("ignored_users")
};
for(const auto &[user_id, object] : ignored_users)
if(!(ret = closure(user_id, object)))
return;
});
return ret;
}
catch(const std::exception &e)
{
log::derror
{
m::log, "Error in ignore list for %s",
string_view{user.user_id}
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::user::ignores::enforce(const string_view &type)
{
if(type == "events")
return bool(enforce_events);
if(type == "invites")
return bool(enforce_invites);
return false;
}

View file

@ -315,92 +315,3 @@ catch(const std::exception &e)
e.what(),
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::presence::get(const std::nothrow_t,
const m::user &user,
const m::presence::closure_event &closure,
const m::event::fetch::opts *const &fopts_p)
{
const m::event::idx event_idx
{
m::presence::get(std::nothrow, user)
};
if(!event_idx)
return false;
const auto &fopts
{
fopts_p? *fopts_p : event::fetch::default_opts
};
const m::event::fetch event
{
event_idx, std::nothrow, fopts
};
if(event.valid)
closure(event);
return event.valid;
}
m::event::idx
IRCD_MODULE_EXPORT
ircd::m::presence::get(const std::nothrow_t,
const m::user &user)
{
const m::user::room user_room
{
user
};
const m::room::state state
{
user_room
};
return state.get(std::nothrow, "ircd.presence", "");
}
m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::presence::set(const m::presence &content)
{
const m::user user
{
json::at<"user_id"_>(content)
};
//TODO: ABA
if(!exists(user))
create(user.user_id);
m::vm::copts copts;
const m::user::room user_room
{
user, &copts
};
//TODO: ABA
return send(user_room, user.user_id, "ircd.presence", "", json::strung{content});
}
const string_view
valid_states[]
{
"online", "offline", "unavailable",
};
bool
IRCD_MODULE_EXPORT
ircd::m::presence::valid_state(const string_view &state)
{
return std::any_of(begin(valid_states), end(valid_states), [&state]
(const string_view &valid)
{
return state == valid;
});
}

View file

@ -8,24 +8,6 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
/// There are three principal component sections in this unit:
///
/// ---------------------------------- _
/// | 1. Incoming federation EDU hook | |
/// ---------------------------------- |
/// ---------------------------------- |
/// | 2. ircd::m::receipt API | |
/// ---------------------------------- |
/// ---------------------------------- |
/// | 3. Outgoing federation EDU hook | V
/// ----------------------------------
///
/// This unit parses and accepts m.receipt EDU's from the federation (1); then
/// it calls the m::receipt API (2) which generates internal PDU's sent to
/// user rooms. Hooks on these events sent to user rooms (3) turn the events
/// into federation EDU's for broadcast (for local users). Note that there are
/// other reactives for these internal events in client/sync, etc.
using namespace ircd;
static void handle_ircd_read(const m::event &, m::vm::eval &);
@ -47,12 +29,6 @@ IRCD_MODULE
"Matrix Receipts"
};
log::log
receipt_log
{
"m.receipt"
};
//
// EDU handler.
//
@ -98,7 +74,7 @@ handle_edu_m_receipt(const m::event &event,
{
log::dwarning
{
receipt_log, "Ignoring m.receipt from '%s' in %s :denied by m.room.server_acl.",
m::receipt::log, "Ignoring m.receipt from '%s' in %s :denied by m.room.server_acl.",
json::get<"origin"_>(event),
string_view{room_id},
};
@ -125,7 +101,7 @@ handle_m_receipt(const m::event &event,
log::dwarning
{
receipt_log, "Unhandled m.receipt type '%s' to room '%s'",
m::receipt::log, "Unhandled m.receipt type '%s' to room '%s'",
type,
string_view{room_id}
};
@ -144,7 +120,7 @@ handle_m_receipt_m_read(const m::event &event,
{
log::dwarning
{
receipt_log, "Ignoring m.receipt m.read from '%s' in %s for alien %s.",
m::receipt::log, "Ignoring m.receipt m.read from '%s' in %s for alien %s.",
json::get<"origin"_>(event),
string_view{room_id},
string_view{user_id},
@ -180,7 +156,7 @@ handle_m_receipt_m_read(const m::room::id &room_id,
{
log::derror
{
receipt_log, "Failed to handle m.receipt m.read for %s in %s for '%s' :%s",
m::receipt::log, "Failed to handle m.receipt m.read for %s in %s for '%s' :%s",
string_view{user_id},
string_view{room_id},
string_view{event_id},
@ -211,7 +187,7 @@ try
{
log::dwarning
{
receipt_log, "Ignoring m.receipt m.read for unknown %s in %s for %s",
m::receipt::log, "Ignoring m.receipt m.read for unknown %s in %s for %s",
string_view{user_id},
string_view{room_id},
string_view{event_id}
@ -229,7 +205,7 @@ catch(const std::exception &e)
{
log::derror
{
receipt_log, "Failed to save m.receipt m.read for %s in %s for %s :%s",
m::receipt::log, "Failed to save m.receipt m.read for %s in %s for %s :%s",
string_view{user_id},
string_view{room_id},
string_view{event_id},
@ -237,188 +213,6 @@ catch(const std::exception &e)
};
}
//
// m::receipt API -> Internal
//
m::event::id::buf
IRCD_MODULE_EXPORT
ircd::m::receipt::read(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id,
const json::object &options)
{
const m::user::room user_room
{
user_id
};
const auto evid
{
send(user_room, user_id, "ircd.read", room_id,
{
{ "event_id", event_id },
{ "ts", options.get("ts", ircd::time<milliseconds>()) },
{ "m.hidden", options.get("m.hidden", false) },
})
};
log::info
{
receipt_log, "%s read by %s in %s options:%s",
string_view{event_id},
string_view{user_id},
string_view{room_id},
string_view{options},
};
return evid;
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::get(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id::closure &closure)
{
const m::user::room user_room
{
user_id
};
const auto event_idx
{
user_room.get(std::nothrow, "ircd.read", room_id)
};
return m::get(std::nothrow, event_idx, "content", [&closure]
(const json::object &content)
{
const json::string &event_id
{
content["event_id"]
};
closure(event_id);
});
}
/// Does the user wish to not send receipts for events sent by its specific
/// sender?
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::ignoring(const m::user &user,
const m::event::id &event_id)
{
bool ret{false};
m::get(std::nothrow, event_id, "sender", [&ret, &user]
(const string_view &sender)
{
const m::user::room user_room{user};
ret = user_room.has("ircd.read.ignore", sender);
});
return ret;
}
/// Does the user wish to not send receipts for events for this entire room?
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::ignoring(const m::user &user,
const m::room::id &room_id)
{
const m::user::room user_room{user};
return user_room.has("ircd.read.ignore", room_id);
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::freshest(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id)
try
{
const m::user::room user_room
{
user_id
};
bool ret{true};
user_room.get("ircd.read", room_id, [&ret, &event_id]
(const m::event &event)
{
const auto &content
{
at<"content"_>(event)
};
const m::event::id &previous_id
{
unquote(content.get("event_id"))
};
if(event_id == previous_id)
{
ret = false;
return;
}
const m::event::idx &previous_idx
{
index(previous_id)
};
const m::event::idx &event_idx
{
index(event_id)
};
ret = event_idx > previous_idx;
});
return ret;
}
catch(const std::exception &e)
{
log::derror
{
receipt_log, "Freshness of receipt in %s from %s for %s :%s",
string_view{room_id},
string_view{user_id},
string_view{event_id},
e.what()
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::receipt::exists(const m::room::id &room_id,
const m::user::id &user_id,
const m::event::id &event_id)
{
const m::user::room user_room
{
user_id
};
bool ret{false};
user_room.get(std::nothrow, "ircd.read", room_id, [&ret, &event_id]
(const m::event &event)
{
const auto &content
{
at<"content"_>(event)
};
ret = unquote(content.get("event_id")) == event_id;
});
return ret;
}
//
// Internal -> Federation
//
@ -482,7 +276,7 @@ catch(const std::exception &e)
{
log::error
{
receipt_log, "Implicit receipt hook for %s :%s",
m::receipt::log, "Implicit receipt hook for %s :%s",
string_view{event.event_id},
e.what(),
};
@ -607,7 +401,7 @@ catch(const std::exception &e)
{
log::error
{
receipt_log, "ircd.read hook on %s for federation broadcast :%s",
m::receipt::log, "ircd.read hook on %s for federation broadcast :%s",
string_view{event.event_id},
e.what(),
};

View file

@ -10,8 +10,6 @@
namespace ircd::m
{
extern conf::item<seconds> alias_fetch_timeout;
extern conf::item<seconds> alias_cache_ttl;
extern const room::id::buf alias_room_id;
extern const room alias_room;
@ -42,20 +40,6 @@ ircd::m::alias_room
alias_room_id
};
decltype(ircd::m::alias_cache_ttl)
ircd::m::alias_cache_ttl
{
{ "name", "ircd.m.room.aliases.cache.ttl" },
{ "default", 604800L },
};
decltype(ircd::m::alias_fetch_timeout)
ircd::m::alias_fetch_timeout
{
{ "name", "ircd.m.room.aliases.fetch.timeout" },
{ "default", 10L },
};
//
// create the alias room as an effect of !ircd created on bootstrap
//
@ -174,425 +158,3 @@ ircd::m::auth_room_aliases(const event &event,
// c. Otherwise, allow
data.allow = true;
}
//
// m::room::aliases
//
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::for_each(const m::room &room,
const string_view &server,
const closure_bool &closure)
{
const room::state state
{
room
};
assert(server);
const event::idx &event_idx
{
state.get(std::nothrow, "m.room.aliases", server)
};
if(!event_idx)
return true;
bool ret{true};
m::get(std::nothrow, event_idx, "content", [&closure, &ret]
(const json::object &content)
{
const json::array &aliases
{
content["aliases"]
};
for(auto it(begin(aliases)); it != end(aliases) && ret; ++it)
{
const json::string &alias(*it);
if(!valid(m::id::ROOM_ALIAS, alias))
continue;
if(!closure(alias))
ret = false;
}
});
return ret;
}
//
// m::room::aliases::cache
//
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::del(const alias &alias)
{
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
make_key(buf, alias)
};
const auto &event_idx
{
alias_room.get(std::nothrow, "ircd.room.alias", key)
};
if(!event_idx)
return false;
const auto event_id
{
m::event_id(event_idx, std::nothrow)
};
if(!event_id)
return false;
const auto ret
{
redact(alias_room, m::me.user_id, event_id, "deleted")
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::set(const alias &alias,
const id &id)
{
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
make_key(buf, alias)
};
const auto ret
{
send(alias_room, m::me.user_id, "ircd.room.alias", key,
{
{ "room_id", id }
})
};
return true;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::get(std::nothrow_t,
const alias &alias,
const id::closure &closure)
{
m::event::idx event_idx
{
getidx(alias)
};
if(!event_idx)
{
if(my_host(alias.host()))
return false;
if(!fetch(std::nothrow, alias, alias.host()))
return false;
event_idx = getidx(alias);
}
const bool expired
{
!my_host(alias.host()) && cache::expired(event_idx)
};
if(expired)
{
log::dwarning
{
log, "Cached alias %s expired age:%ld ttl:%ld",
string_view{alias},
cache::age(event_idx).count(),
milliseconds(seconds(alias_cache_ttl)).count(),
};
fetch(std::nothrow, alias, alias.host());
event_idx = getidx(alias);
}
if(!event_idx)
return false;
bool ret{false};
m::get(std::nothrow, event_idx, "content", [&closure, &ret]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
if(!empty(room_id))
{
ret = true;
closure(room_id);
}
});
return ret;
}
namespace ircd::m
{
thread_local char room_aliases_cache_fetch_hpbuf[384];
}
void
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::fetch(const alias &alias,
const net::hostport &hp)
try
{
const unique_buffer<mutable_buffer> buf
{
16_KiB
};
m::v1::query::opts opts;
opts.remote = hp;
opts.dynamic = true;
m::v1::query::directory request
{
alias, buf, std::move(opts)
};
request.wait(seconds(alias_fetch_timeout));
const http::code &code
{
request.get()
};
const json::object response
{
request
};
if(!response.has("room_id"))
throw m::NOT_FOUND
{
"Server '%s' does not know room_id for %s",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
const m::room::id &room_id
{
unquote(response["room_id"])
};
set(alias, room_id);
}
catch(const ctx::timeout &e)
{
throw m::error
{
http::GATEWAY_TIMEOUT, "M_ROOM_ALIAS_TIMEOUT",
"Server '%s' did not respond with a room_id for %s in time",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
}
catch(const server::unavailable &e)
{
throw m::error
{
http::BAD_GATEWAY, "M_ROOM_ALIAS_UNAVAILABLE",
"Server '%s' is not available to query a room_id for %s",
string(room_aliases_cache_fetch_hpbuf, hp),
string_view{alias},
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::for_each(const string_view &server,
const closure_bool &closure)
{
const m::room::state state
{
alias_room
};
bool ret{true};
const m::room::state::closure_bool reclosure{[&server, &closure, &ret]
(const string_view &type, const string_view &state_key, const m::event::idx &event_idx)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE];
const alias &alias
{
m::id::unswap(state_key, swapbuf)
};
if(server && alias.host() != server)
return false;
if(expired(event_idx))
return true;
m::get(std::nothrow, event_idx, "content", [&closure, &ret, &alias]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
if(!empty(room_id))
ret = closure(alias, room_id);
});
return ret;
}};
state.for_each("ircd.room.alias", server, reclosure);
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::has(const alias &alias)
{
const auto &event_idx
{
getidx(alias)
};
if(!event_idx)
return false;
if(expired(event_idx))
return false;
bool ret{false};
m::get(std::nothrow, event_idx, "content", [&ret]
(const json::object &content)
{
const json::string &room_id
{
content.get("room_id")
};
ret = !empty(room_id);
});
return ret;
}
ircd::system_point
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::expires(const alias &alias)
{
const auto event_idx
{
getidx(alias)
};
if(!event_idx)
return system_point::min();
const milliseconds age
{
cache::age(event_idx)
};
const seconds ttl
{
alias_cache_ttl
};
return now<system_point>() + (ttl - age);
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::expired(const event::idx &event_idx)
{
const milliseconds age
{
cache::age(event_idx)
};
const seconds ttl
{
alias_cache_ttl
};
return age > ttl;
}
ircd::milliseconds
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::age(const event::idx &event_idx)
{
time_t ts;
if(!m::get(event_idx, "origin_server_ts", ts))
return milliseconds::max();
const time_t now
{
ircd::time<milliseconds>()
};
return milliseconds
{
now - ts
};
}
ircd::m::event::idx
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::getidx(const alias &alias)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE];
const string_view &swapped
{
alias.swap(swapbuf)
};
char buf[m::id::room_alias::buf::SIZE];
const string_view &key
{
tolower(buf, swapped)
};
const auto &event_idx
{
alias_room.get(std::nothrow, "ircd.room.alias", key)
};
return event_idx;
}
ircd::string_view
IRCD_MODULE_EXPORT
ircd::m::room::aliases::cache::make_key(const mutable_buffer &out,
const alias &alias)
{
thread_local char swapbuf[m::id::room_alias::buf::SIZE] alignas(16);
const string_view &swapped
{
alias.swap(swapbuf)
};
const string_view &key
{
tolower(out, swapped)
};
return key;
}

View file

@ -10,9 +10,6 @@
namespace ircd::m
{
static bool visible_to_node(const room &, const string_view &node_id, const event &);
static bool visible_to_user(const room &, const string_view &history_visibility, const m::user::id &, const event &);
static void changed_history_visibility(const event &, vm::eval &);
extern hookfn<vm::eval &> changed_history_visibility_hookfn;
}
@ -46,138 +43,3 @@ ircd::m::changed_history_visibility(const event &event,
string_view{event.event_id},
};
}
bool
IRCD_MODULE_EXPORT
ircd::m::visible(const m::event &event,
const string_view &mxid)
{
const m::room room
{
at<"room_id"_>(event), event.event_id
};
const m::room::state state
{
room
};
const event::idx visibility_event_idx
{
state.get(std::nothrow, "m.room.history_visibility", "")
};
char buf[32];
string_view history_visibility{"shared"};
m::get(std::nothrow, visibility_event_idx, "content", [&buf, &history_visibility]
(const json::object &content)
{
const json::string &_history_visibility
{
content.get("history_visibility", "shared")
};
history_visibility = strncpy
{
buf, _history_visibility
};
});
if(history_visibility == "world_readable")
return true;
if(empty(mxid))
return false;
if(m::valid(m::id::USER, mxid))
return visible_to_user(room, history_visibility, mxid, event);
if(rfc3986::valid_remote(std::nothrow, mxid))
return visible_to_node(room, mxid, event);
throw m::UNSUPPORTED
{
"Cannot determine visibility of %s for '%s'",
string_view{room.room_id},
mxid,
};
}
bool
ircd::m::visible_to_user(const m::room &room,
const string_view &history_visibility,
const m::user::id &user_id,
const m::event &event)
{
assert(history_visibility != "world_readable");
// Allow any member event where the state_key string is a user mxid.
if(json::get<"type"_>(event) == "m.room.member")
if(at<"state_key"_>(event) == user_id)
return true;
// Get the membership of the user in the room at the event.
char buf[m::room::MEMBERSHIP_MAX_SIZE];
const string_view membership
{
m::membership(buf, room, user_id)
};
if(membership == "join")
return true;
if(history_visibility == "joined")
return false;
if(membership == "invite")
return true;
if(history_visibility == "invited")
return false;
// The history_visibility is now likely "shared"; though we cannot assert
// that in case some other string is used for any non-spec customization
// or for graceful forward compatibility. We default to "shared" here.
//assert(history_visibility == "shared");
// An m::room instance with no event_id is used to query the room at the
// present state.
const m::room present
{
room.room_id
};
// If the room is not at the present event then we have to run another
// test for membership here. Otherwise the "join" test already failed.
if(!room.event_id)
return false;
return m::membership(present, user_id, m::membership_positive); // join || invite
}
bool
ircd::m::visible_to_node(const m::room &room,
const string_view &node_id,
const m::event &event)
{
// Allow auth chain events XXX: this is too broad
if(m::room::auth::is_power_event(event))
return true;
// Allow any event where the state_key string is a user mxid and the server
// is the host of that user. Note that applies to any type of event.
if(m::valid(m::id::USER, json::get<"state_key"_>(event)))
if(m::user::id(at<"state_key"_>(event)).host() == node_id)
return true;
const m::room::origins origins
{
room
};
// Allow joined servers
if(origins.has(node_id))
return true;
return false;
}

View file

@ -74,331 +74,3 @@ ircd::m::on_changed_room_server_acl(const event &event,
string_view{event.event_id},
};
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/m/room/server_acl.h
//
/// Coarse control over whether ACL's are considered during the vm::eval of an
/// event, ACL's will be checked against the event's origin during processing
/// of the event, regardless of how the event was received, fetched, etc. The
/// m::vm options may dictate further detailed behavior (hard-fail, soft-
/// fail, auth integration, etc). This is the principal configuration option
/// for effecting the server access control list functionality. Though this
/// conf item is independent of other conf items in this module, setting it
/// to false denudes the core functionality.
///
/// Setting this to true is *stricter* than the official specification and
/// fixes several vulnerabilities for bypassing ACL's. This also applies to
/// both PDU's and EDU's, and is agnostic to the method or endpoint by which
/// this server obtained the event. This departs from the specification.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_write)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_write
{
{ "name", "ircd.m.room.server_acl.enable.write" },
{ "default", true },
};
/// Coarse control over whether ACL's apply to endpoints considered
/// non-modifying/passive to the room. If false, ACL's are not checked on
/// endpoints which have no visible effects to the federation; this can
/// increase performance.
///
/// Setting this option to false relaxes the list of endpoints covered by ACL's
/// and departs from the official specification.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_read)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_read
{
{ "name", "ircd.m.room.server_acl.enable.read" },
{ "default", false },
};
/// Coarse control over whether ACL's are considered for event fetching. If
/// true, events originating from an ACL'ed server will not be fetched, nor
/// will an ACL'ed server be queried by the fetch unit for any event. Note that
/// this cannot fully apply for newer event_id's without hostparts, but the
/// fetch unit may discard such events for an ACL'ed server after receiving.
///
/// Setting this to true is *stricter* than the official specification, which
/// is vulnerable to "bouncing" around ACL's.
/// (see: https://github.com/maubot/bouncybot)
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_fetch)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_fetch
{
{ "name", "ircd.m.room.server_acl.enable.fetch" },
{ "default", true },
};
/// Coarse control over whether ACL's are considered when this server
/// transmits transactions to the participants in a room. If true, transactions
/// with all contained PDU's and EDU's will not be sent to ACL'ed servers.
///
/// Setting this to true is *stricter* than the official specification, which
/// leaks all transmissions to ACL'ed servers.
///
/// This option has no effect on the room::server_acl interface itself, it is
/// available for the callsite to check independently before using the iface.
decltype(ircd::m::room::server_acl::enable_send)
IRCD_MODULE_EXPORT_DATA
ircd::m::room::server_acl::enable_send
{
{ "name", "ircd.m.room.server_acl.enable.send" },
{ "default", true },
};
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::check(const m::room::id &room_id,
const net::hostport &server)
try
{
const server_acl server_acl
{
room_id
};
return server_acl(server);
}
catch(const std::exception &e)
{
log::critical
{
log, "Failed to check server_acl for '%s' in %s :%s",
string(server),
string_view{room_id},
e.what()
};
return false;
}
//
// server_acl::server_acl
//
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::server_acl(const m::room &room,
const event::idx &event_idx)
:room
{
room
}
,event_idx
{
!event_idx?
room.get(std::nothrow, "m.room.server_acl", ""):
event_idx
}
{
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::operator()(const net::hostport &server)
const
{
bool ret;
const auto closure{[this, &server, &ret]
(const json::object &content)
{
// Set the content reference here so only one actual IO is made to
// fetch the m.room.server_acl content for all queries.
const scope_restore this_content
{
this->content, content
};
ret = this->check(server);
}};
return !view(closure) || ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::match(const string_view &prop,
const net::hostport &remote)
const
{
// Spec sez when comparing against the server ACLs, the suspect server's
// port number must not be considered.
const string_view &server
{
net::host(remote)
};
return !for_each(prop, [&server]
(const string_view &expression)
{
const globular_match match
{
expression
};
// return false to break on match.
return match(server)? false : true;
});
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::has(const string_view &prop,
const string_view &expr)
const
{
return !for_each(prop, [&expr]
(const string_view &_expr)
{
// false to break on match
return _expr == expr? false : true;
});
}
int
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::getbool(const string_view &prop)
const
{
int ret(-1);
view([&ret, &prop]
(const json::object &object)
{
const string_view &value
{
object[prop]
};
if(value == json::literal_true)
ret = 1;
else if(value == json::literal_false)
ret = 0;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::has(const string_view &prop)
const
{
bool ret{false};
view([&ret, &prop]
(const json::object &object)
{
ret = object.has(prop);
});
return ret;
}
size_t
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::count(const string_view &prop)
const
{
size_t ret(0);
for_each(prop, [&ret]
(const string_view &)
{
++ret;
return true;
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::for_each(const string_view &prop,
const closure_bool &closure)
const
{
bool ret{true};
view([&ret, &closure, &prop]
(const json::object &content)
{
const json::array &list
{
content[prop]
};
if(!list || json::type(list, std::nothrow) != json::ARRAY)
return;
for(auto it(begin(list)); it != end(list) && ret; ++it)
{
if(json::type(*it, json::strict, std::nothrow) != json::STRING)
continue;
if(!closure(json::string(*it)))
ret = false;
}
});
return ret;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::exists()
const
{
return content || event_idx;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::check(const net::hostport &server)
const
{
// c2s 13.29.1 rules
// 1. If there is no m.room.server_acl event in the room state, allow.
if(!exists())
return true;
// 2. If the server name is an IP address (v4 or v6) literal, and
// allow_ip_literals is present and false, deny.
if(getbool("allow_ip_literals") == false)
if(rfc3986::valid(std::nothrow, rfc3986::parser::ip_address, net::host(server)))
return false;
// 3. If the server name matches an entry in the deny list, deny.
if(match("deny", server))
return false;
// 4. If the server name matches an entry in the allow list, allow.
if(match("allow", server))
return true;
// 5. Otherwise, deny.
return false;
}
bool
IRCD_MODULE_EXPORT
ircd::m::room::server_acl::view(const view_closure &closure)
const
{
if(content)
{
closure(content);
return true;
}
return event_idx && m::get(std::nothrow, event_idx, "content", closure);
}