0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-20 11:53:46 +02:00

Compare commits

...

14 commits

Author SHA1 Message Date
Jason Volk 0624b69246 modules/web_hook: Add post-push handler for reacting to push events. 2023-05-01 12:45:37 -07:00
Jason Volk e5ae70c521 modules/web_hook: Add requests for workflows and dispatching. 2023-05-01 12:45:37 -07:00
Jason Volk 759871ce8e ircd::versions: Minor cleanup instance_list related. 2023-05-01 12:39:06 -07:00
Jason Volk 3c9a4f8c57 ircd:Ⓜ️ Assertions for non-empty id constructions. 2023-05-01 10:48:07 -07:00
Jason Volk f6b3b8b758 ircd:Ⓜ️:event::append: Add branch for bundled relations; add m.replace bundle. 2023-05-01 10:48:07 -07:00
Jason Volk 4fe85805bf ircd:Ⓜ️:relates: Add conf item for non-type coarse control of sorting field. 2023-05-01 10:48:07 -07:00
Jason Volk b6cb1180f7 ircd:Ⓜ️:user::keys: Basis for verification cross-signatures between users. 2023-05-01 10:48:07 -07:00
Jason Volk 741304271e ircd:Ⓜ️:event::append: Optimize opts, eliminate double-indirection; various reorg. 2023-05-01 10:48:07 -07:00
Jason Volk cd2b92b8fe modules/client/keys/claim: Restore when_any() loop to smooth remote handling. 2023-04-29 13:57:39 -07:00
Jason Volk feb4ac1fd3 ircd:Ⓜ️:event::append: Move static objects into class. 2023-04-29 13:57:39 -07:00
Jason Volk d4ba215a3b modules/client/user: Relax matching user_id in URL to requestor for bridges. 2023-04-29 13:57:39 -07:00
Jason Volk 8a705f77a9 ircd::db: Fixes for RocksDB 8.1.1 interface changes. 2023-04-29 13:57:37 -07:00
Jason Volk f86fddc3a3 configure: Error on -Wabstract-final-class (clang) 2023-04-28 13:39:35 -07:00
Jason Volk 8eb10c5d1a modules/client/rooms/event: Replace raw event response with client format. 2023-04-28 13:04:06 -07:00
31 changed files with 885 additions and 407 deletions

View file

