diff --git a/src/core/matrix/pdu.rs b/src/core/matrix/pdu.rs index c4f99dd7..da7c03af 100644 --- a/src/core/matrix/pdu.rs +++ b/src/core/matrix/pdu.rs @@ -103,6 +103,10 @@ impl Pdu { let event_id = CanonicalJsonValue::String(event_id.into()); json.insert("event_id".into(), event_id); + Self::from_val(&json) + } + + pub fn from_val(json: &CanonicalJsonObject) -> Result { serde_json::to_value(json) .and_then(serde_json::from_value) .map_err(Into::into) diff --git a/src/core/matrix/pdu/format.rs b/src/core/matrix/pdu/format.rs index 5bcd5eb6..8a98e549 100644 --- a/src/core/matrix/pdu/format.rs +++ b/src/core/matrix/pdu/format.rs @@ -1,10 +1,10 @@ use ruma::{ - CanonicalJsonObject, CanonicalJsonValue, EventId, RoomVersionId, + CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, RoomVersionId, room_version_rules::{EventsReferenceFormatVersion, RoomVersionRules}, }; use crate::{ - Result, err, extract_variant, is_equal_to, + Result, extract_variant, is_equal_to, matrix::{PduEvent, room_version}, }; @@ -69,6 +69,7 @@ fn mutate_outgoing_reference_format(value: &mut CanonicalJsonValue) { } pub fn from_incoming_federation( + room_id: &RoomId, event_id: &EventId, pdu_json: &mut CanonicalJsonObject, room_rules: &RoomVersionRules, @@ -82,10 +83,19 @@ pub fn from_incoming_federation( } } - pdu_json.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.into())); + if !room_rules + .event_format + .require_room_create_room_id + && pdu_json["type"] == "m.room.create" + { + pdu_json.insert("room_id".into(), CanonicalJsonValue::String(room_id.as_str().into())); + } - serde_json::from_value::(serde_json::to_value(&pdu_json)?) - .map_err(|e| err!(Request(BadJson(debug_warn!("Event is not a valid PDU: {e}"))))) + if !room_rules.event_format.require_event_id { + pdu_json.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.into())); + } + + PduEvent::from_val(pdu_json) } fn mutate_incoming_reference_format(value: &mut CanonicalJsonValue) { diff --git a/src/service/membership/join.rs b/src/service/membership/join.rs index c3cb39fd..1f1831c2 100644 --- a/src/service/membership/join.rs +++ b/src/service/membership/join.rs @@ -17,11 +17,8 @@ use ruma::{ }; use tuwunel_core::{ Err, Result, debug, debug_error, debug_info, debug_warn, err, error, implement, info, - matrix::{ - event::{gen_event_id, gen_event_id_canonical_json}, - room_version, - }, - pdu::{PduBuilder, PduEvent}, + matrix::{event::gen_event_id_canonical_json, room_version}, + pdu::{PduBuilder, format::from_incoming_federation}, state_res, trace, utils::{self, IterStream, ReadyExt}, warn, @@ -152,6 +149,8 @@ pub async fn join_remote( )); } + let room_version_rules = room_version::rules(&room_version_id)?; + let mut join_event_stub: CanonicalJsonObject = serde_json::from_str(make_join_response.event.get()).map_err(|e| { err!(BadServerResponse(warn!( @@ -221,27 +220,10 @@ pub async fn join_remote( .expect("event is valid, we just created it"), ); - // We keep the "event_id" in the pdu only in v1 or - // v2 rooms - match room_version_id { - | RoomVersionId::V1 | RoomVersionId::V2 => {}, - | _ => { - join_event_stub.remove("event_id"); - }, - } - - // In order to create a compatible ref hash (EventID) the `hashes` field needs - // to be present - self.services + let event_id = self + .services .server_keys - .hash_and_sign_event(&mut join_event_stub, &room_version_id)?; - - // Generate event id - let event_id = gen_event_id(&join_event_stub, &room_version_id)?; - - // Add event_id back - join_event_stub - .insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into())); + .gen_id_hash_and_sign_event(&mut join_event_stub, &room_version_id)?; // It has enough fields to be called a proper event now let mut join_event = join_event_stub; @@ -342,8 +324,8 @@ pub async fn join_remote( .await; info!("Parsing join event"); - let parsed_join_pdu = PduEvent::from_id_val(&event_id, join_event.clone()) - .map_err(|e| err!(BadServerResponse("Invalid join event PDU: {e:?}")))?; + let parsed_join_pdu = + from_incoming_federation(room_id, &event_id, &mut join_event, &room_version_rules)?; info!("Acquiring server signing keys for response events"); let resp_events = &send_join_response.room_state; @@ -368,26 +350,15 @@ pub async fn join_remote( }) .inspect_err(|e| debug_error!("Invalid send_join state event: {e:?}")) .ready_filter_map(Result::ok) - .fold(HashMap::new(), async |mut state, (event_id, mut value)| { - let pdu = if value["type"] == "m.room.create" { - if !value.contains_key("room_id") { - let room_id = CanonicalJsonValue::String(room_id.as_str().into()); - value.insert("room_id".into(), room_id); - } - - PduEvent::from_rid_val(room_id, &event_id, value.clone()) - } else { - PduEvent::from_id_val(&event_id, value.clone()) - }; - - let pdu = match pdu { - | Ok(pdu) => pdu, - | Err(e) => { + .ready_filter_map(|(event_id, mut value)| { + from_incoming_federation(room_id, &event_id, &mut value, &room_version_rules) + .inspect_err(|e| { debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}"); - return state; - }, - }; - + }) + .map(move |pdu| (event_id, pdu, value)) + .ok() + }) + .fold(HashMap::new(), async |mut state, (event_id, pdu, value)| { self.services .timeline .add_pdu_outlier(&event_id, &value); @@ -423,7 +394,11 @@ pub async fn join_remote( .inspect_err(|e| debug_error!("Invalid send_join auth_chain event: {e:?}")) .ready_filter_map(Result::ok) .ready_for_each(|(event_id, mut value)| { - if !value.contains_key("room_id") { + if !room_version_rules + .event_format + .require_room_create_room_id + && value["type"] == "m.room.create" + { let room_id = CanonicalJsonValue::String(room_id.as_str().into()); value.insert("room_id".into(), room_id); } @@ -438,7 +413,7 @@ pub async fn join_remote( debug!("Running send_join auth check"); state_res::auth_check( - &room_version::rules(&room_version_id)?, + &room_version_rules, &parsed_join_pdu, &async |event_id| self.services.timeline.get_pdu(&event_id).await, &async |event_type, state_key| { @@ -719,27 +694,10 @@ pub async fn join_local( .expect("event is valid, we just created it"), ); - // We keep the "event_id" in the pdu only in v1 or - // v2 rooms - match room_version_id { - | RoomVersionId::V1 | RoomVersionId::V2 => {}, - | _ => { - join_event_stub.remove("event_id"); - }, - } - - // In order to create a compatible ref hash (EventID) the `hashes` field needs - // to be present - self.services + let event_id = self + .services .server_keys - .hash_and_sign_event(&mut join_event_stub, &room_version_id)?; - - // Generate event id - let event_id = gen_event_id(&join_event_stub, &room_version_id)?; - - // Add event_id back - join_event_stub - .insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into())); + .gen_id_hash_and_sign_event(&mut join_event_stub, &room_version_id)?; // It has enough fields to be called a proper event now let join_event = join_event_stub; diff --git a/src/service/membership/leave.rs b/src/service/membership/leave.rs index 5138476f..19c5d43b 100644 --- a/src/service/membership/leave.rs +++ b/src/service/membership/leave.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use futures::{FutureExt, StreamExt, TryFutureExt, pin_mut}; use ruma::{ - CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, RoomVersionId, UserId, + CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, UserId, api::federation, events::{ StateEventType, @@ -11,7 +11,6 @@ use ruma::{ }; use tuwunel_core::{ Err, Result, debug_info, debug_warn, err, implement, - matrix::event::gen_event_id, pdu::PduBuilder, utils::{self, FutureBoolExt, future::ReadyEqExt}, warn, @@ -296,26 +295,10 @@ pub async fn remote_leave(&self, user_id: &UserId, room_id: &RoomId) -> Result { ), ); - // room v3 and above removed the "event_id" field from remote PDU format - match room_version_id { - | RoomVersionId::V1 | RoomVersionId::V2 => {}, - | _ => { - leave_event_stub.remove("event_id"); - }, - } - - // In order to create a compatible ref hash (EventID) the `hashes` field needs - // to be present - self.services + let event_id = self + .services .server_keys - .hash_and_sign_event(&mut leave_event_stub, &room_version_id)?; - - // Generate event id - let event_id = gen_event_id(&leave_event_stub, &room_version_id)?; - - // Add event_id back - leave_event_stub - .insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into())); + .gen_id_hash_and_sign_event(&mut leave_event_stub, &room_version_id)?; // It has enough fields to be called a proper event now let leave_event = leave_event_stub; diff --git a/src/service/rooms/event_handler/handle_outlier_pdu.rs b/src/service/rooms/event_handler/handle_outlier_pdu.rs index 4a5480e5..dbfe0d89 100644 --- a/src/service/rooms/event_handler/handle_outlier_pdu.rs +++ b/src/service/rooms/event_handler/handle_outlier_pdu.rs @@ -70,7 +70,7 @@ pub(super) async fn handle_outlier_pdu( // Now that we have checked the signature and hashes we can make mutations and // convert to our PduEvent type. - let event = from_incoming_federation(event_id, &mut pdu_json, &room_rules)?; + let event = from_incoming_federation(room_id, event_id, &mut pdu_json, &room_rules)?; check_room_id(room_id, &event)?; diff --git a/src/service/rooms/timeline/create.rs b/src/service/rooms/timeline/create.rs index b54603ad..1bcbee5c 100644 --- a/src/service/rooms/timeline/create.rs +++ b/src/service/rooms/timeline/create.rs @@ -3,16 +3,16 @@ use std::cmp; use futures::{StreamExt, TryStreamExt}; use ruma::{ CanonicalJsonObject, CanonicalJsonValue, MilliSecondsSinceUnixEpoch, OwnedEventId, - OwnedRoomId, RoomId, RoomVersionId, UserId, + OwnedRoomId, RoomId, UserId, events::{StateEventType, TimelineEventType, room::create::RoomCreateEventContent}, room_version_rules::RoomIdFormatVersion, uint, }; use serde_json::value::to_raw_value; use tuwunel_core::{ - Err, Error, Result, err, implement, + Error, Result, err, implement, matrix::{ - event::{Event, StateKey, TypeExt, gen_event_id}, + event::{Event, StateKey, TypeExt}, pdu::{EventHash, PduBuilder, PduEvent}, room_version, state_res::{self}, @@ -175,36 +175,29 @@ pub async fn create_hash_and_sign_event( err!(Request(BadJson(warn!("Failed to convert PDU to canonical JSON: {e}")))) })?; - // room v3 and above removed the "event_id" field from remote PDU format - if !matches!(room_version, RoomVersionId::V1 | RoomVersionId::V2) { - pdu_json.remove("event_id"); - } - // room v12 and above removed the placeholder "room_id" field from m.room.create - if matches!(version_rules.room_id_format, RoomIdFormatVersion::V2) + if !version_rules + .event_format + .require_room_create_room_id && pdu.kind == TimelineEventType::RoomCreate { pdu_json.remove("room_id"); } - if let Err(e) = self + pdu.event_id = self .services .server_keys - .hash_and_sign_event(&mut pdu_json, &room_version) - { - use ruma::signatures::Error::PduSize; - - return match e { - | Error::Signatures(PduSize) => { - Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)"))) - }, - | _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))), - }; - } - - // Generate event id - pdu.event_id = gen_event_id(&pdu_json, &room_version)?; - pdu_json.insert("event_id".into(), CanonicalJsonValue::String(pdu.event_id.clone().into())); + .gen_id_hash_and_sign_event(&mut pdu_json, &room_version) + .map_err(|e| { + use Error::Signatures; + use ruma::signatures::Error::PduSize; + match e { + | Signatures(PduSize) => { + err!(Request(TooLarge("PDU exceeds 65535 bytes"))) + }, + | _ => err!(Request(Unknown(warn!("Signing event failed: {e}")))), + } + })?; // Room id is event id for V12+ if matches!(version_rules.room_id_format, RoomIdFormatVersion::V2)