From cff90b1480879bf0f8b901340202482b86e61e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 28 Apr 2021 11:48:20 +0200 Subject: [PATCH 1/8] matrix-sdk: Add encryption info to our sync events. --- matrix_sdk/examples/emoji_verification.rs | 14 +- matrix_sdk/src/event_handler/mod.rs | 81 ++++++-- matrix_sdk_base/src/client.rs | 174 +++++++----------- .../src/deserialized_responses.rs | 135 ++++++++------ matrix_sdk_crypto/src/machine.rs | 112 +++++++---- .../src/olm/group_sessions/inbound.rs | 73 ++++---- 6 files changed, 329 insertions(+), 260 deletions(-) diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 5b153bfd..e3f25136 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -81,7 +81,12 @@ async fn login( let client = &client_ref; let initial = &initial_ref; - for event in &response.to_device.events { + for event in response + .to_device + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { match event { AnyToDeviceEvent::KeyVerificationStart(e) => { let sas = client @@ -124,7 +129,12 @@ async fn login( if !initial.load(Ordering::SeqCst) { for (_room_id, room_info) in response.rooms.join { - for event in room_info.timeline.events { + for event in room_info + .timeline + .events + .iter() + .filter_map(|e| e.event.deserialize().ok()) + { if let AnySyncRoomEvent::Message(event) = event { match event { AnySyncMessageEvent::RoomMessage(m) => { diff --git a/matrix_sdk/src/event_handler/mod.rs b/matrix_sdk/src/event_handler/mod.rs index 17d706b3..370fa2fc 100644 --- a/matrix_sdk/src/event_handler/mod.rs +++ b/matrix_sdk/src/event_handler/mod.rs @@ -75,50 +75,95 @@ impl Handler { pub(crate) async fn handle_sync(&self, response: &SyncResponse) { for (room_id, room_info) in &response.rooms.join { if let Some(room) = self.get_room(room_id) { - for event in &room_info.ephemeral.events { - self.handle_ephemeral_event(room.clone(), event).await; + for event in room_info + .ephemeral + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_ephemeral_event(room.clone(), &event).await; } - for event in &room_info.account_data.events { - self.handle_account_data_event(room.clone(), event).await; + for event in room_info + .account_data + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_account_data_event(room.clone(), &event).await; } - for event in &room_info.state.events { - self.handle_state_event(room.clone(), event).await; + for event in room_info + .state + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_state_event(room.clone(), &event).await; } - for event in &room_info.timeline.events { - self.handle_timeline_event(room.clone(), event).await; + for event in room_info + .timeline + .events + .iter() + .filter_map(|e| e.event.deserialize().ok()) + { + self.handle_timeline_event(room.clone(), &event).await; } } } for (room_id, room_info) in &response.rooms.leave { if let Some(room) = self.get_room(room_id) { - for event in &room_info.account_data.events { - self.handle_account_data_event(room.clone(), event).await; + for event in room_info + .account_data + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_account_data_event(room.clone(), &event).await; } - for event in &room_info.state.events { - self.handle_state_event(room.clone(), event).await; + for event in room_info + .state + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_state_event(room.clone(), &event).await; } - for event in &room_info.timeline.events { - self.handle_timeline_event(room.clone(), event).await; + for event in room_info + .timeline + .events + .iter() + .filter_map(|e| e.event.deserialize().ok()) + { + self.handle_timeline_event(room.clone(), &event).await; } } } for (room_id, room_info) in &response.rooms.invite { if let Some(room) = self.get_room(room_id) { - for event in &room_info.invite_state.events { - self.handle_stripped_state_event(room.clone(), event).await; + for event in room_info + .invite_state + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.handle_stripped_state_event(room.clone(), &event).await; } } } - for event in &response.presence.events { - self.on_presence_event(event).await; + for event in response + .presence + .events + .iter() + .filter_map(|e| e.deserialize().ok()) + { + self.on_presence_event(&event).await; } for (room_id, notifications) in &response.notifications { diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index d1e8fe38..60102b2d 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -37,15 +37,13 @@ use matrix_sdk_common::{ use matrix_sdk_common::{ api::r0::{self as api, push::get_notifications::Notification}, deserialized_responses::{ - AccountData, AmbiguityChanges, Ephemeral, InviteState, InvitedRoom, JoinedRoom, LeftRoom, - MemberEvent, MembersResponse, Presence, Rooms, State, StrippedMemberEvent, SyncResponse, - Timeline, + AmbiguityChanges, JoinedRoom, LeftRoom, MemberEvent, MembersResponse, Rooms, + StrippedMemberEvent, SyncResponse, SyncRoomEvent, Timeline, }, events::{ - presence::PresenceEvent, room::member::{MemberEventContent, MembershipState}, - AnyBasicEvent, AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, - AnyToDeviceEvent, EventContent, EventType, StateEvent, + AnyBasicEvent, AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventContent, + EventType, StateEvent, }, identifiers::{RoomId, UserId}, instant::Instant, @@ -432,15 +430,12 @@ impl BaseClient { let mut push_context = self.get_push_room_context(room, room_info, changes).await?; for event in ruma_timeline.events { - match hoist_room_event_prev_content(&event) { - Ok(mut e) => { - #[cfg(not(feature = "encryption"))] - let raw_event = event; - #[cfg(feature = "encryption")] - let mut raw_event = event; + let mut event: SyncRoomEvent = event.into(); + match hoist_room_event_prev_content(&event.event) { + Ok(e) => { #[allow(clippy::single_match)] - match &mut e { + match &e { AnySyncRoomEvent::State(s) => match s { AnySyncStateEvent::RoomMember(member) => { if let Ok(member) = MemberEvent::try_from(member.clone()) { @@ -487,18 +482,10 @@ impl BaseClient { encrypted, )) => { if let Some(olm) = self.olm_machine().await { - if let Ok(raw_decrypted) = + if let Ok(decrypted) = olm.decrypt_room_event(encrypted, room_id).await { - match raw_decrypted.deserialize() { - Ok(decrypted) => { - e = decrypted; - raw_event = raw_decrypted; - } - Err(e) => { - warn!("Error deserializing a decrypted event {:?} ", e) - } - } + event = decrypted; } } } @@ -517,14 +504,14 @@ impl BaseClient { } if let Some(context) = &push_context { - let actions = push_rules.get_actions(&raw_event, &context).to_vec(); + let actions = push_rules.get_actions(&event.event, &context).to_vec(); if actions.iter().any(|a| matches!(a, Action::Notify)) { changes.add_notification( room_id, Notification::new( actions, - raw_event, + event.event.clone(), false, room_id.clone(), SystemTime::now(), @@ -537,13 +524,13 @@ impl BaseClient { // Requires the possibility to associate custom data with events and to // store them. } - - timeline.events.push(e); } Err(e) => { warn!("Error deserializing event {:?}", e); } } + + timeline.events.push(event); } Ok(timeline) @@ -552,19 +539,17 @@ impl BaseClient { #[allow(clippy::type_complexity)] fn handle_invited_state( &self, - events: Vec>, + events: &[Raw], room_info: &mut RoomInfo, ) -> ( - InviteState, BTreeMap, BTreeMap>, ) { - events.into_iter().fold( - (InviteState::default(), BTreeMap::new(), BTreeMap::new()), - |(mut state, mut members, mut state_events), e| { + events.iter().fold( + (BTreeMap::new(), BTreeMap::new()), + |(mut members, mut state_events), e| { match e.deserialize() { Ok(e) => { - state.events.push(e.clone()); if let AnyStrippedStateEvent::RoomMember(member) = e { match StrippedMemberEvent::try_from(member) { @@ -591,7 +576,7 @@ impl BaseClient { ); } } - (state, members, state_events) + (members, state_events) }, ) } @@ -600,10 +585,9 @@ impl BaseClient { &self, changes: &mut StateChanges, ambiguity_cache: &mut AmbiguityCache, - events: Vec>, + events: &[Raw], room_info: &mut RoomInfo, - ) -> StoreResult<(State, BTreeSet)> { - let mut state = State::default(); + ) -> StoreResult> { let mut members = BTreeMap::new(); let mut state_events = BTreeMap::new(); let mut user_ids = BTreeSet::new(); @@ -611,21 +595,19 @@ impl BaseClient { let room_id = room_info.room_id.clone(); - for event in - events - .into_iter() - .filter_map(|e| match hoist_and_deserialize_state_event(&e) { - Ok(e) => Some(e), - Err(err) => { - warn!( - "Couldn't deserialize state event for room {}: {:?} {:#?}", - room_id, err, e - ); - None - } - }) + for event in events + .iter() + .filter_map(|e| match hoist_and_deserialize_state_event(&e) { + Ok(e) => Some(e), + Err(err) => { + warn!( + "Couldn't deserialize state event for room {}: {:?} {:#?}", + room_id, err, e + ); + None + } + }) { - state.events.push(event.clone()); room_info.handle_state_event(&event.content()); if let AnySyncStateEvent::RoomMember(member) = event { @@ -667,7 +649,7 @@ impl BaseClient { changes.profiles.insert(room_id.as_ref().clone(), profiles); changes.state.insert(room_id.as_ref().clone(), state_events); - Ok((state, user_ids)) + Ok(user_ids) } async fn handle_room_account_data( @@ -675,22 +657,16 @@ impl BaseClient { room_id: &RoomId, events: &[Raw], changes: &mut StateChanges, - ) -> AccountData { + ) { let events: Vec = events.iter().filter_map(|e| e.deserialize().ok()).collect(); for event in &events { changes.add_room_account_data(room_id, event.clone()); } - - AccountData { events } } - async fn handle_account_data( - &self, - events: Vec>, - changes: &mut StateChanges, - ) { + async fn handle_account_data(&self, events: &[Raw], changes: &mut StateChanges) { let events: Vec = events.iter().filter_map(|e| e.deserialize().ok()).collect(); @@ -769,29 +745,17 @@ impl BaseClient { // decryptes to-device events, but leaves room events alone. // This makes sure that we have the deryption keys for the room // events at hand. - o.receive_sync_changes(&to_device, &device_lists, &device_one_time_keys_count) + o.receive_sync_changes(to_device, &device_lists, &device_one_time_keys_count) .await? } else { to_device - .events - .into_iter() - .filter_map(|e| e.deserialize().ok()) - .collect::>() - .into() } }; - #[cfg(not(feature = "encryption"))] - let to_device = to_device - .events - .into_iter() - .filter_map(|e| e.deserialize().ok()) - .collect::>() - .into(); let mut changes = StateChanges::new(next_batch.clone()); let mut ambiguity_cache = AmbiguityCache::new(self.store.clone()); - self.handle_account_data(account_data.events, &mut changes) + self.handle_account_data(&account_data.events, &mut changes) .await; let push_rules = self.get_push_rules(&changes).await?; @@ -809,11 +773,11 @@ impl BaseClient { room_info.update_summary(&new_info.summary); room_info.set_prev_batch(new_info.timeline.prev_batch.as_deref()); - let (state, mut user_ids) = self + let mut user_ids = self .handle_state( &mut changes, &mut ambiguity_cache, - new_info.state.events, + &new_info.state.events, &mut room_info, ) .await?; @@ -834,8 +798,7 @@ impl BaseClient { ) .await?; - let account_data = self - .handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes) + self.handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes) .await; #[cfg(feature = "encryption")] @@ -859,18 +822,15 @@ impl BaseClient { let notification_count = new_info.unread_notifications.into(); room_info.update_notification_count(notification_count); - let ephemeral = Ephemeral { - events: new_info - .ephemeral - .events - .into_iter() - .filter_map(|e| e.deserialize().ok()) - .collect(), - }; - new_rooms.join.insert( room_id, - JoinedRoom::new(timeline, state, account_data, ephemeral, notification_count), + JoinedRoom::new( + timeline, + new_info.state, + new_info.account_data, + new_info.ephemeral, + notification_count, + ), ); changes.add_room(room_info); @@ -884,11 +844,11 @@ impl BaseClient { let mut room_info = room.clone_info(); room_info.mark_as_left(); - let (state, mut user_ids) = self + let mut user_ids = self .handle_state( &mut changes, &mut ambiguity_cache, - new_info.state.events, + &new_info.state.events, &mut room_info, ) .await?; @@ -905,14 +865,14 @@ impl BaseClient { ) .await?; - let account_data = self - .handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes) + self.handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes) .await; changes.add_room(room_info); - new_rooms - .leave - .insert(room_id, LeftRoom::new(timeline, state, account_data)); + new_rooms.leave.insert( + room_id, + LeftRoom::new(timeline, new_info.state, new_info.account_data), + ); } for (room_id, new_info) in rooms.invite { @@ -929,31 +889,25 @@ impl BaseClient { let room = self.store.get_or_create_stripped_room(&room_id).await; let mut room_info = room.clone_info(); - let (state, members, state_events) = - self.handle_invited_state(new_info.invite_state.events, &mut room_info); + let (members, state_events) = + self.handle_invited_state(&new_info.invite_state.events, &mut room_info); changes.stripped_members.insert(room_id.clone(), members); changes.stripped_state.insert(room_id.clone(), state_events); changes.add_stripped_room(room_info); - let room = InvitedRoom { - invite_state: state, - }; - - new_rooms.invite.insert(room_id, room); + new_rooms.invite.insert(room_id, new_info); } - let presence: BTreeMap = presence + changes.presence = presence .events - .into_iter() + .iter() .filter_map(|e| { let event = e.deserialize().ok()?; Some((event.sender.clone(), event)) }) .collect(); - changes.presence = presence; - changes.ambiguity_maps = ambiguity_cache.cache; self.store.save_changes(&changes).await?; @@ -965,12 +919,8 @@ impl BaseClient { let response = SyncResponse { next_batch, rooms: new_rooms, - presence: Presence { - events: changes.presence.into_iter().map(|(_, v)| v).collect(), - }, - account_data: AccountData { - events: changes.account_data.into_iter().map(|(_, e)| e).collect(), - }, + presence, + account_data, to_device, device_lists, device_one_time_keys_count: device_one_time_keys_count diff --git a/matrix_sdk_common/src/deserialized_responses.rs b/matrix_sdk_common/src/deserialized_responses.rs index 45a50380..9ba75ad7 100644 --- a/matrix_sdk_common/src/deserialized_responses.rs +++ b/matrix_sdk_common/src/deserialized_responses.rs @@ -1,3 +1,10 @@ +use ruma::{ + api::client::r0::sync::sync_events::{ + AccountData, Ephemeral, InvitedRoom, Presence, State, ToDevice, + }, + serde::Raw, + DeviceIdBox, +}; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, convert::TryFrom, time::SystemTime}; @@ -9,9 +16,8 @@ use super::{ }, }, events::{ - presence::PresenceEvent, room::member::MemberEventContent, AnyBasicEvent, - AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncRoomEvent, AnySyncStateEvent, - AnyToDeviceEvent, StateEvent, StrippedStateEvent, SyncStateEvent, Unsigned, + room::member::MemberEventContent, AnySyncRoomEvent, StateEvent, StrippedStateEvent, + SyncStateEvent, Unsigned, }, identifiers::{DeviceKeyAlgorithm, EventId, RoomId, UserId}, }; @@ -37,6 +43,72 @@ pub struct AmbiguityChanges { pub changes: BTreeMap>, } +/// The verification state of the device that sent an event to us. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum VerificationState { + /// The device is trusted. + Trusted, + /// The device is not trusted. + Untrusted, + /// The device is not known to us. + UnknownDevice, +} + +/// The algorithm specific information of a decrypted event. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum AlgorithmInfo { + /// The info if the event was encrypted using m.megolm.v1.aes-sha2 + MegolmV1AesSha2 { + /// The curve25519 key of the device that created the megolm decryption + /// key originally. + curve25519_key: String, + /// The signing keys that have created the megolm key that was used to + /// decrypt this session. This map will usually contain a signle ed25519 + /// key. + sender_claimed_keys: BTreeMap, + /// Chain of curve25519 keys through which this session was forwarded, + /// via m.forwarded_room_key events. + forwarding_curve25519_key_chain: Vec, + }, +} + +/// Struct containing information on how an event was decrypted. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct EncryptionInfo { + /// The user ID of the event sender, note this is untrusted data unless the + /// `verification_state` is as well trusted. + pub sender: UserId, + /// The device ID of the device that sent us the event, note this is + /// untrusted data unless `verification_state` is as well trusted. + pub sender_device: DeviceIdBox, + /// Information about the algorithm that was used to encrypt the event. + pub algorithm_info: AlgorithmInfo, + /// The verification state of the device that sent us the event, note this + /// is the state of the device at the time of decryption. It may change in + /// the future if a device gets verified or deleted. + pub verification_state: VerificationState, +} + +/// A customized version of a room event comming from a sync that holds optional +/// decryption info. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SyncRoomEvent { + /// The actual event. + pub event: Raw, + /// The encryption info about the event. Will be `None` if the event was not + /// encrypted. + pub encryption_info: Option, +} + +impl From> for SyncRoomEvent { + fn from(inner: Raw) -> Self { + Self { + encryption_info: None, + event: inner, + } + } +} + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct SyncResponse { /// The batch token to supply in the `since` param of the next `/sync` request. @@ -71,33 +143,6 @@ impl SyncResponse { } } -/// Updates to the presence status of other users. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Presence { - /// A list of events. - pub events: Vec, -} - -/// Data that the user has attached to either the account or a specific room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct AccountData { - /// The list of account data events. - pub events: Vec, -} - -/// Messages sent dirrectly between devices. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct ToDevice { - /// A list of events. - pub events: Vec, -} - -impl From> for ToDevice { - fn from(events: Vec) -> Self { - Self { events } - } -} - #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Rooms { /// The rooms that the user has left or been banned from. @@ -144,20 +189,6 @@ impl JoinedRoom { } } -/// Updates to the rooms that the user has been invited to. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct InvitedRoom { - /// The state of a room that the user has been invited to. - pub invite_state: InviteState, -} - -/// The state of a room that the user has been invited to. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct InviteState { - /// A list of state events. - pub events: Vec, -} - /// Counts of unread notifications for a room. #[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)] pub struct UnreadNotificationsCount { @@ -179,13 +210,6 @@ impl From for UnreadNotificationsCount { } } -/// The ephemeral events in the room that aren't recorded in the timeline or -/// state of the room. e.g. typing. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Ephemeral { - pub events: Vec, -} - #[derive(Clone, Debug, Deserialize, Serialize)] pub struct LeftRoom { /// The timeline of messages and state changes in the room up to the point @@ -220,7 +244,7 @@ pub struct Timeline { pub prev_batch: Option, /// A list of events. - pub events: Vec, + pub events: Vec, } impl Timeline { @@ -233,13 +257,6 @@ impl Timeline { } } -/// State events in the room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct State { - /// A list of state events. - pub events: Vec, -} - #[derive(Clone, Debug, Deserialize, Serialize)] #[serde( try_from = "SyncStateEvent", diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 447d7a12..f2b90fa6 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -27,14 +27,13 @@ use matrix_sdk_common::{ upload_keys, upload_signatures::Request as UploadSignaturesRequest, }, - sync::sync_events::{DeviceLists, ToDevice as RumaToDevice}, + sync::sync_events::{DeviceLists, ToDevice}, }, assign, - deserialized_responses::ToDevice, + deserialized_responses::{AlgorithmInfo, EncryptionInfo, SyncRoomEvent, VerificationState}, events::{ room::encrypted::EncryptedEventContent, room_key::RoomKeyEventContent, - AnyMessageEventContent, AnySyncRoomEvent, AnyToDeviceEvent, SyncMessageEvent, - ToDeviceEvent, + AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent, }, identifiers::{ DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, EventId, RoomId, @@ -42,7 +41,7 @@ use matrix_sdk_common::{ }, locks::Mutex, uuid::Uuid, - Raw, UInt, + UInt, }; #[cfg(feature = "sled_cryptostore")] @@ -802,7 +801,7 @@ impl OlmMachine { /// [`decrypt_room_event`]: #method.decrypt_room_event pub async fn receive_sync_changes( &self, - to_device_events: &RumaToDevice, + to_device_events: ToDevice, changed_devices: &DeviceLists, one_time_keys_counts: &BTreeMap, ) -> OlmResult { @@ -826,14 +825,14 @@ impl OlmMachine { let mut events = Vec::new(); - for event_result in &to_device_events.events { - let mut event = match event_result.deserialize() { + for mut raw_event in to_device_events.events { + let event = match raw_event.deserialize() { Ok(e) => e, Err(e) => { // Skip invalid events. warn!( "Received an invalid to-device event {:?} {:?}", - e, event_result + e, raw_event ); continue; } @@ -841,9 +840,9 @@ impl OlmMachine { info!("Received a to-device event {:?}", event); - match &mut event { + match event { AnyToDeviceEvent::RoomEncrypted(e) => { - let decrypted = match self.decrypt_to_device_event(e).await { + let decrypted = match self.decrypt_to_device_event(&e).await { Ok(e) => e, Err(err) => { warn!( @@ -885,12 +884,10 @@ impl OlmMachine { changes.inbound_group_sessions.push(group_session); } - if let Some(e) = decrypted.deserialized_event { - event = e; - } + raw_event = decrypted.event; } AnyToDeviceEvent::RoomKeyRequest(e) => { - self.key_request_machine.receive_incoming_key_request(e) + self.key_request_machine.receive_incoming_key_request(&e) } AnyToDeviceEvent::KeyVerificationAccept(..) | AnyToDeviceEvent::KeyVerificationCancel(..) @@ -903,7 +900,7 @@ impl OlmMachine { _ => continue, } - events.push(event); + events.push(raw_event); } let changed_sessions = self @@ -915,7 +912,10 @@ impl OlmMachine { self.store.save_changes(changes).await?; - Ok(ToDevice { events }) + let mut to_device = ToDevice::new(); + to_device.events = events; + + Ok(to_device) } /// Request a room key from our devices. @@ -950,6 +950,44 @@ impl OlmMachine { .await?) } + async fn get_encryption_info( + &self, + session: &InboundGroupSession, + sender: &UserId, + device_id: &DeviceId, + ) -> StoreResult { + let verification_state = if let Some(device) = + self.get_device(sender, device_id).await?.filter(|d| { + d.get_key(DeviceKeyAlgorithm::Curve25519) + .map(|k| k == session.sender_key()) + .unwrap_or(false) + }) { + if (self.user_id() == device.user_id() && self.device_id() == device.device_id()) + || device.is_trusted() + { + VerificationState::Trusted + } else { + VerificationState::Untrusted + } + } else { + VerificationState::UnknownDevice + }; + + let sender = sender.clone(); + let device_id = device_id.to_owned(); + + Ok(EncryptionInfo { + sender, + sender_device: device_id, + algorithm_info: AlgorithmInfo::MegolmV1AesSha2 { + curve25519_key: session.sender_key().to_owned(), + sender_claimed_keys: session.signing_keys().to_owned(), + forwarding_curve25519_key_chain: session.forwading_key_chain().to_vec(), + }, + verification_state, + }) + } + /// Decrypt an event from a room timeline. /// /// # Arguments @@ -961,7 +999,7 @@ impl OlmMachine { &self, event: &SyncMessageEvent, room_id: &RoomId, - ) -> MegolmResult> { + ) -> MegolmResult { let content = match &event.content { EncryptedEventContent::MegolmV1AesSha2(c) => c, _ => return Err(EventError::UnsupportedAlgorithm.into()), @@ -989,8 +1027,6 @@ impl OlmMachine { "Successfully decrypted a Megolm event {:?}", decrypted_event ); - // TODO set the encryption info on the event (is it verified, was it - // decrypted, sender key...) if let Ok(e) = decrypted_event.deserialize() { self.verification_machine @@ -998,7 +1034,14 @@ impl OlmMachine { .await?; } - Ok(decrypted_event) + let encryption_info = self + .get_encryption_info(&session, &event.sender, &content.device_id) + .await?; + + Ok(SyncRoomEvent { + encryption_info: Some(encryption_info), + event: decrypted_event, + }) } /// Update the tracked users. @@ -1815,23 +1858,24 @@ pub(crate) mod test { .decrypt_room_event(&event, &room_id) .await .unwrap() + .event .deserialize() .unwrap(); - match decrypted_event { - AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(SyncMessageEvent { - sender, - content, - .. - })) => { - assert_eq!(&sender, alice.user_id()); - if let MessageType::Text(c) = &content.msgtype { - assert_eq!(&c.body, plaintext); - } else { - panic!("Decrypted event has a missmatched content"); - } + if let AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(SyncMessageEvent { + sender, + content, + .. + })) = decrypted_event + { + assert_eq!(&sender, alice.user_id()); + if let MessageType::Text(c) = &content.msgtype { + assert_eq!(&c.body, plaintext); + } else { + panic!("Decrypted event has a missmatched content"); } - _ => panic!("Decrypted room event has the wrong type"), + } else { + panic!("Decrypted room event has the wrong type") } } diff --git a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs index db373133..a23e4ef5 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs @@ -61,9 +61,9 @@ pub struct InboundGroupSession { session_id: Arc, first_known_index: u32, pub(crate) sender_key: Arc, - pub(crate) signing_key: Arc>, + pub(crate) signing_keys: Arc>, pub(crate) room_id: Arc, - forwarding_chains: Arc>>>, + forwarding_chains: Arc>, imported: Arc, } @@ -104,10 +104,10 @@ impl InboundGroupSession { history_visibility: history_visibility.into(), sender_key: sender_key.to_owned().into(), first_known_index, - signing_key: Arc::new(keys), - room_id: Arc::new(room_id.clone()), - forwarding_chains: Arc::new(Mutex::new(None)), - imported: Arc::new(false), + signing_keys: keys.into(), + room_id: room_id.clone().into(), + forwarding_chains: Vec::new().into(), + imported: false.into(), }) } @@ -152,15 +152,15 @@ impl InboundGroupSession { ); Ok(InboundGroupSession { - inner: Arc::new(Mutex::new(session)), + inner: Mutex::new(session).into(), session_id: content.session_id.as_str().into(), sender_key: content.sender_key.as_str().into(), first_known_index, history_visibility: None.into(), - signing_key: Arc::new(sender_claimed_key), - room_id: Arc::new(content.room_id.clone()), - forwarding_chains: Arc::new(Mutex::new(Some(forwarding_chains))), - imported: Arc::new(true), + signing_keys: sender_claimed_key.into(), + room_id: content.room_id.clone().into(), + forwarding_chains: forwarding_chains.into(), + imported: true.into(), }) } @@ -176,9 +176,9 @@ impl InboundGroupSession { PickledInboundGroupSession { pickle: InboundGroupSessionPickle::from(pickle), sender_key: self.sender_key.to_string(), - signing_key: (&*self.signing_key).clone(), + signing_key: (&*self.signing_keys).clone(), room_id: (&*self.room_id).clone(), - forwarding_chains: self.forwarding_chains.lock().await.clone(), + forwarding_chains: self.forwading_key_chain().to_vec(), imported: *self.imported, history_visibility: self.history_visibility.as_ref().clone(), } @@ -197,6 +197,20 @@ impl InboundGroupSession { &self.sender_key } + /// Get the map of signing keys this session was received from. + pub fn signing_keys(&self) -> &BTreeMap { + &self.signing_keys + } + + /// Get the list of ed25519 keys that this session was forwarded through. + /// + /// Each ed25519 key represents a single device. If device A forwards the + /// session to device B and device B to C this list will contain the ed25519 + /// keys of A and B. + pub fn forwading_key_chain(&self) -> &[String] { + &self.forwarding_chains + } + /// Export this session at the given message index. pub async fn export_at_index(&self, message_index: u32) -> ExportedRoomKey { let message_index = std::cmp::max(self.first_known_index(), message_index); @@ -214,14 +228,8 @@ impl InboundGroupSession { room_id: (&*self.room_id).clone(), sender_key: (&*self.sender_key).to_owned(), session_id: self.session_id().to_owned(), - forwarding_curve25519_key_chain: self - .forwarding_chains - .lock() - .await - .as_ref() - .cloned() - .unwrap_or_default(), - sender_claimed_keys: (&*self.signing_key).clone(), + forwarding_curve25519_key_chain: self.forwading_key_chain().to_vec(), + sender_claimed_keys: (&*self.signing_keys).clone(), session_key, } } @@ -246,15 +254,15 @@ impl InboundGroupSession { let session_id = session.session_id(); Ok(InboundGroupSession { - inner: Arc::new(Mutex::new(session)), + inner: Mutex::new(session).into(), session_id: session_id.into(), sender_key: pickle.sender_key.into(), history_visibility: pickle.history_visibility.into(), first_known_index, - signing_key: Arc::new(pickle.signing_key), - room_id: Arc::new(pickle.room_id), - forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)), - imported: Arc::new(pickle.imported), + signing_keys: pickle.signing_key.into(), + room_id: pickle.room_id.into(), + forwarding_chains: pickle.forwarding_chains.into(), + imported: pickle.imported.into(), }) } @@ -379,7 +387,8 @@ pub struct PickledInboundGroupSession { pub room_id: RoomId, /// The list of claimed ed25519 that forwarded us this key. Will be None if /// we dirrectly received this session. - pub forwarding_chains: Option>, + #[serde(default)] + pub forwarding_chains: Vec, /// Flag remembering if the session was dirrectly sent to us by the sender /// or if it was imported. pub imported: bool, @@ -411,21 +420,15 @@ impl TryFrom for InboundGroupSession { let session = OlmInboundGroupSession::import(&key.session_key.0)?; let first_known_index = session.first_known_index(); - let forwarding_chains = if key.forwarding_curve25519_key_chain.is_empty() { - None - } else { - Some(key.forwarding_curve25519_key_chain) - }; - Ok(InboundGroupSession { inner: Arc::new(Mutex::new(session)), session_id: key.session_id.into(), sender_key: key.sender_key.into(), history_visibility: None.into(), first_known_index, - signing_key: Arc::new(key.sender_claimed_keys), + signing_keys: Arc::new(key.sender_claimed_keys), room_id: Arc::new(key.room_id), - forwarding_chains: Arc::new(Mutex::new(forwarding_chains)), + forwarding_chains: Arc::new(key.forwarding_curve25519_key_chain), imported: Arc::new(true), }) } From 4fc21a8860265063d2dcf0e20299c16a9901ab54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 28 Apr 2021 16:46:58 +0200 Subject: [PATCH 2/8] base: Store the raw versions of events in the state store This patch changes the way we store and load the majority of events that are in the state store. This is done so custom fields in the event aren't lost since a deserialize/serialize cycle removes all the unknown fields from the event. --- matrix_sdk_base/src/client.rs | 78 +++++++++--------- matrix_sdk_base/src/rooms/normal.rs | 7 +- matrix_sdk_base/src/store/memory_store.rs | 47 ++++++----- matrix_sdk_base/src/store/mod.rs | 62 ++++++++------- matrix_sdk_base/src/store/sled_store/mod.rs | 87 +++++++++++++++------ 5 files changed, 174 insertions(+), 107 deletions(-) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 60102b2d..528bced4 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -473,7 +473,9 @@ impl BaseClient { } _ => { room_info.handle_state_event(&s.content()); - changes.add_state_event(room_id, s.clone()); + let raw_event: Raw = + Raw::from_json(event.event.clone().into_json()); + changes.add_state_event(room_id, s.clone(), raw_event); } }, @@ -543,12 +545,12 @@ impl BaseClient { room_info: &mut RoomInfo, ) -> ( BTreeMap, - BTreeMap>, + BTreeMap>>, ) { events.iter().fold( (BTreeMap::new(), BTreeMap::new()), - |(mut members, mut state_events), e| { - match e.deserialize() { + |(mut members, mut state_events), raw_event| { + match raw_event.deserialize() { Ok(e) => { if let AnyStrippedStateEvent::RoomMember(member) = e { @@ -566,7 +568,7 @@ impl BaseClient { state_events .entry(e.content().event_type().to_owned()) .or_insert_with(BTreeMap::new) - .insert(e.state_key().to_owned(), e); + .insert(e.state_key().to_owned(), raw_event.clone()); } } Err(err) => { @@ -595,19 +597,18 @@ impl BaseClient { let room_id = room_info.room_id.clone(); - for event in events - .iter() - .filter_map(|e| match hoist_and_deserialize_state_event(&e) { - Ok(e) => Some(e), - Err(err) => { + for raw_event in events { + let event = match hoist_and_deserialize_state_event(raw_event) { + Ok(e) => e, + Err(e) => { warn!( "Couldn't deserialize state event for room {}: {:?} {:#?}", - room_id, err, e + room_id, e, raw_event ); - None + continue; } - }) - { + }; + room_info.handle_state_event(&event.content()); if let AnySyncStateEvent::RoomMember(member) = event { @@ -641,7 +642,7 @@ impl BaseClient { state_events .entry(event.content().event_type().to_owned()) .or_insert_with(BTreeMap::new) - .insert(event.state_key().to_owned(), event); + .insert(event.state_key().to_owned(), raw_event.clone()); } } @@ -658,20 +659,24 @@ impl BaseClient { events: &[Raw], changes: &mut StateChanges, ) { - let events: Vec = - events.iter().filter_map(|e| e.deserialize().ok()).collect(); - - for event in &events { - changes.add_room_account_data(room_id, event.clone()); + for raw_event in events { + if let Ok(event) = raw_event.deserialize() { + changes.add_room_account_data(room_id, event, raw_event.clone()); + } } } async fn handle_account_data(&self, events: &[Raw], changes: &mut StateChanges) { - let events: Vec = - events.iter().filter_map(|e| e.deserialize().ok()).collect(); + let mut account_data = BTreeMap::new(); - for event in &events { - if let AnyBasicEvent::Direct(e) = event { + for raw_event in events { + let event = if let Ok(e) = raw_event.deserialize() { + e + } else { + continue; + }; + + if let AnyBasicEvent::Direct(e) = &event { for (user_id, rooms) in e.content.iter() { for room_id in rooms { if let Some(room) = changes.room_infos.get_mut(room_id) { @@ -684,12 +689,9 @@ impl BaseClient { } } } - } - let account_data: BTreeMap = events - .into_iter() - .map(|e| (e.content().event_type().to_owned(), e)) - .collect(); + account_data.insert(event.content().event_type().to_owned(), raw_event.clone()); + } changes.account_data = account_data; } @@ -904,7 +906,7 @@ impl BaseClient { .iter() .filter_map(|e| { let event = e.deserialize().ok()?; - Some((event.sender.clone(), event)) + Some((event.sender, e.clone())) }) .collect(); @@ -1345,14 +1347,17 @@ impl BaseClient { /// Gets the push rules from `changes` if they have been updated, otherwise get them from the /// store. As a fallback, uses `Ruleset::server_default` if the user is logged in. pub async fn get_push_rules(&self, changes: &StateChanges) -> Result { - if let Some(AnyBasicEvent::PushRules(event)) = - changes.account_data.get(&EventType::PushRules.to_string()) + if let Some(AnyBasicEvent::PushRules(event)) = changes + .account_data + .get(&EventType::PushRules.to_string()) + .and_then(|e| e.deserialize().ok()) { - Ok(event.content.global.clone()) + Ok(event.content.global) } else if let Some(AnyBasicEvent::PushRules(event)) = self .store .get_account_data_event(EventType::PushRules) .await? + .and_then(|e| e.deserialize().ok()) { Ok(event.content.global) } else if let Some(session) = self.get_session().await { @@ -1401,12 +1406,14 @@ impl BaseClient { .get(room_id) .and_then(|types| types.get(EventType::RoomPowerLevels.as_str())) .and_then(|events| events.get("")) + .and_then(|e| e.deserialize().ok()) { - event.content.clone() + event.content } else if let Some(AnySyncStateEvent::RoomPowerLevels(event)) = self .store .get_state_event(room_id, EventType::RoomPowerLevels, "") .await? + .and_then(|e| e.deserialize().ok()) { event.content } else { @@ -1454,8 +1461,9 @@ impl BaseClient { .get(room_id) .and_then(|types| types.get(EventType::RoomPowerLevels.as_str())) .and_then(|events| events.get("")) + .and_then(|e| e.deserialize().ok()) { - let room_power_levels = event.content.clone(); + let room_power_levels = event.content; push_rules.users_power_levels = room_power_levels.users; push_rules.default_power_level = room_power_levels.users_default; diff --git a/matrix_sdk_base/src/rooms/normal.rs b/matrix_sdk_base/src/rooms/normal.rs index 82e9429e..d9dc8862 100644 --- a/matrix_sdk_base/src/rooms/normal.rs +++ b/matrix_sdk_base/src/rooms/normal.rs @@ -393,7 +393,11 @@ impl Room { return Ok(None); }; - let presence = self.store.get_presence_event(user_id).await?; + let presence = self + .store + .get_presence_event(user_id) + .await? + .and_then(|e| e.deserialize().ok()); let profile = self.store.get_profile(self.room_id(), user_id).await?; let max_power_level = self.max_power_level(); let is_room_creator = self @@ -410,6 +414,7 @@ impl Room { .store .get_state_event(self.room_id(), EventType::RoomPowerLevels, "") .await? + .and_then(|e| e.deserialize().ok()) .and_then(|e| { if let AnySyncStateEvent::RoomPowerLevels(e) = e { Some(e) diff --git a/matrix_sdk_base/src/store/memory_store.rs b/matrix_sdk_base/src/store/memory_store.rs index 33ffd1a5..b8182cc1 100644 --- a/matrix_sdk_base/src/store/memory_store.rs +++ b/matrix_sdk_base/src/store/memory_store.rs @@ -23,10 +23,11 @@ use matrix_sdk_common::{ events::{ presence::PresenceEvent, room::member::{MemberEventContent, MembershipState}, - AnyBasicEvent, AnyStrippedStateEvent, AnySyncStateEvent, EventContent, EventType, + AnyBasicEvent, AnyStrippedStateEvent, AnySyncStateEvent, EventType, }, identifiers::{RoomId, UserId}, instant::Instant, + Raw, }; use tracing::info; @@ -39,7 +40,7 @@ use super::{Result, RoomInfo, StateChanges, StateStore}; pub struct MemoryStore { sync_token: Arc>>, filters: Arc>, - account_data: Arc>, + account_data: Arc>>, members: Arc>>, profiles: Arc>>, display_names: Arc>>>, @@ -47,14 +48,14 @@ pub struct MemoryStore { invited_user_ids: Arc>>, room_info: Arc>, #[allow(clippy::type_complexity)] - room_state: Arc>>>, - room_account_data: Arc>>, + room_state: Arc>>>>, + room_account_data: Arc>>>, stripped_room_info: Arc>, #[allow(clippy::type_complexity)] stripped_room_state: - Arc>>>, + Arc>>>>, stripped_members: Arc>>, - presence: Arc>, + presence: Arc>>, } impl MemoryStore { @@ -176,14 +177,14 @@ impl MemoryStore { } for (room, event_types) in &changes.state { - for events in event_types.values() { - for event in events.values() { + for (event_type, events) in event_types { + for (state_key, event) in events { self.room_state .entry(room.clone()) .or_insert_with(DashMap::new) - .entry(event.content().event_type().to_string()) + .entry(event_type.to_owned()) .or_insert_with(DashMap::new) - .insert(event.state_key().to_string(), event.clone()); + .insert(state_key.to_owned(), event.clone()); } } } @@ -211,14 +212,14 @@ impl MemoryStore { } for (room, event_types) in &changes.stripped_state { - for events in event_types.values() { - for event in events.values() { + for (event_type, events) in event_types { + for (state_key, event) in events { self.stripped_room_state .entry(room.clone()) .or_insert_with(DashMap::new) - .entry(event.content().event_type().to_string()) + .entry(event_type.to_owned()) .or_insert_with(DashMap::new) - .insert(event.state_key().to_string(), event.clone()); + .insert(state_key.to_owned(), event.clone()); } } } @@ -228,7 +229,7 @@ impl MemoryStore { Ok(()) } - async fn get_presence_event(&self, user_id: &UserId) -> Result> { + async fn get_presence_event(&self, user_id: &UserId) -> Result>> { #[allow(clippy::map_clone)] Ok(self.presence.get(user_id).map(|p| p.clone())) } @@ -238,7 +239,7 @@ impl MemoryStore { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result> { + ) -> Result>> { #[allow(clippy::map_clone)] Ok(self.room_state.get(room_id).and_then(|e| { e.get(event_type.as_ref()) @@ -304,7 +305,10 @@ impl MemoryStore { self.stripped_room_info.iter().map(|r| r.clone()).collect() } - async fn get_account_data_event(&self, event_type: EventType) -> Result> { + async fn get_account_data_event( + &self, + event_type: EventType, + ) -> Result>> { Ok(self .account_data .get(event_type.as_ref()) @@ -331,7 +335,7 @@ impl StateStore for MemoryStore { self.get_sync_token().await } - async fn get_presence_event(&self, user_id: &UserId) -> Result> { + async fn get_presence_event(&self, user_id: &UserId) -> Result>> { self.get_presence_event(user_id).await } @@ -340,7 +344,7 @@ impl StateStore for MemoryStore { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result> { + ) -> Result>> { self.get_state_event(room_id, event_type, state_key).await } @@ -393,7 +397,10 @@ impl StateStore for MemoryStore { .unwrap_or_default()) } - async fn get_account_data_event(&self, event_type: EventType) -> Result> { + async fn get_account_data_event( + &self, + event_type: EventType, + ) -> Result>> { self.get_account_data_event(event_type).await } } diff --git a/matrix_sdk_base/src/store/mod.rs b/matrix_sdk_base/src/store/mod.rs index d9e33336..2e017d22 100644 --- a/matrix_sdk_base/src/store/mod.rs +++ b/matrix_sdk_base/src/store/mod.rs @@ -31,7 +31,7 @@ use matrix_sdk_common::{ }, identifiers::{RoomId, UserId}, locks::RwLock, - AsyncTraitDeps, + AsyncTraitDeps, Raw, }; #[cfg(feature = "sled_state_store")] use sled::Db; @@ -114,7 +114,7 @@ pub trait StateStore: AsyncTraitDeps { /// /// * `user_id` - The id of the user for which we wish to fetch the presence /// event for. - async fn get_presence_event(&self, user_id: &UserId) -> Result>; + async fn get_presence_event(&self, user_id: &UserId) -> Result>>; /// Get a state event out of the state store. /// @@ -128,7 +128,7 @@ pub trait StateStore: AsyncTraitDeps { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result>; + ) -> Result>>; /// Get the current profile for the given user in the given room. /// @@ -192,7 +192,10 @@ pub trait StateStore: AsyncTraitDeps { /// # Arguments /// /// * `event_type` - The event type of the account data event. - async fn get_account_data_event(&self, event_type: EventType) -> Result>; + async fn get_account_data_event( + &self, + event_type: EventType, + ) -> Result>>; } /// A state store wrapper for the SDK. @@ -345,30 +348,33 @@ pub struct StateChanges { /// A user session, containing an access token and information about the associated user account. pub session: Option, /// A mapping of event type string to `AnyBasicEvent`. - pub account_data: BTreeMap, + pub account_data: BTreeMap>, /// A mapping of `UserId` to `PresenceEvent`. - pub presence: BTreeMap, + pub presence: BTreeMap>, /// A mapping of `RoomId` to a map of users and their `MemberEvent`. pub members: BTreeMap>, /// A mapping of `RoomId` to a map of users and their `MemberEventContent`. pub profiles: BTreeMap>, - pub(crate) ambiguity_maps: BTreeMap>>, /// A mapping of `RoomId` to a map of event type string to a state key and `AnySyncStateEvent`. - pub state: BTreeMap>>, + pub state: BTreeMap>>>, /// A mapping of `RoomId` to a map of event type string to `AnyBasicEvent`. - pub room_account_data: BTreeMap>, + pub room_account_data: BTreeMap>>, /// A map of `RoomId` to `RoomInfo`. pub room_infos: BTreeMap, /// A mapping of `RoomId` to a map of event type to a map of state key to `AnyStrippedStateEvent`. - pub stripped_state: BTreeMap>>, + pub stripped_state: + BTreeMap>>>, /// A mapping of `RoomId` to a map of users and their `StrippedMemberEvent`. pub stripped_members: BTreeMap>, /// A map of `RoomId` to `RoomInfo`. pub invited_room_info: BTreeMap, + /// A map from room id to a map of a display name and a set of user ids that + /// share that display name in the given room. + pub ambiguity_maps: BTreeMap>>, /// A map of `RoomId` to a vector of `Notification`s pub notifications: BTreeMap>, } @@ -383,8 +389,8 @@ impl StateChanges { } /// Update the `StateChanges` struct with the given `PresenceEvent`. - pub fn add_presence_event(&mut self, event: PresenceEvent) { - self.presence.insert(event.sender.clone(), event); + pub fn add_presence_event(&mut self, event: PresenceEvent, raw_event: Raw) { + self.presence.insert(event.sender, raw_event); } /// Update the `StateChanges` struct with the given `RoomInfo`. @@ -400,27 +406,22 @@ impl StateChanges { } /// Update the `StateChanges` struct with the given `AnyBasicEvent`. - pub fn add_account_data(&mut self, event: AnyBasicEvent) { + pub fn add_account_data(&mut self, event: AnyBasicEvent, raw_event: Raw) { self.account_data - .insert(event.content().event_type().to_owned(), event); + .insert(event.content().event_type().to_owned(), raw_event); } /// Update the `StateChanges` struct with the given room with a new `AnyBasicEvent`. - pub fn add_room_account_data(&mut self, room_id: &RoomId, event: AnyBasicEvent) { + pub fn add_room_account_data( + &mut self, + room_id: &RoomId, + event: AnyBasicEvent, + raw_event: Raw, + ) { self.room_account_data .entry(room_id.to_owned()) .or_insert_with(BTreeMap::new) - .insert(event.content().event_type().to_owned(), event); - } - - /// Update the `StateChanges` struct with the given room with a new `AnyStrippedStateEvent`. - pub fn add_stripped_state_event(&mut self, room_id: &RoomId, event: AnyStrippedStateEvent) { - self.stripped_state - .entry(room_id.to_owned()) - .or_insert_with(BTreeMap::new) - .entry(event.content().event_type().to_string()) - .or_insert_with(BTreeMap::new) - .insert(event.state_key().to_string(), event); + .insert(event.content().event_type().to_owned(), raw_event); } /// Update the `StateChanges` struct with the given room with a new `StrippedMemberEvent`. @@ -434,13 +435,18 @@ impl StateChanges { } /// Update the `StateChanges` struct with the given room with a new `AnySyncStateEvent`. - pub fn add_state_event(&mut self, room_id: &RoomId, event: AnySyncStateEvent) { + pub fn add_state_event( + &mut self, + room_id: &RoomId, + event: AnySyncStateEvent, + raw_event: Raw, + ) { self.state .entry(room_id.to_owned()) .or_insert_with(BTreeMap::new) .entry(event.content().event_type().to_string()) .or_insert_with(BTreeMap::new) - .insert(event.state_key().to_string(), event); + .insert(event.state_key().to_string(), raw_event); } /// Update the `StateChanges` struct with the given room with a new `Notification`. diff --git a/matrix_sdk_base/src/store/sled_store/mod.rs b/matrix_sdk_base/src/store/sled_store/mod.rs index ac815683..e14c0a0b 100644 --- a/matrix_sdk_base/src/store/sled_store/mod.rs +++ b/matrix_sdk_base/src/store/sled_store/mod.rs @@ -31,9 +31,10 @@ use matrix_sdk_common::{ events::{ presence::PresenceEvent, room::member::{MemberEventContent, MembershipState}, - AnyBasicEvent, AnySyncStateEvent, EventContent, EventType, + AnyBasicEvent, AnySyncStateEvent, EventType, }, identifiers::{RoomId, UserId}, + Raw, }; use serde::{Deserialize, Serialize}; @@ -405,14 +406,10 @@ impl SledStore { } for (room, event_types) in &changes.state { - for events in event_types.values() { - for event in events.values() { + for (event_type, events) in event_types { + for (state_key, event) in events { state.insert( - ( - room.as_str(), - event.content().event_type(), - event.state_key(), - ) + (room.as_str(), event_type.as_str(), state_key.as_str()) .encode(), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, @@ -456,14 +453,10 @@ impl SledStore { } for (room, event_types) in &changes.stripped_state { - for events in event_types.values() { - for event in events.values() { + for (event_type, events) in event_types { + for (state_key, event) in events { stripped_state.insert( - ( - room.as_str(), - event.content().event_type(), - event.state_key(), - ) + (room.as_str(), event_type.as_str(), state_key.as_str()) .encode(), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, @@ -485,7 +478,7 @@ impl SledStore { Ok(()) } - pub async fn get_presence_event(&self, user_id: &UserId) -> Result> { + pub async fn get_presence_event(&self, user_id: &UserId) -> Result>> { Ok(self .presence .get(user_id.encode())? @@ -498,7 +491,7 @@ impl SledStore { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result> { + ) -> Result>> { Ok(self .room_state .get((room_id.as_str(), event_type.as_str(), state_key).encode())? @@ -597,7 +590,7 @@ impl SledStore { pub async fn get_account_data_event( &self, event_type: EventType, - ) -> Result> { + ) -> Result>> { Ok(self .account_data .get(event_type.encode())? @@ -624,7 +617,7 @@ impl StateStore for SledStore { self.get_sync_token().await } - async fn get_presence_event(&self, user_id: &UserId) -> Result> { + async fn get_presence_event(&self, user_id: &UserId) -> Result>> { self.get_presence_event(user_id).await } @@ -633,7 +626,7 @@ impl StateStore for SledStore { room_id: &RoomId, event_type: EventType, state_key: &str, - ) -> Result> { + ) -> Result>> { self.get_state_event(room_id, event_type, state_key).await } @@ -682,7 +675,10 @@ impl StateStore for SledStore { .await } - async fn get_account_data_event(&self, event_type: EventType) -> Result> { + async fn get_account_data_event( + &self, + event_type: EventType, + ) -> Result>> { self.get_account_data_event(event_type).await } } @@ -693,12 +689,17 @@ mod test { use matrix_sdk_common::{ events::{ - room::member::{MemberEventContent, MembershipState}, - Unsigned, + room::{ + member::{MemberEventContent, MembershipState}, + power_levels::PowerLevelsEventContent, + }, + AnySyncStateEvent, EventType, Unsigned, }, identifiers::{room_id, user_id, EventId, UserId}, + Raw, }; use matrix_sdk_test::async_test; + use serde_json::json; use super::{SledStore, StateChanges}; use crate::deserialized_responses::MemberEvent; @@ -707,6 +708,22 @@ mod test { user_id!("@example:localhost") } + fn power_level_event() -> Raw { + let content = PowerLevelsEventContent::default(); + + let event = json!({ + "event_id": EventId::try_from("$h29iv0s8:example.com").unwrap(), + "content": content, + "sender": user_id(), + "type": "m.room.power_levels", + "origin_server_ts": 0u64, + "state_key": "", + "unsigned": Unsigned::default(), + }); + + serde_json::from_value(event).unwrap() + } + fn membership_event() -> MemberEvent { let content = MemberEventContent { avatar_url: None, @@ -752,4 +769,28 @@ mod test { .unwrap() .is_some()); } + + #[async_test] + async fn test_power_level_saving() { + let store = SledStore::open().unwrap(); + let room_id = room_id!("!test:localhost"); + + let raw_event = power_level_event(); + let event = raw_event.deserialize().unwrap(); + + assert!(store + .get_state_event(&room_id, EventType::RoomPowerLevels, "") + .await + .unwrap() + .is_none()); + let mut changes = StateChanges::default(); + changes.add_state_event(&room_id, event, raw_event); + + store.save_changes(&changes).await.unwrap(); + assert!(store + .get_state_event(&room_id, EventType::RoomPowerLevels, "") + .await + .unwrap() + .is_some()); + } } From b3cf2c5899695f9c42e7935fdf1753d205fa761f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 29 Apr 2021 10:46:47 +0200 Subject: [PATCH 3/8] base: Fix a clippy warning if the encryption feature is turned off --- matrix_sdk_base/src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 528bced4..10175109 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -430,6 +430,7 @@ impl BaseClient { let mut push_context = self.get_push_room_context(room, room_info, changes).await?; for event in ruma_timeline.events { + #[allow(unused_mut)] let mut event: SyncRoomEvent = event.into(); match hoist_room_event_prev_content(&event.event) { From c720abfa878f90addcd30af327c4704d57fd7402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 29 Apr 2021 12:46:21 +0200 Subject: [PATCH 4/8] base: Fix the wasm example --- matrix_sdk/examples/wasm_command_bot/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk/examples/wasm_command_bot/src/lib.rs b/matrix_sdk/examples/wasm_command_bot/src/lib.rs index e7e8c0e5..7f4abbb4 100644 --- a/matrix_sdk/examples/wasm_command_bot/src/lib.rs +++ b/matrix_sdk/examples/wasm_command_bot/src/lib.rs @@ -58,7 +58,7 @@ impl WasmBot { for (room_id, room) in response.rooms.join { for event in room.timeline.events { - if let AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(ev)) = event { + if let Ok(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(ev))) = event.event.deserialize() { self.on_room_message(&room_id, &ev).await } } From 22b333a0d9a7f181338c3276d2b901266e115c47 Mon Sep 17 00:00:00 2001 From: poljar Date: Thu, 29 Apr 2021 15:33:45 +0200 Subject: [PATCH 5/8] Use as_str() to get the string event type. Co-authored-by: Jonas Platte --- matrix_sdk_base/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 10175109..732909e7 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -1350,7 +1350,7 @@ impl BaseClient { pub async fn get_push_rules(&self, changes: &StateChanges) -> Result { if let Some(AnyBasicEvent::PushRules(event)) = changes .account_data - .get(&EventType::PushRules.to_string()) + .get(EventType::PushRules.as_str()) .and_then(|e| e.deserialize().ok()) { Ok(event.content.global) From e71cabc8f0a51d0259bf0a96222e1e11e9456ac3 Mon Sep 17 00:00:00 2001 From: poljar Date: Thu, 29 Apr 2021 15:34:04 +0200 Subject: [PATCH 6/8] crypto: Fix a typo. Co-authored-by: Jonas Platte --- matrix_sdk_common/src/deserialized_responses.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_common/src/deserialized_responses.rs b/matrix_sdk_common/src/deserialized_responses.rs index 9ba75ad7..b3a6d130 100644 --- a/matrix_sdk_common/src/deserialized_responses.rs +++ b/matrix_sdk_common/src/deserialized_responses.rs @@ -89,7 +89,7 @@ pub struct EncryptionInfo { pub verification_state: VerificationState, } -/// A customized version of a room event comming from a sync that holds optional +/// A customized version of a room event coming from a sync that holds optional /// decryption info. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SyncRoomEvent { From 233c4355d805e57bba32d549d082fee82df1743b Mon Sep 17 00:00:00 2001 From: poljar Date: Thu, 29 Apr 2021 15:34:39 +0200 Subject: [PATCH 7/8] crypto: Use encryption info in the docstring for the type of the same name Co-authored-by: Jonas Platte --- matrix_sdk_common/src/deserialized_responses.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_common/src/deserialized_responses.rs b/matrix_sdk_common/src/deserialized_responses.rs index b3a6d130..052717b0 100644 --- a/matrix_sdk_common/src/deserialized_responses.rs +++ b/matrix_sdk_common/src/deserialized_responses.rs @@ -90,7 +90,7 @@ pub struct EncryptionInfo { } /// A customized version of a room event coming from a sync that holds optional -/// decryption info. +/// encryption info. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SyncRoomEvent { /// The actual event. From 6048a1a507a631163dfc8e446b35c50f2f743258 Mon Sep 17 00:00:00 2001 From: poljar Date: Thu, 29 Apr 2021 15:34:53 +0200 Subject: [PATCH 8/8] crypto: Fix a typo Co-authored-by: Jonas Platte --- matrix_sdk_crypto/src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index f2b90fa6..a144a3cd 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1872,7 +1872,7 @@ pub(crate) mod test { if let MessageType::Text(c) = &content.msgtype { assert_eq!(&c.body, plaintext); } else { - panic!("Decrypted event has a missmatched content"); + panic!("Decrypted event has a mismatched content"); } } else { panic!("Decrypted room event has the wrong type")