@ -988,6 +988,7 @@ dnl Compiler specific
AM_COND_IF([CLANG], AM_COND_IF([CLANG],
[ [
RB_MAYBE_CWARN([-Werror=return-stack-address], charybdis_cv_c_gcc_w_error_return_stack_address) RB_MAYBE_CWARN([-Werror=return-stack-address], charybdis_cv_c_gcc_w_error_return_stack_address)
RB_MAYBE_CWARN([-Werror=abstract-final-class], charybdis_cv_c_gcc_w_error_abstract_final_class)
RB_MAYBE_CWARN([-Wundefined-reinterpret-cast], charybdis_cv_c_gcc_w_undefined_reinterpret_cast) RB_MAYBE_CWARN([-Wundefined-reinterpret-cast], charybdis_cv_c_gcc_w_undefined_reinterpret_cast)
RB_MAYBE_CWARN([-Wconditional-uninitialized], charybdis_cv_c_gcc_w_conditional_uninitialized) RB_MAYBE_CWARN([-Wconditional-uninitialized], charybdis_cv_c_gcc_w_conditional_uninitialized)

View file

@ -29,6 +29,29 @@ struct ircd::m::event::append
{ {
struct opts; struct opts;
private:
static const event::keys::exclude exclude_keys;
static const event::keys default_keys;
static conf::item<std::string> exclude_types;
static conf::item<bool> info;
static log::log log;
bool is_ignored(const event &, const opts &) const;
bool is_redacted(const event &, const opts &) const;
bool is_invisible(const event &, const opts &) const;
bool is_excluded(const event &, const opts &) const;
bool bundle_replace(json::stack::object &, const event &, const opts &);
void _relations(json::stack::object &, const event &, const opts &);
void _age(json::stack::object &, const event &, const opts &);
void _txnid(json::stack::object &, const event &, const opts &);
void _prev_state(json::stack::object &, const event &, const opts &);
void _unsigned(json::stack::object &, const event &, const opts &);
bool members(json::stack::object &, const event &, const opts &);
bool object(json::stack::array &, const event &, const opts &);
public:
append(json::stack::object &, const event &, const opts &); append(json::stack::object &, const event &, const opts &);
append(json::stack::object &, const event &); append(json::stack::object &, const event &);
append(json::stack::array &, const event &, const opts &); append(json::stack::array &, const event &, const opts &);
@ -39,11 +62,11 @@ struct ircd::m::event::append
/// can provide the best result. /// can provide the best result.
struct ircd::m::event::append::opts struct ircd::m::event::append::opts
{ {
const event::idx *event_idx {nullptr}; event::idx event_idx {0};
const string_view *client_txnid {nullptr}; string_view client_txnid;
const id::user *user_id {nullptr}; id::user user_id;
const room *user_room {nullptr}; id::room user_room_id;
const int64_t *room_depth {nullptr}; int64_t room_depth {-1L};
const event::keys *keys {nullptr}; const event::keys *keys {nullptr};
const m::event_filter *event_filter {nullptr}; const m::event_filter *event_filter {nullptr};
long age {std::numeric_limits<long>::min()}; long age {std::numeric_limits<long>::min()};
@ -51,6 +74,8 @@ struct ircd::m::event::append::opts
bool query_prev_state {true}; bool query_prev_state {true};
bool query_redacted {true}; bool query_redacted {true};
bool query_visible {false}; bool query_visible {false};
bool bundle_all {false};
bool bundle_replace {false};
}; };
inline inline
@ -64,3 +89,23 @@ ircd::m::event::append::append(json::stack::object &o,
const event &e) const event &e)
:append{o, e, {}} :append{o, e, {}}
{} {}
inline
ircd::m::event::append::append(json::stack::array &array,
const event &event,
const opts &opts)
:returns<bool>{[this, &array, &event, &opts]
{
return object(array, event, opts);
}}
{}
inline
ircd::m::event::append::append(json::stack::object &object,
const event &event,
const opts &opts)
:returns<bool>{[this, &object, &event, &opts]
{
return members(object, event, opts);
}}
{}

View file

@ -44,9 +44,11 @@ struct ircd::m::relates
const event::idx &, const json::object &, const m::relates_to & const event::idx &, const json::object &, const m::relates_to &
>; >;
static conf::item<std::string> latest_column;
event::refs refs; event::refs refs;
bool match_sender {false}; bool match_sender {false};
bool prefetch_depth {false}; bool prefetch_latest {false};
bool prefetch_sender {false}; bool prefetch_sender {false};
private: private:

View file

@ -187,9 +187,11 @@ struct ircd::m::room
const vm::copts *const &copts, const vm::copts *const &copts,
const event::fetch::opts *const &fopts = nullptr) noexcept; const event::fetch::opts *const &fopts = nullptr) noexcept;
room(const id &room_id = {}, room(const id &room_id,
const event::fetch::opts *const &fopts = nullptr) noexcept; const event::fetch::opts *const &fopts = nullptr) noexcept;
room() = default;
// Index of create event // Index of create event
static event::idx index(const id &, std::nothrow_t); static event::idx index(const id &, std::nothrow_t);
static event::idx index(const id &); static event::idx index(const id &);
@ -229,7 +231,9 @@ ircd::m::room::room(const id &room_id,
,event_id{event_id? event::id{event_id} : event::id{}} ,event_id{event_id? event::id{event_id} : event::id{}}
,copts{copts} ,copts{copts}
,fopts{fopts} ,fopts{fopts}
{} {
assert(room_id);
}
inline inline
ircd::m::room::room(const id &room_id, ircd::m::room::room(const id &room_id,
@ -239,7 +243,9 @@ noexcept
:room_id{room_id} :room_id{room_id}
,copts{copts} ,copts{copts}
,fopts{fopts} ,fopts{fopts}
{} {
assert(room_id);
}
inline inline
ircd::m::room::room(const id &room_id, ircd::m::room::room(const id &room_id,
@ -247,7 +253,9 @@ ircd::m::room::room(const id &room_id,
noexcept noexcept
:room_id{room_id} :room_id{room_id}
,fopts{fopts} ,fopts{fopts}
{} {
assert(room_id);
}
inline ircd::m::room::operator inline ircd::m::room::operator
const ircd::m::room::id &() const ircd::m::room::id &()

View file

@ -22,6 +22,7 @@ struct ircd::m::user::keys
void attach_sigs(json::stack::object &, const json::object &, const user::id &) const; void attach_sigs(json::stack::object &, const json::object &, const user::id &) const;
bool attach_sigs(json::stack::object &, const event::idx &, const user::id &) const; bool attach_sigs(json::stack::object &, const event::idx &, const user::id &) const;
void append_sigs(json::stack::object &, const json::object &, const user::id &) const;
void append_keys(json::stack::object &, const json::object &, const user::id &) const; void append_keys(json::stack::object &, const json::object &, const user::id &) const;
bool append_keys(json::stack::object &, const event::idx &, const user::id &) const; bool append_keys(json::stack::object &, const event::idx &, const user::id &) const;

View file

@ -74,13 +74,17 @@ struct ircd::m::user
event::id::buf deoper(); event::id::buf deoper();
event::id::buf oper(); event::id::buf oper();
user(const id &user_id) user(const id &user_id);
:user_id{user_id}
{}
user() = default; user() = default;
}; };
inline
ircd::m::user::user(const id &user_id)
:user_id{user_id}
{
assert(user_id);
}
inline ircd::m::user::operator inline ircd::m::user::operator
const ircd::m::user::id &() const ircd::m::user::id &()
const const

View file

@ -73,16 +73,9 @@ struct ircd::versions
~versions() noexcept; ~versions() noexcept;
}; };
namespace ircd template<>
{ decltype(ircd::versions::list)
template<> ircd::instance_list<ircd::versions>::list;
decltype(versions::allocator)
instance_list<versions>::allocator;
template<>
decltype(versions::list)
instance_list<versions>::list;
}
inline ircd::versions::operator inline ircd::versions::operator
const long &() const long &()

View file

@ -224,7 +224,9 @@ ircd::db::database::cache final
#else #else
Status Insert(const Slice &key, void *value, size_t charge, deleter, Handle **, Priority) noexcept override; Status Insert(const Slice &key, void *value, size_t charge, deleter, Handle **, Priority) noexcept override;
#endif #endif
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER #if defined(IRCD_DB_HAS_CACHE_ASYNC)
Handle *Lookup(const Slice &key, const CacheItemHelper *, CreateContext *, Priority, Statistics *) noexcept override;
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
Handle *Lookup(const Slice &key, const CacheItemHelper *, CreateContext *, Priority, bool, Statistics *) noexcept override; Handle *Lookup(const Slice &key, const CacheItemHelper *, CreateContext *, Priority, bool, Statistics *) noexcept override;
#else #else
Handle *Lookup(const Slice &key, Statistics *) noexcept override; Handle *Lookup(const Slice &key, Statistics *) noexcept override;
@ -263,6 +265,9 @@ ircd::db::database::cache final
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER #ifdef IRCD_DB_HAS_CACHE_ITEMHELPER
const CacheItemHelper *GetCacheItemHelper(Handle *) const noexcept override; const CacheItemHelper *GetCacheItemHelper(Handle *) const noexcept override;
#endif #endif
#ifdef IRCD_DB_HAS_CACHE_ASYNC
Handle *CreateStandalone(const Slice &, ObjectPtr, const CacheItemHelper *, size_t, bool) noexcept override;
#endif
cache(database *const &, cache(database *const &,
std::shared_ptr<struct database::stats>, std::shared_ptr<struct database::stats>,

View file

@ -3321,7 +3321,14 @@ noexcept
return ret; return ret;
} }
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER #if defined(IRCD_DB_HAS_CACHE_ASYNC)
rocksdb::Cache::Handle *
ircd::db::database::cache::Lookup(const Slice &key,
const CacheItemHelper *const helper,
CreateContext *const cc,
Priority pri,
Statistics *const statistics)
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
rocksdb::Cache::Handle * rocksdb::Cache::Handle *
ircd::db::database::cache::Lookup(const Slice &key, ircd::db::database::cache::Lookup(const Slice &key,
const CacheItemHelper *const helper, const CacheItemHelper *const helper,
@ -3355,7 +3362,9 @@ noexcept
auto *const &ret auto *const &ret
{ {
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER #if defined(IRCD_DB_HAS_CACHE_ASYNC)
c->Lookup(key, helper, cc, pri, statistics)
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
c->Lookup(key, helper, cc, pri, wait, statistics) c->Lookup(key, helper, cc, pri, wait, statistics)
#else #else
c->Lookup(key, s) c->Lookup(key, s)
@ -3559,6 +3568,20 @@ const noexcept
} }
#endif #endif
#if defined(IRCD_DB_HAS_CACHE_ASYNC)
rocksdb::Cache::Handle *
ircd::db::database::cache::CreateStandalone(const Slice &key,
ObjectPtr ptr,
const CacheItemHelper *const helper,
size_t charge,
bool allow_uncharged)
noexcept
{
assert(bool(c));
return c->CreateStandalone(key, ptr, helper, charge, allow_uncharged);
}
#endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// database::compaction_filter // database::compaction_filter

View file

@ -193,3 +193,9 @@
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR == 0 && ROCKSDB_PATCH >= 0) || (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR == 0 && ROCKSDB_PATCH >= 0)
#define IRCD_DB_HAS_CACHE_WRAPPER #define IRCD_DB_HAS_CACHE_WRAPPER
#endif #endif
#if ROCKSDB_MAJOR > 8 \
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR > 1) \
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR == 1 && ROCKSDB_PATCH >= 1)
#define IRCD_DB_HAS_CACHE_ASYNC
#endif

View file

@ -13,12 +13,12 @@
// //
template<> template<>
decltype(ircd::util::instance_list<ircd::versions>::allocator) decltype(ircd::versions::allocator)
ircd::util::instance_list<ircd::versions>::allocator ircd::util::instance_list<ircd::versions>::allocator
{}; {};
template<> template<>
decltype(ircd::util::instance_list<ircd::versions>::list) decltype(ircd::versions::list)
ircd::util::instance_list<ircd::versions>::list ircd::util::instance_list<ircd::versions>::list
{ {
allocator allocator

View file

@ -8,31 +8,25 @@
// copyright notice and this permission notice is present in all copies. The // copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file. // full license for this software is available in the LICENSE file.
namespace ircd::m [[gnu::visibility("hidden")]]
{ decltype(ircd::m::event::append::log)
extern const event::keys::exclude event_append_exclude_keys; ircd::m::event::append::log
extern const event::keys event_append_default_keys;
extern conf::item<std::string> event_append_exclude_types;
extern conf::item<bool> event_append_info;
extern log::log event_append_log;
}
decltype(ircd::m::event_append_log)
ircd::m::event_append_log
{ {
"m.event.append" "m.event.append"
}; };
decltype(ircd::m::event_append_info) [[gnu::visibility("hidden")]]
ircd::m::event_append_info decltype(ircd::m::event::append::info)
ircd::m::event::append::info
{ {
{ "name", "ircd.m.event.append.info" }, { "name", "ircd.m.event.append.info" },
{ "default", false }, { "default", false },
{ "persist", false }, { "persist", false },
}; };
decltype(ircd::m::event_append_exclude_types) [[gnu::visibility("hidden")]]
ircd::m::event_append_exclude_types decltype(ircd::m::event::append::exclude_types)
ircd::m::event::append::exclude_types
{ {
{ "name", "ircd.m.event.append.exclude.types" }, { "name", "ircd.m.event.append.exclude.types" },
{ "default", "org.matrix.dummy_event" }, { "default", "org.matrix.dummy_event" },
@ -42,8 +36,9 @@ ircd::m::event_append_exclude_types
/// to the client. This mask is applied only if the caller of event::append{} /// to the client. This mask is applied only if the caller of event::append{}
/// did not supply their mask to apply. It is also inferior to the user's /// did not supply their mask to apply. It is also inferior to the user's
/// filter if supplied. /// filter if supplied.
decltype(ircd::m::event_append_exclude_keys) [[gnu::visibility("hidden")]]
ircd::m::event_append_exclude_keys decltype(ircd::m::event::append::exclude_keys)
ircd::m::event::append::exclude_keys
{ {
"auth_events", "auth_events",
"hashes", "hashes",
@ -53,16 +48,17 @@ ircd::m::event_append_exclude_keys
"signatures", "signatures",
}; };
decltype(ircd::m::event_append_default_keys) [[gnu::visibility("hidden")]]
ircd::m::event_append_default_keys decltype(ircd::m::event::append::default_keys)
ircd::m::event::append::default_keys
{ {
event_append_exclude_keys event::append::exclude_keys
}; };
ircd::m::event::append::append(json::stack::array &array, bool
const event &event_, ircd::m::event::append::object(json::stack::array &array,
const event &event,
const opts &opts) const opts &opts)
:returns<bool>{[&]
{ {
assert(array.s); assert(array.s);
json::stack::checkpoint cp json::stack::checkpoint cp
@ -70,29 +66,24 @@ ircd::m::event::append::append(json::stack::array &array,
*array.s *array.s
}; };
json::stack::object object json::stack::object _object
{ {
array array
}; };
const bool ret const bool ret
{ {
append members(_object, event, opts)
{
object, event_, opts
}
}; };
cp.committing(ret); cp.committing(ret);
return ret; return ret;
}}
{
} }
ircd::m::event::append::append(json::stack::object &object, bool
const event &event, ircd::m::event::append::members(json::stack::object &out,
const opts &opts) const event &event,
:returns<bool>{[&] const opts &opts)
{ {
// Assertions that the event being appended has some required fields. This // Assertions that the event being appended has some required fields. This
// is a central butt-end test of data coming through the system to here. // is a central butt-end test of data coming through the system to here.
@ -101,150 +92,35 @@ ircd::m::event::append::append(json::stack::object &object,
assert(defined(json::get<"sender"_>(event))); assert(defined(json::get<"sender"_>(event)));
//assert(json::get<"origin_server_ts"_>(event)); //assert(json::get<"origin_server_ts"_>(event));
//assert(json::get<"origin_server_ts"_>(event) != json::undefined_number); //assert(json::get<"origin_server_ts"_>(event) != json::undefined_number);
#if defined(RB_DEBUG) if constexpr(RB_DEBUG_LEVEL)
if(unlikely(!defined(json::get<"type"_>(event)))) {
return false; if(unlikely(!defined(json::get<"type"_>(event))))
return false;
if(unlikely(!defined(json::get<"sender"_>(event)))) if(unlikely(!defined(json::get<"sender"_>(event))))
return false; return false;
#endif }
if(opts.event_filter && !m::match(*opts.event_filter, event)) if(opts.event_filter && !m::match(*opts.event_filter, event))
return false; return false;
const auto &not_types if(is_excluded(event, opts))
{
event_append_exclude_types
};
if(!opts.event_filter && token_exists(not_types, ' ', json::get<"type"_>(event)))
{
log::debug
{
log, "Not sending event %s because type '%s' excluded by configuration.",
string_view{event.event_id},
json::get<"type"_>(event),
};
return false; return false;
}
if(opts.query_visible && opts.user_id && !visible(event, *opts.user_id))
{
log::debug
{
log, "Not sending event %s because not visible to %s.",
string_view{event.event_id},
string_view{*opts.user_id},
};
if(is_invisible(event, opts))
return false; return false;
}
const bool has_event_idx
{
opts.event_idx && *opts.event_idx
};
const bool is_state
{
defined(json::get<"state_key"_>(event))
};
const bool query_redacted
{
has_event_idx &&
opts.query_redacted &&
!is_state &&
(!opts.room_depth || *opts.room_depth > json::get<"depth"_>(event))
};
if(query_redacted && m::redacted(*opts.event_idx))
{
log::debug
{
log, "Not sending event %s because redacted.",
string_view{event.event_id},
};
if(is_redacted(event, opts))
return false; return false;
}
const bool has_user if(is_ignored(event, opts))
{ return false;
opts.user_id && opts.user_room
};
const bool check_ignores
{
has_user && !is_state
};
if(check_ignores && *opts.user_id != json::get<"sender"_>(event))
{
const m::user::ignores ignores
{
*opts.user_id
};
if(ignores.enforce("events") && ignores.has(json::get<"sender"_>(event)))
{
log::debug
{
log, "Not sending event %s because %s is ignored by %s",
string_view{event.event_id},
json::get<"sender"_>(event),
string_view{*opts.user_id}
};
return false;
}
}
const bool sender_is_user
{
has_user && json::get<"sender"_>(event) == *opts.user_id
};
const bool has_client_txnid
{
opts.client_txnid && *opts.client_txnid
};
const auto txnid_idx
{
!has_client_txnid && sender_is_user && opts.query_txnid?
opts.user_room->get(std::nothrow, "ircd.client.txnid", event.event_id):
0UL
};
const bool query_prev_state
{
opts.query_prev_state && has_event_idx && is_state
};
const auto prev_state_idx
{
query_prev_state?
room::state::prev(*opts.event_idx):
0UL
};
#if defined(RB_DEBUG) && 0
if(!has_client_txnid && !txnid_idx && sender_is_user && opts.query_txnid)
log::dwarning
{
log, "Could not find transaction_id for %s from %s in %s",
string_view{event.event_id},
json::get<"sender"_>(event),
json::get<"room_id"_>(event)
};
#endif
// For v3+ events
if(!json::get<"event_id"_>(event)) if(!json::get<"event_id"_>(event))
json::stack::member json::stack::member
{ {
object, "event_id", event.event_id out, "event_id", event.event_id
}; };
// Get the list of properties to send to the client so we can strip // Get the list of properties to send to the client so we can strip
@ -254,11 +130,11 @@ ircd::m::event::append::append(json::stack::object &object,
{ {
opts.keys? opts.keys?
*opts.keys: *opts.keys:
event_append_default_keys default_keys
}; };
// Append the event members // Append the event members
for_each(event, [&keys, &object] for_each(event, [&keys, &out]
(const auto &key, const auto &val_) (const auto &key, const auto &val_)
{ {
if(!keys.has(key) && key != "redacts"_sv) if(!keys.has(key) && key != "redacts"_sv)
@ -274,70 +150,77 @@ ircd::m::event::append::append(json::stack::object &object,
json::stack::member json::stack::member
{ {
object, key, val out, key, val
}; };
return true; return true;
}); });
json::stack::object unsigned_ _unsigned(out, event, opts);
{
object, "unsigned"
};
const json::value age if(unlikely(info))
{ log::info
// When the opts give an explicit age, use it.
opts.age != std::numeric_limits<long>::min()?
opts.age:
// If we have depth information, craft a value based on the
// distance to the head depth; if this is 0 in riot the event will
// "stick" at the bottom of the timeline. This may be advantageous
// in the future but for now we make sure the result is non-zero.
json::get<"depth"_>(event) >= 0 && opts.room_depth && *opts.room_depth >= 0L?
(*opts.room_depth + 1 - json::get<"depth"_>(event)) + 1:
// We don't have depth information, so we use the origin_server_ts.
// It is bad if it conflicts with other appends in the room which
// did have depth information.
!opts.room_depth && json::get<"origin_server_ts"_>(event)?
ircd::time<milliseconds>() - json::get<"origin_server_ts"_>(event):
// Finally, this special value will eliminate the age altogether
// during serialization.
json::undefined_number
};
json::stack::member
{
unsigned_, "age", age
};
if(has_client_txnid)
json::stack::member
{ {
unsigned_, "transaction_id", *opts.client_txnid log, "%s %s idx:%lu in %s depth:%ld txnid:%s %s,%s",
string_view{opts.user_id},
string_view{event.event_id},
opts.event_idx,
json::get<"room_id"_>(event),
json::get<"depth"_>(event),
opts.client_txnid,
json::get<"type"_>(event),
json::get<"state_key"_>(event),
}; };
if(txnid_idx) return true;
m::get(std::nothrow, txnid_idx, "content", [&unsigned_] }
(const json::object &content)
{ void
json::stack::member ircd::m::event::append::_unsigned(json::stack::object &out,
{ const event &event,
unsigned_, "transaction_id", unquote(content.get("transaction_id")) const opts &opts)
}; {
}); json::stack::object object
{
out, "unsigned"
};
_age(object, event, opts);
_txnid(object, event, opts);
_relations(object, event, opts);
if(defined(json::get<"state_key"_>(event)))
_prev_state(object, event, opts);
}
void
ircd::m::event::append::_prev_state(json::stack::object &out,
const event &event,
const opts &opts)
{
assert(defined(json::get<"state_key"_>(event)));
const bool query_prev_state
{
true
&& opts.event_idx
&& opts.query_prev_state
};
const auto prev_state_idx
{
query_prev_state?
room::state::prev(opts.event_idx):
0UL
};
if(prev_state_idx) if(prev_state_idx)
{ {
m::get(std::nothrow, prev_state_idx, "content", [&unsigned_] m::get(std::nothrow, prev_state_idx, "content", [&out]
(const json::object &content) (const json::object &content)
{ {
json::stack::member json::stack::member
{ {
unsigned_, "prev_content", content out, "prev_content", content
}; };
}); });
@ -348,7 +231,7 @@ ircd::m::event::append::append(json::stack::object &object,
json::stack::member json::stack::member
{ {
unsigned_, "replaces_state", json::value out, "replaces_state", json::value
{ {
replaces_state_id? replaces_state_id?
string_view{replaces_state_id}: string_view{replaces_state_id}:
@ -356,24 +239,279 @@ ircd::m::event::append::append(json::stack::object &object,
} }
}; };
} }
}
if(unlikely(event_append_info)) void
log::info ircd::m::event::append::_txnid(json::stack::object &out,
const event &event,
const opts &opts)
{
const bool sender_is_user
{
json::get<"sender"_>(event) == opts.user_id
};
const bool query_txnid
{
true
&& !opts.client_txnid
&& opts.query_txnid
&& opts.user_room_id
&& sender_is_user
};
const auto txnid_idx
{
query_txnid?
m::room(opts.user_room_id).get(std::nothrow, "ircd.client.txnid", event.event_id):
0UL
};
if constexpr(RB_DEBUG_LEVEL)
{
const bool missing_txnid
{ {
event_append_log, "%s %s idx:%lu in %s depth:%ld txnid:%s idx:%lu age:%ld %s,%s", true
opts.user_id? string_view{*opts.user_id} : string_view{}, && !opts.client_txnid
string_view{event.event_id}, && !txnid_idx
opts.event_idx? *opts.event_idx : 0UL, && sender_is_user
json::get<"room_id"_>(event), && opts.query_txnid
json::get<"depth"_>(event),
has_client_txnid? *opts.client_txnid : string_view{},
txnid_idx,
int64_t(age),
json::get<"type"_>(event),
json::get<"state_key"_>(event),
}; };
return true; if(unlikely(missing_txnid))
}} log::dwarning
{ {
log, "Could not find transaction_id for %s from %s in %s",
string_view{event.event_id},
json::get<"sender"_>(event),
json::get<"room_id"_>(event)
};
}
if(opts.client_txnid)
json::stack::member
{
out, "transaction_id", opts.client_txnid
};
else if(txnid_idx)
m::get(std::nothrow, txnid_idx, "content", [&out]
(const json::object &content)
{
json::stack::member
{
out, "transaction_id", content.get("transaction_id")
};
});
}
void
ircd::m::event::append::_age(json::stack::object &out,
const event &event,
const opts &opts)
{
const json::value age
{
// When the opts give an explicit age, use it.
opts.age != std::numeric_limits<long>::min()?
opts.age:
// If we have depth information, craft a value based on the
// distance to the head depth; if this is 0 in riot the event will
// "stick" at the bottom of the timeline. This may be advantageous
// in the future but for now we make sure the result is non-zero.
json::get<"depth"_>(event) >= 0 && opts.room_depth >= 0L?
(opts.room_depth + 1 - json::get<"depth"_>(event)) + 1:
// We don't have depth information, so we use the origin_server_ts.
// It is bad if it conflicts with other appends in the room which
// did have depth information.
opts.room_depth < 0 && json::get<"origin_server_ts"_>(event)?
ircd::time<milliseconds>() - json::get<"origin_server_ts"_>(event):
// Finally, this special value will eliminate the age altogether
// during serialization.
json::undefined_number
};
json::stack::member
{
out, "age", age
};
}
void
ircd::m::event::append::_relations(json::stack::object &out,
const event &event,
const opts &opts)
{
assert(out.s);
json::stack::checkpoint cp
{
*out.s, false
};
json::stack::object object
{
out, "m.relations"
};
bool commit
{
cp.committing()
};
if(opts.bundle_all || opts.bundle_replace)
commit |= bundle_replace(object, event, opts);
cp.committing(commit);
}
bool
ircd::m::event::append::bundle_replace(json::stack::object &out,
const event &event,
const opts &opts)
{
const m::replaced replaced
{
opts.event_idx, m::replaced::latest
};
const event::idx &replace_idx
{
replaced
};
if(likely(!replace_idx))
return false;
const m::event::fetch replace
{
std::nothrow, replace_idx
};
if(unlikely(!replace.valid))
return false;
json::stack::object object
{
out, "m.replace"
};
object.append(replace);
return true;
}
bool
ircd::m::event::append::is_excluded(const event &event,
const opts &opts)
const
{
const auto &not_types
{
exclude_types
};
const bool ret
{
true
&& !opts.event_filter
&& token_exists(not_types, ' ', json::get<"type"_>(event))
};
if(ret)
log::debug
{
log, "Not sending event %s because type '%s' excluded by configuration.",
string_view{event.event_id},
json::get<"type"_>(event),
};
return ret;
}
bool
ircd::m::event::append::is_invisible(const event &event,
const opts &opts)
const
{
const bool ret
{
true
&& opts.query_visible
&& opts.user_id
&& !visible(event, opts.user_id)
};
if(ret)
log::debug
{
log, "Not sending event %s because not visible to %s.",
string_view{event.event_id},
string_view{opts.user_id},
};
return ret;
}
bool
ircd::m::event::append::is_redacted(const event &event,
const opts &opts)
const
{
const bool ret
{
true
&& opts.event_idx
&& opts.query_redacted
&& !defined(json::get<"state_key"_>(event))
&& opts.room_depth > json::get<"depth"_>(event)
&& m::redacted(opts.event_idx)
};
if(ret)
log::debug
{
log, "Not sending event %s because redacted.",
string_view{event.event_id},
};
return ret;
}
bool
ircd::m::event::append::is_ignored(const event &event,
const opts &opts)
const
{
const bool check_ignores
{
true
&& !defined(json::get<"state_key"_>(event))
&& opts.user_id
&& opts.user_room_id
&& opts.user_id != json::get<"sender"_>(event)
};
if(!check_ignores)
return false;
const m::user::ignores ignores
{
opts.user_id
};
if(ignores.enforce("events") && ignores.has(json::get<"sender"_>(event)))
{
log::debug
{
log, "Not sending event %s because %s is ignored by %s",
string_view{event.event_id},
json::get<"sender"_>(event),
string_view{opts.user_id}
};
return true;
}
return false;
} }

View file

@ -8,6 +8,13 @@
// copyright notice and this permission notice is present in all copies. The // copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file. // full license for this software is available in the LICENSE file.
decltype(ircd::m::relates::latest_column)
ircd::m::relates::latest_column
{
{ "name", "ircd.m.relates.latest_column" },
{ "default", "origin_server_ts" },
};
bool bool
ircd::m::relates::prefetch(const string_view &type) ircd::m::relates::prefetch(const string_view &type)
const const
@ -21,8 +28,8 @@ const
refs.for_each(dbs::ref::M_RELATES, [this, &ret] refs.for_each(dbs::ref::M_RELATES, [this, &ret]
(const auto &event_idx, const auto &) (const auto &event_idx, const auto &)
{ {
if(this->prefetch_depth) if(this->prefetch_latest)
ret |= m::prefetch(event_idx, "depth"); ret |= m::prefetch(event_idx, string_view{latest_column});
if(this->prefetch_sender || this->match_sender) if(this->prefetch_sender || this->match_sender)
ret |= m::prefetch(event_idx, "sender"); ret |= m::prefetch(event_idx, "sender");
@ -87,6 +94,11 @@ ircd::m::relates::latest(const string_view &type,
uint *const at) uint *const at)
const const
{ {
const string_view &column
{
latest_column
};
if(at) if(at)
*at = -1; *at = -1;
@ -97,10 +109,10 @@ const
(const event::idx &event_idx, const json::object &, const m::relates_to &) (const event::idx &event_idx, const json::object &, const m::relates_to &)
noexcept noexcept
{ {
int64_t depth{0}; int64_t val{0};
if((depth = m::get(std::nothrow, event_idx, "depth", depth)) > best) if((val = m::get(std::nothrow, event_idx, column, val)) > best)
{ {
best = depth; best = val;
ret = event_idx; ret = event_idx;
if(at) if(at)

View file

@ -246,6 +246,7 @@ const
}; };
attach_sigs(user_sigs, device_keys, user_id); attach_sigs(user_sigs, device_keys, user_id);
const m::room::state state const m::room::state state
{ {
user_room user_room
@ -299,12 +300,29 @@ const
out, "signatures" out, "signatures"
}; };
// signatures of the key's owner
assert(user_room.user.user_id);
append_sigs(sigs, device_keys, user_room.user.user_id);
// signatures of a cross-signer
assert(user_id);
if(user_id != user_room.user.user_id)
append_sigs(sigs, device_keys, user_id);
}
void
ircd::m::user::keys::append_sigs(json::stack::object &out,
const json::object &device_keys,
const user::id &user_id)
const
{
json::stack::object user_sigs json::stack::object user_sigs
{ {
sigs, user_id out, user_id
}; };
attach_sigs(user_sigs, device_keys, user_id); attach_sigs(user_sigs, device_keys, user_id);
const json::object device_keys_keys const json::object device_keys_keys
{ {
device_keys["keys"] device_keys["keys"]
@ -318,6 +336,11 @@ const
state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_keys_keys] state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_keys_keys]
(const string_view &, const string_view &state_key, const auto &event_idx) (const string_view &, const string_view &state_key, const auto &event_idx)
{ {
const auto &[target, source]
{
unmake_sigs_state_key(state_key)
};
for(const auto &[key_id_, key] : device_keys_keys) for(const auto &[key_id_, key] : device_keys_keys)
{ {
const auto &key_id const auto &key_id
@ -325,11 +348,6 @@ const
split(key_id_, ':').second split(key_id_, ':').second
}; };
const auto &[target, source]
{
unmake_sigs_state_key(state_key)
};
if(target != key_id) if(target != key_id)
continue; continue;

View file

@ -376,10 +376,10 @@ append_event(json::stack::array &out,
{ {
out, event, out, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
}, },
}; };
} }

View file

@ -228,9 +228,29 @@ recv_responses(const host_users_map &map,
} }
// remote handle // remote handle
for(auto &[remote, request] : queries) while(!queries.empty())
{ {
assert(!failures.count(remote)); auto next
{
ctx::when_any(begin(queries), end(queries), []
(auto &it) -> m::fed::user::keys::claim &
{
return it->second;
})
};
const bool ok
{
next.wait_until(timeout, std::nothrow)
};
const auto it(next.get());
const unwind remove{[&queries, &it]
{
queries.erase(it);
}};
auto &[remote, request] {*it};
recv_response(remote, request, failures, one_time_keys, timeout); recv_response(remote, request, failures, one_time_keys, timeout);
} }
} }

View file

@ -48,19 +48,21 @@ ircd::m::post_keys_signatures_upload(client &client,
user::tokens::device(std::nothrow, request.access_token) user::tokens::device(std::nothrow, request.access_token)
}; };
for(const auto &[user_id, device_keys_] : request) for(const auto &[user_id_, device_keys_] : request)
{ {
if(!valid(m::id::USER, user_id))
continue;
const json::object device_keys const json::object device_keys
{ {
device_keys_ device_keys_
}; };
const m::user::id user_id
{
user_id_
};
const user::room user_room const user::room user_room
{ {
user::id{user_id} user_id
}; };
for(const auto &[tgt_id, keys] : device_keys) for(const auto &[tgt_id, keys] : device_keys)
@ -71,7 +73,7 @@ ircd::m::post_keys_signatures_upload(client &client,
user::keys::make_sigs_state_key(state_key_buf, tgt_id, src_dev) user::keys::make_sigs_state_key(state_key_buf, tgt_id, src_dev)
}; };
send(user_room, user_id, "ircd.keys.signatures", state_key, keys); send(user_room, request.user_id, "ircd.keys.signatures", state_key, keys);
} }
} }

View file

@ -206,7 +206,7 @@ ircd::m::get_notifications(client &client,
{ {
event_object, event, event_object, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.keys = &notification_event_keys, .keys = &notification_event_keys,
//.query_txnid = false, //.query_txnid = false,
//.query_prev_state = false, //.query_prev_state = false,

View file

@ -132,10 +132,10 @@ get__context(client &client,
{ {
_event, event, _event, event,
{ {
.event_idx = &event.event_idx, .event_idx = event.event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
} }
}; };
} }
@ -179,10 +179,10 @@ get__context(client &client,
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
.query_txnid = true, .query_txnid = true,
} }
}; };
@ -233,10 +233,10 @@ get__context(client &client,
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
.query_txnid = true, .query_txnid = true,
} }
}; };
@ -301,10 +301,10 @@ get__context(client &client,
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
.query_txnid = false, .query_txnid = false,
} }
}; };

View file

@ -46,8 +46,32 @@ get__event(client &client,
event_id, fopts event_id, fopts
}; };
const unique_mutable_buffer buf
{
m::event::MAX_SIZE
};
json::stack out{buf};
{
json::stack::object top{out};
m::event::append
{
top, event,
{
.event_idx = event.event_idx,
.user_id = request.user_id,
.query_prev_state = false,
.query_redacted = false,
.query_visible = false,
}
};
};
return m::resource::response return m::resource::response
{ {
client, event.source client, json::object
{
out.completed()
}
}; };
} }

View file

@ -202,10 +202,10 @@ get__initialsync_local(client &client,
{ {
state, state_event, state, state_event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user.user_id, .user_id = user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
.query_txnid = false, .query_txnid = false,
} }
}; };
@ -255,14 +255,17 @@ get__initialsync_local(client &client,
if(!visible(event, user.user_id)) if(!visible(event, user.user_id))
continue; continue;
m::event::append(chunk, event, m::event::append
{ {
.event_idx = &event_idx, chunk, event,
.user_id = &user.user_id, {
.user_room = &user_room, .event_idx = event_idx,
.room_depth = &room_depth, .user_id = user.user_id,
.query_txnid = true, .user_room_id = user_room.room_id,
}); .room_depth = room_depth,
.query_txnid = true,
}
};
} }
} }

