From 116f85360fa17b16e8e9353b20be5e21dc87e2de Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sat, 26 Apr 2025 08:24:47 +0000 Subject: [PATCH] Toward abstracting Pdu into trait Event. Co-authored-by: Jade Ellis Signed-off-by: Jason Volk --- src/admin/debug/commands.rs | 11 +- src/admin/user/commands.rs | 8 +- src/api/client/context.rs | 12 +- src/api/client/membership.rs | 5 +- src/api/client/message.rs | 4 +- src/api/client/relations.rs | 4 +- src/api/client/room/event.rs | 2 +- src/api/client/room/initial_sync.rs | 6 +- src/api/client/search.rs | 6 +- src/api/client/state.rs | 18 +- src/api/client/sync/v3.rs | 13 +- src/api/client/sync/v4.rs | 7 +- src/api/client/sync/v5.rs | 12 +- src/api/client/threads.rs | 7 +- src/api/server/invite.rs | 7 +- src/core/matrix/event.rs | 141 +++++++--- src/core/matrix/event/content.rs | 21 ++ src/core/matrix/event/format.rs | 219 +++++++++++++++ src/core/matrix/event/redact.rs | 86 ++++++ src/core/matrix/event/type_ext.rs | 32 +++ src/core/matrix/mod.rs | 8 +- src/core/matrix/pdu.rs | 123 +++++++-- src/core/matrix/pdu/id.rs | 1 + src/core/matrix/pdu/redact.rs | 116 +------- src/core/matrix/pdu/strip.rs | 257 ------------------ src/core/matrix/{pdu => }/state_key.rs | 3 - src/core/matrix/state_res/benches.rs | 167 ++---------- src/core/matrix/state_res/event_auth.rs | 61 +++-- src/core/matrix/state_res/mod.rs | 29 +- src/core/matrix/state_res/test_utils.rs | 192 +++---------- src/core/mod.rs | 4 +- src/service/admin/mod.rs | 21 +- src/service/pusher/mod.rs | 56 ++-- .../rooms/event_handler/handle_outlier_pdu.rs | 2 +- .../event_handler/upgrade_outlier_pdu.rs | 4 +- src/service/rooms/search/mod.rs | 4 +- src/service/rooms/spaces/mod.rs | 8 +- src/service/rooms/state/mod.rs | 22 +- src/service/rooms/threads/mod.rs | 16 +- src/service/rooms/timeline/mod.rs | 7 +- src/service/sending/sender.rs | 6 +- 41 files changed, 842 insertions(+), 886 deletions(-) create mode 100644 src/core/matrix/event/content.rs create mode 100644 src/core/matrix/event/format.rs create mode 100644 src/core/matrix/event/redact.rs create mode 100644 src/core/matrix/event/type_ext.rs delete mode 100644 src/core/matrix/pdu/strip.rs rename src/core/matrix/{pdu => }/state_key.rs (67%) diff --git a/src/admin/debug/commands.rs b/src/admin/debug/commands.rs index a397e0fc..2323e3b8 100644 --- a/src/admin/debug/commands.rs +++ b/src/admin/debug/commands.rs @@ -7,7 +7,10 @@ use std::{ use conduwuit::{ Err, Result, debug_error, err, info, - matrix::pdu::{PduEvent, PduId, RawPduId}, + matrix::{ + Event, + pdu::{PduEvent, PduId, RawPduId}, + }, trace, utils, utils::{ stream::{IterStream, ReadyExt}, @@ -19,7 +22,7 @@ use futures::{FutureExt, StreamExt, TryStreamExt}; use ruma::{ CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId, - api::federation::event::get_room_state, + api::federation::event::get_room_state, events::AnyStateEvent, serde::Raw, }; use service::rooms::{ short::{ShortEventId, ShortRoomId}, @@ -296,12 +299,12 @@ pub(super) async fn get_remote_pdu( #[admin_command] pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result { let room_id = self.services.rooms.alias.resolve(&room).await?; - let room_state: Vec<_> = self + let room_state: Vec> = self .services .rooms .state_accessor .room_state_full_pdus(&room_id) - .map_ok(PduEvent::into_state_event) + .map_ok(Event::into_format) .try_collect() .await?; diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 89f7a9fc..3750d758 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -1,13 +1,15 @@ use std::{collections::BTreeMap, fmt::Write as _}; -use api::client::{full_user_deactivate, join_room_by_id_helper, leave_room}; +use api::client::{ + full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, update_avatar_url, + update_displayname, +}; use conduwuit::{ Err, Result, debug, debug_warn, error, info, is_equal_to, - matrix::pdu::PduBuilder, + matrix::{Event, pdu::PduBuilder}, utils::{self, ReadyExt}, warn, }; -use conduwuit_api::client::{leave_all_rooms, update_avatar_url, update_displayname}; use futures::{FutureExt, StreamExt}; use ruma::{ OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId, diff --git a/src/api/client/context.rs b/src/api/client/context.rs index ca787a16..4a7d34d2 100644 --- a/src/api/client/context.rs +++ b/src/api/client/context.rs @@ -1,8 +1,6 @@ use axum::extract::State; use conduwuit::{ - Err, Result, at, debug_warn, err, - matrix::pdu::PduEvent, - ref_at, + Err, Event, Result, at, debug_warn, err, ref_at, utils::{ IterStream, future::TryExtExt, @@ -179,12 +177,12 @@ pub(crate) async fn get_context_route( .broad_filter_map(|event_id: &OwnedEventId| { services.rooms.timeline.get_pdu(event_id.as_ref()).ok() }) - .map(PduEvent::into_state_event) + .map(Event::into_format) .collect() .await; Ok(get_context::v3::Response { - event: base_event.map(at!(1)).map(PduEvent::into_room_event), + event: base_event.map(at!(1)).map(Event::into_format), start: events_before .last() @@ -203,13 +201,13 @@ pub(crate) async fn get_context_route( events_before: events_before .into_iter() .map(at!(1)) - .map(PduEvent::into_room_event) + .map(Event::into_format) .collect(), events_after: events_after .into_iter() .map(at!(1)) - .map(PduEvent::into_room_event) + .map(Event::into_format) .collect(), state, diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs index 85d0cd21..3c2a6fe3 100644 --- a/src/api/client/membership.rs +++ b/src/api/client/membership.rs @@ -9,7 +9,8 @@ use std::{ use axum::extract::State; use axum_client_ip::InsecureClientIp; use conduwuit::{ - Err, Result, at, debug, debug_error, debug_info, debug_warn, err, error, info, is_matching, + Err, Event, Result, at, debug, debug_error, debug_info, debug_warn, err, error, info, + is_matching, matrix::{ StateKey, pdu::{PduBuilder, PduEvent, gen_event_id, gen_event_id_canonical_json}, @@ -880,7 +881,7 @@ pub(crate) async fn get_member_events_route( .ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember) .map(at!(1)) .ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership)) - .map(PduEvent::into_member_event) + .map(Event::into_format) .collect() .await, }) diff --git a/src/api/client/message.rs b/src/api/client/message.rs index 7a87a9b0..e32d020f 100644 --- a/src/api/client/message.rs +++ b/src/api/client/message.rs @@ -175,7 +175,7 @@ pub(crate) async fn get_message_events_route( let chunk = events .into_iter() .map(at!(1)) - .map(PduEvent::into_room_event) + .map(Event::into_format) .collect(); Ok(get_message_events::v3::Response { @@ -241,7 +241,7 @@ async fn get_member_event( .rooms .state_accessor .room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str()) - .map_ok(PduEvent::into_state_event) + .map_ok(Event::into_format) .await .ok() } diff --git a/src/api/client/relations.rs b/src/api/client/relations.rs index b8c2dd4d..ad726b90 100644 --- a/src/api/client/relations.rs +++ b/src/api/client/relations.rs @@ -1,7 +1,7 @@ use axum::extract::State; use conduwuit::{ Result, at, - matrix::pdu::PduCount, + matrix::{Event, pdu::PduCount}, utils::{IterStream, ReadyExt, result::FlatOk, stream::WidebandExt}, }; use conduwuit_service::{Services, rooms::timeline::PdusIterItem}; @@ -167,7 +167,7 @@ async fn paginate_relations_with_filter( chunk: events .into_iter() .map(at!(1)) - .map(|pdu| pdu.to_message_like_event()) + .map(Event::into_format) .collect(), }) } diff --git a/src/api/client/room/event.rs b/src/api/client/room/event.rs index 2b115b5c..47228d67 100644 --- a/src/api/client/room/event.rs +++ b/src/api/client/room/event.rs @@ -40,5 +40,5 @@ pub(crate) async fn get_room_event_route( event.add_age().ok(); - Ok(get_room_event::v3::Response { event: event.into_room_event() }) + Ok(get_room_event::v3::Response { event: event.into_format() }) } diff --git a/src/api/client/room/initial_sync.rs b/src/api/client/room/initial_sync.rs index ca63610b..8b9f3ca0 100644 --- a/src/api/client/room/initial_sync.rs +++ b/src/api/client/room/initial_sync.rs @@ -1,6 +1,6 @@ use axum::extract::State; use conduwuit::{ - Err, PduEvent, Result, at, + Err, Event, Result, at, utils::{BoolExt, stream::TryTools}, }; use futures::TryStreamExt; @@ -38,7 +38,7 @@ pub(crate) async fn room_initial_sync_route( .rooms .state_accessor .room_state_full_pdus(room_id) - .map_ok(PduEvent::into_state_event) + .map_ok(Event::into_format) .try_collect() .await?; @@ -55,7 +55,7 @@ pub(crate) async fn room_initial_sync_route( chunk: events .into_iter() .map(at!(1)) - .map(PduEvent::into_room_event) + .map(Event::into_format) .collect(), }; diff --git a/src/api/client/search.rs b/src/api/client/search.rs index d4dcde57..cc745694 100644 --- a/src/api/client/search.rs +++ b/src/api/client/search.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use axum::extract::State; use conduwuit::{ Err, Result, at, is_true, - matrix::pdu::PduEvent, + matrix::Event, result::FlatOk, utils::{IterStream, stream::ReadyExt}, }; @@ -144,7 +144,7 @@ async fn category_room_events( .map(at!(2)) .flatten() .stream() - .map(PduEvent::into_room_event) + .map(Event::into_format) .map(|result| SearchResult { rank: None, result: Some(result), @@ -185,7 +185,7 @@ async fn procure_room_state(services: &Services, room_id: &RoomId) -> Result>(); let account_data_events = services @@ -877,10 +875,7 @@ async fn load_joined_room( events: room_events, }, state: RoomState { - events: state_events - .into_iter() - .map(PduEvent::into_sync_state_event) - .collect(), + events: state_events.into_iter().map(Event::into_format).collect(), }, ephemeral: Ephemeral { events: edus }, unread_thread_notifications: BTreeMap::new(), diff --git a/src/api/client/sync/v4.rs b/src/api/client/sync/v4.rs index f153b2da..cabd67e4 100644 --- a/src/api/client/sync/v4.rs +++ b/src/api/client/sync/v4.rs @@ -6,7 +6,7 @@ use std::{ use axum::extract::State; use conduwuit::{ - Err, Error, PduCount, PduEvent, Result, debug, error, extract_variant, + Err, Error, Event, PduCount, PduEvent, Result, at, debug, error, extract_variant, matrix::TypeStateKey, utils::{ BoolExt, IterStream, ReadyExt, TryFutureExtExt, @@ -604,7 +604,8 @@ pub(crate) async fn sync_events_v4_route( .iter() .stream() .filter_map(|item| ignored_filter(&services, item.clone(), sender_user)) - .map(|(_, pdu)| pdu.to_sync_room_event()) + .map(at!(1)) + .map(Event::into_format) .collect() .await; @@ -626,7 +627,7 @@ pub(crate) async fn sync_events_v4_route( .state_accessor .room_state_get(room_id, &state.0, &state.1) .await - .map(PduEvent::into_sync_state_event) + .map(PduEvent::into_format) .ok() }) .collect() diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs index f3fc0f44..e4cefba0 100644 --- a/src/api/client/sync/v5.rs +++ b/src/api/client/sync/v5.rs @@ -7,11 +7,8 @@ use std::{ use axum::extract::State; use conduwuit::{ - Err, Error, Result, error, extract_variant, is_equal_to, - matrix::{ - TypeStateKey, - pdu::{PduCount, PduEvent}, - }, + Err, Error, Result, at, error, extract_variant, is_equal_to, + matrix::{Event, TypeStateKey, pdu::PduCount}, trace, utils::{ BoolExt, FutureBoolExt, IterStream, ReadyExt, TryFutureExtExt, @@ -515,7 +512,8 @@ where .iter() .stream() .filter_map(|item| ignored_filter(services, item.clone(), sender_user)) - .map(|(_, pdu)| pdu.to_sync_room_event()) + .map(at!(1)) + .map(Event::into_format) .collect() .await; @@ -537,7 +535,7 @@ where .state_accessor .room_state_get(room_id, &state.0, &state.1) .await - .map(PduEvent::into_sync_state_event) + .map(Event::into_format) .ok() }) .collect() diff --git a/src/api/client/threads.rs b/src/api/client/threads.rs index 5b838bef..ca176eda 100644 --- a/src/api/client/threads.rs +++ b/src/api/client/threads.rs @@ -1,7 +1,10 @@ use axum::extract::State; use conduwuit::{ Result, at, - matrix::pdu::{PduCount, PduEvent}, + matrix::{ + Event, + pdu::{PduCount, PduEvent}, + }, }; use futures::StreamExt; use ruma::{api::client::threads::get_threads, uint}; @@ -56,7 +59,7 @@ pub(crate) async fn get_threads_route( chunk: threads .into_iter() .map(at!(1)) - .map(PduEvent::into_room_event) + .map(Event::into_format) .collect(), }) } diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index 01961378..0d26d787 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -2,7 +2,8 @@ use axum::extract::State; use axum_client_ip::InsecureClientIp; use base64::{Engine as _, engine::general_purpose}; use conduwuit::{ - Err, Error, PduEvent, Result, err, pdu::gen_event_id, utils, utils::hash::sha256, warn, + Err, Error, PduEvent, Result, err, matrix::Event, pdu::gen_event_id, utils, + utils::hash::sha256, warn, }; use ruma::{ CanonicalJsonValue, OwnedUserId, UserId, @@ -111,7 +112,7 @@ pub(crate) async fn create_invite_route( let pdu: PduEvent = serde_json::from_value(event.into()) .map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?; - invite_state.push(pdu.to_stripped_state_event()); + invite_state.push(pdu.to_format()); // If we are active in the room, the remote server will notify us about the // join/invite through /send. If we are not in the room, we need to manually @@ -144,7 +145,7 @@ pub(crate) async fn create_invite_route( .send_appservice_request( appservice.registration.clone(), ruma::api::appservice::event::push_events::v1::Request { - events: vec![pdu.to_room_event()], + events: vec![pdu.to_format()], txn_id: general_purpose::URL_SAFE_NO_PAD .encode(sha256::hash(pdu.event_id.as_bytes())) .into(), diff --git a/src/core/matrix/event.rs b/src/core/matrix/event.rs index e4c478cd..5b12770b 100644 --- a/src/core/matrix/event.rs +++ b/src/core/matrix/event.rs @@ -1,63 +1,114 @@ -use ruma::{EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, events::TimelineEventType}; -use serde_json::value::RawValue as RawJsonValue; +mod content; +mod format; +mod redact; +mod type_ext; + +use ruma::{ + EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, UserId, + events::TimelineEventType, +}; +use serde::Deserialize; +use serde_json::{Value as JsonValue, value::RawValue as RawJsonValue}; + +pub use self::type_ext::TypeExt; +use super::state_key::StateKey; +use crate::Result; /// Abstraction of a PDU so users can have their own PDU types. pub trait Event { + /// Serialize into a Ruma JSON format, consuming. + #[inline] + fn into_format(self) -> T + where + T: From>, + Self: Sized, + { + format::Owned(self).into() + } + + /// Serialize into a Ruma JSON format + #[inline] + fn to_format<'a, T>(&'a self) -> T + where + T: From>, + Self: Sized + 'a, + { + format::Ref(self).into() + } + + #[inline] + fn get_content_as_value(&self) -> JsonValue + where + Self: Sized, + { + content::as_value(self) + } + + #[inline] + fn get_content(&self) -> Result + where + for<'de> T: Deserialize<'de>, + Self: Sized, + { + content::get::(self) + } + + #[inline] + fn redacts_id(&self, room_version: &RoomVersionId) -> Option + where + Self: Sized, + { + redact::redacts_id(self, room_version) + } + + #[inline] + fn is_redacted(&self) -> bool + where + Self: Sized, + { + redact::is_redacted(self) + } + + fn is_owned(&self) -> bool; + + // + // Canonical properties + // + + /// All the authenticating events for this event. + fn auth_events(&self) -> impl DoubleEndedIterator + Send + '_; + + /// The event's content. + fn content(&self) -> &RawJsonValue; + /// The `EventId` of this event. fn event_id(&self) -> &EventId; + /// The time of creation on the originating server. + fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch; + + /// The events before this event. + fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_; + + /// If this event is a redaction event this is the event it redacts. + fn redacts(&self) -> Option<&EventId>; + /// The `RoomId` of this event. fn room_id(&self) -> &RoomId; /// The `UserId` of this event. fn sender(&self) -> &UserId; - /// The time of creation on the originating server. - fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch; - - /// The event type. - fn event_type(&self) -> &TimelineEventType; - - /// The event's content. - fn content(&self) -> &RawJsonValue; - /// The state key for this event. fn state_key(&self) -> Option<&str>; - /// The events before this event. - // Requires GATs to avoid boxing (and TAIT for making it convenient). - fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_; + /// The event type. + fn kind(&self) -> &TimelineEventType; - /// All the authenticating events for this event. - // Requires GATs to avoid boxing (and TAIT for making it convenient). - fn auth_events(&self) -> impl DoubleEndedIterator + Send + '_; + /// Metadata container; peer-trusted only. + fn unsigned(&self) -> Option<&RawJsonValue>; - /// If this event is a redaction event this is the event it redacts. - fn redacts(&self) -> Option<&EventId>; -} - -impl Event for &T { - fn event_id(&self) -> &EventId { (*self).event_id() } - - fn room_id(&self) -> &RoomId { (*self).room_id() } - - fn sender(&self) -> &UserId { (*self).sender() } - - fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { (*self).origin_server_ts() } - - fn event_type(&self) -> &TimelineEventType { (*self).event_type() } - - fn content(&self) -> &RawJsonValue { (*self).content() } - - fn state_key(&self) -> Option<&str> { (*self).state_key() } - - fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_ { - (*self).prev_events() - } - - fn auth_events(&self) -> impl DoubleEndedIterator + Send + '_ { - (*self).auth_events() - } - - fn redacts(&self) -> Option<&EventId> { (*self).redacts() } + //#[deprecated] + #[inline] + fn event_type(&self) -> &TimelineEventType { self.kind() } } diff --git a/src/core/matrix/event/content.rs b/src/core/matrix/event/content.rs new file mode 100644 index 00000000..1ee7ebd2 --- /dev/null +++ b/src/core/matrix/event/content.rs @@ -0,0 +1,21 @@ +use serde::Deserialize; +use serde_json::value::Value as JsonValue; + +use super::Event; +use crate::{Result, err}; + +#[inline] +#[must_use] +pub(super) fn as_value(event: &E) -> JsonValue { + get(event).expect("Failed to represent Event content as JsonValue") +} + +#[inline] +pub(super) fn get(event: &E) -> Result +where + T: for<'de> Deserialize<'de>, + E: Event, +{ + serde_json::from_str(event.content().get()) + .map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}")))) +} diff --git a/src/core/matrix/event/format.rs b/src/core/matrix/event/format.rs new file mode 100644 index 00000000..988cf4f0 --- /dev/null +++ b/src/core/matrix/event/format.rs @@ -0,0 +1,219 @@ +use ruma::{ + events::{ + AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, + AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, room::member::RoomMemberEventContent, + space::child::HierarchySpaceChildEvent, + }, + serde::Raw, +}; +use serde_json::json; + +use super::{Event, redact}; + +pub struct Owned(pub(super) E); + +pub struct Ref<'a, E: Event>(pub(super) &'a E); + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let (redacts, content) = redact::copy(event); + let mut json = json!({ + "content": content, + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "sender": event.sender(), + "type": event.event_type(), + }); + + if let Some(redacts) = redacts { + json["redacts"] = json!(redacts); + } + if let Some(state_key) = event.state_key() { + json["state_key"] = json!(state_key); + } + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let (redacts, content) = redact::copy(event); + let mut json = json!({ + "content": content, + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "room_id": event.room_id(), + "sender": event.sender(), + "type": event.kind(), + }); + + if let Some(redacts) = redacts { + json["redacts"] = json!(redacts); + } + if let Some(state_key) = event.state_key() { + json["state_key"] = json!(state_key); + } + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let (redacts, content) = redact::copy(event); + let mut json = json!({ + "content": content, + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "room_id": event.room_id(), + "sender": event.sender(), + "type": event.kind(), + }); + + if let Some(redacts) = &redacts { + json["redacts"] = json!(redacts); + } + if let Some(state_key) = event.state_key() { + json["state_key"] = json!(state_key); + } + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let mut json = json!({ + "content": event.content(), + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "room_id": event.room_id(), + "sender": event.sender(), + "state_key": event.state_key(), + "type": event.kind(), + }); + + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let mut json = json!({ + "content": event.content(), + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "sender": event.sender(), + "state_key": event.state_key(), + "type": event.kind(), + }); + + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let json = json!({ + "content": event.content(), + "sender": event.sender(), + "state_key": event.state_key(), + "type": event.kind(), + }); + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let json = json!({ + "content": event.content(), + "origin_server_ts": event.origin_server_ts(), + "sender": event.sender(), + "state_key": event.state_key(), + "type": event.kind(), + }); + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} + +impl From> for Raw> { + fn from(event: Owned) -> Self { Ref(&event.0).into() } +} + +impl<'a, E: Event> From> for Raw> { + fn from(event: Ref<'a, E>) -> Self { + let event = event.0; + let mut json = json!({ + "content": event.content(), + "event_id": event.event_id(), + "origin_server_ts": event.origin_server_ts(), + "redacts": event.redacts(), + "room_id": event.room_id(), + "sender": event.sender(), + "state_key": event.state_key(), + "type": event.kind(), + }); + + if let Some(unsigned) = event.unsigned() { + json["unsigned"] = json!(unsigned); + } + + serde_json::from_value(json).expect("Failed to serialize Event value") + } +} diff --git a/src/core/matrix/event/redact.rs b/src/core/matrix/event/redact.rs new file mode 100644 index 00000000..5deac874 --- /dev/null +++ b/src/core/matrix/event/redact.rs @@ -0,0 +1,86 @@ +use ruma::{ + OwnedEventId, RoomVersionId, + events::{TimelineEventType, room::redaction::RoomRedactionEventContent}, +}; +use serde::Deserialize; +use serde_json::value::{RawValue as RawJsonValue, to_raw_value}; + +use super::Event; + +/// Copies the `redacts` property of the event to the `content` dict and +/// vice-versa. +/// +/// This follows the specification's +/// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property): +/// +/// > For backwards-compatibility with older clients, servers should add a +/// > redacts property to the top level of m.room.redaction events in when +/// > serving such events over the Client-Server API. +/// +/// > For improved compatibility with newer clients, servers should add a +/// > redacts property to the content of m.room.redaction events in older +/// > room versions when serving such events over the Client-Server API. +#[must_use] +pub(super) fn copy(event: &E) -> (Option, Box) { + if *event.event_type() != TimelineEventType::RoomRedaction { + return (event.redacts().map(ToOwned::to_owned), event.content().to_owned()); + } + + let Ok(mut content) = event.get_content::() else { + return (event.redacts().map(ToOwned::to_owned), event.content().to_owned()); + }; + + if let Some(redacts) = content.redacts { + return (Some(redacts), event.content().to_owned()); + } + + if let Some(redacts) = event.redacts().map(ToOwned::to_owned) { + content.redacts = Some(redacts); + return ( + event.redacts().map(ToOwned::to_owned), + to_raw_value(&content).expect("Must be valid, we only added redacts field"), + ); + } + + (event.redacts().map(ToOwned::to_owned), event.content().to_owned()) +} + +#[must_use] +pub(super) fn is_redacted(event: &E) -> bool { + let Some(unsigned) = event.unsigned() else { + return false; + }; + + let Ok(unsigned) = ExtractRedactedBecause::deserialize(unsigned) else { + return false; + }; + + unsigned.redacted_because.is_some() +} + +#[must_use] +pub(super) fn redacts_id( + event: &E, + room_version: &RoomVersionId, +) -> Option { + use RoomVersionId::*; + + if *event.kind() != TimelineEventType::RoomRedaction { + return None; + } + + match *room_version { + | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => + event.redacts().map(ToOwned::to_owned), + | _ => + event + .get_content::() + .ok()? + .redacts, + } +} + +#[derive(Deserialize)] +struct ExtractRedactedBecause { + redacted_because: Option, +} diff --git a/src/core/matrix/event/type_ext.rs b/src/core/matrix/event/type_ext.rs new file mode 100644 index 00000000..9b824d41 --- /dev/null +++ b/src/core/matrix/event/type_ext.rs @@ -0,0 +1,32 @@ +use ruma::events::{StateEventType, TimelineEventType}; + +use super::StateKey; + +/// Convenience trait for adding event type plus state key to state maps. +pub trait TypeExt { + fn with_state_key(self, state_key: impl Into) -> (StateEventType, StateKey); +} + +impl TypeExt for StateEventType { + fn with_state_key(self, state_key: impl Into) -> (StateEventType, StateKey) { + (self, state_key.into()) + } +} + +impl TypeExt for &StateEventType { + fn with_state_key(self, state_key: impl Into) -> (StateEventType, StateKey) { + (self.clone(), state_key.into()) + } +} + +impl TypeExt for TimelineEventType { + fn with_state_key(self, state_key: impl Into) -> (StateEventType, StateKey) { + (self.into(), state_key.into()) + } +} + +impl TypeExt for &TimelineEventType { + fn with_state_key(self, state_key: impl Into) -> (StateEventType, StateKey) { + (self.clone().into(), state_key.into()) + } +} diff --git a/src/core/matrix/mod.rs b/src/core/matrix/mod.rs index 8c978173..b38d4c9a 100644 --- a/src/core/matrix/mod.rs +++ b/src/core/matrix/mod.rs @@ -2,8 +2,10 @@ pub mod event; pub mod pdu; +pub mod state_key; pub mod state_res; -pub use event::Event; -pub use pdu::{PduBuilder, PduCount, PduEvent, PduId, RawPduId, StateKey}; -pub use state_res::{EventTypeExt, RoomVersion, StateMap, TypeStateKey}; +pub use event::{Event, TypeExt as EventTypeExt}; +pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId}; +pub use state_key::StateKey; +pub use state_res::{RoomVersion, StateMap, TypeStateKey}; diff --git a/src/core/matrix/pdu.rs b/src/core/matrix/pdu.rs index 188586bd..e64baeb8 100644 --- a/src/core/matrix/pdu.rs +++ b/src/core/matrix/pdu.rs @@ -7,8 +7,6 @@ mod id; mod raw_id; mod redact; mod relation; -mod state_key; -mod strip; #[cfg(test)] mod tests; mod unsigned; @@ -27,37 +25,50 @@ pub use self::{ builder::{Builder, Builder as PduBuilder}, count::Count, event_id::*, - id::*, + id::{ShortId, *}, raw_id::*, - state_key::{ShortStateKey, StateKey}, }; -use super::Event; +use super::{Event, StateKey}; use crate::Result; /// Persistent Data Unit (Event) #[derive(Clone, Deserialize, Serialize, Debug)] pub struct Pdu { pub event_id: OwnedEventId, + pub room_id: OwnedRoomId, + pub sender: OwnedUserId, + #[serde(skip_serializing_if = "Option::is_none")] pub origin: Option, + pub origin_server_ts: UInt, + #[serde(rename = "type")] pub kind: TimelineEventType, + pub content: Box, + #[serde(skip_serializing_if = "Option::is_none")] pub state_key: Option, + pub prev_events: Vec, + pub depth: UInt, + pub auth_events: Vec, + #[serde(skip_serializing_if = "Option::is_none")] pub redacts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub unsigned: Option>, + pub hashes: EventHash, - #[serde(default, skip_serializing_if = "Option::is_none")] + // BTreeMap, BTreeMap> + #[serde(default, skip_serializing_if = "Option::is_none")] pub signatures: Option>, } @@ -79,31 +90,91 @@ impl Pdu { } impl Event for Pdu { - fn event_id(&self) -> &EventId { &self.event_id } - - fn room_id(&self) -> &RoomId { &self.room_id } - - fn sender(&self) -> &UserId { &self.sender } - - fn event_type(&self) -> &TimelineEventType { &self.kind } - - fn content(&self) -> &RawJsonValue { &self.content } - - fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { - MilliSecondsSinceUnixEpoch(self.origin_server_ts) - } - - fn state_key(&self) -> Option<&str> { self.state_key.as_deref() } - - fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_ { - self.prev_events.iter().map(AsRef::as_ref) - } - + #[inline] fn auth_events(&self) -> impl DoubleEndedIterator + Send + '_ { self.auth_events.iter().map(AsRef::as_ref) } + #[inline] + fn content(&self) -> &RawJsonValue { &self.content } + + #[inline] + fn event_id(&self) -> &EventId { &self.event_id } + + #[inline] + fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { + MilliSecondsSinceUnixEpoch(self.origin_server_ts) + } + + #[inline] + fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_ { + self.prev_events.iter().map(AsRef::as_ref) + } + + #[inline] fn redacts(&self) -> Option<&EventId> { self.redacts.as_deref() } + + #[inline] + fn room_id(&self) -> &RoomId { &self.room_id } + + #[inline] + fn sender(&self) -> &UserId { &self.sender } + + #[inline] + fn state_key(&self) -> Option<&str> { self.state_key.as_deref() } + + #[inline] + fn kind(&self) -> &TimelineEventType { &self.kind } + + #[inline] + fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_deref() } + + #[inline] + fn is_owned(&self) -> bool { true } +} + +impl Event for &Pdu { + #[inline] + fn auth_events(&self) -> impl DoubleEndedIterator + Send + '_ { + self.auth_events.iter().map(AsRef::as_ref) + } + + #[inline] + fn content(&self) -> &RawJsonValue { &self.content } + + #[inline] + fn event_id(&self) -> &EventId { &self.event_id } + + #[inline] + fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { + MilliSecondsSinceUnixEpoch(self.origin_server_ts) + } + + #[inline] + fn prev_events(&self) -> impl DoubleEndedIterator + Send + '_ { + self.prev_events.iter().map(AsRef::as_ref) + } + + #[inline] + fn redacts(&self) -> Option<&EventId> { self.redacts.as_deref() } + + #[inline] + fn room_id(&self) -> &RoomId { &self.room_id } + + #[inline] + fn sender(&self) -> &UserId { &self.sender } + + #[inline] + fn state_key(&self) -> Option<&str> { self.state_key.as_deref() } + + #[inline] + fn kind(&self) -> &TimelineEventType { &self.kind } + + #[inline] + fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_deref() } + + #[inline] + fn is_owned(&self) -> bool { false } } /// Prevent derived equality which wouldn't limit itself to event_id diff --git a/src/core/matrix/pdu/id.rs b/src/core/matrix/pdu/id.rs index 0b23a29f..896d677b 100644 --- a/src/core/matrix/pdu/id.rs +++ b/src/core/matrix/pdu/id.rs @@ -3,6 +3,7 @@ use crate::utils::u64_from_u8x8; pub type ShortRoomId = ShortId; pub type ShortEventId = ShortId; +pub type ShortStateKey = ShortId; pub type ShortId = u64; #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/src/core/matrix/pdu/redact.rs b/src/core/matrix/pdu/redact.rs index 409debfe..e6a03209 100644 --- a/src/core/matrix/pdu/redact.rs +++ b/src/core/matrix/pdu/redact.rs @@ -1,117 +1,29 @@ -use ruma::{ - OwnedEventId, RoomVersionId, - canonical_json::redact_content_in_place, - events::{TimelineEventType, room::redaction::RoomRedactionEventContent}, -}; -use serde::Deserialize; -use serde_json::{ - json, - value::{RawValue as RawJsonValue, to_raw_value}, -}; +use ruma::{RoomVersionId, canonical_json::redact_content_in_place}; +use serde_json::{json, value::to_raw_value}; -use crate::{Error, Result, implement}; - -#[derive(Deserialize)] -struct ExtractRedactedBecause { - redacted_because: Option, -} +use crate::{Error, Result, err, implement}; #[implement(super::Pdu)] pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: &Self) -> Result { self.unsigned = None; let mut content = serde_json::from_str(self.content.get()) - .map_err(|_| Error::bad_database("PDU in db has invalid content."))?; + .map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?; redact_content_in_place(&mut content, room_version_id, self.kind.to_string()) .map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?; - self.unsigned = Some( - to_raw_value(&json!({ - "redacted_because": serde_json::to_value(reason).expect("to_value(Pdu) always works") - })) - .expect("to string always works"), - ); + let reason = serde_json::to_value(reason).expect("Failed to preserialize reason"); - self.content = to_raw_value(&content).expect("to string always works"); + let redacted_because = json!({ + "redacted_because": reason, + }); + + self.unsigned = to_raw_value(&redacted_because) + .expect("Failed to serialize unsigned") + .into(); + + self.content = to_raw_value(&content).expect("Failed to serialize content"); Ok(()) } - -#[implement(super::Pdu)] -#[must_use] -pub fn is_redacted(&self) -> bool { - let Some(unsigned) = &self.unsigned else { - return false; - }; - - let Ok(unsigned) = ExtractRedactedBecause::deserialize(&**unsigned) else { - return false; - }; - - unsigned.redacted_because.is_some() -} - -/// Copies the `redacts` property of the event to the `content` dict and -/// vice-versa. -/// -/// This follows the specification's -/// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property): -/// -/// > For backwards-compatibility with older clients, servers should add a -/// > redacts -/// > property to the top level of m.room.redaction events in when serving -/// > such events -/// > over the Client-Server API. -/// -/// > For improved compatibility with newer clients, servers should add a -/// > redacts property -/// > to the content of m.room.redaction events in older room versions when -/// > serving -/// > such events over the Client-Server API. -#[implement(super::Pdu)] -#[must_use] -pub fn copy_redacts(&self) -> (Option, Box) { - if self.kind == TimelineEventType::RoomRedaction { - if let Ok(mut content) = - serde_json::from_str::(self.content.get()) - { - match content.redacts { - | Some(redacts) => { - return (Some(redacts), self.content.clone()); - }, - | _ => match self.redacts.clone() { - | Some(redacts) => { - content.redacts = Some(redacts); - return ( - self.redacts.clone(), - to_raw_value(&content) - .expect("Must be valid, we only added redacts field"), - ); - }, - | _ => {}, - }, - } - } - } - - (self.redacts.clone(), self.content.clone()) -} - -#[implement(super::Pdu)] -#[must_use] -pub fn redacts_id(&self, room_version: &RoomVersionId) -> Option { - use RoomVersionId::*; - - if self.kind != TimelineEventType::RoomRedaction { - return None; - } - - match *room_version { - | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => self.redacts.clone(), - | _ => - self.get_content::() - .ok()? - .redacts, - } -} diff --git a/src/core/matrix/pdu/strip.rs b/src/core/matrix/pdu/strip.rs deleted file mode 100644 index a39e7d35..00000000 --- a/src/core/matrix/pdu/strip.rs +++ /dev/null @@ -1,257 +0,0 @@ -use ruma::{ - events::{ - AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, - AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, room::member::RoomMemberEventContent, - space::child::HierarchySpaceChildEvent, - }, - serde::Raw, -}; -use serde_json::{json, value::Value as JsonValue}; - -use crate::implement; - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_room_event(self) -> Raw { self.to_room_event() } - -#[implement(super::Pdu)] -#[must_use] -pub fn to_room_event(&self) -> Raw { - let value = self.to_room_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn to_room_event_value(&self) -> JsonValue { - let (redacts, content) = self.copy_redacts(); - let mut json = json!({ - "content": content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "room_id": self.room_id, - }); - - if let Some(unsigned) = &self.unsigned { - json["unsigned"] = json!(unsigned); - } - if let Some(state_key) = &self.state_key { - json["state_key"] = json!(state_key); - } - if let Some(redacts) = &redacts { - json["redacts"] = json!(redacts); - } - - json -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_message_like_event(self) -> Raw { self.to_message_like_event() } - -#[implement(super::Pdu)] -#[must_use] -pub fn to_message_like_event(&self) -> Raw { - let value = self.to_message_like_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn to_message_like_event_value(&self) -> JsonValue { - let (redacts, content) = self.copy_redacts(); - let mut json = json!({ - "content": content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "room_id": self.room_id, - }); - - if let Some(unsigned) = &self.unsigned { - json["unsigned"] = json!(unsigned); - } - if let Some(state_key) = &self.state_key { - json["state_key"] = json!(state_key); - } - if let Some(redacts) = &redacts { - json["redacts"] = json!(redacts); - } - - json -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_sync_room_event(self) -> Raw { self.to_sync_room_event() } - -#[implement(super::Pdu)] -#[must_use] -pub fn to_sync_room_event(&self) -> Raw { - let value = self.to_sync_room_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn to_sync_room_event_value(&self) -> JsonValue { - let (redacts, content) = self.copy_redacts(); - let mut json = json!({ - "content": content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - }); - - if let Some(unsigned) = &self.unsigned { - json["unsigned"] = json!(unsigned); - } - if let Some(state_key) = &self.state_key { - json["state_key"] = json!(state_key); - } - if let Some(redacts) = &redacts { - json["redacts"] = json!(redacts); - } - - json -} - -#[implement(super::Pdu)] -#[must_use] -pub fn into_state_event(self) -> Raw { - let value = self.into_state_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_state_event_value(self) -> JsonValue { - let mut json = json!({ - "content": self.content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "room_id": self.room_id, - "state_key": self.state_key, - }); - - if let Some(unsigned) = self.unsigned { - json["unsigned"] = json!(unsigned); - } - - json -} - -#[implement(super::Pdu)] -#[must_use] -pub fn into_sync_state_event(self) -> Raw { - let value = self.into_sync_state_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_sync_state_event_value(self) -> JsonValue { - let mut json = json!({ - "content": self.content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "state_key": self.state_key, - }); - - if let Some(unsigned) = &self.unsigned { - json["unsigned"] = json!(unsigned); - } - - json -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_stripped_state_event(self) -> Raw { - self.to_stripped_state_event() -} - -#[implement(super::Pdu)] -#[must_use] -pub fn to_stripped_state_event(&self) -> Raw { - let value = self.to_stripped_state_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn to_stripped_state_event_value(&self) -> JsonValue { - json!({ - "content": self.content, - "type": self.kind, - "sender": self.sender, - "state_key": self.state_key, - }) -} - -#[implement(super::Pdu)] -#[must_use] -pub fn into_stripped_spacechild_state_event(self) -> Raw { - let value = self.into_stripped_spacechild_state_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_stripped_spacechild_state_event_value(self) -> JsonValue { - json!({ - "content": self.content, - "type": self.kind, - "sender": self.sender, - "state_key": self.state_key, - "origin_server_ts": self.origin_server_ts, - }) -} - -#[implement(super::Pdu)] -#[must_use] -pub fn into_member_event(self) -> Raw> { - let value = self.into_member_event_value(); - serde_json::from_value(value).expect("Failed to serialize Event value") -} - -#[implement(super::Pdu)] -#[must_use] -#[inline] -pub fn into_member_event_value(self) -> JsonValue { - let mut json = json!({ - "content": self.content, - "type": self.kind, - "event_id": self.event_id, - "sender": self.sender, - "origin_server_ts": self.origin_server_ts, - "redacts": self.redacts, - "room_id": self.room_id, - "state_key": self.state_key, - }); - - if let Some(unsigned) = self.unsigned { - json["unsigned"] = json!(unsigned); - } - - json -} diff --git a/src/core/matrix/pdu/state_key.rs b/src/core/matrix/state_key.rs similarity index 67% rename from src/core/matrix/pdu/state_key.rs rename to src/core/matrix/state_key.rs index 4af4fcf7..06d614f8 100644 --- a/src/core/matrix/pdu/state_key.rs +++ b/src/core/matrix/state_key.rs @@ -1,8 +1,5 @@ use smallstr::SmallString; -use super::ShortId; - pub type StateKey = SmallString<[u8; INLINE_SIZE]>; -pub type ShortStateKey = ShortId; const INLINE_SIZE: usize = 48; diff --git a/src/core/matrix/state_res/benches.rs b/src/core/matrix/state_res/benches.rs index 12eeab9d..69088369 100644 --- a/src/core/matrix/state_res/benches.rs +++ b/src/core/matrix/state_res/benches.rs @@ -13,7 +13,6 @@ use ruma::{ EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, Signatures, UserId, events::{ StateEventType, TimelineEventType, - pdu::{EventHash, Pdu, RoomV3Pdu}, room::{ join_rules::{JoinRule, RoomJoinRulesEventContent}, member::{MembershipState, RoomMemberEventContent}, @@ -26,8 +25,10 @@ use serde_json::{ value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value}, }; -use self::event::PduEvent; -use crate::state_res::{self as state_res, Error, Event, Result, StateMap}; +use crate::{ + matrix::{Event, Pdu, pdu::EventHash}, + state_res::{self as state_res, Error, Result, StateMap}, +}; static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0); @@ -60,7 +61,7 @@ fn resolution_shallow_auth_chain(c: &mut test::Bencher) { c.iter(|| async { let ev_map = store.0.clone(); let state_sets = [&state_at_bob, &state_at_charlie]; - let fetch = |id: OwnedEventId| ready(ev_map.get(&id).clone()); + let fetch = |id: OwnedEventId| ready(ev_map.get(&id).map(ToOwned::to_owned)); let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some()); let auth_chain_sets: Vec> = state_sets .iter() @@ -142,7 +143,7 @@ fn resolve_deeper_event_set(c: &mut test::Bencher) { }) .collect(); - let fetch = |id: OwnedEventId| ready(inner.get(&id).clone()); + let fetch = |id: OwnedEventId| ready(inner.get(&id).map(ToOwned::to_owned)); let exists = |id: OwnedEventId| ready(inner.get(&id).is_some()); let _ = match state_res::resolve( &RoomVersionId::V6, @@ -246,7 +247,7 @@ impl TestStore { } } -impl TestStore { +impl TestStore { #[allow(clippy::type_complexity)] fn set_up( &mut self, @@ -380,7 +381,7 @@ fn to_pdu_event( content: Box, auth_events: &[S], prev_events: &[S], -) -> PduEvent +) -> Pdu where S: AsRef, { @@ -403,30 +404,28 @@ where .map(event_id) .collect::>(); - let state_key = state_key.map(ToOwned::to_owned); - PduEvent { + Pdu { event_id: id.try_into().unwrap(), - rest: Pdu::RoomV3Pdu(RoomV3Pdu { - room_id: room_id().to_owned(), - sender: sender.to_owned(), - origin_server_ts: MilliSecondsSinceUnixEpoch(ts.try_into().unwrap()), - state_key, - kind: ev_type, - content, - redacts: None, - unsigned: btreemap! {}, - auth_events, - prev_events, - depth: uint!(0), - hashes: EventHash::new(String::new()), - signatures: Signatures::new(), - }), + room_id: room_id().to_owned(), + sender: sender.to_owned(), + origin_server_ts: ts.try_into().unwrap(), + state_key: state_key.map(Into::into), + kind: ev_type, + content, + origin: None, + redacts: None, + unsigned: None, + auth_events, + prev_events, + depth: uint!(0), + hashes: EventHash { sha256: String::new() }, + signatures: None, } } // all graphs start with these input events #[allow(non_snake_case)] -fn INITIAL_EVENTS() -> HashMap { +fn INITIAL_EVENTS() -> HashMap { vec![ to_pdu_event::<&EventId>( "CREATE", @@ -508,7 +507,7 @@ fn INITIAL_EVENTS() -> HashMap { // all graphs start with these input events #[allow(non_snake_case)] -fn BAN_STATE_SET() -> HashMap { +fn BAN_STATE_SET() -> HashMap { vec![ to_pdu_event( "PA", @@ -551,119 +550,3 @@ fn BAN_STATE_SET() -> HashMap { .map(|ev| (ev.event_id().to_owned(), ev)) .collect() } - -/// Convenience trait for adding event type plus state key to state maps. -trait EventTypeExt { - fn with_state_key(self, state_key: impl Into) -> (StateEventType, String); -} - -impl EventTypeExt for &TimelineEventType { - fn with_state_key(self, state_key: impl Into) -> (StateEventType, String) { - (self.to_string().into(), state_key.into()) - } -} - -mod event { - use ruma::{ - EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, UserId, - events::{TimelineEventType, pdu::Pdu}, - }; - use serde::{Deserialize, Serialize}; - use serde_json::value::RawValue as RawJsonValue; - - use super::Event; - - impl Event for PduEvent { - fn event_id(&self) -> &EventId { &self.event_id } - - fn room_id(&self) -> &RoomId { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.room_id, - | Pdu::RoomV3Pdu(ev) => &ev.room_id, - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn sender(&self) -> &UserId { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.sender, - | Pdu::RoomV3Pdu(ev) => &ev.sender, - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn event_type(&self) -> &TimelineEventType { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.kind, - | Pdu::RoomV3Pdu(ev) => &ev.kind, - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn content(&self) -> &RawJsonValue { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.content, - | Pdu::RoomV3Pdu(ev) => &ev.content, - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.origin_server_ts, - | Pdu::RoomV3Pdu(ev) => ev.origin_server_ts, - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn state_key(&self) -> Option<&str> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.state_key.as_deref(), - | Pdu::RoomV3Pdu(ev) => ev.state_key.as_deref(), - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn prev_events(&self) -> Box + Send + '_> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => - Box::new(ev.prev_events.iter().map(|(id, _)| id.as_ref())), - | Pdu::RoomV3Pdu(ev) => Box::new(ev.prev_events.iter().map(AsRef::as_ref)), - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn auth_events(&self) -> Box + Send + '_> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => - Box::new(ev.auth_events.iter().map(|(id, _)| id.as_ref())), - | Pdu::RoomV3Pdu(ev) => Box::new(ev.auth_events.iter().map(AsRef::as_ref)), - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - - fn redacts(&self) -> Option<&EventId> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.redacts.as_deref(), - | Pdu::RoomV3Pdu(ev) => ev.redacts.as_deref(), - #[cfg(not(feature = "unstable-exhaustive-types"))] - | _ => unreachable!("new PDU version"), - } - } - } - - #[derive(Clone, Debug, Deserialize, Serialize)] - pub(crate) struct PduEvent { - pub(crate) event_id: OwnedEventId, - #[serde(flatten)] - pub(crate) rest: Pdu, - } -} diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 759ab5cb..8c760860 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -136,17 +136,17 @@ pub fn auth_types_for_event( event_id = incoming_event.event_id().as_str(), ) )] -pub async fn auth_check( +pub async fn auth_check( room_version: &RoomVersion, - incoming_event: &Incoming, - current_third_party_invite: Option<&Incoming>, + incoming_event: &E, + current_third_party_invite: Option<&E>, fetch_state: F, ) -> Result where F: Fn(&StateEventType, &str) -> Fut + Send, - Fut: Future> + Send, - Fetched: Event + Send, - Incoming: Event + Send + Sync, + Fut: Future> + Send, + E: Event + Send + Sync, + for<'a> &'a E: Event + Send, { debug!( event_id = format!("{}", incoming_event.event_id()), @@ -514,20 +514,24 @@ where /// event and the current State. #[allow(clippy::too_many_arguments)] #[allow(clippy::cognitive_complexity)] -fn valid_membership_change( +fn valid_membership_change( room_version: &RoomVersion, target_user: &UserId, - target_user_membership_event: Option<&impl Event>, + target_user_membership_event: Option<&E>, sender: &UserId, - sender_membership_event: Option<&impl Event>, - current_event: impl Event, - current_third_party_invite: Option<&impl Event>, - power_levels_event: Option<&impl Event>, - join_rules_event: Option<&impl Event>, + sender_membership_event: Option<&E>, + current_event: &E, + current_third_party_invite: Option<&E>, + power_levels_event: Option<&E>, + join_rules_event: Option<&E>, user_for_join_auth: Option<&UserId>, user_for_join_auth_membership: &MembershipState, - create_room: &impl Event, -) -> Result { + create_room: &E, +) -> Result +where + E: Event + Send + Sync, + for<'a> &'a E: Event + Send, +{ #[derive(Deserialize)] struct GetThirdPartyInvite { third_party_invite: Option>, @@ -820,7 +824,7 @@ fn valid_membership_change( /// /// Does the event have the correct userId as its state_key if it's not the "" /// state_key. -fn can_send_event(event: impl Event, ple: Option, user_level: Int) -> bool { +fn can_send_event(event: &impl Event, ple: Option<&impl Event>, user_level: Int) -> bool { let event_type_power_level = get_send_level(event.event_type(), event.state_key(), ple); debug!( @@ -846,8 +850,8 @@ fn can_send_event(event: impl Event, ple: Option, user_level: Int) - /// Confirm that the event sender has the required power levels. fn check_power_levels( room_version: &RoomVersion, - power_event: impl Event, - previous_power_event: Option, + power_event: &impl Event, + previous_power_event: Option<&impl Event>, user_level: Int, ) -> Option { match power_event.state_key() { @@ -1010,7 +1014,7 @@ fn get_deserialize_levels( /// given event. fn check_redaction( _room_version: &RoomVersion, - redaction_event: impl Event, + redaction_event: &impl Event, user_level: Int, redact_level: Int, ) -> Result { @@ -1039,7 +1043,7 @@ fn check_redaction( fn get_send_level( e_type: &TimelineEventType, state_key: Option<&str>, - power_lvl: Option, + power_lvl: Option<&impl Event>, ) -> Int { power_lvl .and_then(|ple| { @@ -1062,7 +1066,7 @@ fn verify_third_party_invite( target_user: Option<&UserId>, sender: &UserId, tp_id: &ThirdPartyInvite, - current_third_party_invite: Option, + current_third_party_invite: Option<&impl Event>, ) -> bool { // 1. Check for user being banned happens before this is called // checking for mxid and token keys is done by ruma when deserializing @@ -1128,12 +1132,15 @@ mod tests { }; use serde_json::value::to_raw_value as to_raw_json_value; - use crate::state_res::{ - Event, EventTypeExt, RoomVersion, StateMap, - event_auth::valid_membership_change, - test_utils::{ - INITIAL_EVENTS, INITIAL_EVENTS_CREATE_ROOM, PduEvent, alice, charlie, ella, event_id, - member_content_ban, member_content_join, room_id, to_pdu_event, + use crate::{ + matrix::{Event, EventTypeExt, Pdu as PduEvent}, + state_res::{ + RoomVersion, StateMap, + event_auth::valid_membership_change, + test_utils::{ + INITIAL_EVENTS, INITIAL_EVENTS_CREATE_ROOM, alice, charlie, ella, event_id, + member_content_ban, member_content_join, room_id, to_pdu_event, + }, }, }; diff --git a/src/core/matrix/state_res/mod.rs b/src/core/matrix/state_res/mod.rs index 651f6130..ed5aa034 100644 --- a/src/core/matrix/state_res/mod.rs +++ b/src/core/matrix/state_res/mod.rs @@ -37,7 +37,7 @@ pub use self::{ }; use crate::{ debug, debug_error, - matrix::{event::Event, pdu::StateKey}, + matrix::{Event, StateKey}, trace, utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt}, warn, @@ -90,7 +90,7 @@ where SetIter: Iterator> + Clone + Send, Hasher: BuildHasher + Send + Sync, E: Event + Clone + Send + Sync, - for<'b> &'b E: Send, + for<'b> &'b E: Event + Send, { debug!("State resolution starting"); @@ -522,6 +522,7 @@ where Fut: Future> + Send, S: Stream + Send + 'a, E: Event + Clone + Send + Sync, + for<'b> &'b E: Event + Send, { debug!("starting iterative auth check"); @@ -552,7 +553,7 @@ where let auth_events = &auth_events; let mut resolved_state = unconflicted_state; - for event in &events_to_check { + for event in events_to_check { let state_key = event .state_key() .ok_or_else(|| Error::InvalidPdu("State event had no state key".to_owned()))?; @@ -607,11 +608,15 @@ where }); let fetch_state = |ty: &StateEventType, key: &str| { - future::ready(auth_state.get(&ty.with_state_key(key))) + future::ready( + auth_state + .get(&ty.with_state_key(key)) + .map(ToOwned::to_owned), + ) }; let auth_result = - auth_check(room_version, &event, current_third_party.as_ref(), fetch_state).await; + auth_check(room_version, &event, current_third_party, fetch_state).await; match auth_result { | Ok(true) => { @@ -794,11 +799,11 @@ where } } -fn is_type_and_key(ev: impl Event, ev_type: &TimelineEventType, state_key: &str) -> bool { +fn is_type_and_key(ev: &impl Event, ev_type: &TimelineEventType, state_key: &str) -> bool { ev.event_type() == ev_type && ev.state_key() == Some(state_key) } -fn is_power_event(event: impl Event) -> bool { +fn is_power_event(event: &impl Event) -> bool { match event.event_type() { | TimelineEventType::RoomPowerLevels | TimelineEventType::RoomJoinRules @@ -859,15 +864,19 @@ mod tests { use serde_json::{json, value::to_raw_value as to_raw_json_value}; use super::{ - Event, EventTypeExt, StateMap, is_power_event, + StateMap, is_power_event, room_version::RoomVersion, test_utils::{ - INITIAL_EVENTS, PduEvent, TestStore, alice, bob, charlie, do_check, ella, event_id, + INITIAL_EVENTS, TestStore, alice, bob, charlie, do_check, ella, event_id, member_content_ban, member_content_join, room_id, to_init_pdu_event, to_pdu_event, zara, }, }; - use crate::{debug, utils::stream::IterStream}; + use crate::{ + debug, + matrix::{Event, EventTypeExt, Pdu as PduEvent}, + utils::stream::IterStream, + }; async fn test_event_sort() { use futures::future::ready; diff --git a/src/core/matrix/state_res/test_utils.rs b/src/core/matrix/state_res/test_utils.rs index c6945f66..9f24c51b 100644 --- a/src/core/matrix/state_res/test_utils.rs +++ b/src/core/matrix/state_res/test_utils.rs @@ -10,7 +10,6 @@ use ruma::{ UserId, event_id, events::{ TimelineEventType, - pdu::{EventHash, Pdu, RoomV3Pdu}, room::{ join_rules::{JoinRule, RoomJoinRulesEventContent}, member::{MembershipState, RoomMemberEventContent}, @@ -23,17 +22,16 @@ use serde_json::{ value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value}, }; -pub(crate) use self::event::PduEvent; use super::auth_types_for_event; use crate::{ Result, info, - matrix::{Event, EventTypeExt, StateMap}, + matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash}, }; static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0); pub(crate) async fn do_check( - events: &[PduEvent], + events: &[Pdu], edges: Vec>, expected_state_ids: Vec, ) { @@ -81,8 +79,8 @@ pub(crate) async fn do_check( } } - // event_id -> PduEvent - let mut event_map: HashMap = HashMap::new(); + // event_id -> Pdu + let mut event_map: HashMap = HashMap::new(); // event_id -> StateMap let mut state_at_event: HashMap> = HashMap::new(); @@ -265,7 +263,7 @@ impl TestStore { // A StateStore implementation for testing #[allow(clippy::type_complexity)] -impl TestStore { +impl TestStore { pub(crate) fn set_up( &mut self, ) -> (StateMap, StateMap, StateMap) { @@ -390,7 +388,7 @@ pub(crate) fn to_init_pdu_event( ev_type: TimelineEventType, state_key: Option<&str>, content: Box, -) -> PduEvent { +) -> Pdu { let ts = SERVER_TIMESTAMP.fetch_add(1, SeqCst); let id = if id.contains('$') { id.to_owned() @@ -398,24 +396,22 @@ pub(crate) fn to_init_pdu_event( format!("${id}:foo") }; - let state_key = state_key.map(ToOwned::to_owned); - PduEvent { + Pdu { event_id: id.try_into().unwrap(), - rest: Pdu::RoomV3Pdu(RoomV3Pdu { - room_id: room_id().to_owned(), - sender: sender.to_owned(), - origin_server_ts: MilliSecondsSinceUnixEpoch(ts.try_into().unwrap()), - state_key, - kind: ev_type, - content, - redacts: None, - unsigned: BTreeMap::new(), - auth_events: vec![], - prev_events: vec![], - depth: uint!(0), - hashes: EventHash::new("".to_owned()), - signatures: ServerSignatures::default(), - }), + room_id: room_id().to_owned(), + sender: sender.to_owned(), + origin_server_ts: ts.try_into().unwrap(), + state_key: state_key.map(Into::into), + kind: ev_type, + content, + origin: None, + redacts: None, + unsigned: None, + auth_events: vec![], + prev_events: vec![], + depth: uint!(0), + hashes: EventHash { sha256: "".to_owned() }, + signatures: None, } } @@ -427,7 +423,7 @@ pub(crate) fn to_pdu_event( content: Box, auth_events: &[S], prev_events: &[S], -) -> PduEvent +) -> Pdu where S: AsRef, { @@ -448,30 +444,28 @@ where .map(event_id) .collect::>(); - let state_key = state_key.map(ToOwned::to_owned); - PduEvent { + Pdu { event_id: id.try_into().unwrap(), - rest: Pdu::RoomV3Pdu(RoomV3Pdu { - room_id: room_id().to_owned(), - sender: sender.to_owned(), - origin_server_ts: MilliSecondsSinceUnixEpoch(ts.try_into().unwrap()), - state_key, - kind: ev_type, - content, - redacts: None, - unsigned: BTreeMap::new(), - auth_events, - prev_events, - depth: uint!(0), - hashes: EventHash::new("".to_owned()), - signatures: ServerSignatures::default(), - }), + room_id: room_id().to_owned(), + sender: sender.to_owned(), + origin_server_ts: ts.try_into().unwrap(), + state_key: state_key.map(Into::into), + kind: ev_type, + content, + origin: None, + redacts: None, + unsigned: None, + auth_events, + prev_events, + depth: uint!(0), + hashes: EventHash { sha256: "".to_owned() }, + signatures: None, } } // all graphs start with these input events #[allow(non_snake_case)] -pub(crate) fn INITIAL_EVENTS() -> HashMap { +pub(crate) fn INITIAL_EVENTS() -> HashMap { vec![ to_pdu_event::<&EventId>( "CREATE", @@ -553,7 +547,7 @@ pub(crate) fn INITIAL_EVENTS() -> HashMap { // all graphs start with these input events #[allow(non_snake_case)] -pub(crate) fn INITIAL_EVENTS_CREATE_ROOM() -> HashMap { +pub(crate) fn INITIAL_EVENTS_CREATE_ROOM() -> HashMap { vec![to_pdu_event::<&EventId>( "CREATE", alice(), @@ -575,111 +569,3 @@ pub(crate) fn INITIAL_EDGES() -> Vec { .map(event_id) .collect::>() } - -pub(crate) mod event { - use ruma::{ - EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, UserId, - events::{TimelineEventType, pdu::Pdu}, - }; - use serde::{Deserialize, Serialize}; - use serde_json::value::RawValue as RawJsonValue; - - use crate::Event; - - impl Event for PduEvent { - fn event_id(&self) -> &EventId { &self.event_id } - - fn room_id(&self) -> &RoomId { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.room_id, - | Pdu::RoomV3Pdu(ev) => &ev.room_id, - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn sender(&self) -> &UserId { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.sender, - | Pdu::RoomV3Pdu(ev) => &ev.sender, - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn event_type(&self) -> &TimelineEventType { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.kind, - | Pdu::RoomV3Pdu(ev) => &ev.kind, - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn content(&self) -> &RawJsonValue { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => &ev.content, - | Pdu::RoomV3Pdu(ev) => &ev.content, - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.origin_server_ts, - | Pdu::RoomV3Pdu(ev) => ev.origin_server_ts, - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn state_key(&self) -> Option<&str> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.state_key.as_deref(), - | Pdu::RoomV3Pdu(ev) => ev.state_key.as_deref(), - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - #[allow(refining_impl_trait)] - fn prev_events(&self) -> Box + Send + '_> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => - Box::new(ev.prev_events.iter().map(|(id, _)| id.as_ref())), - | Pdu::RoomV3Pdu(ev) => Box::new(ev.prev_events.iter().map(AsRef::as_ref)), - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - #[allow(refining_impl_trait)] - fn auth_events(&self) -> Box + Send + '_> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => - Box::new(ev.auth_events.iter().map(|(id, _)| id.as_ref())), - | Pdu::RoomV3Pdu(ev) => Box::new(ev.auth_events.iter().map(AsRef::as_ref)), - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - - fn redacts(&self) -> Option<&EventId> { - match &self.rest { - | Pdu::RoomV1Pdu(ev) => ev.redacts.as_deref(), - | Pdu::RoomV3Pdu(ev) => ev.redacts.as_deref(), - #[allow(unreachable_patterns)] - | _ => unreachable!("new PDU version"), - } - } - } - - #[derive(Clone, Debug, Deserialize, Serialize)] - #[allow(clippy::exhaustive_structs)] - pub(crate) struct PduEvent { - pub(crate) event_id: OwnedEventId, - #[serde(flatten)] - pub(crate) rest: Pdu, - } -} diff --git a/src/core/mod.rs b/src/core/mod.rs index aaacd4d8..d99139be 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -25,7 +25,9 @@ pub use info::{ rustc_flags_capture, version, version::{name, version}, }; -pub use matrix::{Event, EventTypeExt, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res}; +pub use matrix::{ + Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res, +}; pub use server::Server; pub use utils::{ctor, dtor, implement, result, result::Result}; diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index a76c3ef6..66c373ec 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -9,8 +9,8 @@ use std::{ }; use async_trait::async_trait; -use conduwuit::{ - Error, PduEvent, Result, Server, debug, err, error, error::default_log, pdu::PduBuilder, +use conduwuit_core::{ + Error, Event, Result, Server, debug, err, error, error::default_log, pdu::PduBuilder, }; pub use create::create_admin_room; use futures::{Future, FutureExt, TryFutureExt}; @@ -361,7 +361,10 @@ impl Service { Ok(()) } - pub async fn is_admin_command(&self, pdu: &PduEvent, body: &str) -> bool { + pub async fn is_admin_command(&self, event: &E, body: &str) -> bool + where + E: Event + Send + Sync, + { // Server-side command-escape with public echo let is_escape = body.starts_with('\\'); let is_public_escape = is_escape && body.trim_start_matches('\\').starts_with("!admin"); @@ -376,8 +379,10 @@ impl Service { return false; } + let user_is_local = self.services.globals.user_is_local(event.sender()); + // only allow public escaped commands by local admins - if is_public_escape && !self.services.globals.user_is_local(&pdu.sender) { + if is_public_escape && !user_is_local { return false; } @@ -387,20 +392,20 @@ impl Service { } // Prevent unescaped !admin from being used outside of the admin room - if is_public_prefix && !self.is_admin_room(&pdu.room_id).await { + if is_public_prefix && !self.is_admin_room(event.room_id()).await { return false; } // Only senders who are admin can proceed - if !self.user_is_admin(&pdu.sender).await { + if !self.user_is_admin(event.sender()).await { return false; } // This will evaluate to false if the emergency password is set up so that // the administrator can execute commands as the server user let emergency_password_set = self.services.server.config.emergency_password.is_some(); - let from_server = pdu.sender == *server_user && !emergency_password_set; - if from_server && self.is_admin_room(&pdu.room_id).await { + let from_server = event.sender() == server_user && !emergency_password_set; + if from_server && self.is_admin_room(event.room_id()).await { return false; } diff --git a/src/service/pusher/mod.rs b/src/service/pusher/mod.rs index 27490fb8..192ef447 100644 --- a/src/service/pusher/mod.rs +++ b/src/service/pusher/mod.rs @@ -1,12 +1,12 @@ use std::{fmt::Debug, mem, sync::Arc}; use bytes::BytesMut; -use conduwuit::{ - Err, PduEvent, Result, debug_warn, err, trace, +use conduwuit_core::{ + Err, Event, Result, debug_warn, err, trace, utils::{stream::TryIgnore, string_from_bytes}, warn, }; -use database::{Deserialized, Ignore, Interfix, Json, Map}; +use conduwuit_database::{Deserialized, Ignore, Interfix, Json, Map}; use futures::{Stream, StreamExt}; use ipaddress::IPAddress; use ruma::{ @@ -272,22 +272,26 @@ impl Service { } } - #[tracing::instrument(skip(self, user, unread, pusher, ruleset, pdu))] - pub async fn send_push_notice( + #[tracing::instrument(skip(self, user, unread, pusher, ruleset, event))] + pub async fn send_push_notice( &self, user: &UserId, unread: UInt, pusher: &Pusher, ruleset: Ruleset, - pdu: &PduEvent, - ) -> Result<()> { + event: &E, + ) -> Result + where + E: Event + Send + Sync, + for<'a> &'a E: Event + Send, + { let mut notify = None; let mut tweaks = Vec::new(); let power_levels: RoomPowerLevelsEventContent = self .services .state_accessor - .room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "") + .room_state_get(event.room_id(), &StateEventType::RoomPowerLevels, "") .await .and_then(|ev| { serde_json::from_str(ev.content.get()).map_err(|e| { @@ -296,8 +300,9 @@ impl Service { }) .unwrap_or_default(); + let serialized = event.to_format(); for action in self - .get_actions(user, &ruleset, &power_levels, &pdu.to_sync_room_event(), &pdu.room_id) + .get_actions(user, &ruleset, &power_levels, &serialized, event.room_id()) .await { let n = match action { @@ -319,7 +324,7 @@ impl Service { } if notify == Some(true) { - self.send_notice(unread, pusher, tweaks, pdu).await?; + self.send_notice(unread, pusher, tweaks, event).await?; } // Else the event triggered no actions @@ -369,13 +374,16 @@ impl Service { } #[tracing::instrument(skip(self, unread, pusher, tweaks, event))] - async fn send_notice( + async fn send_notice( &self, unread: UInt, pusher: &Pusher, tweaks: Vec, - event: &PduEvent, - ) -> Result { + event: &E, + ) -> Result + where + E: Event + Send + Sync, + { // TODO: email match &pusher.kind { | PusherKind::Http(http) => { @@ -421,8 +429,8 @@ impl Service { let d = vec![device]; let mut notifi = Notification::new(d); - notifi.event_id = Some((*event.event_id).to_owned()); - notifi.room_id = Some((*event.room_id).to_owned()); + notifi.event_id = Some(event.event_id().to_owned()); + notifi.room_id = Some(event.room_id().to_owned()); if http .data .get("org.matrix.msc4076.disable_badge_count") @@ -442,7 +450,7 @@ impl Service { ) .await?; } else { - if event.kind == TimelineEventType::RoomEncrypted + if *event.kind() == TimelineEventType::RoomEncrypted || tweaks .iter() .any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_))) @@ -451,29 +459,29 @@ impl Service { } else { notifi.prio = NotificationPriority::Low; } - notifi.sender = Some(event.sender.clone()); - notifi.event_type = Some(event.kind.clone()); - notifi.content = serde_json::value::to_raw_value(&event.content).ok(); + notifi.sender = Some(event.sender().to_owned()); + notifi.event_type = Some(event.kind().to_owned()); + notifi.content = serde_json::value::to_raw_value(event.content()).ok(); - if event.kind == TimelineEventType::RoomMember { + if *event.kind() == TimelineEventType::RoomMember { notifi.user_is_target = - event.state_key.as_deref() == Some(event.sender.as_str()); + event.state_key() == Some(event.sender().as_str()); } notifi.sender_display_name = - self.services.users.displayname(&event.sender).await.ok(); + self.services.users.displayname(event.sender()).await.ok(); notifi.room_name = self .services .state_accessor - .get_name(&event.room_id) + .get_name(event.room_id()) .await .ok(); notifi.room_alias = self .services .state_accessor - .get_canonical_alias(&event.room_id) + .get_canonical_alias(event.room_id()) .await .ok(); diff --git a/src/service/rooms/event_handler/handle_outlier_pdu.rs b/src/service/rooms/event_handler/handle_outlier_pdu.rs index feebf8c1..5cc6be55 100644 --- a/src/service/rooms/event_handler/handle_outlier_pdu.rs +++ b/src/service/rooms/event_handler/handle_outlier_pdu.rs @@ -126,7 +126,7 @@ pub(super) async fn handle_outlier_pdu<'a>( let state_fetch = |ty: &StateEventType, sk: &str| { let key = (ty.to_owned(), sk.into()); - ready(auth_events.get(&key)) + ready(auth_events.get(&key).map(ToOwned::to_owned)) }; let auth_check = state_res::event_auth::auth_check( diff --git a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs index 97d3df97..00b18c06 100644 --- a/src/service/rooms/event_handler/upgrade_outlier_pdu.rs +++ b/src/service/rooms/event_handler/upgrade_outlier_pdu.rs @@ -2,7 +2,7 @@ use std::{borrow::Borrow, collections::BTreeMap, iter::once, sync::Arc, time::In use conduwuit::{ Err, Result, debug, debug_info, err, implement, - matrix::{EventTypeExt, PduEvent, StateKey, state_res}, + matrix::{Event, EventTypeExt, PduEvent, StateKey, state_res}, trace, utils::stream::{BroadbandExt, ReadyExt}, warn, @@ -108,7 +108,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu( let state_fetch = |k: &StateEventType, s: &str| { let key = k.with_state_key(s); - ready(auth_events.get(&key).cloned()) + ready(auth_events.get(&key).map(ToOwned::to_owned)) }; let auth_check = state_res::event_auth::auth_check( diff --git a/src/service/rooms/search/mod.rs b/src/service/rooms/search/mod.rs index 4100dd75..b9d067a6 100644 --- a/src/service/rooms/search/mod.rs +++ b/src/service/rooms/search/mod.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use conduwuit::{ - PduCount, PduEvent, Result, +use conduwuit_core::{ + Event, PduCount, PduEvent, Result, arrayvec::ArrayVec, implement, utils::{ diff --git a/src/service/rooms/spaces/mod.rs b/src/service/rooms/spaces/mod.rs index 53d2b742..de2647ca 100644 --- a/src/service/rooms/spaces/mod.rs +++ b/src/service/rooms/spaces/mod.rs @@ -5,8 +5,8 @@ mod tests; use std::{fmt::Write, sync::Arc}; use async_trait::async_trait; -use conduwuit::{ - Err, Error, PduEvent, Result, implement, +use conduwuit_core::{ + Err, Error, Event, PduEvent, Result, implement, utils::{ IterStream, future::{BoolExt, TryExtExt}, @@ -142,7 +142,7 @@ pub async fn get_summary_and_children_local( let children_pdus: Vec<_> = self .get_space_child_events(current_room) - .map(PduEvent::into_stripped_spacechild_state_event) + .map(Event::into_format) .collect() .await; @@ -511,7 +511,7 @@ async fn cache_insert( room_id: room_id.clone(), children_state: self .get_space_child_events(&room_id) - .map(PduEvent::into_stripped_spacechild_state_event) + .map(Event::into_format) .collect() .await, encryption, diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state/mod.rs index 803ba9d7..9eb02221 100644 --- a/src/service/rooms/state/mod.rs +++ b/src/service/rooms/state/mod.rs @@ -1,8 +1,8 @@ use std::{collections::HashMap, fmt::Write, iter::once, sync::Arc}; use async_trait::async_trait; -use conduwuit::{ - PduEvent, Result, err, +use conduwuit_core::{ + Event, PduEvent, Result, err, result::FlatOk, state_res::{self, StateMap}, utils::{ @@ -11,7 +11,7 @@ use conduwuit::{ }, warn, }; -use database::{Deserialized, Ignore, Interfix, Map}; +use conduwuit_database::{Deserialized, Ignore, Interfix, Map}; use futures::{ FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future::join_all, pin_mut, }; @@ -319,30 +319,34 @@ impl Service { } #[tracing::instrument(skip_all, level = "debug")] - pub async fn summary_stripped(&self, event: &PduEvent) -> Vec> { + pub async fn summary_stripped<'a, E>(&self, event: &'a E) -> Vec> + where + E: Event + Send + Sync, + &'a E: Event + Send, + { let cells = [ (&StateEventType::RoomCreate, ""), (&StateEventType::RoomJoinRules, ""), (&StateEventType::RoomCanonicalAlias, ""), (&StateEventType::RoomName, ""), (&StateEventType::RoomAvatar, ""), - (&StateEventType::RoomMember, event.sender.as_str()), // Add recommended events + (&StateEventType::RoomMember, event.sender().as_str()), // Add recommended events (&StateEventType::RoomEncryption, ""), (&StateEventType::RoomTopic, ""), ]; - let fetches = cells.iter().map(|(event_type, state_key)| { + let fetches = cells.into_iter().map(|(event_type, state_key)| { self.services .state_accessor - .room_state_get(&event.room_id, event_type, state_key) + .room_state_get(event.room_id(), event_type, state_key) }); join_all(fetches) .await .into_iter() .filter_map(Result::ok) - .map(PduEvent::into_stripped_state_event) - .chain(once(event.to_stripped_state_event())) + .map(Event::into_format) + .chain(once(event.to_format())) .collect() } diff --git a/src/service/rooms/threads/mod.rs b/src/service/rooms/threads/mod.rs index a680df55..9566eb61 100644 --- a/src/service/rooms/threads/mod.rs +++ b/src/service/rooms/threads/mod.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeMap, sync::Arc}; -use conduwuit::{ - Result, err, +use conduwuit_core::{ + Event, Result, err, matrix::pdu::{PduCount, PduEvent, PduId, RawPduId}, utils::{ ReadyExt, @@ -49,7 +49,11 @@ impl crate::Service for Service { } impl Service { - pub async fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> { + pub async fn add_to_thread<'a, E>(&self, root_event_id: &EventId, event: &'a E) -> Result + where + E: Event + Send + Sync, + &'a E: Event + Send, + { let root_id = self .services .timeline @@ -86,7 +90,7 @@ impl Service { }) { // Thread already existed relations.count = relations.count.saturating_add(uint!(1)); - relations.latest_event = pdu.to_message_like_event(); + relations.latest_event = event.to_format(); let content = serde_json::to_value(relations).expect("to_value always works"); @@ -99,7 +103,7 @@ impl Service { } else { // New thread let relations = BundledThread { - latest_event: pdu.to_message_like_event(), + latest_event: event.to_format(), count: uint!(1), current_user_participated: true, }; @@ -129,7 +133,7 @@ impl Service { users.push(root_pdu.sender); }, } - users.push(pdu.sender.clone()); + users.push(event.sender().to_owned()); self.update_participants(&root_id, &users) } diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index 534d8faf..bcad1309 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -375,8 +375,6 @@ impl Service { .await .unwrap_or_default(); - let sync_pdu = pdu.to_sync_room_event(); - let mut push_target: HashSet<_> = self .services .state_cache @@ -401,6 +399,7 @@ impl Service { } } + let serialized = pdu.to_format(); for user in &push_target { let rules_for_user = self .services @@ -418,7 +417,7 @@ impl Service { for action in self .services .pusher - .get_actions(user, &rules_for_user, &power_levels, &sync_pdu, &pdu.room_id) + .get_actions(user, &rules_for_user, &power_levels, &serialized, &pdu.room_id) .await { match action { @@ -768,7 +767,7 @@ impl Service { let auth_fetch = |k: &StateEventType, s: &str| { let key = (k.clone(), s.into()); - ready(auth_events.get(&key)) + ready(auth_events.get(&key).map(ToOwned::to_owned)) }; let auth_check = state_res::auth_check( diff --git a/src/service/sending/sender.rs b/src/service/sending/sender.rs index fab02f6b..408ab17d 100644 --- a/src/service/sending/sender.rs +++ b/src/service/sending/sender.rs @@ -9,8 +9,8 @@ use std::{ }; use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; -use conduwuit::{ - Error, Result, debug, err, error, +use conduwuit_core::{ + Error, Event, Result, debug, err, error, result::LogErr, trace, utils::{ @@ -697,7 +697,7 @@ impl Service { match event { | SendingEvent::Pdu(pdu_id) => { if let Ok(pdu) = self.services.timeline.get_pdu_from_id(pdu_id).await { - pdu_jsons.push(pdu.into_room_event()); + pdu_jsons.push(pdu.to_format()); } }, | SendingEvent::Edu(edu) =>