@ -5,19 +5,23 @@ use ruma::{
membership ::{
ban_user , forget_room , get_member_events , invite_user , join_room_by_id ,
join_room_by_id_or_alias , joined_members , joined_rooms , kick_user , leave_room ,
unban_user , Incoming ThirdPartySigned,
unban_user , ThirdPartySigned,
} ,
} ,
federation ::{ self , membership ::create_invite } ,
} ,
canonical_json ::to_canonical_value ,
events ::{
room ::member ::{ MembershipState , RoomMemberEventContent } ,
room ::{
join_rules ::{ AllowRule , JoinRule , RoomJoinRulesEventContent } ,
member ::{ MembershipState , RoomMemberEventContent } ,
power_levels ::RoomPowerLevelsEventContent ,
} ,
RoomEventType , StateEventType ,
} ,
serde ::Base64 ,
CanonicalJsonObject , CanonicalJsonValue , EventId , OwnedEventId , OwnedRoomId , OwnedServerName ,
Owned UserId, RoomId , RoomVersionId , UserId ,
state_res, CanonicalJsonObject, CanonicalJsonValue , EventId , OwnedEventId , OwnedRoomId ,
Owned ServerName, Owned UserId, RoomId , RoomVersionId , UserId ,
} ;
use serde_json ::value ::{ to_raw_value , RawValue as RawJsonValue } ;
use std ::{
@ -41,7 +45,7 @@ use super::get_alias_helper;
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_route (
body : Ruma < join_room_by_id ::v3 ::Incoming Request> ,
body : Ruma < join_room_by_id ::v3 :: Request> ,
) -> Result < join_room_by_id ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -65,6 +69,7 @@ pub async fn join_room_by_id_route(
join_room_by_id_helper (
body . sender_user . as_deref ( ) ,
& body . room_id ,
body . reason . clone ( ) ,
& servers ,
body . third_party_signed . as_ref ( ) ,
)
@ -78,7 +83,7 @@ pub async fn join_room_by_id_route(
/// - If the server knowns about this room: creates the join event and does auth rules locally
/// - If the server does not know about the room: asks other servers over federation
pub async fn join_room_by_id_or_alias_route (
body : Ruma < join_room_by_id_or_alias ::v3 ::Incoming Request> ,
body : Ruma < join_room_by_id_or_alias ::v3 :: Request> ,
) -> Result < join_room_by_id_or_alias ::v3 ::Response > {
let sender_user = body . sender_user . as_deref ( ) . expect ( "user is authenticated" ) ;
let body = body . body ;
@ -104,7 +109,7 @@ pub async fn join_room_by_id_or_alias_route(
( servers , room_id )
}
Err ( room_alias ) = > {
let response = get_alias_helper ( & room_alias ) . await ? ;
let response = get_alias_helper ( room_alias ) . await ? ;
( response . servers . into_iter ( ) . collect ( ) , response . room_id )
}
@ -113,6 +118,7 @@ pub async fn join_room_by_id_or_alias_route(
let join_room_response = join_room_by_id_helper (
Some ( sender_user ) ,
& room_id ,
body . reason . clone ( ) ,
& servers ,
body . third_party_signed . as_ref ( ) ,
)
@ -129,11 +135,11 @@ pub async fn join_room_by_id_or_alias_route(
///
/// - This should always work if the user is currently joined.
pub async fn leave_room_route (
body : Ruma < leave_room ::v3 ::Incoming Request> ,
body : Ruma < leave_room ::v3 :: Request> ,
) -> Result < leave_room ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
leave_room ( sender_user , & body . room_id ). await ? ;
leave_room ( sender_user , & body . room_id , body . reason . clone ( ) ). await ? ;
Ok ( leave_room ::v3 ::Response ::new ( ) )
}
@ -142,12 +148,19 @@ pub async fn leave_room_route(
///
/// Tries to send an invite event into the room.
pub async fn invite_user_route (
body : Ruma < invite_user ::v3 ::Incoming Request> ,
body : Ruma < invite_user ::v3 :: Request> ,
) -> Result < invite_user ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
if let invite_user ::v3 ::IncomingInvitationRecipient ::UserId { user_id } = & body . recipient {
invite_helper ( sender_user , user_id , & body . room_id , false ) . await ? ;
if let invite_user ::v3 ::InvitationRecipient ::UserId { user_id } = & body . recipient {
invite_helper (
sender_user ,
user_id ,
& body . room_id ,
body . reason . clone ( ) ,
false ,
)
. await ? ;
Ok ( invite_user ::v3 ::Response { } )
} else {
Err ( Error ::BadRequest ( ErrorKind ::NotFound , "User not found." ) )
@ -158,7 +171,7 @@ pub async fn invite_user_route(
///
/// Tries to send a kick event into the room.
pub async fn kick_user_route (
body : Ruma < kick_user ::v3 ::Incoming Request> ,
body : Ruma < kick_user ::v3 :: Request> ,
) -> Result < kick_user ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -181,7 +194,7 @@ pub async fn kick_user_route(
. map_err ( | _ | Error ::bad_database ( "Invalid member event in database." ) ) ? ;
event . membership = MembershipState ::Leave ;
// TODO: reason
event . reason = body . reason . clone ( ) ;
let mutex_state = Arc ::clone (
services ( )
@ -215,13 +228,9 @@ pub async fn kick_user_route(
/// # `POST /_matrix/client/r0/rooms/{roomId}/ban`
///
/// Tries to send a ban event into the room.
pub async fn ban_user_route (
body : Ruma < ban_user ::v3 ::IncomingRequest > ,
) -> Result < ban_user ::v3 ::Response > {
pub async fn ban_user_route ( body : Ruma < ban_user ::v3 ::Request > ) -> Result < ban_user ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
// TODO: reason
let event = services ( )
. rooms
. state_accessor
@ -238,7 +247,7 @@ pub async fn ban_user_route(
is_direct : None ,
third_party_invite : None ,
blurhash : services ( ) . users . blurhash ( & body . user_id ) ? ,
reason : None ,
reason : body . reason . clone ( ) ,
join_authorized_via_users_server : None ,
} ) ,
| event | {
@ -284,7 +293,7 @@ pub async fn ban_user_route(
///
/// Tries to send an unban event into the room.
pub async fn unban_user_route (
body : Ruma < unban_user ::v3 ::Incoming Request> ,
body : Ruma < unban_user ::v3 :: Request> ,
) -> Result < unban_user ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -307,6 +316,7 @@ pub async fn unban_user_route(
. map_err ( | _ | Error ::bad_database ( "Invalid member event in database." ) ) ? ;
event . membership = MembershipState ::Leave ;
event . reason = body . reason . clone ( ) ;
let mutex_state = Arc ::clone (
services ( )
@ -346,7 +356,7 @@ pub async fn unban_user_route(
/// Note: Other devices of the user have no way of knowing the room was forgotten, so this has to
/// be called from every device
pub async fn forget_room_route (
body : Ruma < forget_room ::v3 ::Incoming Request> ,
body : Ruma < forget_room ::v3 :: Request> ,
) -> Result < forget_room ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -382,7 +392,7 @@ pub async fn joined_rooms_route(
///
/// - Only works if the user is currently joined
pub async fn get_member_events_route (
body : Ruma < get_member_events ::v3 ::Incoming Request> ,
body : Ruma < get_member_events ::v3 :: Request> ,
) -> Result < get_member_events ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -418,7 +428,7 @@ pub async fn get_member_events_route(
/// - The sender user must be in the room
/// - TODO: An appservice just needs a puppet joined
pub async fn joined_members_route (
body : Ruma < joined_members ::v3 ::Incoming Request> ,
body : Ruma < joined_members ::v3 :: Request> ,
) -> Result < joined_members ::v3 ::Response > {
let sender_user = body . sender_user . as_ref ( ) . expect ( "user is authenticated" ) ;
@ -458,8 +468,9 @@ pub async fn joined_members_route(
async fn join_room_by_id_helper (
sender_user : Option < & UserId > ,
room_id : & RoomId ,
reason : Option < String > ,
servers : & [ OwnedServerName ] ,
_third_party_signed : Option < & Incoming ThirdPartySigned> ,
_third_party_signed : Option < & ThirdPartySigned> ,
) -> Result < join_room_by_id ::v3 ::Response > {
let sender_user = sender_user . expect ( "user is authenticated" ) ;
@ -480,33 +491,10 @@ async fn join_room_by_id_helper(
. state_cache
. server_in_room ( services ( ) . globals . server_name ( ) , room_id ) ?
{
let mut make_join_response_and_server = Err ( Error ::BadServerResponse (
"No server available to assist in joining." ,
) ) ;
for remote_server in servers {
let make_join_response = services ( )
. sending
. send_federation_request (
remote_server ,
federation ::membership ::prepare_join_event ::v1 ::Request {
room_id ,
user_id : sender_user ,
ver : & services ( ) . globals . supported_room_versions ( ) ,
} ,
)
. await ;
make_join_response_and_server = make_join_response . map ( | r | ( r , remote_server ) ) ;
if make_join_response_and_server . is_ok ( ) {
break ;
}
}
let ( make_join_response , remote_server ) =
make_join_request ( sender_user , room_id , servers ) . await ? ;
let ( make_join_response , remote_server ) = make_join_response_and_server ? ;
let room_version = match make_join_response . room_version {
let room_version_id = match make_join_response . room_version {
Some ( room_version )
if services ( )
. globals
@ -554,7 +542,7 @@ async fn join_room_by_id_helper(
is_direct : None ,
third_party_invite : None ,
blurhash : services ( ) . users . blurhash ( sender_user ) ? ,
reason : None ,
reason ,
join_authorized_via_users_server ,
} )
. expect ( "event is valid, we just created it" ) ,
@ -568,14 +556,14 @@ async fn join_room_by_id_helper(
services ( ) . globals . server_name ( ) . as_str ( ) ,
services ( ) . globals . keypair ( ) ,
& mut join_event_stub ,
& room_version ,
& room_version _id ,
)
. expect ( "event is valid, we just created it" ) ;
// Generate event id
let event_id = format! (
"${}" ,
ruma ::signatures ::reference_hash ( & join_event_stub , & room_version )
ruma ::signatures ::reference_hash ( & join_event_stub , & room_version _id )
. expect ( "ruma can calculate reference hashes" )
) ;
let event_id = < & EventId > ::try_from ( event_id . as_str ( ) )
@ -588,23 +576,67 @@ async fn join_room_by_id_helper(
) ;
// It has enough fields to be called a proper event now
let join_event = join_event_stub ;
let mut join_event = join_event_stub ;
let send_join_response = services ( )
. sending
. send_federation_request (
remote_server ,
& remote_server ,
federation ::membership ::create_join_event ::v2 ::Request {
room_id ,
event_id ,
pdu : & PduEvent ::convert_to_outgoing_federation_event ( join_event . clone ( ) ) ,
room_id : room_id . to_owned ( ) ,
event_id : event_id . to_owned ( ) ,
pdu : PduEvent ::convert_to_outgoing_federation_event ( join_event . clone ( ) ) ,
} ,
)
. await ? ;
if let Some ( signed_raw ) = & send_join_response . room_state . event {
let ( signed_event_id , signed_value ) =
match gen_event_id_canonical_json ( signed_raw , & room_version_id ) {
Ok ( t ) = > t ,
Err ( _ ) = > {
// Event could not be converted to canonical json
return Err ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Could not convert event to canonical json." ,
) ) ;
}
} ;
if signed_event_id ! = event_id {
return Err ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Server sent event with wrong event id" ,
) ) ;
}
if let Ok ( signature ) = signed_value [ "signatures" ]
. as_object ( )
. ok_or ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Server sent invalid signatures type" ,
) )
. and_then ( | e | {
e . get ( remote_server . as_str ( ) ) . ok_or ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Server did not send its signature" ,
) )
} )
{
join_event
. get_mut ( "signatures" )
. expect ( "we created a valid pdu" )
. as_object_mut ( )
. expect ( "we created a valid pdu" )
. insert ( remote_server . to_string ( ) , signature . clone ( ) ) ;
} else {
warn ! ( "Server {} sent invalid sendjoin event" , remote_server ) ;
}
}
services ( ) . rooms . short . get_or_create_shortroomid ( room_id ) ? ;
let parsed_pdu = PduEvent ::from_id_val ( event_id , join_event . clone ( ) )
let parsed_ join_ pdu = PduEvent ::from_id_val ( event_id , join_event . clone ( ) )
. map_err ( | _ | Error ::BadServerResponse ( "Invalid join event PDU." ) ) ? ;
let mut state = HashMap ::new ( ) ;
@ -613,14 +645,14 @@ async fn join_room_by_id_helper(
services ( )
. rooms
. event_handler
. fetch_join_signing_keys ( & send_join_response , & room_version , & pub_key_map )
. fetch_join_signing_keys ( & send_join_response , & room_version _id , & pub_key_map )
. await ? ;
for result in send_join_response
. room_state
. state
. iter ( )
. map ( | pdu | validate_and_add_event_id ( pdu , & room_version , & pub_key_map ) )
. map ( | pdu | validate_and_add_event_id ( pdu , & room_version _id , & pub_key_map ) )
{
let ( event_id , value ) = match result {
Ok ( t ) = > t ,
@ -645,31 +677,11 @@ async fn join_room_by_id_helper(
}
}
let incoming_shortstatekey = services ( ) . rooms . short . get_or_create_shortstatekey (
& parsed_pdu . kind . to_string ( ) . into ( ) ,
parsed_pdu
. state_key
. as_ref ( )
. expect ( "Pdu is a membership state event" ) ,
) ? ;
state . insert ( incoming_shortstatekey , parsed_pdu . event_id . clone ( ) ) ;
let create_shortstatekey = services ( )
. rooms
. short
. get_shortstatekey ( & StateEventType ::RoomCreate , "" ) ?
. expect ( "Room exists" ) ;
if state . get ( & create_shortstatekey ) . is_none ( ) {
return Err ( Error ::BadServerResponse ( "State contained no create event." ) ) ;
}
for result in send_join_response
. room_state
. auth_chain
. iter ( )
. map ( | pdu | validate_and_add_event_id ( pdu , & room_version , & pub_key_map ) )
. map ( | pdu | validate_and_add_event_id ( pdu , & room_version_id , & pub_key_map ) )
{
let ( event_id , value ) = match result {
Ok ( t ) = > t ,
@ -682,6 +694,34 @@ async fn join_room_by_id_helper(
. add_pdu_outlier ( & event_id , & value ) ? ;
}
if ! state_res ::event_auth ::auth_check (
& state_res ::RoomVersion ::new ( & room_version_id ) . expect ( "room version is supported" ) ,
& parsed_join_pdu ,
None ::< PduEvent > , // TODO: third party invite
| k , s | {
services ( )
. rooms
. timeline
. get_pdu (
state . get (
& services ( )
. rooms
. short
. get_or_create_shortstatekey ( & k . to_string ( ) . into ( ) , s )
. ok ( ) ? ,
) ? ,
)
. ok ( ) ?
} ,
)
. map_err ( | _e | Error ::BadRequest ( ErrorKind ::InvalidParam , "Auth check failed" ) ) ?
{
return Err ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Auth check failed" ,
) ) ;
}
let ( statehash_before_join , new , removed ) = services ( ) . rooms . state_compressor . save_state (
room_id ,
state
@ -705,12 +745,12 @@ async fn join_room_by_id_helper(
// We append to state before appending the pdu, so we don't have a moment in time with the
// pdu without it's state. This is okay because append_pdu can't fail.
let statehash_after_join = services ( ) . rooms . state . append_to_state ( & parsed_ pdu) ? ;
let statehash_after_join = services ( ) . rooms . state . append_to_state ( & parsed_ join_ pdu) ? ;
services ( ) . rooms . timeline . append_pdu (
& parsed_ pdu,
& parsed_ join_ pdu,
join_event ,
vec! [ ( * parsed_ pdu. event_id ) . to_owned ( ) ] ,
vec! [ ( * parsed_ join_ pdu. event_id ) . to_owned ( ) ] ,
& state_lock ,
) ? ;
@ -721,6 +761,95 @@ async fn join_room_by_id_helper(
. state
. set_room_state ( room_id , statehash_after_join , & state_lock ) ? ;
} else {
let join_rules_event = services ( ) . rooms . state_accessor . room_state_get (
room_id ,
& StateEventType ::RoomJoinRules ,
"" ,
) ? ;
let power_levels_event = services ( ) . rooms . state_accessor . room_state_get (
room_id ,
& StateEventType ::RoomPowerLevels ,
"" ,
) ? ;
let join_rules_event_content : Option < RoomJoinRulesEventContent > = join_rules_event
. as_ref ( )
. map ( | join_rules_event | {
serde_json ::from_str ( join_rules_event . content . get ( ) ) . map_err ( | e | {
warn ! ( "Invalid join rules event: {}" , e ) ;
Error ::bad_database ( "Invalid join rules event in db." )
} )
} )
. transpose ( ) ? ;
let power_levels_event_content : Option < RoomPowerLevelsEventContent > = power_levels_event
. as_ref ( )
. map ( | power_levels_event | {
serde_json ::from_str ( power_levels_event . content . get ( ) ) . map_err ( | e | {
warn ! ( "Invalid power levels event: {}" , e ) ;
Error ::bad_database ( "Invalid power levels event in db." )
} )
} )
. transpose ( ) ? ;
let restriction_rooms = match join_rules_event_content {
Some ( RoomJoinRulesEventContent {
join_rule : JoinRule ::Restricted ( restricted ) ,
} )
| Some ( RoomJoinRulesEventContent {
join_rule : JoinRule ::KnockRestricted ( restricted ) ,
} ) = > restricted
. allow
. into_iter ( )
. filter_map ( | a | match a {
AllowRule ::RoomMembership ( r ) = > Some ( r . room_id ) ,
_ = > None ,
} )
. collect ( ) ,
_ = > Vec ::new ( ) ,
} ;
let authorized_user = restriction_rooms
. iter ( )
. find_map ( | restriction_room_id | {
if ! services ( )
. rooms
. state_cache
. is_joined ( sender_user , restriction_room_id )
. ok ( ) ?
{
return None ;
}
let authorized_user = power_levels_event_content
. as_ref ( )
. and_then ( | c | {
c . users
. iter ( )
. filter ( | ( uid , i ) | {
uid . server_name ( ) = = services ( ) . globals . server_name ( )
& & * * i > ruma ::int ! ( 0 )
& & services ( )
. rooms
. state_cache
. is_joined ( uid , restriction_room_id )
. unwrap_or ( false )
} )
. max_by_key ( | ( _ , i ) | * i )
. map ( | ( u , _ ) | u . to_owned ( ) )
} )
. or_else ( | | {
// TODO: Check here if user is actually allowed to invite. Currently the auth
// check will just fail in this case.
services ( )
. rooms
. state_cache
. room_members ( restriction_room_id )
. filter_map ( | r | r . ok ( ) )
. find ( | uid | uid . server_name ( ) = = services ( ) . globals . server_name ( ) )
} ) ;
Some ( authorized_user )
} )
. flatten ( ) ;
let event = RoomMemberEventContent {
membership : MembershipState ::Join ,
displayname : services ( ) . users . displayname ( sender_user ) ? ,
@ -728,11 +857,12 @@ async fn join_room_by_id_helper(
is_direct : None ,
third_party_invite : None ,
blurhash : services ( ) . users . blurhash ( sender_user ) ? ,
reason : None ,
join_authorized_via_users_server : None ,
reason : reason . clone ( ) ,
join_authorized_via_users_server : authorized_user ,
} ;
services ( ) . rooms . timeline . build_and_append_pdu (
// Try normal join first
let error = match services ( ) . rooms . timeline . build_and_append_pdu (
PduBuilder {
event_type : RoomEventType ::RoomMember ,
content : to_raw_value ( & event ) . expect ( "event is valid, we just created it" ) ,
@ -743,14 +873,193 @@ async fn join_room_by_id_helper(
sender_user ,
room_id ,
& state_lock ,
) ? ;
}
) {
Ok ( _event_id ) = > return Ok ( join_room_by_id ::v3 ::Response ::new ( room_id . to_owned ( ) ) ) ,
Err ( e ) = > e ,
} ;
drop ( state_lock ) ;
if ! restriction_rooms . is_empty ( ) {
// We couldn't do the join locally, maybe federation can help to satisfy the restricted
// join requirements
let ( make_join_response , remote_server ) =
make_join_request ( sender_user , room_id , servers ) . await ? ;
let room_version_id = match make_join_response . room_version {
Some ( room_version_id )
if services ( )
. globals
. supported_room_versions ( )
. contains ( & room_version_id ) = >
{
room_version_id
}
_ = > return Err ( Error ::BadServerResponse ( "Room version is not supported" ) ) ,
} ;
let mut join_event_stub : CanonicalJsonObject =
serde_json ::from_str ( make_join_response . event . get ( ) ) . map_err ( | _ | {
Error ::BadServerResponse ( "Invalid make_join event json received from server." )
} ) ? ;
let join_authorized_via_users_server = join_event_stub
. get ( "content" )
. map ( | s | {
s . as_object ( ) ?
. get ( "join_authorised_via_users_server" ) ?
. as_str ( )
} )
. and_then ( | s | OwnedUserId ::try_from ( s . unwrap_or_default ( ) ) . ok ( ) ) ;
// TODO: Is origin needed?
join_event_stub . insert (
"origin" . to_owned ( ) ,
CanonicalJsonValue ::String ( services ( ) . globals . server_name ( ) . as_str ( ) . to_owned ( ) ) ,
) ;
join_event_stub . insert (
"origin_server_ts" . to_owned ( ) ,
CanonicalJsonValue ::Integer (
utils ::millis_since_unix_epoch ( )
. try_into ( )
. expect ( "Timestamp is valid js_int value" ) ,
) ,
) ;
join_event_stub . insert (
"content" . to_owned ( ) ,
to_canonical_value ( RoomMemberEventContent {
membership : MembershipState ::Join ,
displayname : services ( ) . users . displayname ( sender_user ) ? ,
avatar_url : services ( ) . users . avatar_url ( sender_user ) ? ,
is_direct : None ,
third_party_invite : None ,
blurhash : services ( ) . users . blurhash ( sender_user ) ? ,
reason ,
join_authorized_via_users_server ,
} )
. expect ( "event is valid, we just created it" ) ,
) ;
// We don't leave the event id in the pdu because that's only allowed in v1 or v2 rooms
join_event_stub . remove ( "event_id" ) ;
// In order to create a compatible ref hash (EventID) the `hashes` field needs to be present
ruma ::signatures ::hash_and_sign_event (
services ( ) . globals . server_name ( ) . as_str ( ) ,
services ( ) . globals . keypair ( ) ,
& mut join_event_stub ,
& room_version_id ,
)
. expect ( "event is valid, we just created it" ) ;
// Generate event id
let event_id = format! (
"${}" ,
ruma ::signatures ::reference_hash ( & join_event_stub , & room_version_id )
. expect ( "ruma can calculate reference hashes" )
) ;
let event_id = < & EventId > ::try_from ( event_id . as_str ( ) )
. expect ( "ruma's reference hashes are valid event ids" ) ;
// Add event_id back
join_event_stub . insert (
"event_id" . to_owned ( ) ,
CanonicalJsonValue ::String ( event_id . as_str ( ) . to_owned ( ) ) ,
) ;
// It has enough fields to be called a proper event now
let join_event = join_event_stub ;
let send_join_response = services ( )
. sending
. send_federation_request (
& remote_server ,
federation ::membership ::create_join_event ::v2 ::Request {
room_id : room_id . to_owned ( ) ,
event_id : event_id . to_owned ( ) ,
pdu : PduEvent ::convert_to_outgoing_federation_event ( join_event . clone ( ) ) ,
} ,
)
. await ? ;
if let Some ( signed_raw ) = send_join_response . room_state . event {
let ( signed_event_id , signed_value ) =
match gen_event_id_canonical_json ( & signed_raw , & room_version_id ) {
Ok ( t ) = > t ,
Err ( _ ) = > {
// Event could not be converted to canonical json
return Err ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Could not convert event to canonical json." ,
) ) ;
}
} ;
if signed_event_id ! = event_id {
return Err ( Error ::BadRequest (
ErrorKind ::InvalidParam ,
"Server sent event with wrong event id" ,
) ) ;
}
drop ( state_lock ) ;
let pub_key_map = RwLock ::new ( BTreeMap ::new ( ) ) ;
services ( )
. rooms
. event_handler
. handle_incoming_pdu (
& remote_server ,
& signed_event_id ,
room_id ,
signed_value ,
true ,
& pub_key_map ,
)
. await ? ;
} else {
return Err ( error ) ;
}
} else {
return Err ( error ) ;
}
}
Ok ( join_room_by_id ::v3 ::Response ::new ( room_id . to_owned ( ) ) )
}
async fn make_join_request (
sender_user : & UserId ,
room_id : & RoomId ,
servers : & [ OwnedServerName ] ,
) -> Result < (
federation ::membership ::prepare_join_event ::v1 ::Response ,
OwnedServerName ,
) > {
let mut make_join_response_and_server = Err ( Error ::BadServerResponse (
"No server available to assist in joining." ,
) ) ;
for remote_server in servers {
if remote_server = = services ( ) . globals . server_name ( ) {
continue ;
}
let make_join_response = services ( )
. sending
. send_federation_request (
remote_server ,
federation ::membership ::prepare_join_event ::v1 ::Request {
room_id : room_id . to_owned ( ) ,
user_id : sender_user . to_owned ( ) ,
ver : services ( ) . globals . supported_room_versions ( ) ,
} ,
)
. await ;
make_join_response_and_server = make_join_response . map ( | r | ( r , remote_server . clone ( ) ) ) ;
if make_join_response_and_server . is_ok ( ) {
break ;
}
}
make_join_response_and_server
}
fn validate_and_add_event_id (
pdu : & RawJsonValue ,
room_version : & RoomVersionId ,
@ -823,10 +1132,11 @@ pub(crate) async fn invite_helper<'a>(
sender_user : & UserId ,
user_id : & UserId ,
room_id : & RoomId ,
reason : Option < String > ,
is_direct : bool ,
) -> Result < ( ) > {
if user_id . server_name ( ) ! = services ( ) . globals . server_name ( ) {
let ( pdu _json, invite_room_state ) = {
let ( pdu , pdu _json, invite_room_state ) = {
let mutex_state = Arc ::clone (
services ( )
. globals
@ -845,7 +1155,7 @@ pub(crate) async fn invite_helper<'a>(
membership : MembershipState ::Invite ,
third_party_invite : None ,
blurhash : None ,
reason : None ,
reason ,
join_authorized_via_users_server : None ,
} )
. expect ( "member event is valid value" ) ;
@ -867,31 +1177,21 @@ pub(crate) async fn invite_helper<'a>(
drop ( state_lock ) ;
( pdu _json, invite_room_state )
( pdu , pdu _json, invite_room_state )
} ;
// Generate event id
let expected_event_id = format! (
"${}" ,
ruma ::signatures ::reference_hash (
& pdu_json ,
& services ( ) . rooms . state . get_room_version ( room_id ) ?
)
. expect ( "ruma can calculate reference hashes" )
) ;
let expected_event_id = < & EventId > ::try_from ( expected_event_id . as_str ( ) )
. expect ( "ruma's reference hashes are valid event ids" ) ;
let room_version_id = services ( ) . rooms . state . get_room_version ( room_id ) ? ;
let response = services ( )
. sending
. send_federation_request (
user_id . server_name ( ) ,
create_invite ::v2 ::Request {
room_id ,
event_id : expected_event_id ,
room_version : & services ( ) . rooms . state . get_room_version ( room_id ) ? ,
event : & PduEvent ::convert_to_outgoing_federation_event ( pdu_json . clone ( ) ) ,
invite_room_state : & invite_room_state ,
room_id : room_id . to_owned ( ) ,
event_id : ( * pdu . event_id ) . to_owned ( ) ,
room_version : room_version_id . clone ( ) ,
event : PduEvent ::convert_to_outgoing_federation_event ( pdu_json . clone ( ) ) ,
invite_room_state ,
} ,
)
. await ? ;
@ -899,7 +1199,8 @@ pub(crate) async fn invite_helper<'a>(
let pub_key_map = RwLock ::new ( BTreeMap ::new ( ) ) ;
// We do not add the event_id field to the pdu here because of signature and hashes checks
let ( event_id , value ) = match gen_event_id_canonical_json ( & response . event ) {
let ( event_id , value ) = match gen_event_id_canonical_json ( & response . event , & room_version_id )
{
Ok ( t ) = > t ,
Err ( _ ) = > {
// Event could not be converted to canonical json
@ -910,7 +1211,7 @@ pub(crate) async fn invite_helper<'a>(
}
} ;
if expected_event_id ! = event_id {
if * pdu . event_id ! = * event_id {
warn ! ( "Server {} changed invite event, that's not allowed in the spec: ours: {:?}, theirs: {:?}" , user_id . server_name ( ) , pdu_json , value ) ;
}
@ -978,7 +1279,7 @@ pub(crate) async fn invite_helper<'a>(
is_direct : Some ( is_direct ) ,
third_party_invite : None ,
blurhash : services ( ) . users . blurhash ( user_id ) ? ,
reason : None ,
reason ,
join_authorized_via_users_server : None ,
} )
. expect ( "event is valid, we just created it" ) ,
@ -1017,13 +1318,13 @@ pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
Err ( _ ) = > continue ,
} ;
let _ = leave_room ( user_id , & room_id ). await ;
let _ = leave_room ( user_id , & room_id , None ). await ;
}
Ok ( ( ) )
}
pub async fn leave_room ( user_id : & UserId , room_id : & RoomId ) -> Result < ( ) > {
pub async fn leave_room ( user_id : & UserId , room_id : & RoomId , reason : Option < String > ) -> Result < ( ) > {
// Ask a remote server if we don't have this room
if ! services ( ) . rooms . metadata . exists ( room_id ) ?
& & room_id . server_name ( ) ! = services ( ) . globals . server_name ( )
@ -1063,21 +1364,35 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
) ;
let state_lock = mutex_state . lock ( ) . await ;
let mut event : RoomMemberEventContent = serde_json ::from_str (
services ( )
. rooms
. state_accessor
. room_state_get ( room_id , & StateEventType ::RoomMember , user_id . as_str ( ) ) ?
. ok_or ( Error ::BadRequest (
ErrorKind ::BadState ,
"Cannot leave a room you are not a member of." ,
) ) ?
. content
. get ( ) ,
)
. map_err ( | _ | Error ::bad_database ( "Invalid member event in database." ) ) ? ;
let member_event = services ( ) . rooms . state_accessor . room_state_get (
room_id ,
& StateEventType ::RoomMember ,
user_id . as_str ( ) ,
) ? ;
// Fix for broken rooms
let member_event = match member_event {
None = > {
error ! ( "Trying to leave a room you are not a member of." ) ;
services ( ) . rooms . state_cache . update_membership (
room_id ,
user_id ,
MembershipState ::Leave ,
user_id ,
None ,
true ,
) ? ;
return Ok ( ( ) ) ;
}
Some ( e ) = > e ,
} ;
let mut event : RoomMemberEventContent = serde_json ::from_str ( member_event . content . get ( ) )
. map_err ( | _ | Error ::bad_database ( "Invalid member event in database." ) ) ? ;
event . membership = MembershipState ::Leave ;
event . reason = reason ;
services ( ) . rooms . timeline . build_and_append_pdu (
PduBuilder {
@ -1124,7 +1439,10 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
. sending
. send_federation_request (
& remote_server ,
federation ::membership ::prepare_leave_event ::v1 ::Request { room_id , user_id } ,
federation ::membership ::prepare_leave_event ::v1 ::Request {
room_id : room_id . to_owned ( ) ,
user_id : user_id . to_owned ( ) ,
} ,
)
. await ;
@ -1201,9 +1519,9 @@ async fn remote_leave_room(user_id: &UserId, room_id: &RoomId) -> Result<()> {
. send_federation_request (
& remote_server ,
federation ::membership ::create_leave_event ::v2 ::Request {
room_id ,
event_id : & event_id ,
pdu : & PduEvent ::convert_to_outgoing_federation_event ( leave_event . clone ( ) ) ,
room_id : room_id . to_owned ( ) ,
event_id ,
pdu : PduEvent ::convert_to_outgoing_federation_event ( leave_event . clone ( ) ) ,
} ,
)
. await ? ;