View file

@ -165,8 +165,8 @@ get__members(client &client,
{ {
chunk, event, chunk, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &request.user_id, .user_id = request.user_id,
.query_txnid = false, .query_txnid = false,
.query_prev_state = false, .query_prev_state = false,
.query_redacted = false, .query_redacted = false,

View file

@ -152,10 +152,10 @@ get__messages(client &client,
{ {
chunk, event, chunk, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &user_room.user.user_id, .user_id = user_room.user.user_id,
.user_room = &user_room, .user_room_id = user_room.room_id,
.room_depth = &room_depth, .room_depth = room_depth,
} }
} }
}; };

View file

@ -174,8 +174,8 @@ relations_chunk_append(client &client,
{ {
chunk, event, chunk, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &request.user_id, .user_id = request.user_id,
.query_txnid = false, .query_txnid = false,
} }
}; };

View file

@ -206,8 +206,8 @@ append_event(const m::resource::request &request,
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &request.user_id, .user_id = request.user_id,
.query_txnid = false, .query_txnid = false,
.query_prev_state = false, .query_prev_state = false,
.query_redacted = false, .query_redacted = false,

View file

@ -550,8 +550,8 @@ try
{ {
result_event, event, result_event, event,
{ {
.event_idx = &result.event_idx, .event_idx = result.event_idx,
.user_id = &query.user_id, .user_id = query.user_id,
.event_filter = &event_filter, .event_filter = &event_filter,
.query_prev_state = false, .query_prev_state = false,
.query_visible = true, .query_visible = true,
@ -599,8 +599,8 @@ try
{ {
events_before, event, events_before, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &query.user_id, .user_id = query.user_id,
.event_filter = &event_filter, .event_filter = &event_filter,
.query_prev_state = false, .query_prev_state = false,
.query_visible = true, .query_visible = true,
@ -628,8 +628,8 @@ try
{ {
events_after, event, events_after, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &query.user_id, .user_id = query.user_id,
.event_filter = &event_filter, .event_filter = &event_filter,
.query_prev_state = false, .query_prev_state = false,
.query_visible = true, .query_visible = true,

View file

@ -221,10 +221,10 @@ ircd::m::sync::room_state_linear_events(data &data)
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
.query_txnid = false, .query_txnid = false,
.query_prev_state = true, .query_prev_state = true,
} }
@ -265,10 +265,10 @@ ircd::m::sync::room_state_linear_events(data &data)
{ {
array, *data.event, array, *data.event,
{ {
.event_idx = &data.event_idx, .event_idx = data.event_idx,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
.query_txnid = false, .query_txnid = false,
.query_prev_state = true, .query_prev_state = true,
} }
@ -377,10 +377,10 @@ ircd::m::sync::room_state_polylog_events(data &data)
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
.query_txnid = false, .query_txnid = false,
.query_prev_state = false, .query_prev_state = false,
} }
@ -517,10 +517,10 @@ ircd::m::sync::room_state_phased_events(data &data)
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
.query_txnid = false, .query_txnid = false,
.query_prev_state = true, .query_prev_state = true,
} }
@ -664,10 +664,10 @@ ircd::m::sync::room_state_phased_member_events(data &data,
{ {
array, event, array, event,
{ {
.event_idx = &sender_idx, .event_idx = sender_idx,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
.query_txnid = false, .query_txnid = false,
.query_prev_state = false, .query_prev_state = false,
} }

View file

@ -212,11 +212,11 @@ ircd::m::sync::room_timeline_linear(data &data)
{ {
array, *data.event, array, *data.event,
{ {
.event_idx = &data.event_idx, .event_idx = data.event_idx,
.client_txnid = &data.client_txnid, .client_txnid = data.client_txnid,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
} }
}; };
} }
@ -289,11 +289,11 @@ ircd::m::sync::_room_timeline_linear_command(data &data)
{ {
array, *data.event, array, *data.event,
{ {
.event_idx = &data.event_idx, .event_idx = data.event_idx,
.client_txnid = &data.client_txnid, .client_txnid = data.client_txnid,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
} }
}; };
} }
@ -400,11 +400,11 @@ ircd::m::sync::_room_timeline_polylog_events(data &data,
{ {
array, event, array, event,
{ {
.event_idx = &event_idx, .event_idx = event_idx,
.client_txnid = &data.client_txnid, .client_txnid = data.client_txnid,
.user_id = &data.user.user_id, .user_id = data.user.user_id,
.user_room = &data.user_room, .user_room_id = data.user_room.room_id,
.room_depth = &data.room_depth, .room_depth = data.room_depth,
} }
}; };
} }

View file

@ -43,17 +43,17 @@ get_user(client &client,
url::decode(user_id, request.parv[0]) url::decode(user_id, request.parv[0])
}; };
if(request.user_id != user_id)
throw m::UNSUPPORTED
{
"Getting user data as someone else is not yet supported"
};
const string_view &cmd const string_view &cmd
{ {
request.parv[1] request.parv[1]
}; };
if(request.user_id != user_id && !request.bridge_id)
throw m::UNSUPPORTED
{
"Getting user data as someone else is only for bridges."
};
if(cmd == "filter") if(cmd == "filter")
return get__filter(client, request, user_id); return get__filter(client, request, user_id);
@ -93,10 +93,10 @@ post_user(client &client,
url::decode(user_id, request.parv[0]) url::decode(user_id, request.parv[0])
}; };
if(request.user_id != user_id) if(request.user_id != user_id && !request.bridge_id)
throw m::UNSUPPORTED throw m::UNSUPPORTED
{ {
"Posting user data as someone else is not yet supported" "Posting user data as someone else is only for bridges."
}; };
const string_view &cmd const string_view &cmd
@ -140,10 +140,10 @@ put_user(client &client,
url::decode(user_id, request.parv[0]) url::decode(user_id, request.parv[0])
}; };
if(request.user_id != user_id) if(request.user_id != user_id && !request.bridge_id)
throw m::UNSUPPORTED throw m::UNSUPPORTED
{ {
"Putting user data as someone else is not yet supported" "Putting user data as someone else is only for bridges."
}; };
if(request.parv.size() < 2) if(request.parv.size() < 2)
@ -193,10 +193,10 @@ delete_user(client &client,
url::decode(user_id, request.parv[0]) url::decode(user_id, request.parv[0])
}; };
if(request.user_id != user_id) if(request.user_id != user_id && !request.bridge_id)
throw m::UNSUPPORTED throw m::UNSUPPORTED
{ {
"Deleting user data as someone else is not yet supported" "Deleting user data as someone else is only for bridges."
}; };
if(request.parv.size() < 2) if(request.parv.size() < 2)

