2018-02-04 03:22:01 +01:00
// 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.
2017-12-12 21:33:14 +01:00
2019-05-04 00:34:56 +02:00
///////////////////////////////////////////////////////////////////////////////
//
// event/pretty.h
//
std : : string
ircd : : m : : pretty ( const event & event )
{
std : : string ret ;
std : : stringstream s ;
pubsetbuf ( s , ret , 4096 ) ;
pretty ( s , event ) ;
resizebuf ( s , ret ) ;
return ret ;
}
std : : ostream &
ircd : : m : : pretty ( std : : ostream & s ,
const event & event )
{
const auto out { [ & s ]
( const string_view & key , auto & & val )
{
if ( defined ( json : : value ( val ) ) )
s < < std : : setw ( 16 ) < < std : : right < < key < < " : " < < val < < std : : endl ;
} } ;
const string_view top_keys [ ]
{
" origin " ,
" event_id " ,
" room_id " ,
" sender " ,
" type " ,
" depth " ,
" state_key " ,
" redacts " ,
} ;
json : : for_each ( event , top_keys , out ) ;
const auto & ts { json : : get < " origin_server_ts " _ > ( event ) } ;
{
thread_local char buf [ 128 ] ;
s < < std : : setw ( 16 ) < < std : : right < < " origin_server_ts " < < " : "
< < timef ( buf , ts / 1000L , ircd : : localtime )
< < " ( " < < ts < < " ) "
< < std : : endl ;
}
const json : : object & contents { json : : get < " content " _ > ( event ) } ;
if ( ! contents . empty ( ) )
s < < std : : setw ( 16 ) < < std : : right < < " content " < < " : "
< < size ( contents ) < < " keys; "
< < size ( string_view { contents } ) < < " bytes. "
< < std : : endl ;
const auto & hashes { json : : get < " hashes " _ > ( event ) } ;
for ( const auto & hash : hashes )
{
s < < std : : setw ( 16 ) < < std : : right < < " [hash] " < < " : "
< < hash . first
< < " "
< < unquote ( hash . second )
< < std : : endl ;
}
const auto & signatures { json : : get < " signatures " _ > ( event ) } ;
for ( const auto & signature : signatures )
{
s < < std : : setw ( 16 ) < < std : : right < < " [signature] " < < " : "
< < signature . first < < " " ;
for ( const auto & key : json : : object { signature . second } )
s < < key . first < < " " ;
s < < std : : endl ;
}
const auto & auth_events { json : : get < " auth_events " _ > ( event ) } ;
for ( const json : : array auth_event : auth_events )
{
s < < std : : setw ( 16 ) < < std : : right < < " [auth event] "
< < " : " < < unquote ( auth_event [ 0 ] ) ;
for ( const auto & hash : json : : object { auth_event [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
const auto & prev_states { json : : get < " prev_state " _ > ( event ) } ;
for ( const json : : array prev_state : prev_states )
{
s < < std : : setw ( 16 ) < < std : : right < < " [prev state] "
< < " : " < < unquote ( prev_state [ 0 ] ) ;
for ( const auto & hash : json : : object { prev_state [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
const auto & prev_events { json : : get < " prev_events " _ > ( event ) } ;
for ( const json : : array prev_event : prev_events )
{
s < < std : : setw ( 16 ) < < std : : right < < " [prev_event] "
< < " : " < < unquote ( prev_event [ 0 ] ) ;
for ( const auto & hash : json : : object { prev_event [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
if ( ! contents . empty ( ) )
for ( const json : : object : : member & content : contents )
s < < std : : setw ( 16 ) < < std : : right < < " [content] " < < " : "
< < std : : setw ( 7 ) < < std : : left < < reflect ( json : : type ( content . second ) ) < < " "
< < std : : setw ( 5 ) < < std : : right < < size ( string_view { content . second } ) < < " bytes "
< < ' : ' < < content . first
< < std : : endl ;
return s ;
}
std : : string
ircd : : m : : pretty_oneline ( const event & event ,
const bool & content_keys )
{
std : : string ret ;
std : : stringstream s ;
pubsetbuf ( s , ret , 4096 ) ;
pretty_oneline ( s , event , content_keys ) ;
resizebuf ( s , ret ) ;
return ret ;
}
std : : ostream &
ircd : : m : : pretty_oneline ( std : : ostream & s ,
const event & event ,
const bool & content_keys )
{
if ( defined ( json : : get < " room_id " _ > ( event ) ) )
s < < json : : get < " room_id " _ > ( event ) < < " " ;
else
s < < " * " ;
if ( json : : get < " depth " _ > ( event ) ! = json : : undefined_number )
s < < json : : get < " depth " _ > ( event ) < < " " ;
else
s < < " * " ;
thread_local char sdbuf [ 48 ] ;
if ( json : : get < " origin_server_ts " _ > ( event ) ! = json : : undefined_number )
s < < smalldate ( sdbuf , json : : get < " origin_server_ts " _ > ( event ) / 1000L ) < < " " ;
else
s < < " * " ;
if ( defined ( json : : get < " origin " _ > ( event ) ) )
s < < ' : ' < < json : : get < " origin " _ > ( event ) < < " " ;
else
s < < " :* " ;
if ( defined ( json : : get < " sender " _ > ( event ) ) )
s < < json : : get < " sender " _ > ( event ) < < " " ;
else
s < < " * " ;
if ( defined ( json : : get < " event_id " _ > ( event ) ) )
s < < json : : get < " event_id " _ > ( event ) < < " " ;
else
s < < " * " ;
const auto & auth_events { json : : get < " auth_events " _ > ( event ) } ;
s < < " A: " < < auth_events . count ( ) < < " " ;
const auto & prev_states { json : : get < " prev_state " _ > ( event ) } ;
s < < " S: " < < prev_states . count ( ) < < " " ;
const auto & prev_events { json : : get < " prev_events " _ > ( event ) } ;
s < < " E: " < < prev_events . count ( ) < < " " ;
const auto & hashes { json : : get < " hashes " _ > ( event ) } ;
s < < " [ " ;
for ( const auto & hash : hashes )
s < < hash . first < < " " ;
s < < " ] " ;
const auto & signatures { json : : get < " signatures " _ > ( event ) } ;
s < < " [ " ;
for ( const auto & signature : signatures )
{
s < < signature . first < < " [ " ;
for ( const auto & key : json : : object { signature . second } )
s < < key . first < < " " ;
s < < " ] " ;
}
s < < " ] " ;
if ( defined ( json : : get < " type " _ > ( event ) ) )
s < < json : : get < " type " _ > ( event ) < < " " ;
else
s < < " * " ;
const auto & state_key
{
json : : get < " state_key " _ > ( event )
} ;
if ( defined ( state_key ) & & empty ( state_key ) )
s < < " \" \" " < < " " ;
else if ( defined ( state_key ) )
s < < state_key < < " " ;
else
s < < " * " < < " " ;
const string_view & membership
{
json : : get < " type " _ > ( event ) = = " m.room.member " ?
m : : membership ( event ) :
" * " _sv
} ;
s < < membership < < " " ;
if ( defined ( json : : get < " redacts " _ > ( event ) ) )
s < < json : : get < " redacts " _ > ( event ) < < " " ;
else
s < < " * " ;
const json : : object & contents
{
content_keys ?
json : : get < " content " _ > ( event ) :
json : : object { }
} ;
if ( ! contents . empty ( ) )
{
s < < " + " < < string_view { contents } . size ( ) < < " bytes : " ;
for ( const auto & content : contents )
s < < content . first < < " " ;
}
return s ;
}
std : : string
ircd : : m : : pretty_msgline ( const event & event )
{
std : : string ret ;
std : : stringstream s ;
pubsetbuf ( s , ret , 4096 ) ;
pretty_msgline ( s , event ) ;
resizebuf ( s , ret ) ;
return ret ;
}
std : : ostream &
ircd : : m : : pretty_msgline ( std : : ostream & s ,
const event & event )
{
s < < json : : get < " depth " _ > ( event ) < < " : " ;
s < < json : : get < " type " _ > ( event ) < < " " ;
s < < json : : get < " sender " _ > ( event ) < < " " ;
s < < json : : get < " event_id " _ > ( event ) < < " " ;
const auto & state_key
{
json : : get < " state_key " _ > ( event )
} ;
if ( defined ( state_key ) & & empty ( state_key ) )
s < < " \" \" " < < " " ;
else if ( defined ( state_key ) )
s < < state_key < < " " ;
else
s < < " * " < < " " ;
const json : : object & content
{
json : : get < " content " _ > ( event )
} ;
switch ( hash ( json : : get < " type " _ > ( event ) ) )
{
case " m.room.message " _ :
s < < unquote ( content . get ( " msgtype " ) ) < < " " ;
s < < unquote ( content . get ( " body " ) ) < < " " ;
break ;
default :
s < < string_view { content } ;
break ;
}
return s ;
}
std : : string
ircd : : m : : pretty ( const event : : prev & prev )
{
std : : string ret ;
std : : stringstream s ;
pubsetbuf ( s , ret , 4096 ) ;
pretty ( s , prev ) ;
resizebuf ( s , ret ) ;
return ret ;
}
std : : ostream &
ircd : : m : : pretty ( std : : ostream & s ,
const event : : prev & prev )
{
const auto out { [ & s ]
( const string_view & key , auto & & val )
{
if ( json : : defined ( val ) )
s < < key < < " : " < < val < < std : : endl ;
} } ;
const auto & auth_events { json : : get < " auth_events " _ > ( prev ) } ;
for ( const json : : array auth_event : auth_events )
{
s < < std : : setw ( 16 ) < < std : : right < < " [auth event] "
< < " : " < < unquote ( auth_event [ 0 ] ) ;
for ( const auto & hash : json : : object { auth_event [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
const auto & prev_states { json : : get < " prev_state " _ > ( prev ) } ;
for ( const json : : array prev_state : prev_states )
{
s < < std : : setw ( 16 ) < < std : : right < < " [prev state] "
< < " : " < < unquote ( prev_state [ 0 ] ) ;
for ( const auto & hash : json : : object { prev_state [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
const auto & prev_events { json : : get < " prev_events " _ > ( prev ) } ;
for ( const json : : array prev_event : prev_events )
{
s < < std : : setw ( 16 ) < < std : : right < < " [prev_event] "
< < " : " < < unquote ( prev_event [ 0 ] ) ;
for ( const auto & hash : json : : object { prev_event [ 1 ] } )
s < < " " < < unquote ( hash . first )
< < " : " < < unquote ( hash . second ) ;
s < < std : : endl ;
}
return s ;
}
std : : ostream &
ircd : : m : : pretty_oneline ( std : : ostream & s ,
const event : : prev & prev )
{
const auto & auth_events { json : : get < " auth_events " _ > ( prev ) } ;
s < < " A[ " ;
for ( const json : : array auth_event : auth_events )
s < < unquote ( auth_event [ 0 ] ) < < " " ;
s < < " ] " ;
const auto & prev_states { json : : get < " prev_state " _ > ( prev ) } ;
s < < " S[ " ;
for ( const json : : array prev_state : prev_states )
s < < unquote ( prev_state [ 0 ] ) < < " " ;
s < < " ] " ;
const auto & prev_events { json : : get < " prev_events " _ > ( prev ) } ;
s < < " E[ " ;
for ( const json : : array prev_event : prev_events )
s < < unquote ( prev_event [ 0 ] ) < < " " ;
s < < " ] " ;
return s ;
}
2019-03-11 23:43:07 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/append.h
//
void
ircd : : m : : append ( json : : stack : : array & array ,
const event & event_ ,
const event_append_opts & opts )
{
json : : stack : : object object
{
array
} ;
append ( object , event_ , opts ) ;
}
void
ircd : : m : : append ( json : : stack : : object & object ,
2019-05-04 00:34:56 +02:00
const event & event ,
2019-03-11 23:43:07 +01:00
const event_append_opts & opts )
{
2019-05-04 00:34:56 +02:00
const bool has_event_idx
{
opts . event_idx & & * opts . event_idx
} ;
const bool has_client_txnid
{
opts . client_txnid & & * opts . client_txnid
} ;
const bool has_user
{
opts . user_id & & opts . user_room
} ;
const bool sender_is_user
{
has_user & & json : : get < " sender " _ > ( event ) = = * opts . user_id
} ;
const auto txnid_idx
{
! has_client_txnid & & sender_is_user & & opts . query_txnid ?
opts . user_room - > get ( std : : nothrow , " ircd.client.txnid " , at < " event_id " _ > ( event ) ) :
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 " ,
json : : get < " event_id " _ > ( event ) ,
json : : get < " sender " _ > ( event ) ,
json : : get < " room_id " _ > ( event )
} ;
# endif
if ( ! json : : get < " state_key " _ > ( event ) & & has_user )
{
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' " ,
json : : get < " event_id " _ > ( event ) ,
json : : get < " sender " _ > ( event ) ,
string_view { * opts . user_id }
} ;
2019-03-11 23:43:07 +01:00
2019-05-04 00:34:56 +02:00
return ;
}
}
object . append ( event ) ;
if ( json : : get < " state_key " _ > ( event ) & & has_event_idx )
{
const auto prev_idx
{
room : : state : : prev ( * opts . event_idx )
} ;
if ( prev_idx )
m : : get ( std : : nothrow , prev_idx , " content " , [ & object ]
( const json : : object & content )
{
json : : stack : : member
{
object , " prev_content " , content
} ;
} ) ;
}
json : : stack : : object unsigned_
2019-03-11 23:43:07 +01:00
{
2019-05-04 00:34:56 +02:00
object , " unsigned "
} ;
json : : stack : : member
{
unsigned_ , " age " , json : : value
{
2019-05-25 23:51:42 +02:00
// When the opts give an explicit age, use it.
2019-05-04 00:34:56 +02:00
opts . age ! = std : : numeric_limits < long > : : min ( ) ?
opts . age :
2019-05-25 23:51:42 +02:00
// 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.
2019-05-04 00:34:56 +02:00
json : : get < " depth " _ > ( event ) > = 0 & & opts . room_depth & & * opts . room_depth > = 0L ?
( ( * opts . room_depth + 1 ) - json : : get < " depth " _ > ( event ) ) + 100 :
2019-05-25 23:51:42 +02:00
// 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.
2019-05-04 00:34:56 +02:00
! opts . room_depth & & json : : get < " origin_server_ts " _ > ( event ) ?
ircd : : time < milliseconds > ( ) - json : : get < " origin_server_ts " _ > ( event ) :
2019-05-25 23:51:42 +02:00
// Finally, this special value will eliminate the age altogether
// during serialization.
2019-05-04 00:34:56 +02:00
json : : undefined_number
}
2019-03-11 23:43:07 +01:00
} ;
2019-05-04 00:34:56 +02:00
if ( has_client_txnid )
json : : stack : : member
{
unsigned_ , " transaction_id " , * opts . client_txnid
} ;
if ( txnid_idx )
m : : get ( std : : nothrow , txnid_idx , " content " , [ & unsigned_ ]
( const json : : object & content )
{
json : : stack : : member
{
unsigned_ , " transaction_id " , unquote ( content . get ( " transaction_id " ) )
} ;
} ) ;
2019-03-11 23:43:07 +01:00
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/conforms.h
//
2018-05-04 23:22:17 +02:00
2019-05-04 00:34:56 +02:00
namespace ircd : : m : : vm
{
extern const m : : hookfn < eval & > issue_missing_auth ;
extern const m : : hookfn < eval & > conform_check_origin ;
extern const m : : hookfn < eval & > conform_check_size ;
extern const m : : hookfn < eval & > conform_report ;
}
/// Check if an eval with a copts structure (indicating this server is
/// creating the event) has an origin set to !my_host().
decltype ( ircd : : m : : vm : : conform_check_origin )
ircd : : m : : vm : : conform_check_origin
{
{
{ " _site " , " vm.conform " }
} ,
[ ] ( const m : : event & event , eval & eval )
{
if ( unlikely ( eval . copts & & ! my_host ( at < " origin " _ > ( event ) ) ) )
throw error
{
fault : : GENERAL , " Issuing event for origin: %s " , at < " origin " _ > ( event )
} ;
}
} ;
/// Check if an event originating from this server exceeds maximum size.
decltype ( ircd : : m : : vm : : conform_check_size )
ircd : : m : : vm : : conform_check_size
{
{
{ " _site " , " vm.conform " } ,
{ " origin " , my_host ( ) }
} ,
[ ] ( const m : : event & event , eval & eval )
{
const size_t & event_size
{
serialized ( event )
} ;
if ( event_size > size_t ( event : : max_size ) )
throw m : : BAD_JSON
{
" Event is %zu bytes which is larger than the maximum %zu bytes " ,
event_size ,
size_t ( event : : max_size )
} ;
}
} ;
/// Check if an event originating from this server exceeds maximum size.
decltype ( ircd : : m : : vm : : conform_report )
ircd : : m : : vm : : conform_report
{
{
{ " _site " , " vm.conform " }
} ,
[ ] ( const m : : event & event , eval & eval )
{
assert ( eval . opts ) ;
auto & opts ( * eval . opts ) ;
// When opts.conformed is set the report is already generated
if ( opts . conformed )
{
eval . report = opts . report ;
return ;
}
// Generate the report here.
eval . report = event : : conforms
{
event , opts . non_conform . report
} ;
// When opts.conforming is false a bad report is not an error.
if ( ! opts . conforming )
return ;
// Otherwise this will kill the eval
if ( ! eval . report . clean ( ) )
throw error
{
fault : : INVALID , " Non-conforming event: %s " , string ( eval . report )
} ;
}
} ;
2019-01-26 23:15:36 +01:00
namespace ircd : : m
2018-05-04 23:22:17 +02:00
{
2019-05-04 00:34:56 +02:00
constexpr size_t event_conforms_num { num_of < event : : conforms : : code > ( ) } ;
2019-01-26 23:15:36 +01:00
extern const std : : array < string_view , event_conforms_num > event_conforms_reflects ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
decltype ( ircd : : m : : event_conforms_reflects )
ircd : : m : : event_conforms_reflects
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
" INVALID_OR_MISSING_EVENT_ID " ,
" INVALID_OR_MISSING_ROOM_ID " ,
" INVALID_OR_MISSING_SENDER_ID " ,
" MISSING_TYPE " ,
" MISSING_ORIGIN " ,
" INVALID_ORIGIN " ,
" INVALID_OR_MISSING_REDACTS_ID " ,
" MISSING_CONTENT_MEMBERSHIP " ,
" INVALID_CONTENT_MEMBERSHIP " ,
2019-02-13 02:14:02 +01:00
" MISSING_MEMBER_STATE_KEY " ,
" INVALID_MEMBER_STATE_KEY " ,
2019-01-26 23:15:36 +01:00
" MISSING_PREV_EVENTS " ,
" MISSING_PREV_STATE " ,
2019-02-13 00:05:29 +01:00
" MISSING_AUTH_EVENTS " ,
2019-01-26 23:15:36 +01:00
" DEPTH_NEGATIVE " ,
" DEPTH_ZERO " ,
" MISSING_SIGNATURES " ,
" MISSING_ORIGIN_SIGNATURE " ,
" MISMATCH_ORIGIN_SENDER " ,
" MISMATCH_ORIGIN_EVENT_ID " ,
2019-02-13 00:22:53 +01:00
" MISMATCH_CREATE_SENDER " ,
2019-02-13 00:34:40 +01:00
" MISMATCH_ALIASES_STATE_KEY " ,
2019-01-26 23:15:36 +01:00
" SELF_REDACTS " ,
" SELF_PREV_EVENT " ,
" SELF_PREV_STATE " ,
2019-02-13 00:05:29 +01:00
" SELF_AUTH_EVENT " ,
2019-01-26 23:15:36 +01:00
" DUP_PREV_EVENT " ,
" DUP_PREV_STATE " ,
2019-02-13 00:05:29 +01:00
" DUP_AUTH_EVENT " ,
2019-01-26 23:15:36 +01:00
} ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
std : : ostream &
ircd : : m : : operator < < ( std : : ostream & s , const event : : conforms & conforms )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
thread_local char buf [ 1024 ] ;
s < < conforms . string ( buf ) ;
return s ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : string_view
ircd : : m : : reflect ( const event : : conforms : : code & code )
try
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
return event_conforms_reflects . at ( code ) ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
catch ( const std : : out_of_range & e )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
return " ?????? " _sv ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : code
ircd : : m : : event : : conforms : : reflect ( const string_view & name )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
const auto it
{
std : : find ( begin ( event_conforms_reflects ) , end ( event_conforms_reflects ) , name )
} ;
if ( it = = end ( event_conforms_reflects ) )
throw std : : out_of_range
{
" There is no event::conforms code by that name. "
} ;
return code ( std : : distance ( begin ( event_conforms_reflects ) , it ) ) ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : conforms ( const event & e ,
const uint64_t & skip )
: conforms { e }
2018-06-09 23:32:46 +02:00
{
2019-01-26 23:15:36 +01:00
report & = ~ skip ;
2018-06-09 23:32:46 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : conforms ( const event & e )
: report { 0 }
2018-05-05 01:13:10 +02:00
{
2019-01-26 23:15:36 +01:00
if ( ! valid ( m : : id : : EVENT , json : : get < " event_id " _ > ( e ) ) )
set ( INVALID_OR_MISSING_EVENT_ID ) ;
2018-05-05 01:13:10 +02:00
2019-01-26 23:15:36 +01:00
if ( ! valid ( m : : id : : ROOM , json : : get < " room_id " _ > ( e ) ) )
set ( INVALID_OR_MISSING_ROOM_ID ) ;
2018-05-05 01:13:10 +02:00
2019-01-26 23:15:36 +01:00
if ( ! valid ( m : : id : : USER , json : : get < " sender " _ > ( e ) ) )
set ( INVALID_OR_MISSING_SENDER_ID ) ;
2018-05-05 01:13:10 +02:00
2019-01-26 23:15:36 +01:00
if ( empty ( json : : get < " type " _ > ( e ) ) )
set ( MISSING_TYPE ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
if ( empty ( json : : get < " origin " _ > ( e ) ) )
set ( MISSING_ORIGIN ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
//TODO: XXX
if ( false )
set ( INVALID_ORIGIN ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
if ( empty ( json : : get < " signatures " _ > ( e ) ) )
set ( MISSING_SIGNATURES ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
if ( empty ( json : : object { json : : get < " signatures " _ > ( e ) . get ( json : : get < " origin " _ > ( e ) ) } ) )
set ( MISSING_ORIGIN_SIGNATURE ) ;
2019-01-15 21:49:53 +01:00
2019-01-26 23:15:36 +01:00
if ( ! has ( INVALID_OR_MISSING_SENDER_ID ) )
if ( json : : get < " origin " _ > ( e ) ! = m : : id : : user { json : : get < " sender " _ > ( e ) } . host ( ) )
set ( MISMATCH_ORIGIN_SENDER ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
if ( ! has ( INVALID_OR_MISSING_EVENT_ID ) )
if ( json : : get < " origin " _ > ( e ) ! = m : : id : : event { json : : get < " event_id " _ > ( e ) } . host ( ) )
set ( MISMATCH_ORIGIN_EVENT_ID ) ;
2018-12-27 03:25:59 +01:00
2019-02-13 00:22:53 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.create " )
if ( m : : room : : id ( json : : get < " room_id " _ > ( e ) ) . host ( ) ! = m : : user : : id ( json : : get < " sender " _ > ( e ) ) . host ( ) )
set ( MISMATCH_CREATE_SENDER ) ;
2019-02-13 00:34:40 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.aliases " )
if ( m : : user : : id ( json : : get < " sender " _ > ( e ) ) . host ( ) ! = json : : get < " state_key " _ > ( e ) )
set ( MISMATCH_ALIASES_STATE_KEY ) ;
2019-01-26 23:15:36 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.redaction " )
if ( ! valid ( m : : id : : EVENT , json : : get < " redacts " _ > ( e ) ) )
set ( INVALID_OR_MISSING_REDACTS_ID ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
if ( json : : get < " redacts " _ > ( e ) )
if ( json : : get < " redacts " _ > ( e ) = = json : : get < " event_id " _ > ( e ) )
set ( SELF_REDACTS ) ;
2019-01-24 21:02:58 +01:00
2019-01-26 23:15:36 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.member " )
if ( empty ( unquote ( json : : get < " content " _ > ( e ) . get ( " membership " ) ) ) )
set ( MISSING_CONTENT_MEMBERSHIP ) ;
2019-01-12 02:38:33 +01:00
2019-01-26 23:15:36 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.member " )
if ( ! all_of < std : : islower > ( unquote ( json : : get < " content " _ > ( e ) . get ( " membership " ) ) ) )
set ( INVALID_CONTENT_MEMBERSHIP ) ;
2019-01-12 02:38:33 +01:00
2019-02-13 02:14:02 +01:00
if ( json : : get < " type " _ > ( e ) = = " m.room.member " )
if ( empty ( json : : get < " state_key " _ > ( e ) ) )
set ( MISSING_MEMBER_STATE_KEY ) ;
if ( json : : get < " type " _ > ( e ) = = " m.room.member " )
if ( ! valid ( m : : id : : USER , json : : get < " state_key " _ > ( e ) ) )
set ( INVALID_MEMBER_STATE_KEY ) ;
2019-01-26 23:15:36 +01:00
if ( json : : get < " type " _ > ( e ) ! = " m.room.create " )
if ( empty ( json : : get < " prev_events " _ > ( e ) ) )
set ( MISSING_PREV_EVENTS ) ;
2018-12-27 03:25:59 +01:00
2019-01-26 23:15:36 +01:00
/*
if ( json : : get < " type " _ > ( e ) ! = " m.room.create " )
if ( ! empty ( json : : get < " state_key " _ > ( e ) ) )
if ( empty ( json : : get < " prev_state " _ > ( e ) ) )
set ( MISSING_PREV_STATE ) ;
*/
2018-05-05 01:13:10 +02:00
2019-02-13 00:05:29 +01:00
if ( json : : get < " type " _ > ( e ) ! = " m.room.create " )
if ( empty ( json : : get < " auth_events " _ > ( e ) ) )
set ( MISSING_AUTH_EVENTS ) ;
2019-01-26 23:15:36 +01:00
if ( json : : get < " depth " _ > ( e ) ! = json : : undefined_number & & json : : get < " depth " _ > ( e ) < 0 )
set ( DEPTH_NEGATIVE ) ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
if ( json : : get < " type " _ > ( e ) ! = " m.room.create " )
if ( json : : get < " depth " _ > ( e ) = = 0 )
set ( DEPTH_ZERO ) ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
const prev p { e } ;
size_t i { 0 } , j { 0 } ;
for ( const json : : array & pe : json : : get < " prev_events " _ > ( p ) )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
if ( unquote ( pe . at ( 0 ) ) = = json : : get < " event_id " _ > ( e ) )
set ( SELF_PREV_EVENT ) ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
j = 0 ;
for ( const json : : array & pe_ : json : : get < " prev_events " _ > ( p ) )
if ( i ! = j + + )
if ( pe_ . at ( 0 ) = = pe . at ( 0 ) )
set ( DUP_PREV_EVENT ) ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
+ + i ;
}
i = 0 ;
for ( const json : : array & ps : json : : get < " prev_state " _ > ( p ) )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
if ( unquote ( ps . at ( 0 ) ) = = json : : get < " event_id " _ > ( e ) )
set ( SELF_PREV_STATE ) ;
2018-05-04 23:22:17 +02:00
2019-01-26 23:15:36 +01:00
j = 0 ;
for ( const json : : array & ps_ : json : : get < " prev_state " _ > ( p ) )
if ( i ! = j + + )
if ( ps_ . at ( 0 ) = = ps . at ( 0 ) )
set ( DUP_PREV_STATE ) ;
+ + i ;
}
2019-02-13 00:05:29 +01:00
i = 0 ;
for ( const json : : array & ps : json : : get < " auth_events " _ > ( p ) )
{
if ( unquote ( ps . at ( 0 ) ) = = json : : get < " event_id " _ > ( e ) )
set ( SELF_AUTH_EVENT ) ;
j = 0 ;
for ( const json : : array & ps_ : json : : get < " auth_events " _ > ( p ) )
if ( i ! = j + + )
if ( ps_ . at ( 0 ) = = ps . at ( 0 ) )
set ( DUP_AUTH_EVENT ) ;
+ + i ;
}
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
void
ircd : : m : : event : : conforms : : operator | = ( const code & code )
&
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
set ( code ) ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
void
ircd : : m : : event : : conforms : : del ( const code & code )
2018-05-04 23:22:17 +02:00
{
2019-01-26 23:15:36 +01:00
report & = ~ ( 1UL < < code ) ;
2018-05-04 23:22:17 +02:00
}
2019-01-26 23:15:36 +01:00
void
ircd : : m : : event : : conforms : : set ( const code & code )
2018-06-09 23:32:46 +02:00
{
2019-01-26 23:15:36 +01:00
report | = ( 1UL < < code ) ;
2018-06-09 23:32:46 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : string_view
ircd : : m : : event : : conforms : : string ( const mutable_buffer & out )
const
2018-06-09 23:32:46 +02:00
{
2019-01-26 23:15:36 +01:00
mutable_buffer buf { out } ;
for ( uint64_t i ( 0 ) ; i < num_of < code > ( ) ; + + i )
2018-06-09 23:32:46 +02:00
{
2019-01-26 23:15:36 +01:00
if ( ! has ( code ( i ) ) )
continue ;
2018-06-09 23:32:46 +02:00
2019-01-26 23:15:36 +01:00
if ( begin ( buf ) ! = begin ( out ) )
consume ( buf , copy ( buf , " " _sv ) ) ;
2018-06-09 23:32:46 +02:00
2019-01-26 23:15:36 +01:00
consume ( buf , copy ( buf , m : : reflect ( code ( i ) ) ) ) ;
}
return { data ( out ) , begin ( buf ) } ;
2018-06-09 23:32:46 +02:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : has ( const code & code )
const
2018-06-09 23:32:46 +02:00
{
2019-01-26 23:15:36 +01:00
return report & ( 1UL < < code ) ;
2018-06-09 23:32:46 +02:00
}
2018-02-21 02:04:17 +01:00
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : has ( const uint & code )
const
2018-02-21 02:04:17 +01:00
{
2019-01-26 23:15:36 +01:00
return ( report & ( 1UL < < code ) ) = = code ;
2018-02-21 02:04:17 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : operator ! ( )
const
2018-02-21 02:04:17 +01:00
{
2019-01-26 23:15:36 +01:00
return clean ( ) ;
2018-02-21 02:04:17 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : conforms : : operator bool ( )
const
{
return ! clean ( ) ;
}
2018-01-20 14:48:39 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : event : : conforms : : clean ( )
const
2018-03-18 04:47:21 +01:00
{
2019-01-26 23:15:36 +01:00
return report = = 0 ;
}
2018-03-18 04:47:21 +01:00
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
2018-03-18 04:47:21 +01:00
//
2019-01-26 23:15:36 +01:00
// event/prefetch.h
2018-03-18 04:47:21 +01:00
//
2019-01-26 23:15:36 +01:00
void
ircd : : m : : prefetch ( const event : : id & event_id ,
const event : : fetch : : opts & opts )
2018-01-20 14:48:39 +01:00
{
2019-01-26 23:15:36 +01:00
prefetch ( index ( event_id ) , opts ) ;
2019-01-16 23:35:10 +01:00
}
2018-01-20 14:48:39 +01:00
2019-01-26 23:15:36 +01:00
void
ircd : : m : : prefetch ( const event : : id & event_id ,
const string_view & key )
2019-01-16 23:35:10 +01:00
{
2019-01-26 23:15:36 +01:00
prefetch ( index ( event_id ) , key ) ;
2018-01-20 14:48:39 +01:00
}
2018-02-09 20:22:46 +01:00
2019-01-26 23:15:36 +01:00
void
ircd : : m : : prefetch ( const event : : idx & event_idx ,
const event : : fetch : : opts & opts )
2018-05-27 11:02:08 +02:00
{
2019-01-26 23:15:36 +01:00
const event : : keys keys { opts . keys } ;
const vector_view < const string_view > cols { keys } ;
for ( const auto & col : cols )
if ( col )
prefetch ( event_idx , col ) ;
2018-05-27 11:02:08 +02:00
}
2019-01-26 23:15:36 +01:00
void
ircd : : m : : prefetch ( const event : : idx & event_idx ,
const string_view & key )
2018-05-27 11:02:08 +02:00
{
2019-01-26 23:15:36 +01:00
const auto & column_idx
2018-05-27 11:02:08 +02:00
{
2019-01-26 23:15:36 +01:00
json : : indexof < event > ( key )
2018-05-27 11:02:08 +02:00
} ;
2019-01-26 23:15:36 +01:00
auto & column
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
dbs : : event_column . at ( column_idx )
2018-03-07 20:54:56 +01:00
} ;
2019-01-26 23:15:36 +01:00
db : : prefetch ( column , byte_view < string_view > { event_idx } ) ;
2018-05-27 11:02:08 +02:00
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/cached.h
//
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : cached ( const id : : event & event_id )
{
return cached ( event_id , event : : fetch : : default_opts ) ;
2018-03-07 20:54:56 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : cached ( const id : : event & event_id ,
const event : : fetch : : opts & opts )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
if ( ! db : : cached ( dbs : : event_idx , event_id , opts . gopts ) )
return false ;
const event : : idx event_idx
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
index ( event_id , std : : nothrow )
2018-03-07 20:54:56 +01:00
} ;
2019-01-26 23:15:36 +01:00
return event_idx ?
cached ( event_idx , opts . gopts ) :
false ;
2018-03-07 20:54:56 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : cached ( const event : : idx & event_idx )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
return cached ( event_idx , event : : fetch : : default_opts ) ;
}
2018-03-21 08:22:20 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : cached ( const event : : idx & event_idx ,
const event : : fetch : : opts & opts )
{
const byte_view < string_view > & key
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
event_idx
2018-03-07 20:54:56 +01:00
} ;
2019-02-12 23:29:57 +01:00
if ( event : : fetch : : should_seek_json ( opts ) )
return db : : cached ( dbs : : event_json , key , opts . gopts ) ;
const auto & selection
2018-03-07 20:54:56 +01:00
{
2019-02-12 23:29:57 +01:00
opts . keys
} ;
const auto result
{
cached_keys ( event_idx , opts )
} ;
2018-03-07 20:54:56 +01:00
2019-02-12 23:29:57 +01:00
for ( size_t i ( 0 ) ; i < selection . size ( ) ; + + i )
{
2019-01-26 23:15:36 +01:00
auto & column
{
2019-02-12 23:29:57 +01:00
dbs : : event_column . at ( i )
2019-01-26 23:15:36 +01:00
} ;
2018-03-07 20:54:56 +01:00
2019-02-12 23:29:57 +01:00
if ( column & & selection . test ( i ) & & ! result . test ( i ) )
{
if ( ! db : : has ( column , key , opts . gopts ) )
continue ;
2018-03-23 07:53:11 +01:00
2019-02-12 23:29:57 +01:00
return false ;
}
}
2019-01-26 23:15:36 +01:00
2019-02-12 23:29:57 +01:00
return true ;
}
2019-01-26 23:15:36 +01:00
2019-02-12 23:29:57 +01:00
ircd : : m : : event : : keys : : selection
ircd : : m : : cached_keys ( const event : : idx & event_idx ,
const event : : fetch : : opts & opts )
{
const byte_view < string_view > & key
{
event_idx
} ;
event : : keys : : selection ret ( 0 ) ;
const event : : keys : : selection & selection ( opts . keys ) ;
for ( size_t i ( 0 ) ; i < selection . size ( ) ; + + i )
{
auto & column
{
dbs : : event_column . at ( i )
} ;
if ( column & & db : : cached ( column , key , opts . gopts ) )
ret . set ( i ) ;
}
return ret ;
2018-03-23 07:53:11 +01:00
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/get.h
//
2019-02-12 19:03:20 +01:00
std : : string
ircd : : m : : get ( const event : : id & event_id ,
const string_view & key )
{
std : : string ret ;
get ( event_id , key , [ & ret ]
( const string_view & value )
{
ret = value ;
} ) ;
return ret ;
}
std : : string
ircd : : m : : get ( const event : : idx & event_idx ,
const string_view & key )
{
std : : string ret ;
get ( event_idx , key , [ & ret ]
( const string_view & value )
{
ret = value ;
} ) ;
return ret ;
}
std : : string
ircd : : m : : get ( std : : nothrow_t ,
const event : : id & event_id ,
const string_view & key )
{
std : : string ret ;
get ( std : : nothrow , event_id , key , [ & ret ]
( const string_view & value )
{
ret = value ;
} ) ;
return ret ;
}
std : : string
ircd : : m : : get ( std : : nothrow_t ,
const event : : idx & event_idx ,
const string_view & key )
{
std : : string ret ;
get ( std : : nothrow , event_idx , key , [ & ret ]
( const string_view & value )
{
ret = value ;
} ) ;
return ret ;
}
2019-01-26 23:15:36 +01:00
ircd : : const_buffer
ircd : : m : : get ( const event : : id & event_id ,
const string_view & key ,
const mutable_buffer & out )
2018-03-23 07:53:11 +01:00
{
2019-01-26 23:15:36 +01:00
const auto & ret
2018-03-23 07:53:11 +01:00
{
2019-01-26 23:15:36 +01:00
get ( std : : nothrow , index ( event_id ) , key , out )
2018-03-23 07:53:11 +01:00
} ;
2019-01-26 23:15:36 +01:00
if ( ! ret )
throw m : : NOT_FOUND
{
" %s for %s not found in database " ,
key ,
string_view { event_id }
} ;
return ret ;
2018-03-23 07:53:11 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : const_buffer
ircd : : m : : get ( const event : : idx & event_idx ,
const string_view & key ,
const mutable_buffer & out )
2018-03-23 07:53:11 +01:00
{
2019-01-26 23:15:36 +01:00
const const_buffer ret
2018-03-23 07:53:11 +01:00
{
2019-01-26 23:15:36 +01:00
get ( std : : nothrow , event_idx , key , out )
2018-03-23 07:53:11 +01:00
} ;
2019-01-26 23:15:36 +01:00
if ( ! ret )
throw m : : NOT_FOUND
{
" %s for event_idx[%lu] not found in database " ,
key ,
event_idx
} ;
2018-03-23 07:53:11 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
2018-03-23 07:53:11 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : const_buffer
ircd : : m : : get ( std : : nothrow_t ,
const event : : id & event_id ,
const string_view & key ,
const mutable_buffer & buf )
2018-03-23 07:53:11 +01:00
{
2019-01-26 23:15:36 +01:00
return get ( std : : nothrow , index ( event_id ) , key , buf ) ;
2018-03-23 07:53:11 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : const_buffer
ircd : : m : : get ( std : : nothrow_t ,
const event : : idx & event_idx ,
const string_view & key ,
const mutable_buffer & buf )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
const_buffer ret ;
get ( std : : nothrow , event_idx , key , [ & buf , & ret ]
( const string_view & value )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
ret = { data ( buf ) , copy ( buf , value ) } ;
} ) ;
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
void
ircd : : m : : get ( const event : : id & event_id ,
const string_view & key ,
const event : : fetch : : view_closure & closure )
{
if ( ! get ( std : : nothrow , index ( event_id ) , key , closure ) )
throw m : : NOT_FOUND
{
" %s for %s not found in database " ,
key ,
string_view { event_id }
} ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
void
ircd : : m : : get ( const event : : idx & event_idx ,
const string_view & key ,
const event : : fetch : : view_closure & closure )
{
if ( ! get ( std : : nothrow , event_idx , key , closure ) )
throw m : : NOT_FOUND
{
" %s for event_idx[%lu] not found in database " ,
key ,
event_idx
} ;
2018-03-07 20:54:56 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : get ( std : : nothrow_t ,
const event : : id & event_id ,
const string_view & key ,
const event : : fetch : : view_closure & closure )
2018-05-16 04:53:08 +02:00
{
2019-01-26 23:15:36 +01:00
return get ( std : : nothrow , index ( event_id ) , key , closure ) ;
}
2018-05-16 04:53:08 +02:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : get ( std : : nothrow_t ,
const event : : idx & event_idx ,
const string_view & key ,
const event : : fetch : : view_closure & closure )
{
const string_view & column_key
2018-05-16 04:53:08 +02:00
{
2019-01-26 23:15:36 +01:00
byte_view < string_view > { event_idx }
2018-05-16 04:53:08 +02:00
} ;
2019-01-26 23:15:36 +01:00
const auto & column_idx
2018-05-16 04:53:08 +02:00
{
2019-01-26 23:15:36 +01:00
json : : indexof < event > ( key )
2018-05-16 04:53:08 +02:00
} ;
2019-01-26 23:15:36 +01:00
auto & column
2018-05-16 04:53:08 +02:00
{
2019-01-26 23:15:36 +01:00
dbs : : event_column . at ( column_idx )
2018-05-16 04:53:08 +02:00
} ;
2019-02-12 19:02:37 +01:00
if ( column )
return column ( column_key , std : : nothrow , closure ) ;
// If the event property being sought doesn't have its own column we
// fall back to fetching the full JSON and closing over the property.
bool ret { false } ;
dbs : : event_json ( column_key , std : : nothrow , [ & closure , & key , & ret ]
( const json : : object & event )
{
string_view value
{
event [ key ]
} ;
if ( ! value )
return ;
// The user expects an unquoted string to their closure, the same as
// if this value was found in its own column.
if ( json : : type ( value ) = = json : : STRING )
value = unquote ( value ) ;
ret = true ;
closure ( value ) ;
} ) ;
return ret ;
2019-01-26 23:15:36 +01:00
}
2018-05-16 04:53:08 +02:00
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/fetch.h
//
2018-05-16 04:53:08 +02:00
2019-01-26 23:15:36 +01:00
//
// seek
//
2018-05-16 04:53:08 +02:00
2019-01-26 23:15:36 +01:00
void
ircd : : m : : seek ( event : : fetch & fetch ,
const event : : id & event_id )
2018-03-21 01:59:08 +01:00
{
2019-01-26 23:15:36 +01:00
if ( ! seek ( fetch , event_id , std : : nothrow ) )
throw m : : NOT_FOUND
{
" %s not found in database " , event_id
} ;
2018-03-21 01:59:08 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : seek ( event : : fetch & fetch ,
const event : : id & event_id ,
std : : nothrow_t )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
const auto & event_idx
2018-03-08 23:17:47 +01:00
{
2019-01-26 23:15:36 +01:00
index ( event_id , std : : nothrow )
} ;
2018-03-08 23:17:47 +01:00
2019-01-26 23:15:36 +01:00
if ( ! event_idx )
{
fetch . valid = false ;
return fetch . valid ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
return seek ( fetch , event_idx , std : : nothrow ) ;
}
void
ircd : : m : : seek ( event : : fetch & fetch ,
const event : : idx & event_idx )
2018-03-07 20:54:56 +01:00
{
2019-01-26 23:15:36 +01:00
if ( ! seek ( fetch , event_idx , std : : nothrow ) )
throw m : : NOT_FOUND
{
" %lu not found in database " , event_idx
} ;
2018-03-21 01:59:08 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : seek ( event : : fetch & fetch ,
const event : : idx & event_idx ,
std : : nothrow_t )
2018-03-21 01:59:08 +01:00
{
2019-03-02 00:55:56 +01:00
fetch . event_idx = event_idx ;
const string_view & key
2018-03-21 01:59:08 +01:00
{
2019-03-02 00:55:56 +01:00
byte_view < string_view > ( event_idx )
2018-03-21 01:59:08 +01:00
} ;
2019-03-02 00:55:56 +01:00
auto & event
2019-01-26 23:15:36 +01:00
{
2019-03-02 00:55:56 +01:00
static_cast < m : : event & > ( fetch )
2019-01-26 23:15:36 +01:00
} ;
assert ( fetch . fopts ) ;
const auto & opts ( * fetch . fopts ) ;
if ( ! fetch . should_seek_json ( opts ) )
if ( ( fetch . valid = db : : seek ( fetch . row , key , opts . gopts ) ) )
if ( ( fetch . valid = fetch . assign_from_row ( key ) ) )
return fetch . valid ;
if ( ( fetch . valid = fetch . _json . load ( key , opts . gopts ) ) )
fetch . valid = fetch . assign_from_json ( key ) ;
return fetch . valid ;
2018-03-21 01:59:08 +01:00
}
2019-01-26 23:15:36 +01:00
//
// event::fetch
//
decltype ( ircd : : m : : event : : fetch : : default_opts )
ircd : : m : : event : : fetch : : default_opts
{ } ;
void
ircd : : m : : event : : fetch : : event_id ( const idx & idx ,
const id : : closure & closure )
2018-03-21 01:59:08 +01:00
{
2019-01-26 23:15:36 +01:00
if ( ! get ( std : : nothrow , idx , " event_id " , closure ) )
throw m : : NOT_FOUND
{
" %lu not found in database " , idx
} ;
2018-03-21 01:59:08 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : event : : fetch : : event_id ( const idx & idx ,
std : : nothrow_t ,
const id : : closure & closure )
2018-03-21 01:59:08 +01:00
{
2019-01-26 23:15:36 +01:00
return get ( std : : nothrow , idx , " event_id " , closure ) ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
//
// event::fetch::fetch
//
/// Seek to event_id and populate this event from database.
/// Throws if event not in database.
ircd : : m : : event : : fetch : : fetch ( const event : : id & event_id ,
const opts & opts )
: fetch
{
index ( event_id ) , opts
}
{
2018-03-21 01:59:08 +01:00
}
2019-01-26 23:15:36 +01:00
/// Seek to event_id and populate this event from database.
/// Event is not populated if not found in database.
ircd : : m : : event : : fetch : : fetch ( const event : : id & event_id ,
std : : nothrow_t ,
const opts & opts )
: fetch
{
index ( event_id , std : : nothrow ) , std : : nothrow , opts
}
2018-03-21 01:59:08 +01:00
{
}
2019-01-26 23:15:36 +01:00
/// Seek to event_idx and populate this event from database.
/// Throws if event not in database.
ircd : : m : : event : : fetch : : fetch ( const event : : idx & event_idx ,
const opts & opts )
: fetch
2018-03-21 01:59:08 +01:00
{
2019-01-26 23:15:36 +01:00
event_idx , std : : nothrow , opts
}
{
if ( ! valid )
throw m : : NOT_FOUND
{
" idx %zu not found in database " , event_idx
} ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
/// Seek to event_idx and populate this event from database.
/// Event is not populated if not found in database.
ircd : : m : : event : : fetch : : fetch ( const event : : idx & event_idx ,
std : : nothrow_t ,
const opts & opts )
: fopts
{
& opts
2018-03-07 20:54:56 +01:00
}
2019-03-02 00:55:56 +01:00
, event_idx
{
event_idx
}
2019-01-26 23:15:36 +01:00
, _json
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
dbs : : event_json ,
event_idx & & should_seek_json ( opts ) ?
key ( & event_idx ) :
string_view { } ,
opts . gopts
}
, row
{
* dbs : : events ,
event_idx & & ! _json . valid ( key ( & event_idx ) ) ?
key ( & event_idx ) :
string_view { } ,
event_idx & & ! _json . valid ( key ( & event_idx ) ) ?
event : : keys { opts . keys } :
event : : keys { event : : keys : : include { } } ,
cell ,
opts . gopts
}
, valid
{
event_idx & & _json . valid ( key ( & event_idx ) ) ?
assign_from_json ( key ( & event_idx ) ) :
assign_from_row ( key ( & event_idx ) )
}
{
}
2018-03-21 03:19:55 +01:00
2019-01-26 23:15:36 +01:00
/// Seekless constructor.
ircd : : m : : event : : fetch : : fetch ( const opts & opts )
: fopts
{
& opts
}
, _json
{
dbs : : event_json ,
string_view { } ,
opts . gopts
}
, row
{
* dbs : : events ,
string_view { } ,
! should_seek_json ( opts ) ?
event : : keys { opts . keys } :
event : : keys { event : : keys : : include { } } ,
cell ,
opts . gopts
}
, valid
{
false
}
{
2018-03-21 03:19:55 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : fetch : : assign_from_json ( const string_view & key )
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
auto & event
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
static_cast < m : : event & > ( * this )
2018-03-21 03:19:55 +01:00
} ;
2019-01-26 23:15:36 +01:00
assert ( _json . valid ( key ) ) ;
const json : : object source
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
_json . val ( )
2018-03-21 03:19:55 +01:00
} ;
2019-01-26 23:15:36 +01:00
assert ( fopts ) ;
event =
{
source , fopts - > keys
} ;
2018-03-21 03:19:55 +01:00
2019-01-26 23:15:36 +01:00
assert ( ! empty ( source ) ) ;
assert ( data ( event . source ) = = data ( source ) ) ;
return true ;
2018-03-21 03:19:55 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : fetch : : assign_from_row ( const string_view & key )
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
auto & event
2018-05-11 11:05:08 +02:00
{
2019-01-26 23:15:36 +01:00
static_cast < m : : event & > ( * this )
2018-05-11 11:05:08 +02:00
} ;
2019-01-26 23:15:36 +01:00
if ( ! row . valid ( key ) )
return false ;
2018-03-21 03:19:55 +01:00
2019-01-26 23:15:36 +01:00
assign ( event , row , key ) ;
event . source = { } ;
return true ;
2018-03-21 03:19:55 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : event : : fetch : : should_seek_json ( const opts & opts )
2018-03-21 03:19:55 +01:00
{
2019-01-26 23:15:36 +01:00
// User always wants to make the event_json query regardless
// of their keys selection.
if ( opts . query_json_force )
return true ;
// If and only if selected keys have direct columns we can return
// false to seek direct columns. If any other keys are selected we
/// must perform the event_json query instead.
for ( size_t i ( 0 ) ; i < opts . keys . size ( ) ; + + i )
if ( opts . keys . test ( i ) )
if ( ! dbs : : event_column . at ( i ) )
return true ;
2018-03-21 03:19:55 +01:00
return false ;
}
2018-03-07 20:54:56 +01:00
2019-01-26 23:15:36 +01:00
ircd : : string_view
ircd : : m : : event : : fetch : : key ( const event : : idx * const & event_idx )
2018-03-21 03:01:47 +01:00
{
2019-01-26 23:15:36 +01:00
assert ( event_idx ) ;
return byte_view < string_view > ( * event_idx ) ;
}
2018-03-21 03:01:47 +01:00
2019-01-26 23:15:36 +01:00
//
// event::fetch::opts
//
2018-03-21 03:01:47 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : fetch : : opts : : opts ( const db : : gopts & gopts ,
const event : : keys : : selection & keys )
: opts
{
keys , gopts
}
{
2018-03-21 03:01:47 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : fetch : : opts : : opts ( const event : : keys : : selection & keys ,
const db : : gopts & gopts )
: keys { keys }
, gopts { gopts }
2018-03-21 03:01:47 +01:00
{
2019-01-26 23:15:36 +01:00
}
2018-03-21 06:03:53 +01:00
2019-02-07 01:29:17 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/event_id.h
//
ircd : : m : : event : : id : : buf
ircd : : m : : event_id ( const event : : idx & event_idx )
{
event : : id : : buf ret ;
event_id ( event_idx , ret ) ;
return ret ;
}
ircd : : m : : event : : id : : buf
ircd : : m : : event_id ( const event : : idx & event_idx ,
std : : nothrow_t )
{
event : : id : : buf ret ;
event_id ( event_idx , ret , std : : nothrow ) ;
return ret ;
}
ircd : : m : : event : : id
ircd : : m : : event_id ( const event : : idx & event_idx ,
event : : id : : buf & buf )
{
const event : : id ret
{
event_id ( event_idx , buf , std : : nothrow )
} ;
if ( ! ret )
throw m : : NOT_FOUND
{
" Cannot find event ID from idx[%lu] " , event_idx
} ;
return ret ;
}
ircd : : m : : event : : id
ircd : : m : : event_id ( const event : : idx & event_idx ,
event : : id : : buf & buf ,
std : : nothrow_t )
{
event_id ( event_idx , std : : nothrow , [ & buf ]
( const event : : id & eid )
{
buf = eid ;
} ) ;
return buf ;
}
bool
ircd : : m : : event_id ( const event : : idx & event_idx ,
std : : nothrow_t ,
const event : : id : : closure & closure )
{
return get ( std : : nothrow , event_idx , " event_id " , closure ) ;
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/index.h
//
2018-03-21 06:03:53 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : idx
ircd : : m : : index ( const event & event )
try
{
return index ( at < " event_id " _ > ( event ) ) ;
2018-03-21 06:03:53 +01:00
}
2019-01-26 23:15:36 +01:00
catch ( const json : : not_found & )
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
throw m : : NOT_FOUND
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
" Cannot find index for event without an event_id. "
2018-03-21 06:03:53 +01:00
} ;
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : idx
ircd : : m : : index ( const event & event ,
std : : nothrow_t )
try
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
return index ( at < " event_id " _ > ( event ) , std : : nothrow ) ;
2018-03-21 06:03:53 +01:00
}
2019-01-26 23:15:36 +01:00
catch ( const json : : not_found & )
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
return 0 ;
}
2018-03-21 06:03:53 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : idx
ircd : : m : : index ( const event : : id & event_id )
{
const auto ret
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
index ( event_id , std : : nothrow )
2019-01-24 21:04:08 +01:00
} ;
2018-03-21 06:03:53 +01:00
2019-01-26 23:15:36 +01:00
if ( ! ret )
throw m : : NOT_FOUND
{
" no index found for %s " ,
string_view { event_id }
} ;
return ret ;
2018-03-21 06:03:53 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event : : idx
ircd : : m : : index ( const event : : id & event_id ,
std : : nothrow_t )
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
event : : idx ret { 0 } ;
index ( event_id , std : : nothrow , [ & ret ]
( const event : : idx & event_idx )
{
ret = event_idx ;
} ) ;
2018-03-21 03:01:47 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
}
bool
ircd : : m : : index ( const event : : id & event_id ,
std : : nothrow_t ,
const event : : closure_idx & closure )
{
auto & column
2018-03-21 06:03:53 +01:00
{
2019-01-26 23:15:36 +01:00
dbs : : event_idx
2018-03-21 06:03:53 +01:00
} ;
2018-03-21 03:01:47 +01:00
2019-02-14 03:27:55 +01:00
if ( ! event_id )
return false ;
2019-01-26 23:15:36 +01:00
return column ( event_id , std : : nothrow , [ & closure ]
( const string_view & value )
{
const event : : idx & event_idx
{
byte_view < event : : idx > ( value )
} ;
closure ( event_idx ) ;
} ) ;
2018-03-21 03:01:47 +01:00
}
2019-02-08 11:17:14 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/auth.h
//
2019-02-13 02:50:44 +01:00
//
// event::auth
//
void
ircd : : m : : event : : auth : : check ( const event & event )
{
const auto reason
{
failed ( event )
} ;
if ( reason )
throw m : : ACCESS_DENIED
{
" Authorization of %s failed against its auth_events :%s " ,
json : : get < " event_id " _ > ( event ) ,
reason
} ;
}
2019-02-08 11:17:14 +01:00
bool
2019-02-13 02:50:44 +01:00
ircd : : m : : event : : auth : : check ( std : : nothrow_t ,
const event & event )
2019-02-08 11:17:14 +01:00
{
2019-02-13 02:50:44 +01:00
return ! failed ( event ) ;
}
2019-02-08 11:17:14 +01:00
2019-02-13 02:50:44 +01:00
ircd : : string_view
2019-05-04 00:34:56 +02:00
ircd : : m : : event : : auth : : failed ( const m : : event & event )
2019-02-13 02:50:44 +01:00
{
2019-05-04 00:34:56 +02:00
const m : : event : : prev refs ( event ) ;
const auto count ( refs . auth_events_count ( ) ) ;
if ( count > 4 )
return " Too many auth_events references. " ;
2019-02-08 11:17:14 +01:00
2019-05-04 00:34:56 +02:00
const m : : event * authv [ 4 ] ;
const m : : event : : fetch auth [ 4 ]
2019-02-13 02:50:44 +01:00
{
2019-05-04 00:34:56 +02:00
{ count > 0 ? refs . auth_event ( 0 ) : event : : id { } , std : : nothrow } ,
{ count > 1 ? refs . auth_event ( 1 ) : event : : id { } , std : : nothrow } ,
{ count > 2 ? refs . auth_event ( 2 ) : event : : id { } , std : : nothrow } ,
{ count > 3 ? refs . auth_event ( 3 ) : event : : id { } , std : : nothrow } ,
2019-02-13 02:50:44 +01:00
} ;
2019-02-08 11:17:14 +01:00
2019-05-04 00:34:56 +02:00
size_t i ( 0 ) , j ( 0 ) ;
for ( ; i < 4 ; + + i )
if ( auth [ i ] . valid )
authv [ j + + ] = & auth [ i ] ;
return failed ( event , { authv , j } ) ;
2019-02-13 02:50:44 +01:00
}
2019-02-08 11:17:14 +01:00
2019-02-13 02:50:44 +01:00
ircd : : string_view
2019-05-04 00:34:56 +02:00
ircd : : m : : event : : auth : : failed ( const m : : event & event ,
2019-02-13 03:02:51 +01:00
const vector_view < const m : : event * > & auth_events )
2019-02-13 02:50:44 +01:00
{
2019-05-04 00:34:56 +02:00
const m : : event : : prev refs { event } ;
2019-02-08 11:17:14 +01:00
2019-05-04 00:34:56 +02:00
// 1. If type is m.room.create
if ( json : : get < " type " _ > ( event ) = = " m.room.create " )
2019-02-13 02:50:44 +01:00
{
2019-05-04 00:34:56 +02:00
// a. If it has any previous events, reject.
if ( count ( refs ) | | ! empty ( auth_events ) )
return " m.room.create has previous events. " ;
2019-02-08 11:17:14 +01:00
2019-05-04 00:34:56 +02:00
// b. If the domain of the room_id does not match the domain of the
// sender, reject.
assert ( ! conforms ( event ) . has ( conforms : : MISMATCH_CREATE_SENDER ) ) ;
if ( conforms ( event ) . has ( conforms : : MISMATCH_CREATE_SENDER ) )
return " m.room.create room_id domain does not match sender domain. " ;
2019-02-13 02:50:44 +01:00
2019-05-04 00:34:56 +02:00
// c. If content.room_version is present and is not a recognised
// version, reject.
if ( json : : get < " content " _ > ( event ) . has ( " room_version " ) )
if ( unquote ( json : : get < " content " _ > ( event ) . get ( " room_version " ) ) ! = " 1 " )
return " m.room.create room_version is not recognized. " ;
2019-02-13 02:50:44 +01:00
2019-05-04 00:34:56 +02:00
// d. If content has no creator field, reject.
assert ( ! empty ( json : : get < " content " _ > ( event ) . get ( " creator " ) ) ) ;
if ( empty ( json : : get < " content " _ > ( event ) . get ( " creator " ) ) )
return " m.room.create content.creator is missing. " ;
2019-02-08 11:17:14 +01:00
2019-05-04 00:34:56 +02:00
// e. Otherwise, allow.
return { } ;
}
2019-02-08 11:50:10 +01:00
2019-05-04 00:34:56 +02:00
// 2. Reject if event has auth_events that:
const m : : event * auth_create { nullptr } ;
const m : : event * auth_power { nullptr } ;
const m : : event * auth_join_rules { nullptr } ;
const m : : event * auth_member_target { nullptr } ;
const m : : event * auth_member_sender { nullptr } ;
for ( size_t i ( 0 ) ; i < auth_events . size ( ) ; + + i )
{
// a. have duplicate entries for a given type and state_key pair
assert ( auth_events . at ( i ) ) ;
const m : : event & a ( * auth_events . at ( i ) ) ;
for ( size_t j ( 0 ) ; j < auth_events . size ( ) ; + + j ) if ( i ! = j )
{
assert ( auth_events . at ( j ) ) ;
const auto & b ( * auth_events . at ( j ) ) ;
if ( json : : get < " type " _ > ( a ) = = json : : get < " type " _ > ( b ) )
if ( json : : get < " state_key " _ > ( a ) = = json : : get < " state_key " _ > ( b ) )
return " Duplicate (type,state_key) in auth_events. " ;
}
2019-02-08 16:07:19 +01:00
2019-05-04 00:34:56 +02:00
// b. have entries whose type and state_key don't match those specified by
// the auth events selection algorithm described in the server...
const auto & type ( json : : get < " type " _ > ( a ) ) ;
if ( type = = " m.room.create " )
{
assert ( ! auth_create ) ;
auth_create = & a ;
continue ;
}
2019-02-08 11:50:10 +01:00
2019-05-04 00:34:56 +02:00
if ( type = = " m.room.power_levels " )
{
assert ( ! auth_power ) ;
auth_power = & a ;
continue ;
}
if ( type = = " m.room.join_rules " )
{
assert ( ! auth_join_rules ) ;
auth_join_rules = & a ;
continue ;
}
if ( type = = " m.room.member " )
{
if ( json : : get < " sender " _ > ( event ) = = json : : get < " state_key " _ > ( a ) )
{
assert ( ! auth_member_sender ) ;
auth_member_sender = & a ;
}
if ( json : : get < " state_key " _ > ( event ) = = json : : get < " state_key " _ > ( a ) )
{
assert ( ! auth_member_target ) ;
auth_member_target = & a ;
}
continue ;
}
return " Reference in auth_events is not an auth_event. " ;
}
// 3. If event does not have a m.room.create in its auth_events, reject.
if ( ! auth_create )
return " Missing m.room.create in auth_events " ;
const m : : room : : power power
{
auth_power ? * auth_power : m : : event { } , * auth_create
} ;
// 4. If type is m.room.aliases:
if ( json : : get < " type " _ > ( event ) = = " m.room.aliases " )
{
// a. If event has no state_key, reject.
assert ( ! conforms ( event ) . has ( conforms : : MISMATCH_ALIASES_STATE_KEY ) ) ;
if ( empty ( json : : get < " state_key " _ > ( event ) ) )
return " m.room.aliases event is missing a state_key. " ;
// b. If sender's domain doesn't matches state_key, reject.
if ( json : : get < " state_key " _ > ( event ) ! = m : : user : : id ( json : : get < " sender " _ > ( event ) ) . host ( ) )
return " m.room.aliases event state_key is not the sender's domain. " ;
// c. Otherwise, allow
return { } ;
}
// 5. If type is m.room.member:
if ( json : : get < " type " _ > ( event ) = = " m.room.member " )
{
// a. If no state_key key ...
assert ( ! conforms ( event ) . has ( conforms : : MISSING_MEMBER_STATE_KEY ) ) ;
if ( empty ( json : : get < " state_key " _ > ( event ) ) )
return " m.room.member event is missing a state_key. " ;
// a. ... or membership key in content, reject.
assert ( ! conforms ( event ) . has ( conforms : : MISSING_CONTENT_MEMBERSHIP ) ) ;
if ( empty ( unquote ( json : : get < " content " _ > ( event ) . get ( " membership " ) ) ) )
return " m.room.member event is missing a content.membership. " ;
assert ( ! conforms ( event ) . has ( conforms : : INVALID_MEMBER_STATE_KEY ) ) ;
if ( ! valid ( m : : id : : USER , json : : get < " state_key " _ > ( event ) ) )
return " m.room.member event state_key is not a valid user mxid. " ;
// b. If membership is join
if ( membership ( event ) = = " join " )
{
// i. If the only previous event is an m.room.create and the
// state_key is the creator, allow.
if ( refs . prev_events_count ( ) = = 1 & & refs . auth_events_count ( ) = = 1 )
if ( auth_create & & at < " event_id " _ > ( * auth_create ) = = refs . prev_event ( 0 ) )
return { } ;
// ii. If the sender does not match state_key, reject.
if ( json : : get < " sender " _ > ( event ) ! = json : : get < " state_key " _ > ( event ) )
return " m.room.member membership=join sender does not match state_key. " ;
// iii. If the sender is banned, reject.
if ( auth_member_target )
if ( membership ( * auth_member_target ) = = " ban " )
return " m.room.member membership=join references membership=ban auth_event. " ;
if ( auth_member_sender )
if ( membership ( * auth_member_sender ) = = " ban " )
return " m.room.member membership=join references membership=ban auth_event. " ;
if ( auth_join_rules )
{
// iv. If the join_rule is invite then allow if membership state
// is invite or join.
if ( unquote ( json : : get < " content " _ > ( * auth_join_rules ) . get ( " join_rule " ) ) = = " invite " )
if ( auth_member_target )
{
if ( membership ( * auth_member_target ) = = " invite " )
return { } ;
if ( membership ( * auth_member_target ) = = " join " )
return { } ;
}
// v. If the join_rule is public, allow.
if ( unquote ( json : : get < " content " _ > ( * auth_join_rules ) . get ( " join_rule " ) ) = = " public " )
return { } ;
}
// vi. Otherwise, reject.
return " m.room.member membership=join fails authorization. " ;
}
// c. If membership is invite
if ( membership ( event ) = = " invite " )
{
// i. If content has third_party_invite key
if ( json : : get < " content " _ > ( event ) . has ( " third_party_invite " ) )
{
//TODO: XXX
// 1. If target user is banned, reject.
// 2. If content.third_party_invite does not have a signed key, reject.
// 3. If signed does not have mxid and token keys, reject.
// 4. If mxid does not match state_key, reject.
// 5. If there is no m.room.third_party_invite event in the current room state with state_key matching token, reject.
// 6. If sender does not match sender of the m.room.third_party_invite, reject.
// 7. If any signature in signed matches any public key in the m.room.third_party_invite event, allow. The public keys are in content of m.room.third_party_invite as:
// 7.1. A single public key in the public_key field.
// 7.2. A list of public keys in the public_keys field.
// 8. Otherwise, reject.
return " third_party_invite fails authorization. " ;
}
// ii. If the sender's current membership state is not join, reject.
if ( auth_member_sender )
if ( membership ( * auth_member_sender ) ! = " join " )
return " m.room.member membership=invite sender must have membership=join. " ;
// iii. If target user's current membership state is join or ban, reject.
if ( auth_member_target )
{
if ( membership ( * auth_member_target ) = = " join " )
return " m.room.member membership=invite target cannot have membership=join. " ;
if ( membership ( * auth_member_target ) = = " ban " )
return " m.room.member membership=invite target cannot have membership=ban. " ;
}
// iv. If the sender's power level is greater than or equal to the invite level,
// allow.
if ( power ( at < " sender " _ > ( event ) , " invite " ) )
return { } ;
// v. Otherwise, reject.
return " m.room.member membership=invite fails authorization. " ;
}
// d. If membership is leave
if ( membership ( event ) = = " leave " )
{
// i. If the sender matches state_key, allow if and only if that
// user's current membership state is invite or join.
if ( json : : get < " sender " _ > ( event ) = = json : : get < " state_key " _ > ( event ) )
{
if ( auth_member_target & & membership ( * auth_member_target ) = = " join " )
return { } ;
if ( auth_member_target & & membership ( * auth_member_target ) = = " invite " )
return { } ;
return " m.room.member membership=leave self-target must have membership=join|invite. " ;
}
// ii. If the sender's current membership state is not join, reject.
if ( auth_member_sender )
if ( membership ( * auth_member_sender ) ! = " join " )
return " m.room.member membership=leave sender must have membership=join. " ;
// iii. If the target user's current membership state is ban, and the sender's
// power level is less than the ban level, reject.
if ( auth_member_target )
if ( membership ( * auth_member_target ) = = " ban " )
if ( ! power ( at < " sender " _ > ( event ) , " ban " ) )
return " m.room.member membership=ban->leave sender must have ban power to unban. " ;
// iv. If the sender's power level is greater than or equal to the
// kick level, and the target user's power level is less than the
// sender's power level, allow.
if ( power ( at < " sender " _ > ( event ) , " kick " ) )
if ( power . level_user ( at < " state_key " _ > ( event ) ) < power . level_user ( at < " sender " _ > ( event ) ) )
return { } ;
// v. Otherwise, reject.
return " m.room.member membership=leave fails authorization. " ;
}
// e. If membership is ban
if ( membership ( event ) = = " ban " )
{
// i. If the sender's current membership state is not join, reject.
if ( auth_member_sender )
if ( membership ( * auth_member_sender ) ! = " join " )
return " m.room.member membership=ban sender must have membership=join. " ;
// ii. If the sender's power level is greater than or equal to the
// ban level, and the target user's power level is less than the
// sender's power level, allow.
if ( power ( at < " sender " _ > ( event ) , " ban " ) )
if ( power . level_user ( at < " state_key " _ > ( event ) ) < power . level_user ( at < " sender " _ > ( event ) ) )
return { } ;
// iii. Otherwise, reject.
return " m.room.member membership=ban fails authorization. " ;
}
// f. Otherwise, the membership is unknown. Reject.
return " m.room.member membership=unknown. " ;
}
// 6. If the sender's current membership state is not join, reject.
if ( auth_member_sender )
if ( membership ( * auth_member_sender ) ! = " join " )
return " sender is not joined to room. " ;
// 7. If type is m.room.third_party_invite:
if ( json : : get < " type " _ > ( event ) = = " m.room.third_party_invite " )
{
// a. Allow if and only if sender's current power level is greater
// than or equal to the invite level.
if ( power ( at < " sender " _ > ( event ) , " invite " ) )
return { } ;
return " sender has power level less than required for invite. " ;
}
// 8. If the event type's required power level is greater than the
// sender's power level, reject.
if ( ! power ( at < " sender " _ > ( event ) , " events " , at < " type " _ > ( event ) ) )
return " sender has insufficient power for event type. " ;
// 9. If the event has a state_key that starts with an @ and does not
// match the sender, reject.
if ( startswith ( json : : get < " state_key " _ > ( event ) , ' @ ' ) )
if ( at < " state_key " _ > ( event ) ! = at < " sender " _ > ( event ) )
return " sender cannot set another user's mxid in a state_key. " ;
// 10. If type is m.room.power_levels:
if ( json : : get < " type " _ > ( event ) = = " m.room.power_levels " )
{
// a. If users key in content is not a dictionary with keys that are
// valid user IDs with values that are integers (or a string that is
// an integer), reject.
if ( json : : type ( json : : get < " content " _ > ( event ) . get ( " users " ) ) ! = json : : OBJECT )
return " m.room.power_levels content.users is not a json object. " ;
for ( const auto & member : json : : object ( at < " content " _ > ( event ) . at ( " users " ) ) )
{
if ( ! m : : valid ( m : : id : : USER , member . first ) )
return " m.room.power_levels content.users key is not a user mxid " ;
if ( ! try_lex_cast < int64_t > ( unquote ( member . second ) ) )
return " m.room.power_levels content.users value is not an integer. " ;
}
// b. If there is no previous m.room.power_levels event in the room, allow.
if ( ! auth_power )
return { } ;
const m : : room : : power old_power { * auth_power , * auth_create } ;
const m : : room : : power new_power { event , * auth_create } ;
const int64_t current_level
{
old_power . level_user ( at < " sender " _ > ( event ) )
} ;
// c. For each of the keys users_default, events_default,
// state_default, ban, redact, kick, invite, as well as each entry
// being changed under the events or users keys:
static const string_view keys [ ]
{
" users_default " ,
" events_default " ,
" state_default " ,
" ban " ,
" redact " ,
" kick " ,
" invite " ,
} ;
for ( const auto & key : keys )
{
const int64_t old_level ( old_power . level ( key ) ) ;
const int64_t new_level ( new_power . level ( key ) ) ;
if ( old_level = = new_level )
continue ;
// i. If the current value is higher than the sender's current
// power level, reject.
if ( old_level > current_level )
return " m.room.power_levels property denied to sender's power level. " ;
// ii. If the new value is higher than the sender's current power
// level, reject.
if ( new_level > current_level )
return " m.room.power_levels property exceeds sender's power level. " ;
}
string_view ret ;
using closure = m : : room : : power : : closure_bool ;
old_power . for_each ( " users " , closure { [ & ret , & new_power , & current_level ]
( const string_view & user_id , const int64_t & old_level )
{
if ( new_power . has_user ( user_id ) )
if ( new_power . level_user ( user_id ) = = old_level )
return true ;
// i. If the current value is higher than the sender's current
// power level, reject.
if ( old_level > current_level )
{
ret = " m.room.power_levels user property denied to sender's power level. " ;
return false ;
} ;
// ii. If the new value is higher than the sender's current power
// level, reject.
if ( new_power . level_user ( user_id ) > current_level )
{
ret = " m.room.power_levels user property exceeds sender's power level. " ;
return false ;
} ;
return true ;
} } ) ;
if ( ret )
return ret ;
new_power . for_each ( " users " , closure { [ & ret , & old_power , & current_level ]
( const string_view & user_id , const int64_t & new_level )
{
if ( old_power . has_user ( user_id ) )
if ( old_power . level_user ( user_id ) = = new_level )
return true ;
// i. If the current value is higher than the sender's current
// power level, reject.
if ( new_level > current_level )
{
ret = " m.room.power_levels user property exceeds sender's power level. " ;
return false ;
} ;
return true ;
} } ) ;
if ( ret )
return ret ;
old_power . for_each ( " events " , closure { [ & ret , & new_power , & current_level ]
( const string_view & type , const int64_t & old_level )
{
if ( new_power . has_event ( type ) )
if ( new_power . level_event ( type ) = = old_level )
return true ;
// i. If the current value is higher than the sender's current
// power level, reject.
if ( old_level > current_level )
{
ret = " m.room.power_levels event property denied to sender's power level. " ;
return false ;
} ;
// ii. If the new value is higher than the sender's current power
// level, reject.
if ( new_power . level_event ( type ) > current_level )
{
ret = " m.room.power_levels event property exceeds sender's power level. " ;
return false ;
} ;
return true ;
} } ) ;
if ( ret )
return ret ;
new_power . for_each ( " events " , closure { [ & ret , & old_power , & current_level ]
( const string_view & type , const int64_t & new_level )
{
if ( old_power . has_event ( type ) )
if ( old_power . level_event ( type ) = = new_level )
return true ;
// i. If the current value is higher than the sender's current
// power level, reject.
if ( new_level > current_level )
{
ret = " m.room.power_levels event property exceeds sender's power level. " ;
return false ;
} ;
return true ;
} } ) ;
// d. For each entry being changed under the users key...
old_power . for_each ( " users " , closure { [ & ret , & event , & new_power , & current_level ]
( const string_view & user_id , const int64_t & old_level )
{
// ...other than the sender's own entry:
if ( user_id = = at < " sender " _ > ( event ) )
return true ;
if ( new_power . has_user ( user_id ) )
if ( new_power . level_user ( user_id ) = = old_level )
return true ;
// i. If the current value is equal to the sender's current power
// level, reject.
if ( old_level = = current_level )
{
ret = " m.room.power_levels user property denied to sender's power level. " ;
return false ;
} ;
return true ;
} } ) ;
if ( ret )
return ret ;
// e. Otherwise, allow.
assert ( ! ret ) ;
return ret ;
}
// 11. If type is m.room.redaction:
if ( json : : get < " type " _ > ( event ) = = " m.room.redaction " )
{
// a. If the sender's power level is greater than or equal to the
// redact level, allow.
if ( power ( at < " sender " _ > ( event ) , " redact " ) )
return { } ;
// b. If the domain of the event_id of the event being redacted is the
// same as the domain of the event_id of the m.room.redaction, allow.
if ( event : : id ( json : : get < " redacts " _ > ( event ) ) . host ( ) = = event : : id ( at < " event_id " _ > ( event ) ) . host ( ) )
return { } ;
// c. Otherwise, reject.
return " m.room.redaction fails authorization. " ;
}
// 12. Otherwise, allow.
return { } ;
}
bool
ircd : : m : : event : : auth : : is_power_event ( const m : : event & event )
{
if ( ! json : : get < " type " _ > ( event ) )
return false ;
if ( json : : get < " type " _ > ( event ) = = " m.room.create " )
return true ;
if ( json : : get < " type " _ > ( event ) = = " m.room.power_levels " )
return true ;
if ( json : : get < " type " _ > ( event ) = = " m.room.join_rules " )
return true ;
if ( json : : get < " type " _ > ( event ) ! = " m.room.member " )
return false ;
if ( ! json : : get < " sender " _ > ( event ) | | ! json : : get < " state_key " _ > ( event ) )
return false ;
if ( json : : get < " sender " _ > ( event ) = = json : : get < " state_key " _ > ( event ) )
return false ;
if ( membership ( event ) = = " leave " | | membership ( event ) = = " ban " )
return true ;
return false ;
}
//
// event::auth::refs
//
size_t
ircd : : m : : event : : auth : : refs : : count ( )
const noexcept
{
return count ( string_view { } ) ;
}
size_t
ircd : : m : : event : : auth : : refs : : count ( const string_view & type )
const noexcept
{
size_t ret ( 0 ) ;
for_each ( type , [ & ret ] ( const auto & )
{
+ + ret ;
return true ;
} ) ;
return ret ;
}
2019-02-08 11:50:10 +01:00
bool
2019-02-13 02:50:44 +01:00
ircd : : m : : event : : auth : : refs : : has ( const event : : idx & idx )
2019-02-08 11:50:10 +01:00
const noexcept
{
return ! for_each ( [ & idx ] ( const event : : idx & ref )
{
return ref ! = idx ; // true to continue, false to break
} ) ;
}
2019-02-08 16:07:19 +01:00
bool
2019-02-13 02:50:44 +01:00
ircd : : m : : event : : auth : : refs : : has ( const string_view & type )
2019-02-08 16:07:19 +01:00
const noexcept
{
bool ret { false } ;
for_each ( type , [ & ret ] ( const auto & )
{
ret = true ;
return false ;
} ) ;
return ret ;
}
2019-02-08 11:50:10 +01:00
bool
2019-02-13 02:50:44 +01:00
ircd : : m : : event : : auth : : refs : : for_each ( const closure_bool & closure )
2019-02-08 11:50:10 +01:00
const
2019-02-08 16:07:19 +01:00
{
return for_each ( string_view { } , closure ) ;
}
bool
2019-02-13 02:50:44 +01:00
ircd : : m : : event : : auth : : refs : : for_each ( const string_view & type ,
const closure_bool & closure )
2019-02-08 16:07:19 +01:00
const
2019-02-08 11:50:10 +01:00
{
2019-02-13 02:50:44 +01:00
assert ( idx ) ;
2019-02-14 23:02:34 +01:00
const event : : refs erefs
2019-02-08 11:50:10 +01:00
{
2019-02-14 23:02:34 +01:00
idx
2019-02-08 11:50:10 +01:00
} ;
2019-05-10 07:43:28 +02:00
return erefs . for_each ( dbs : : ref : : NEXT_AUTH , [ this , & type , & closure ]
2019-02-14 23:02:34 +01:00
( const event : : idx & ref , const dbs : : ref & )
2019-02-08 11:50:10 +01:00
{
2019-02-08 16:07:19 +01:00
bool match ;
const auto matcher
{
[ & type , & match ] ( const string_view & type_ )
{
match = type = = type_ ;
}
} ;
if ( type )
{
if ( ! m : : get ( std : : nothrow , ref , " type " , matcher ) )
2019-02-14 23:02:34 +01:00
return true ;
2019-02-08 16:07:19 +01:00
if ( ! match )
2019-02-14 23:02:34 +01:00
return true ;
2019-02-08 16:07:19 +01:00
}
2019-02-08 11:50:10 +01:00
assert ( idx ! = ref ) ;
if ( ! closure ( ref ) )
return false ;
2019-02-14 23:02:34 +01:00
return true ;
} ) ;
2019-02-08 11:50:10 +01:00
}
2019-02-13 02:50:44 +01:00
//
// event::auth::chain
//
size_t
ircd : : m : : event : : auth : : chain : : depth ( )
const noexcept
{
size_t ret ( 0 ) ;
2019-02-17 00:03:49 +01:00
for_each ( [ & ret ] ( const auto & )
2019-02-13 02:50:44 +01:00
{
+ + ret ;
} ) ;
return ret ;
}
bool
ircd : : m : : event : : auth : : chain : : has ( const string_view & type )
const noexcept
{
bool ret ( false ) ;
2019-02-17 00:03:49 +01:00
for_each ( closure_bool { [ & type , & ret ]
( const auto & idx )
2019-02-13 02:50:44 +01:00
{
2019-02-17 00:03:49 +01:00
m : : get ( std : : nothrow , idx , " type " , [ & type , & ret ]
( const auto & value )
{
ret = value = = type ;
} ) ;
2019-02-13 02:50:44 +01:00
return ! ret ;
2019-02-17 00:03:49 +01:00
} } ) ;
2019-02-13 02:50:44 +01:00
return ret ;
}
2019-02-17 00:03:49 +01:00
bool
ircd : : m : : event : : auth : : chain : : for_each ( const closure & closure )
const
{
return for_each ( closure_bool { [ & closure ]
( const auto & idx )
{
closure ( idx ) ;
return true ;
} } ) ;
}
2019-02-13 02:50:44 +01:00
bool
ircd : : m : : event : : auth : : chain : : for_each ( const closure_bool & closure )
const
{
2019-02-15 01:48:45 +01:00
return chain : : for_each ( * this , closure ) ;
}
bool
ircd : : m : : event : : auth : : chain : : for_each ( const auth : : chain & c ,
const closure_bool & closure )
{
2019-05-04 00:34:56 +02:00
m : : event : : fetch e , a ;
std : : set < event : : idx > ae ;
std : : deque < event : : idx > aq { c . idx } ; do
2019-02-15 01:48:45 +01:00
{
2019-05-04 00:34:56 +02:00
const auto idx ( aq . front ( ) ) ;
aq . pop_front ( ) ;
if ( ! seek ( e , idx , std : : nothrow ) )
continue ;
2019-02-15 01:48:45 +01:00
2019-05-04 00:34:56 +02:00
const m : : event : : prev prev { e } ;
const auto count ( prev . auth_events_count ( ) ) ;
for ( size_t i ( 0 ) ; i < count & & i < 4 ; + + i )
{
const auto & auth_event_id ( prev . auth_event ( i ) ) ;
const auto & auth_event_idx ( index ( auth_event_id , std : : nothrow ) ) ;
if ( ! auth_event_idx )
continue ;
auto it ( ae . lower_bound ( auth_event_idx ) ) ;
if ( it = = end ( ae ) | | * it ! = auth_event_idx )
{
seek ( a , auth_event_idx , std : : nothrow ) ;
ae . emplace_hint ( it , auth_event_idx ) ;
if ( a . valid )
aq . emplace_back ( auth_event_idx ) ;
}
}
}
while ( ! aq . empty ( ) ) ;
for ( const auto & idx : ae )
if ( ! closure ( idx ) )
return false ;
return true ;
2019-02-13 02:50:44 +01:00
}
2019-05-17 08:57:51 +02:00
///////////////////////////////////////////////////////////////////////////////
//
// event/horizon.h
//
size_t
ircd : : m : : event : : horizon : : rebuild ( )
{
throw not_implemented { } ;
}
bool
ircd : : m : : event : : horizon : : has ( const event : : id & event_id )
{
char buf [ m : : dbs : : EVENT_HORIZON_KEY_MAX_SIZE ] ;
const string_view & key
{
m : : dbs : : event_horizon_key ( buf , event_id , 0UL )
} ;
auto it
{
m : : dbs : : event_horizon . begin ( key )
} ;
return bool ( it ) ;
}
size_t
ircd : : m : : event : : horizon : : count ( )
const
{
size_t ret ( 0 ) ;
2019-05-17 11:14:29 +02:00
for_each ( [ & ret ]
( const auto & , const auto & )
2019-05-17 08:57:51 +02:00
{
+ + ret ;
return true ;
} ) ;
return ret ;
}
bool
ircd : : m : : event : : horizon : : has ( const event : : idx & event_idx )
const
{
return ! for_each ( [ & event_idx ]
2019-05-17 11:14:29 +02:00
( const auto & , const auto & _event_idx )
2019-05-17 08:57:51 +02:00
{
// false to break; true to continue.
return _event_idx = = event_idx ? false : true ;
} ) ;
}
bool
ircd : : m : : event : : horizon : : for_each ( const closure_bool & closure )
const
{
2019-05-17 11:14:29 +02:00
if ( ! this - > event_id )
{
db : : column & column { dbs : : event_horizon } ;
for ( auto it ( column . begin ( ) ) ; it ; + + it )
{
const auto & parts
{
split ( it - > first , " \0 " _sv )
} ;
const auto & event_id
{
parts . first
} ;
const auto & event_idx
{
byte_view < event : : idx > ( parts . second )
} ;
if ( ! closure ( event_id , event_idx ) )
return false ;
}
return true ;
}
2019-05-17 08:57:51 +02:00
char buf [ m : : dbs : : EVENT_HORIZON_KEY_MAX_SIZE ] ;
const string_view & key
{
m : : dbs : : event_horizon_key ( buf , event_id , 0UL )
} ;
2019-05-17 11:14:29 +02:00
for ( auto it ( m : : dbs : : event_horizon . begin ( key ) ) ; it ; + + it )
2019-05-17 08:57:51 +02:00
{
const auto & event_idx
{
std : : get < 0 > ( m : : dbs : : event_horizon_key ( it - > first ) )
} ;
2019-05-17 11:14:29 +02:00
if ( ! closure ( event_id , event_idx ) )
2019-05-17 08:57:51 +02:00
return false ;
}
return true ;
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
2019-02-07 01:27:48 +01:00
//
// event/refs.h
//
2019-02-09 23:51:19 +01:00
void
ircd : : m : : event : : refs : : rebuild ( )
{
2019-05-04 00:34:56 +02:00
static const size_t pool_size { 96 } ;
static const size_t log_interval { 8192 } ;
2019-02-09 23:51:19 +01:00
2019-05-04 00:34:56 +02:00
db : : txn txn
2019-02-09 23:51:19 +01:00
{
2019-05-04 00:34:56 +02:00
* m : : dbs : : events
2019-02-09 23:51:19 +01:00
} ;
2019-05-04 00:34:56 +02:00
auto & column
{
dbs : : event_json
} ;
auto it
{
column . begin ( )
} ;
ctx : : dock dock ;
ctx : : pool pool ;
pool . min ( pool_size ) ;
size_t i ( 0 ) , j ( 0 ) ;
const ctx : : uninterruptible : : nothrow ui ;
for ( ; it ; + + it )
{
if ( ctx : : interruption_requested ( ) )
break ;
const m : : event : : idx event_idx
{
byte_view < m : : event : : idx > ( it - > first )
} ;
std : : string event { it - > second } ;
pool ( [ & txn , & dock , & i , & j , event ( std : : move ( event ) ) , event_idx ]
{
m : : dbs : : write_opts wopts ;
wopts . event_idx = event_idx ;
2019-05-15 06:34:29 +02:00
wopts . appendix . reset ( ) ;
wopts . appendix . set ( dbs : : appendix : : EVENT_REFS ) ;
m : : dbs : : write ( txn , json : : object { event } , wopts ) ;
2019-05-04 00:34:56 +02:00
if ( + + j % log_interval = = 0 ) log : : info
{
m : : log , " Refs builder @%zu:%zu of %lu (@idx: %lu) " ,
i ,
j ,
m : : vm : : sequence : : retired ,
event_idx
} ;
if ( j > = i )
dock . notify_one ( ) ;
} ) ;
+ + i ;
}
dock . wait ( [ & i , & j ]
{
return i = = j ;
} ) ;
txn ( ) ;
2019-02-09 23:51:19 +01:00
}
2019-02-07 01:27:48 +01:00
size_t
ircd : : m : : event : : refs : : count ( )
const noexcept
2019-02-14 23:02:34 +01:00
{
return count ( dbs : : ref ( - 1 ) ) ;
}
size_t
ircd : : m : : event : : refs : : count ( const dbs : : ref & type )
const noexcept
2019-02-07 01:27:48 +01:00
{
assert ( idx ) ;
size_t ret ( 0 ) ;
2019-02-14 23:02:34 +01:00
for_each ( type , [ & ret ]
( const event : : idx & ref , const dbs : : ref & )
2019-02-07 01:27:48 +01:00
{
+ + ret ;
return true ;
} ) ;
return ret ;
}
bool
ircd : : m : : event : : refs : : has ( const event : : idx & idx )
const noexcept
{
2019-02-14 23:02:34 +01:00
return has ( dbs : : ref ( - 1 ) , idx ) ;
}
bool
ircd : : m : : event : : refs : : has ( const dbs : : ref & type ,
const event : : idx & idx )
const noexcept
{
return ! for_each ( type , [ & idx ]
( const event : : idx & ref , const dbs : : ref & )
2019-02-07 01:27:48 +01:00
{
return ref ! = idx ; // true to continue, false to break
} ) ;
}
bool
ircd : : m : : event : : refs : : for_each ( const closure_bool & closure )
const
2019-02-14 23:02:34 +01:00
{
return for_each ( dbs : : ref ( - 1 ) , closure ) ;
}
bool
ircd : : m : : event : : refs : : for_each ( const dbs : : ref & type ,
const closure_bool & closure )
const
2019-02-07 01:27:48 +01:00
{
2019-02-14 22:11:37 +01:00
assert ( idx ) ;
thread_local char buf [ dbs : : EVENT_REFS_KEY_MAX_SIZE ] ;
2019-02-14 23:02:34 +01:00
// Allow -1 to iterate through all types by starting
// the iteration at type value 0 and then ignoring the
// type as a loop continue condition.
const bool all_type ( type = = dbs : : ref ( uint8_t ( - 1 ) ) ) ;
2019-05-10 07:43:28 +02:00
const auto & _type { all_type ? dbs : : ref : : NEXT : type } ;
assert ( uint8_t ( dbs : : ref : : NEXT ) = = 0 ) ;
2019-02-14 22:11:37 +01:00
const string_view key
2019-02-07 01:27:48 +01:00
{
2019-02-14 23:02:34 +01:00
dbs : : event_refs_key ( buf , idx , _type , 0 )
2019-02-07 01:27:48 +01:00
} ;
auto it
{
2019-02-14 22:11:37 +01:00
dbs : : event_refs . begin ( key )
2019-02-07 01:27:48 +01:00
} ;
for ( ; it ; + + it )
{
const auto parts
{
dbs : : event_refs_key ( it - > first )
} ;
2019-02-14 22:11:37 +01:00
const auto & type
2019-02-07 01:27:48 +01:00
{
std : : get < 0 > ( parts )
} ;
2019-02-14 23:02:34 +01:00
if ( ! all_type & & type ! = _type )
2019-02-14 22:11:37 +01:00
break ;
const auto & ref
{
std : : get < 1 > ( parts )
} ;
2019-02-07 01:27:48 +01:00
assert ( idx ! = ref ) ;
2019-02-14 23:02:34 +01:00
if ( ! closure ( ref , type ) )
2019-02-07 01:27:48 +01:00
return false ;
}
return true ;
}
///////////////////////////////////////////////////////////////////////////////
2018-02-10 22:28:22 +01:00
//
2019-01-26 23:15:36 +01:00
// event/prev.h
2018-02-10 22:28:22 +01:00
//
2019-04-29 20:05:43 +02:00
bool
ircd : : m : : event : : prev : : prev_event_exists ( const size_t & idx )
const
{
return m : : exists ( prev_state ( idx ) ) ;
}
bool
ircd : : m : : event : : prev : : prev_state_exists ( const size_t & idx )
const
{
return m : : exists ( prev_state ( idx ) ) ;
}
bool
ircd : : m : : event : : prev : : auth_event_exists ( const size_t & idx )
const
{
return m : : exists ( auth_event ( idx ) ) ;
}
2018-08-26 08:28:23 +02:00
bool
ircd : : m : : event : : prev : : prev_events_has ( const event : : id & event_id )
const
{
for ( const json : : array & p : json : : get < " prev_events " _ > ( * this ) )
if ( unquote ( p . at ( 0 ) ) = = event_id )
return true ;
return false ;
}
bool
ircd : : m : : event : : prev : : prev_states_has ( const event : : id & event_id )
const
{
for ( const json : : array & p : json : : get < " prev_state " _ > ( * this ) )
if ( unquote ( p . at ( 0 ) ) = = event_id )
return true ;
return false ;
}
bool
ircd : : m : : event : : prev : : auth_events_has ( const event : : id & event_id )
const
{
for ( const json : : array & p : json : : get < " auth_events " _ > ( * this ) )
if ( unquote ( p . at ( 0 ) ) = = event_id )
return true ;
return false ;
}
size_t
ircd : : m : : event : : prev : : prev_events_count ( )
const
{
return json : : get < " prev_events " _ > ( * this ) . count ( ) ;
}
size_t
ircd : : m : : event : : prev : : prev_states_count ( )
const
{
return json : : get < " prev_state " _ > ( * this ) . count ( ) ;
}
size_t
ircd : : m : : event : : prev : : auth_events_count ( )
const
{
return json : : get < " auth_events " _ > ( * this ) . count ( ) ;
}
2018-02-10 22:28:22 +01:00
ircd : : m : : event : : id
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : auth_event ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
return std : : get < 0 > ( auth_events ( idx ) ) ;
}
ircd : : m : : event : : id
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : prev_state ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
return std : : get < 0 > ( prev_states ( idx ) ) ;
}
ircd : : m : : event : : id
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : prev_event ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
return std : : get < 0 > ( prev_events ( idx ) ) ;
}
std : : tuple < ircd : : m : : event : : id , ircd : : string_view >
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : auth_events ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
const json : : array & auth_event
{
at < " auth_events " _ > ( * this ) . at ( idx )
} ;
return
{
unquote ( auth_event . at ( 0 ) ) , unquote ( auth_event [ 1 ] )
} ;
}
std : : tuple < ircd : : m : : event : : id , ircd : : string_view >
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : prev_states ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
const json : : array & state_event
{
at < " prev_state " _ > ( * this ) . at ( idx )
} ;
return
{
unquote ( state_event . at ( 0 ) ) , unquote ( state_event [ 1 ] )
} ;
}
std : : tuple < ircd : : m : : event : : id , ircd : : string_view >
2019-04-29 19:57:43 +02:00
ircd : : m : : event : : prev : : prev_events ( const size_t & idx )
2018-02-10 22:28:22 +01:00
const
{
const json : : array & prev_event
{
at < " prev_events " _ > ( * this ) . at ( idx )
} ;
return
{
unquote ( prev_event . at ( 0 ) ) , unquote ( prev_event [ 1 ] )
} ;
}
2018-09-01 13:59:39 +02:00
void
2019-01-26 23:15:36 +01:00
ircd : : m : : for_each ( const event : : prev & prev ,
const event : : id : : closure & closure )
2018-09-01 13:59:39 +02:00
{
2019-01-26 23:15:36 +01:00
m : : for_each ( prev , event : : id : : closure_bool { [ & closure ]
( const event : : id & event_id )
2018-09-01 13:59:39 +02:00
{
2019-01-26 23:15:36 +01:00
closure ( event_id ) ;
return true ;
} } ) ;
2018-09-01 13:59:39 +02:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : for_each ( const event : : prev & prev ,
const event : : id : : closure_bool & closure )
2018-05-12 04:21:56 +02:00
{
2019-01-26 23:15:36 +01:00
return json : : until ( prev , [ & closure ]
( const auto & key , const json : : array & prevs )
{
for ( const json : : array & prev : prevs )
if ( ! closure ( event : : id ( unquote ( prev . at ( 0 ) ) ) ) )
return false ;
2018-05-12 04:21:56 +02:00
2019-01-26 23:15:36 +01:00
return true ;
} ) ;
2018-05-12 04:21:56 +02:00
}
2019-01-26 23:15:36 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// event/event.h
//
2019-05-04 00:34:56 +02:00
namespace ircd : : m
{
static json : : object make_hashes ( const mutable_buffer & out , const sha256 : : buf & hash ) ;
}
2019-01-26 23:15:36 +01:00
/// The maximum size of an event we will create. This may also be used in
/// some contexts for what we will accept, but the protocol limit and hard
/// worst-case buffer size is still event::MAX_SIZE.
decltype ( ircd : : m : : event : : max_size )
ircd : : m : : event : : max_size
2018-05-12 04:21:56 +02:00
{
2019-01-26 23:15:36 +01:00
{ " name " , " m.event.max_size " } ,
{ " default " , 65507L } ,
} ;
2018-05-12 04:21:56 +02:00
2019-01-26 23:15:36 +01:00
ircd : : json : : object
ircd : : m : : hashes ( const mutable_buffer & out ,
const event & event )
2018-05-12 04:21:56 +02:00
{
2019-01-26 23:15:36 +01:00
const sha256 : : buf hash_
2019-01-15 23:46:35 +01:00
{
2019-01-26 23:15:36 +01:00
hash ( event )
2019-01-15 23:46:35 +01:00
} ;
2019-01-26 23:15:36 +01:00
return make_hashes ( out , hash_ ) ;
}
2018-05-12 04:21:56 +02:00
2019-01-26 23:15:36 +01:00
ircd : : json : : object
ircd : : m : : event : : hashes ( const mutable_buffer & out ,
json : : iov & event ,
const string_view & content )
{
const sha256 : : buf hash_
2018-05-12 04:21:56 +02:00
{
2019-01-26 23:15:36 +01:00
hash ( event , content )
2018-05-12 04:21:56 +02:00
} ;
2019-01-26 23:15:36 +01:00
return make_hashes ( out , hash_ ) ;
2018-05-12 04:21:56 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : json : : object
ircd : : m : : make_hashes ( const mutable_buffer & out ,
const sha256 : : buf & hash )
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
static const auto b64bufsz ( b64encode_size ( sizeof ( hash ) ) ) ;
thread_local char hashb64buf [ b64bufsz ] ;
const json : : members hashes
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
{ " sha256 " , b64encode_unpadded ( hashb64buf , hash ) }
2019-01-16 21:12:59 +01:00
} ;
2019-01-26 23:15:36 +01:00
return json : : stringify ( mutable_buffer { out } , hashes ) ;
2019-01-16 21:12:59 +01:00
}
2019-02-07 05:52:42 +01:00
ircd : : sha256 : : buf
ircd : : m : : event : : hash ( const json : : object & event )
try
{
static const size_t iov_max { json : : iov : : max_size } ;
thread_local std : : array < json : : object : : member , iov_max > member ;
size_t i ( 0 ) ;
for ( const auto & m : event )
{
if ( m . first = = " signatures " | |
2019-05-12 01:09:06 +02:00
m . first = = " hashes " | |
2019-02-07 05:52:42 +01:00
m . first = = " unsigned " | |
2019-05-12 01:09:06 +02:00
m . first = = " age_ts " | |
m . first = = " outlier " | |
m . first = = " destinations " )
2019-02-07 05:52:42 +01:00
continue ;
member . at ( i + + ) = m ;
}
thread_local char buf [ event : : MAX_SIZE ] ;
const string_view reimage
{
json : : stringify ( buf , member . data ( ) , member . data ( ) + i )
} ;
return sha256 { reimage } ;
}
catch ( const std : : out_of_range & e )
{
throw m : : BAD_JSON
{
" Object has more than %zu member properties. " ,
json : : iov : : max_size
} ;
}
2019-01-26 23:15:36 +01:00
ircd : : sha256 : : buf
ircd : : m : : event : : hash ( json : : iov & event ,
const string_view & content )
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
const json : : iov : : push _content
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
event , { " content " , content }
2019-01-16 21:12:59 +01:00
} ;
2019-01-26 23:15:36 +01:00
return m : : hash ( event ) ;
2019-01-16 21:12:59 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : sha256 : : buf
ircd : : m : : hash ( const event & event )
2019-01-16 21:12:59 +01:00
{
2019-02-07 05:52:42 +01:00
if ( event . source )
return event : : hash ( event . source ) ;
2019-01-26 23:15:36 +01:00
2019-02-07 05:52:42 +01:00
m : : event event_ { event } ;
json : : get < " signatures " _ > ( event_ ) = { } ;
json : : get < " hashes " _ > ( event_ ) = { } ;
2019-01-16 21:12:59 +01:00
2019-02-07 05:52:42 +01:00
thread_local char buf [ event : : MAX_SIZE ] ;
const string_view preimage
2019-01-26 23:15:36 +01:00
{
2019-02-07 05:52:42 +01:00
stringify ( buf , event_ )
2019-01-26 23:15:36 +01:00
} ;
2019-02-07 05:52:42 +01:00
return sha256 { preimage } ;
2019-01-16 21:12:59 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : verify_hash ( const event & event )
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
const sha256 : : buf hash
2019-01-16 21:12:59 +01:00
{
2019-01-26 23:15:36 +01:00
m : : hash ( event )
2019-01-16 21:12:59 +01:00
} ;
2019-01-26 23:15:36 +01:00
return verify_hash ( event , hash ) ;
2019-01-16 21:12:59 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : verify_hash ( const event & event ,
const sha256 : : buf & hash )
2018-02-09 20:22:46 +01:00
{
2019-01-26 23:15:36 +01:00
static const size_t hashb64sz
{
size_t ( hash . size ( ) * 1.34 ) + 1
} ;
thread_local char b64buf [ hashb64sz ] ;
return verify_sha256b64 ( event , b64encode_unpadded ( b64buf , hash ) ) ;
2018-02-09 20:22:46 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : verify_sha256b64 ( const event & event ,
const string_view & b64 )
try
2018-02-09 20:22:46 +01:00
{
2019-01-26 23:15:36 +01:00
const json : : object & object
2018-04-18 04:14:39 +02:00
{
2019-01-26 23:15:36 +01:00
at < " hashes " _ > ( event )
2018-04-18 04:14:39 +02:00
} ;
2019-01-26 23:15:36 +01:00
const string_view & hash
2019-01-17 23:08:26 +01:00
{
2019-01-26 23:15:36 +01:00
unquote ( object . at ( " sha256 " ) )
} ;
2019-01-17 23:08:26 +01:00
2019-01-26 23:15:36 +01:00
return hash = = b64 ;
2018-04-18 04:14:39 +02:00
}
2019-01-26 23:15:36 +01:00
catch ( const json : : not_found & )
2018-04-18 04:14:39 +02:00
{
2019-01-26 23:15:36 +01:00
return false ;
2018-04-18 04:14:39 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : json : : object
ircd : : m : : event : : signatures ( const mutable_buffer & out ,
json : : iov & event ,
const json : : iov & content )
2018-04-18 04:14:39 +02:00
{
2019-01-26 23:15:36 +01:00
const ed25519 : : sig sig
2019-01-15 23:46:35 +01:00
{
2019-01-26 23:15:36 +01:00
sign ( event , content )
2019-01-15 23:46:35 +01:00
} ;
2019-01-26 23:15:36 +01:00
thread_local char sigb64buf [ b64encode_size ( sizeof ( sig ) ) ] ;
const json : : members sigb64
2018-04-18 04:14:39 +02:00
{
2019-01-26 23:15:36 +01:00
{ self : : public_key_id , b64encode_unpadded ( sigb64buf , sig ) }
2018-04-18 04:14:39 +02:00
} ;
2019-01-26 23:15:36 +01:00
const json : : members sigs
{
2019-02-05 09:41:15 +01:00
{ event . at ( " origin " ) , sigb64 }
2019-01-26 23:15:36 +01:00
} ;
2019-01-15 23:46:35 +01:00
2019-01-26 23:15:36 +01:00
return json : : stringify ( mutable_buffer { out } , sigs ) ;
2018-02-09 20:22:46 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : m : : event
ircd : : m : : signatures ( const mutable_buffer & out_ ,
const m : : event & event_ )
{
thread_local char content [ event : : MAX_SIZE ] ;
m : : event event
{
essential ( event_ , content )
} ;
2019-01-17 02:53:47 +01:00
2019-01-26 23:15:36 +01:00
thread_local char buf [ event : : MAX_SIZE ] ;
const json : : object & preimage
{
stringify ( buf , event )
} ;
2019-01-17 02:53:47 +01:00
2019-01-26 23:15:36 +01:00
const ed25519 : : sig sig
{
sign ( preimage )
} ;
2019-02-05 09:41:15 +01:00
const auto sig_host
{
my_host ( json : : get < " origin " _ > ( event ) ) ?
json : : get < " origin " _ > ( event ) :
my_host ( )
} ;
2019-01-26 23:15:36 +01:00
thread_local char sigb64buf [ b64encode_size ( sizeof ( sig ) ) ] ;
const json : : member my_sig
{
2019-02-05 09:41:15 +01:00
sig_host , json : : members
2018-04-18 04:14:39 +02:00
{
2019-01-26 23:15:36 +01:00
{ self : : public_key_id , b64encode_unpadded ( sigb64buf , sig ) }
}
} ;
2018-04-18 04:14:39 +02:00
2019-01-26 23:15:36 +01:00
static const size_t SIG_MAX { 64 } ;
thread_local std : : array < json : : member , SIG_MAX > sigs ;
2018-04-18 04:14:39 +02:00
2019-01-26 23:15:36 +01:00
size_t i ( 0 ) ;
sigs . at ( i + + ) = my_sig ;
for ( const auto & other : json : : get < " signatures " _ > ( event_ ) )
if ( ! my_host ( unquote ( other . first ) ) )
sigs . at ( i + + ) = { other . first , other . second } ;
2019-01-17 02:53:47 +01:00
2019-01-26 23:15:36 +01:00
event = event_ ;
mutable_buffer out { out_ } ;
json : : get < " signatures " _ > ( event ) = json : : stringify ( out , sigs . data ( ) , sigs . data ( ) + i ) ;
return event ;
2018-04-18 04:14:39 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( json : : iov & event ,
const json : : iov & contents )
2018-05-20 06:20:44 +02:00
{
2019-01-26 23:15:36 +01:00
return sign ( event , contents , self : : secret_key ) ;
2018-05-20 06:20:44 +02:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( json : : iov & event ,
const json : : iov & contents ,
const ed25519 : : sk & sk )
2018-05-20 06:20:44 +02:00
{
2019-01-26 23:15:36 +01:00
ed25519 : : sig sig ;
essential ( event , contents , [ & sk , & sig ]
( json : : iov & event )
{
sig = m : : sign ( event , sk ) ;
} ) ;
2018-05-20 06:20:44 +02:00
2019-01-26 23:15:36 +01:00
return sig ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : sign ( const event & event )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
return sign ( event , self : : secret_key ) ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : sign ( const event & event ,
const ed25519 : : sk & sk )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
thread_local char buf [ event : : MAX_SIZE ] ;
const string_view preimage
{
stringify ( buf , event )
} ;
return event : : sign ( preimage , sk ) ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( const json : : object & event )
2019-01-26 22:49:34 +01:00
{
2019-01-26 23:15:36 +01:00
return sign ( event , self : : secret_key ) ;
2019-01-26 22:49:34 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( const json : : object & event ,
const ed25519 : : sk & sk )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
//TODO: skip rewrite
thread_local char buf [ event : : MAX_SIZE ] ;
const string_view preimage
{
stringify ( buf , event )
} ;
return sign ( preimage , sk ) ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( const string_view & event )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
return sign ( event , self : : secret_key ) ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : ed25519 : : sig
ircd : : m : : event : : sign ( const string_view & event ,
const ed25519 : : sk & sk )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
const ed25519 : : sig sig
{
sk . sign ( event )
} ;
return sig ;
2019-01-18 20:10:21 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : verify ( const event & event )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
const string_view & origin
{
at < " origin " _ > ( event )
} ;
return verify ( event , origin ) ;
2019-01-18 20:10:21 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : verify ( const event & event ,
const string_view & origin )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
const json : : object & signatures
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
at < " signatures " _ > ( event )
2019-01-18 20:10:21 +01:00
} ;
2019-01-26 23:15:36 +01:00
const json : : object & origin_sigs
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
signatures . at ( origin )
2019-01-18 20:10:21 +01:00
} ;
2019-01-26 23:15:36 +01:00
for ( const auto & p : origin_sigs )
if ( verify ( event , origin , unquote ( p . first ) ) )
return true ;
2019-01-18 20:10:21 +01:00
2019-01-26 23:15:36 +01:00
return false ;
2019-01-18 20:10:21 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : verify ( const event & event ,
const string_view & origin ,
const string_view & keyid )
try
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
const m : : node node
{
2019-05-27 02:44:12 +02:00
origin
2019-01-26 23:15:36 +01:00
} ;
2019-01-18 20:10:21 +01:00
2019-01-26 23:15:36 +01:00
bool ret { false } ;
node . key ( keyid , [ & ret , & event , & origin , & keyid ]
( const ed25519 : : pk & pk )
{
ret = verify ( event , pk , origin , keyid ) ;
} ) ;
2019-01-18 20:10:21 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
}
catch ( const m : : NOT_FOUND & e )
2019-01-18 20:10:21 +01:00
{
2019-01-26 23:15:36 +01:00
log : : derror
{
" Failed to verify %s because key %s for %s :%s " ,
string_view { json : : get < " event_id " _ > ( event ) } ,
keyid ,
origin ,
e . what ( )
} ;
2019-01-18 20:10:21 +01:00
return false ;
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : verify ( const event & event ,
const ed25519 : : pk & pk ,
const string_view & origin ,
const string_view & keyid )
2018-05-20 12:01:58 +02:00
{
2019-01-26 23:15:36 +01:00
const json : : object & signatures
{
at < " signatures " _ > ( event )
} ;
2018-05-20 12:01:58 +02:00
2019-01-26 23:15:36 +01:00
const json : : object & origin_sigs
{
signatures . at ( origin )
} ;
2018-05-20 12:01:58 +02:00
2019-01-26 23:15:36 +01:00
const ed25519 : : sig sig
{
[ & origin_sigs , & keyid ] ( auto & buf )
{
b64decode ( buf , unquote ( origin_sigs . at ( keyid ) ) ) ;
}
} ;
2018-02-21 03:09:39 +01:00
2019-01-26 23:15:36 +01:00
return verify ( event , pk , sig ) ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : verify ( const event & event_ ,
const ed25519 : : pk & pk ,
const ed25519 : : sig & sig )
2018-02-21 03:09:39 +01:00
{
2019-02-08 08:41:29 +01:00
thread_local char buf [ 2 ] [ event : : MAX_SIZE ] ;
2019-01-26 23:15:36 +01:00
m : : event event
{
2019-02-08 08:41:29 +01:00
essential ( event_ , buf [ 0 ] )
2019-01-26 23:15:36 +01:00
} ;
2018-02-21 03:09:39 +01:00
2019-01-26 23:15:36 +01:00
const json : : object & preimage
{
2019-02-08 08:41:29 +01:00
stringify ( buf [ 1 ] , event )
2019-01-26 23:15:36 +01:00
} ;
2018-02-21 03:09:39 +01:00
2019-02-08 08:41:29 +01:00
return pk . verify ( preimage , sig ) ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : event : : verify ( const json : : object & event ,
const ed25519 : : pk & pk ,
const ed25519 : : sig & sig )
2018-02-22 00:00:02 +01:00
{
2019-01-26 23:15:36 +01:00
thread_local char buf [ event : : MAX_SIZE ] ;
const string_view preimage
2018-02-22 00:00:02 +01:00
{
2019-01-26 23:15:36 +01:00
stringify ( buf , event )
2018-02-22 00:00:02 +01:00
} ;
2019-02-08 08:41:29 +01:00
return pk . verify ( preimage , sig ) ;
2018-02-21 23:24:34 +01:00
}
2019-01-26 23:15:36 +01:00
void
ircd : : m : : event : : essential ( json : : iov & event ,
const json : : iov & contents ,
2019-05-04 00:34:56 +02:00
const event : : closure_iov_mutable & closure )
2018-02-21 03:09:39 +01:00
{
2019-05-04 00:34:56 +02:00
const auto & type
2019-01-26 23:15:36 +01:00
{
2019-05-04 00:34:56 +02:00
event . at ( " type " )
2019-01-26 23:15:36 +01:00
} ;
2018-02-21 23:24:34 +01:00
2019-05-04 00:34:56 +02:00
if ( type = = " m.room.aliases " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " aliases " , contents . at ( " aliases " ) }
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.create " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " creator " , contents . at ( " creator " ) }
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.history_visibility " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " history_visibility " , contents . at ( " history_visibility " ) }
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.join_rules " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " join_rule " , contents . at ( " join_rule " ) }
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.member " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " membership " , contents . at ( " membership " ) }
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.power_levels " )
{
const json : : iov : : push _content { event ,
{
" content " , json : : members
{
{ " ban " , contents . at ( " ban " ) } ,
{ " events " , contents . at ( " events " ) } ,
{ " events_default " , contents . at ( " events_default " ) } ,
{ " kick " , contents . at ( " kick " ) } ,
{ " redact " , contents . at ( " redact " ) } ,
{ " state_default " , contents . at ( " state_default " ) } ,
{ " users " , contents . at ( " users " ) } ,
{ " users_default " , contents . at ( " users_default " ) } ,
}
} } ;
closure ( event ) ;
}
else if ( type = = " m.room.redaction " )
{
// This simply finds the redacts key and swaps it with jsundefined for
// the scope's duration. The redacts key will still be present and
// visible in the json::iov which is incorrect if directly serialized.
// However, this iov is turned into a json::tuple (m::event) which ends
// up being serialized for signing. That serialization is where the
// jsundefined redacts value is ignored.
auto & redacts { event . at ( " redacts " ) } ;
json : : value temp ( std : : move ( redacts ) ) ;
redacts = json : : value { } ;
const unwind _ { [ & redacts , & temp ]
{
redacts = std : : move ( temp ) ;
} } ;
const json : : iov : : push _content
{
event , { " content " , " {} " }
} ;
closure ( event ) ;
}
else
{
const json : : iov : : push _content
{
event , { " content " , " {} " }
} ;
closure ( event ) ;
}
2019-01-26 23:15:36 +01:00
}
2018-02-21 23:24:34 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : event
ircd : : m : : essential ( m : : event event ,
const mutable_buffer & contentbuf )
{
2019-05-04 00:34:56 +02:00
const auto & type
{
json : : at < " type " _ > ( event )
} ;
json : : object & content
{
json : : get < " content " _ > ( event )
} ;
2018-02-21 03:09:39 +01:00
2019-05-04 00:34:56 +02:00
mutable_buffer essential
2019-01-26 23:15:36 +01:00
{
2019-05-04 00:34:56 +02:00
contentbuf
2019-01-26 23:15:36 +01:00
} ;
2018-03-09 16:11:00 +01:00
2019-05-04 00:34:56 +02:00
if ( type = = " m.room.aliases " )
{
content = json : : stringify ( essential , json : : members
{
{ " aliases " , content . at ( " aliases " ) }
} ) ;
}
else if ( type = = " m.room.create " )
{
content = json : : stringify ( essential , json : : members
{
{ " creator " , content . at ( " creator " ) }
} ) ;
}
else if ( type = = " m.room.history_visibility " )
{
content = json : : stringify ( essential , json : : members
{
{ " history_visibility " , content . at ( " history_visibility " ) }
} ) ;
}
else if ( type = = " m.room.join_rules " )
{
content = json : : stringify ( essential , json : : members
{
{ " join_rule " , content . at ( " join_rule " ) }
} ) ;
}
else if ( type = = " m.room.member " )
{
content = json : : stringify ( essential , json : : members
{
{ " membership " , content . at ( " membership " ) }
} ) ;
}
else if ( type = = " m.room.power_levels " )
{
content = json : : stringify ( essential , json : : members
{
{ " ban " , content . at ( " ban " ) } ,
{ " events " , content . at ( " events " ) } ,
{ " events_default " , content . at ( " events_default " ) } ,
{ " kick " , content . at ( " kick " ) } ,
{ " redact " , content . at ( " redact " ) } ,
{ " state_default " , content . at ( " state_default " ) } ,
{ " users " , content . at ( " users " ) } ,
{ " users_default " , content . at ( " users_default " ) } ,
} ) ;
}
else if ( type = = " m.room.redaction " )
{
json : : get < " redacts " _ > ( event ) = string_view { } ;
content = " {} " _sv ;
}
else
{
content = " {} " _sv ;
}
json : : get < " signatures " _ > ( event ) = { } ;
return event ;
2019-01-26 23:15:36 +01:00
}
2018-03-09 16:11:00 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : id : : event
ircd : : m : : make_id ( const event & event ,
id : : event : : buf & buf )
{
const crh : : sha256 : : buf hash { event } ;
return make_id ( event , buf , hash ) ;
}
2018-03-09 16:11:00 +01:00
2019-01-26 23:15:36 +01:00
ircd : : m : : id : : event
ircd : : m : : make_id ( const event & event ,
id : : event : : buf & buf ,
const const_buffer & hash )
{
char readable [ b58encode_size ( sha256 : : digest_size ) ] ;
const id : : event ret
{
buf , b58encode ( readable , hash ) , my_host ( )
} ;
2018-03-09 16:11:00 +01:00
2019-01-26 23:15:36 +01:00
buf . assigned ( ret ) ;
return ret ;
}
2018-02-21 03:09:39 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : before ( const event & a ,
const event & b )
{
const event : : prev prev { b } ;
return prev . prev_events_has ( at < " event_id " _ > ( a ) ) ;
}
2018-03-12 04:48:34 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : operator > = ( const event & a , const event & b )
{
assert ( json : : get < " room_id " _ > ( a ) = = json : : get < " room_id " _ > ( b ) ) ;
return at < " depth " _ > ( a ) > = at < " depth " _ > ( b ) ;
}
2018-02-21 23:24:34 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : operator < = ( const event & a , const event & b )
{
assert ( json : : get < " room_id " _ > ( a ) = = json : : get < " room_id " _ > ( b ) ) ;
return at < " depth " _ > ( a ) < = at < " depth " _ > ( b ) ;
}
2018-02-21 23:24:34 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : operator > ( const event & a , const event & b )
{
assert ( json : : get < " room_id " _ > ( a ) = = json : : get < " room_id " _ > ( b ) ) ;
return at < " depth " _ > ( a ) > at < " depth " _ > ( b ) ;
}
2018-02-21 23:24:34 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : operator < ( const event & a , const event & b )
{
assert ( json : : get < " room_id " _ > ( a ) = = json : : get < " room_id " _ > ( b ) ) ;
return at < " depth " _ > ( a ) < at < " depth " _ > ( b ) ;
}
2018-02-21 03:09:39 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : operator = = ( const event & a , const event & b )
{
assert ( json : : get < " room_id " _ > ( a ) = = json : : get < " room_id " _ > ( b ) ) ;
return at < " event_id " _ > ( a ) = = at < " event_id " _ > ( b ) ;
}
2018-02-21 03:09:39 +01:00
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : bad ( const id : : event & event_id )
{
bool ret { false } ;
index ( event_id , std : : nothrow , [ & ret ]
( const event : : idx & event_idx )
2018-03-12 04:48:34 +01:00
{
2019-01-26 23:15:36 +01:00
ret = event_idx = = 0 ;
} ) ;
2018-03-12 04:48:34 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
}
2018-03-12 04:48:34 +01:00
2019-01-26 23:15:36 +01:00
size_t
ircd : : m : : count ( const event : : prev & prev )
{
size_t ret { 0 } ;
m : : for_each ( prev , [ & ret ] ( const event : : id & event_id )
2018-03-12 04:48:34 +01:00
{
2019-01-26 23:15:36 +01:00
+ + ret ;
} ) ;
2018-03-12 04:48:34 +01:00
2019-01-26 23:15:36 +01:00
return ret ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : good ( const id : : event & event_id )
2018-02-28 05:55:59 +01:00
{
2019-01-26 23:15:36 +01:00
return index ( event_id , std : : nothrow ) ! = 0 ;
2018-02-28 05:55:59 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : exists ( const id : : event & event_id ,
const bool & good )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
return good ?
m : : good ( event_id ) :
m : : exists ( event_id ) ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : exists ( const id : : event & event_id )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
auto & column
{
dbs : : event_idx
} ;
return has ( column , event_id ) ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
ircd : : string_view
ircd : : m : : membership ( const event & event )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
const json : : object & content
{
json : : get < " content " _ > ( event )
} ;
2019-04-18 23:35:46 +02:00
const string_view & membership
{
json : : get < " membership " _ > ( event )
} ;
if ( membership )
return membership ;
const json : : string & content_membership
{
content . get ( " membership " )
} ;
return content_membership ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
size_t
ircd : : m : : degree ( const event & event )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
return degree ( event : : prev { event } ) ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
size_t
ircd : : m : : degree ( const event : : prev & prev )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
size_t ret { 0 } ;
json : : for_each ( prev , [ & ret ]
( const auto & , const json : : array & prevs )
{
ret + = prevs . count ( ) ;
} ) ;
return ret ;
2018-02-21 03:09:39 +01:00
}
2019-01-26 23:15:36 +01:00
bool
ircd : : m : : my ( const event & event )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
const auto & origin ( json : : get < " origin " _ > ( event ) ) ;
const auto & eid ( json : : get < " event_id " _ > ( event ) ) ;
return
origin ?
my_host ( origin ) :
eid ?
my ( event : : id ( eid ) ) :
false ;
2018-02-21 03:09:39 +01:00
}
bool
2019-01-26 23:15:36 +01:00
ircd : : m : : my ( const id : : event & event_id )
2018-02-21 03:09:39 +01:00
{
2019-01-26 23:15:36 +01:00
return self : : host ( event_id . host ( ) ) ;
2018-02-21 03:09:39 +01:00
}
2019-05-04 00:34:56 +02:00
//
// event::event
//
ircd : : m : : event : : event ( const json : : object & source )
: super_type
{
source
}
, source
{
source
}
{
}
ircd : : m : : event : : event ( const json : : object & source ,
const keys & keys )
: super_type
{
source , keys
}
, source
{
source
}
{
}