View file

@ -677,14 +677,15 @@ ircd::m::bridge::append(const config &config,
const event::idx &event_idx, const event::idx &event_idx,
const event &event) const event &event)
{ {
event::append::opts opts;
opts.event_idx = &event_idx;
opts.query_txnid = false;
opts.query_prev_state = true;
opts.query_redacted = false;
event::append event::append
{ {
events, event, opts events, event, event::append::opts
{
.event_idx = event_idx,
.query_txnid = false,
.query_prev_state = true,
.query_redacted = false,
},
}; };
log::debug log::debug

View file

@ -174,6 +174,10 @@ static bool
github_handle__push(std::ostream &, github_handle__push(std::ostream &,
const json::object &content); const json::object &content);
static bool
github_post_handle__push(const m::event::id &,
const json::object &content);
static bool static bool
github_handle__pull_request(std::ostream &, github_handle__pull_request(std::ostream &,
const json::object &content); const json::object &content);
@ -355,6 +359,9 @@ github_handle(client &client,
m::msghtml(room_id, user_id, view(out, buf[0]), view(alt, buf[1]), "m.notice") m::msghtml(room_id, user_id, view(out, buf[0]), view(alt, buf[1]), "m.notice")
}; };
if(type == "push")
github_post_handle__push(evid, request.content);
log::info log::info
{ {
"Webhook [%s] '%s' delivered to %s %s", "Webhook [%s] '%s' delivered to %s %s",
@ -933,6 +940,33 @@ github_hook_shot_retry(const string_view &repo,
); );
} }
static bool
github_hook_shot_for_each_fail(const string_view &repo,
const string_view &hook,
const function_bool<json::object> &closure)
{
bool ret {true};
github_hook_shot_for_each(repo, hook, true, [&ret, &closure]
(const json::object &object)
{
if(object.at<bool>("redelivery"))
return true;
const json::string status
{
object["status"]
};
if(status == "OK")
return false;
ret = closure(object);
return ret;
});
return ret;
}
static bool static bool
github_run_for_each_jobs(const string_view &repo, github_run_for_each_jobs(const string_view &repo,
const string_view &run_id, const string_view &run_id,
@ -992,6 +1026,59 @@ github_run_rerun_failed(const string_view &repo,
github_request(buf, "POST", repo, "actions/runs/%s/rerun-failed-jobs", run_id); github_request(buf, "POST", repo, "actions/runs/%s/rerun-failed-jobs", run_id);
} }
static void
github_run_dispatch(const string_view &repo,
const string_view &name,
const string_view &ref = "master",
const json::members inputs = {})
{
const json::strung content{json::members
{
{ "ref", ref },
{ "inputs", inputs },
}};
unique_const_buffer buf;
github_request(content, buf, "POST", repo, "actions/workflows/%s/dispatches", name);
}
static bool
github_flow_for_each(const string_view &repo,
const function_bool<json::object> &closure,
const bool active_only = true)
{
unique_const_buffer buf;
const json::object response
{
github_request
(
//TODO: pagination token
buf, "GET", repo, "actions/workflows?per_page=100&page=1"
)
};
const json::array workflows
{
response["workflows"]
};
for(const json::object flow : workflows)
{
const json::string state
{
flow["state"]
};
if(active_only && state != "active")
continue;
if(!closure(flow))
return false;
}
return true;
}
bool bool
github_handle__workflow_run(std::ostream &out, github_handle__workflow_run(std::ostream &out,
std::ostream &alt, std::ostream &alt,
@ -1511,40 +1598,77 @@ try
json::object{relates_content}.get("body") json::object{relates_content}.get("body")
}; };
if(!startswith(relates_body, "job status table ")) if(startswith(relates_body, "job status table "))
return;
const auto suffix
{ {
lstrip(relates_body, "job status table ") const auto suffix
}; {
lstrip(relates_body, "job status table ")
};
string_view token[3]; string_view token[3];
ircd::tokens(suffix, ' ', token); ircd::tokens(suffix, ' ', token);
const auto &[repopath, run_id, attempt] {token}; const auto &[repopath, run_id, attempt] {token};
assert(repopath); assert(repopath);
assert(run_id); assert(run_id);
if(!repopath || !run_id) if(!repopath || !run_id)
return; return;
switch(hash(key)) switch(hash(key))
{
case hash("🚮"_sv):
github_run_delete(repopath, run_id);
m::redact(room, user_id, relates_event_id, "deleted");
break;
case hash(""_sv):
github_run_cancel(repopath, run_id);
break;
case hash("🔄"_sv):
github_run_rerun_failed(repopath, run_id);
break;
case hash("↪️"_sv):
github_run_rerun(repopath, run_id);
break;
}
}
else if(startswith(relates_body, "push by "))
{ {
case hash("🚮"_sv): const auto suffix
github_run_delete(repopath, run_id); {
m::redact(room, user_id, relates_event_id, "deleted"); lstrip(relates_body, "push by ")
break; };
case hash(""_sv): string_view token[5];
github_run_cancel(repopath, run_id); ircd::tokens(suffix, ' ', token);
break; const auto &[pusher, _by_, repo, _at_, commit] {token};
assert(pusher);
assert(repo);
assert(commit);
if(!repo)
return;
case hash("🔄"_sv): if(startswith(key, "▶️"))
github_run_rerun_failed(repopath, run_id); {
break; const auto id
{
between(key, '(', ')')
};
case hash("↪️"_sv): const auto ref
github_run_rerun(repopath, run_id); {
break; // commit // hash not supported by github
"master"
};
github_run_dispatch(repo, id, ref, json::members
{
});
return;
}
} }
} }
catch(const ctx::interrupted &) catch(const ctx::interrupted &)
@ -1864,6 +1988,54 @@ github_handle__push(std::ostream &out,
return true; return true;
} }
static bool
github_post_handle__push(const m::event::id &push_event_id,
const json::object &content)
{
const m::user::id::buf _webhook_user
{
string_view{webhook_user}, my_host()
};
const auto _webhook_room
{
m::room_id(string_view(webhook_room))
};
const auto repo
{
github_repopath(content)
};
github_flow_for_each(repo, [&]
(const json::object &flow)
{
const json::string name
{
flow["name"]
};
const json::string id
{
flow["id"]
};
const fmt::bsprintf<128> key
{
"▶️ %s (%s)", name, id
};
const auto annote_id
{
m::annotate(_webhook_room, _webhook_user, push_event_id, string_view(key))
};
return true;
});
return true;
}
bool bool
github_handle__pull_request(std::ostream &out, github_handle__pull_request(std::ostream &out,
const json::object &content) const json::object &content)