7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | // Copyright 2020 Damir Jelić |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | // Copyright 2020 The Matrix.org Foundation C.I.C. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | // you may not use this file except in compliance with the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | // You may obtain a copy of the License at |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | // Unless required by applicable law or agreed to in writing, software |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | // See the License for the specific language governing permissions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | // limitations under the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | use std::sync::Arc; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | collections::{BTreeMap, BTreeSet, HashMap}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | fmt, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | ops::Deref, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | use eyeball::{SharedObservable, Subscriber}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | use eyeball_im::{Vector, VectorDiff}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | use futures_util::Stream; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | use matrix_sdk_common::{cross_process_lock::CrossProcessLockConfig, timer}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | use matrix_sdk_crypto::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | CollectStrategy, DecryptionSettings, EncryptionSettings, OlmError, OlmMachine, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | TrustRequirement, store::DynCryptoStore, store::types::RoomPendingKeyBundleDetails, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | types::requests::ToDeviceRequest, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | #[cfg(doc)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | use ruma::DeviceId; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | use ruma::events::room::{history_visibility::HistoryVisibility, member::MembershipState}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | use ruma::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | OwnedRoomId, OwnedUserId, RoomId, UserId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | api::client::{self as api, sync::sync_events::v5}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | events::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | StateEvent, StateEventType, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | ignored_user_list::IgnoredUserListEventContent, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | push_rules::{PushRulesEvent, PushRulesEventContent}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | room::member::SyncRoomMemberEvent, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | push::Ruleset, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | time::Instant, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | use tokio::sync::{Mutex, broadcast}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | use tokio::sync::{RwLock, RwLockReadGuard}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | use tracing::{Level, debug, enabled, info, instrument, warn}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | use crate::RoomMemberships; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | use crate::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | RoomStateFilter, SessionMeta, StateStore, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | deserialized_responses::DisplayName, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | error::{Error, Result}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | event_cache::store::{EventCacheStoreLock, EventCacheStoreLockState}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | media::store::MediaStoreLock, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | response_processors::{self as processors, Context}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | room::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | Room, RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, RoomMembersUpdate, RoomState, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | store::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | AvatarCache, BaseStateStore, DynStateStore, MemoryStore, Result as StoreResult, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | RoomLoadSettings, StateChanges, StateStoreDataKey, StateStoreDataValue, StateStoreExt, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | StoreConfig, ambiguity_map::AmbiguityCache, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | sync::{RoomUpdates, SyncResponse}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | /// A no (network) IO client implementation. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | /// This client is a state machine that receives responses and events and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | /// accordingly updates its state. It is not designed to be used directly, but |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | /// rather through `matrix_sdk::Client`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | /// ```rust |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | /// use matrix_sdk_base::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | /// BaseClient, DmRoomDefinition, ThreadingSupport, store::StoreConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | /// }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | /// use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | /// let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | /// StoreConfig::new(CrossProcessLockConfig::multi_process( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | /// "cross-process-holder-name".to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | /// )), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | /// ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | /// DmRoomDefinition::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | /// ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | #[derive(Clone)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | pub struct BaseClient { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | /// The state store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | pub(crate) state_store: BaseStateStore, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | /// The store used by the event cache. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | event_cache_store: EventCacheStoreLock, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | /// The store used by the media cache. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | media_store: MediaStoreLock, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | /// The store used for encryption. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | /// This field is only meant to be used for `OlmMachine` initialization. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | /// All operations on it happen inside the `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | crypto_store: Arc<DynCryptoStore>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | /// The olm-machine that is created once the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | /// [`SessionMeta`][crate::session::SessionMeta] is set via |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | /// [`BaseClient::activate`] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | olm_machine: Arc<RwLock<Option<OlmMachine>>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | /// Observable of when a user is ignored/unignored. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | pub(crate) ignore_user_list_changes: SharedObservable<Vec<String>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | /// The strategy to use for picking recipient devices, when sending an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | /// encrypted message. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | pub room_key_recipient_strategy: CollectStrategy, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | /// The settings to use for decrypting events. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | pub decryption_settings: DecryptionSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | /// If the client should handle verification events received when syncing. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | pub handle_verification_events: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | /// Whether the client supports threads or not. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | pub threading_support: ThreadingSupport, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | /// The definition of what is considered a DM room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | pub dm_room_definition: DmRoomDefinition, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | #[cfg(not(tarpaulin_include))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | impl fmt::Debug for BaseClient { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | f.debug_struct("BaseClient") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | .field("session_meta", &self.state_store.session_meta()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | .field("sync_token", &self.state_store.sync_token) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | .finish_non_exhaustive() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | /// Whether this client instance supports threading or not. Currently used to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | /// determine how the client handles read receipts and unread count computations |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | /// on the base SDK level. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | /// Timelines on the other hand have a separate `TimelineFocus` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | /// `hide_threaded_events` associated value that can be used to hide threaded |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | /// events but also to enable threaded read receipt sending. This is because |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | /// certain timeline instances should ignore threading no matter what's defined |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | /// at the client level. One such example are media filtered timelines which |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | /// should contain all the room's media no matter what thread its in (unless |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | /// explicitly opted into). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | #[derive(Clone, Copy, Debug)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | pub enum ThreadingSupport { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | /// Threading enabled. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | Enabled { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | /// Enable client-wide thread subscriptions support (MSC4306 / MSC4308). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | /// This may cause filtering out of thread subscriptions, and loading |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | /// the thread subscriptions via the sliding sync extension, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | /// when the room list service is being used. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | with_subscriptions: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | /// Threading disabled. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | impl BaseClient { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | /// Create a new client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | /// * `config` - the configuration for the stores (state store, event cache |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | /// store and crypto store). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | pub fn new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | config: StoreConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | threading_support: ThreadingSupport, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | dm_room_definition: DmRoomDefinition, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | ) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | let store = BaseStateStore::new(config.state_store); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | BaseClient { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | state_store: store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | event_cache_store: config.event_cache_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | media_store: config.media_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | crypto_store: config.crypto_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | olm_machine: Default::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | ignore_user_list_changes: Default::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | room_key_recipient_strategy: Default::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | decryption_settings: DecryptionSettings { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | sender_device_trust_requirement: TrustRequirement::Untrusted, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | handle_verification_events: true, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | threading_support, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | dm_room_definition, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | /// Clones the current base client to use the same crypto store but a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | /// different, in-memory store config, and resets transient state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | pub async fn clone_with_in_memory_state_store( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | cross_process_mode: CrossProcessLockConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | handle_verification_events: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | ) -> Result<Self> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | let config = StoreConfig::new(cross_process_mode).state_store(MemoryStore::new()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | let config = config.crypto_store(self.crypto_store.clone()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | let copy = Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | state_store: BaseStateStore::new(config.state_store), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | event_cache_store: config.event_cache_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | media_store: config.media_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | // We copy the crypto store as well as the `OlmMachine` for two reasons: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | // 1. The `self.crypto_store` is the same as the one used inside the `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | // 2. We need to ensure that the parent and child use the same data and caches inside |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | // the `OlmMachine` so the various ratchets and places where new randomness gets |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | // introduced don't diverge, i.e. one-time keys that get generated by the Olm Account |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | // or Olm sessions when they encrypt or decrypt messages. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | crypto_store: self.crypto_store.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | olm_machine: self.olm_machine.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | ignore_user_list_changes: Default::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | room_key_recipient_strategy: self.room_key_recipient_strategy.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | decryption_settings: self.decryption_settings.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | handle_verification_events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | threading_support: self.threading_support, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | dm_room_definition: self.dm_room_definition.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | copy.state_store.derive_from_other(&self.state_store).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | Ok(copy) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | /// Clones the current base client to use the same crypto store but a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | /// different, in-memory store config, and resets transient state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | #[cfg(not(feature = "e2e-encryption"))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | #[allow(clippy::unused_async)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | pub async fn clone_with_in_memory_state_store( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | cross_process_store_config: CrossProcessLockConfig, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | _handle_verification_events: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | ) -> Result<Self> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | let config = StoreConfig::new(cross_process_store_config).state_store(MemoryStore::new()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | Ok(Self::new(config, ThreadingSupport::Disabled, DmRoomDefinition::default())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | /// Get the session meta information. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | /// If the client is currently logged in, this will return a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | /// [`SessionMeta`] object which contains the user ID and device ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | /// Otherwise it returns `None`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | pub fn session_meta(&self) -> Option<&SessionMeta> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | self.state_store.session_meta() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | /// Get all the rooms this client knows about. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | pub fn rooms(&self) -> Vec<Room> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | self.state_store.rooms() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 277 | /// Get all the rooms this client knows about, filtered by room state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 278 | pub fn rooms_filtered(&self, filter: RoomStateFilter) -> Vec<Room> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 279 | self.state_store.rooms_filtered(filter) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 280 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 281 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 282 | /// Get a stream of all the rooms changes, in addition to the existing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 283 | /// rooms. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 284 | pub fn rooms_stream( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 285 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 286 | ) -> (Vector<Room>, impl Stream<Item = Vec<VectorDiff<Room>>> + use<>) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 287 | self.state_store.rooms_stream() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 288 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 289 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 290 | /// Lookup the Room for the given RoomId, or create one, if it didn't exist |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 291 | /// yet in the store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 292 | pub fn get_or_create_room(&self, room_id: &RoomId, room_state: RoomState) -> Room { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 293 | self.state_store.get_or_create_room(room_id, room_state) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 294 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 295 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 296 | /// Get a reference to the state store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 297 | pub fn state_store(&self) -> &DynStateStore { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 298 | self.state_store.deref() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 299 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 300 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 301 | /// Get a reference to the event cache store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 302 | pub fn event_cache_store(&self) -> &EventCacheStoreLock { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 303 | &self.event_cache_store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 304 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 305 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 306 | /// Get a reference to the media store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 307 | pub fn media_store(&self) -> &MediaStoreLock { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 308 | &self.media_store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 309 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 310 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 311 | /// Check whether the client has been activated. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 312 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 313 | /// See [`BaseClient::activate`] to know what it means. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 314 | pub fn is_active(&self) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 315 | self.state_store.session_meta().is_some() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 316 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 317 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 318 | /// Activate the client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 319 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 320 | /// A client is considered active when: |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 321 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 322 | /// 1. It has a `SessionMeta` (user ID, device ID and access token), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 323 | /// 2. Has loaded cached data from storage, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 324 | /// 3. If encryption is enabled, it also initialized or restored its |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 325 | /// `OlmMachine`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 326 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 327 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 328 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 329 | /// * `session_meta` - The meta of a session that the user already has from |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 330 | /// a previous login call. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 331 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 332 | /// * `custom_account` - A custom |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 333 | /// [`matrix_sdk_crypto::vodozemac::olm::Account`] to be used for the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 334 | /// identity and one-time keys of this [`BaseClient`]. If no account is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 335 | /// provided, a new default one or one from the store will be used. If an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 336 | /// account is provided and one already exists in the store for this |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 337 | /// [`UserId`]/[`DeviceId`] combination, an error will be raised. This is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 338 | /// useful if one wishes to create identity keys before knowing the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 339 | /// user/device IDs, e.g., to use the identity key as the device ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 340 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 341 | /// * `room_load_settings` — Specify how many rooms must be restored; use |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 342 | /// `::default()` if you don't know which value to pick. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 343 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 344 | /// # Panics |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 345 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 346 | /// This method panics if it is called twice. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 347 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 348 | /// [`UserId`]: ruma::UserId |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 349 | pub async fn activate( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 350 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 351 | session_meta: SessionMeta, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 352 | room_load_settings: RoomLoadSettings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 353 | #[cfg(feature = "e2e-encryption")] custom_account: Option< |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 354 | crate::crypto::vodozemac::olm::Account, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 355 | >, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 356 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 357 | debug!(user_id = ?session_meta.user_id, device_id = ?session_meta.device_id, "Activating the client"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 358 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 359 | self.state_store.load_rooms(&session_meta.user_id, room_load_settings).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 360 | self.state_store.load_sync_token().await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 361 | self.state_store.set_session_meta(session_meta); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 362 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 363 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 364 | self.regenerate_olm(custom_account).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 365 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 366 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 367 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 368 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 369 | /// Recreate an `OlmMachine` from scratch. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 370 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 371 | /// In particular, this will clear all its caches. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 372 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 373 | pub async fn regenerate_olm( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 374 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 375 | custom_account: Option<crate::crypto::vodozemac::olm::Account>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 376 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 377 | tracing::debug!("regenerating OlmMachine"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 378 | let session_meta = self.session_meta().ok_or(Error::OlmError(OlmError::MissingSession))?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 379 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 380 | // Recreate the `OlmMachine` and wipe the in-memory cache in the store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 381 | // because we suspect it has stale data. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 382 | let olm_machine = OlmMachine::with_store( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 383 | &session_meta.user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 384 | &session_meta.device_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 385 | self.crypto_store.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 386 | custom_account, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 387 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 388 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 389 | .map_err(OlmError::from)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 390 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 391 | *self.olm_machine.write().await = Some(olm_machine); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 392 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 393 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 394 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 395 | /// Get the current, if any, sync token of the client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 396 | /// This will be None if the client didn't sync at least once. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 397 | pub async fn sync_token(&self) -> Option<String> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 398 | self.state_store.sync_token.read().await.clone() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 399 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 400 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 401 | /// User has knocked on a room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 402 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 403 | /// Update the internal and cached state accordingly. Return the final Room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 404 | pub async fn room_knocked(&self, room_id: &RoomId) -> Result<Room> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 405 | let room = self.state_store.get_or_create_room(room_id, RoomState::Knocked); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 406 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 407 | if room.state() != RoomState::Knocked { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 408 | let store_guard = self.state_store.lock().lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 409 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 410 | // We are no longer joined to the room, so the invite acceptance details are no |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 411 | // longer relevant. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 412 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 413 | if let Some(olm_machine) = self.olm_machine().await.as_ref() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 414 | olm_machine.store().clear_room_pending_key_bundle(room_id).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 415 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 416 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 417 | room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 418 | info.mark_as_knocked(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 419 | info.mark_state_partially_synced(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 420 | info.mark_members_missing(); // the own member event changed |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 421 | (info, RoomInfoNotableUpdateReasons::MEMBERSHIP) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 422 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 423 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 424 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 425 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 426 | Ok(room) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 427 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 428 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 429 | /// The user has joined a room using this specific client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 430 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 431 | /// This method should be called if the user accepts an invite or if they |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 432 | /// join a public room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 433 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 434 | /// The method will create a [`Room`] object if one does not exist yet and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 435 | /// set the state of the [`Room`] to [`RoomState::Joined`]. The [`Room`] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 436 | /// object will be persisted in the cache. Please note that the [`Room`] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 437 | /// will be a stub until a sync has been received with the full room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 438 | /// state using [`BaseClient::receive_sync_response`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 439 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 440 | /// Update the internal and cached state accordingly. Return the final Room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 441 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 442 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 443 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 444 | /// * `room_id` - The unique ID identifying the joined room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 445 | /// * `inviter` - When joining this room in response to an invitation, the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 446 | /// inviter should be recorded before sending the join request to the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 447 | /// server. Providing the inviter here ensures that the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 448 | /// [`RoomPendingKeyBundleDetails`] are stored for this room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 449 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 450 | /// # Examples |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 451 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 452 | /// ```rust |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 453 | /// # use matrix_sdk_base::{BaseClient, store::StoreConfig, RoomState, ThreadingSupport, DmRoomDefinition}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 454 | /// # use ruma::{OwnedRoomId, OwnedUserId, RoomId}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 455 | /// use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 456 | /// # async { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 457 | /// # let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 458 | /// StoreConfig::new(CrossProcessLockConfig::multi_process("example")), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 459 | /// ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 460 | /// DmRoomDefinition::default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 461 | /// ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 462 | /// # async fn send_join_request() -> anyhow::Result<OwnedRoomId> { todo!() } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 463 | /// # async fn maybe_get_inviter(room_id: &RoomId) -> anyhow::Result<Option<OwnedUserId>> { todo!() } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 464 | /// # let room_id: &RoomId = todo!(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 465 | /// let maybe_inviter = maybe_get_inviter(room_id).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 466 | /// let room_id = send_join_request().await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 467 | /// let room = client.room_joined(&room_id, maybe_inviter).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 468 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 469 | /// assert_eq!(room.state(), RoomState::Joined); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 470 | /// # matrix_sdk_test::TestResult::Ok(()) }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 471 | /// ``` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 472 | pub async fn room_joined( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 473 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 474 | room_id: &RoomId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 475 | inviter: Option<OwnedUserId>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 476 | ) -> Result<Room> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 477 | let room = self.state_store.get_or_create_room(room_id, RoomState::Joined); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 478 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 479 | // If the state isn't `RoomState::Joined` then this means that we knew about |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 480 | // this room before. Let's modify the existing state now. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 481 | if room.state() != RoomState::Joined { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 482 | let store_guard = self.state_store_lock().lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 483 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 484 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 485 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 486 | // If our previous state was an invite and we're now in the joined state, this |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 487 | // means that the user has explicitly accepted an invite. Let's |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 488 | // remember some details about the invite. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 489 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 490 | // This is somewhat of a workaround for our lack of cryptographic membership. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 491 | // Later on we will decide if historic room keys should be accepted |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 492 | // based on this info. If a user has accepted an invite and we receive a room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 493 | // key bundle shortly after, we might accept it. If we don't do |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 494 | // this, the homeserver could trick us into accepting any historic room key |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 495 | // bundle. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 496 | let previous_state = room.state(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 497 | if previous_state == RoomState::Invited |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 498 | && let Some(inviter) = inviter |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 499 | && let Some(olm_machine) = self.olm_machine().await.as_ref() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 500 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 501 | olm_machine.store().store_room_pending_key_bundle(room_id, &inviter).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 502 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 503 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 504 | #[cfg(not(feature = "e2e-encryption"))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 505 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 506 | // suppress unused argument warning |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 507 | let _ = inviter; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 508 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 509 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 510 | room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 511 | info.mark_as_joined(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 512 | info.mark_state_partially_synced(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 513 | info.mark_members_missing(); // the own member event changed |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 514 | (info, RoomInfoNotableUpdateReasons::MEMBERSHIP) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 515 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 516 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 517 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 518 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 519 | Ok(room) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 520 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 521 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 522 | /// User has left a room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 523 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 524 | /// Update the internal and cached state accordingly. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 525 | pub async fn room_left(&self, room_id: &RoomId) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 526 | let room = self.state_store.get_or_create_room(room_id, RoomState::Left); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 527 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 528 | if room.state() != RoomState::Left { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 529 | let store_guard = self.state_store.lock().lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 530 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 531 | // We are no longer joined to the room, so the invite acceptance details are no |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 532 | // longer relevant. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 533 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 534 | if let Some(olm_machine) = self.olm_machine().await.as_ref() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 535 | olm_machine.store().clear_room_pending_key_bundle(room_id).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 536 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 537 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 538 | room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 539 | info.mark_as_left(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 540 | info.mark_state_partially_synced(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 541 | info.mark_members_missing(); // the own member event changed |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 542 | (info, RoomInfoNotableUpdateReasons::MEMBERSHIP) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 543 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 544 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 545 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 546 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 547 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 548 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 549 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 550 | /// Get a lock to the state store, with an exclusive access. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 551 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 552 | /// It doesn't give an access to the state store itself. It's rather a lock |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 553 | /// to synchronise all accesses to the state store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 554 | pub fn state_store_lock(&self) -> &Mutex<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 555 | self.state_store.lock() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 556 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 557 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 558 | /// Receive a response from a sync call. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 559 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 560 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 561 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 562 | /// * `response` - The response that we received after a successful sync. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 563 | #[instrument(skip_all)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 564 | pub async fn receive_sync_response( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 565 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 566 | response: api::sync::sync_events::v3::Response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 567 | ) -> Result<SyncResponse> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 568 | self.receive_sync_response_with_requested_required_states( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 569 | response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 570 | &RequestedRequiredStates::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 571 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 572 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 573 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 574 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 575 | /// Receive a response from a sync call, with the requested required state |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 576 | /// events. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 577 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 578 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 579 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 580 | /// * `response` - The response that we received after a successful sync. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 581 | /// * `requested_required_states` - The requested required state events. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 582 | pub async fn receive_sync_response_with_requested_required_states( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 583 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 584 | response: api::sync::sync_events::v3::Response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 585 | requested_required_states: &RequestedRequiredStates, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 586 | ) -> Result<SyncResponse> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 587 | // The server might respond multiple times with the same sync token, in |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 588 | // that case we already received this response and there's nothing to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 589 | // do. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 590 | if self.state_store.sync_token.read().await.as_ref() == Some(&response.next_batch) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 591 | info!("Got the same sync response twice"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 592 | return Ok(SyncResponse::default()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 593 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 594 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 595 | let now = if enabled!(Level::INFO) { Some(Instant::now()) } else { None }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 596 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 597 | // Acquire the state store lock and hold on to it while processing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 598 | // the sync response below. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 599 | let state_store_guard = self.state_store_lock().lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 600 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 601 | let user_id = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 602 | .session_meta() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 603 | .expect("Sync shouldn't run without an authenticated user") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 604 | .user_id |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 605 | .to_owned(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 606 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 607 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 608 | let olm_machine = self.olm_machine().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 609 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 610 | let mut context = Context::new(StateChanges::new(response.next_batch.clone())); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 611 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 612 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 613 | let processors::e2ee::to_device::Output { processed_to_device_events: to_device } = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 614 | processors::e2ee::to_device::from_sync_v2( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 615 | &response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 616 | olm_machine.as_ref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 617 | &self.decryption_settings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 618 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 619 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 620 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 621 | #[cfg(not(feature = "e2e-encryption"))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 622 | let to_device = response |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 623 | .to_device |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 624 | .events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 625 | .into_iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 626 | .map(|raw| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 627 | use matrix_sdk_common::deserialized_responses::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 628 | ProcessedToDeviceEvent, ToDeviceUnableToDecryptInfo, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 629 | ToDeviceUnableToDecryptReason, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 630 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 631 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 632 | if let Ok(Some(event_type)) = raw.get_field::<String>("type") { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 633 | if event_type == "m.room.encrypted" { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 634 | ProcessedToDeviceEvent::UnableToDecrypt { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 635 | encrypted_event: raw, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 636 | utd_info: ToDeviceUnableToDecryptInfo { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 637 | reason: ToDeviceUnableToDecryptReason::EncryptionIsDisabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 638 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 639 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 640 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 641 | ProcessedToDeviceEvent::PlainText(raw) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 642 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 643 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 644 | // Exclude events with no type |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 645 | ProcessedToDeviceEvent::Invalid(raw) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 646 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 647 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 648 | .collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 649 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 650 | let mut ambiguity_cache = AmbiguityCache::new(self.state_store.inner.clone()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 651 | let mut avatar_cache = AvatarCache::new(self.state_store.inner.clone()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 652 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 653 | let global_account_data_processor = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 654 | processors::account_data::global(&response.account_data.events); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 655 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 656 | let push_rules = self.get_push_rules(&global_account_data_processor).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 657 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 658 | let mut room_updates = RoomUpdates::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 659 | let mut notifications = Default::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 660 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 661 | let mut updated_members_in_room: BTreeMap<OwnedRoomId, BTreeSet<OwnedUserId>> = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 662 | BTreeMap::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 663 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 664 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 665 | let e2ee_context = processors::e2ee::E2EE::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 666 | olm_machine.as_ref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 667 | &self.decryption_settings, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 668 | self.handle_verification_events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 669 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 670 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 671 | for (room_id, joined_room) in response.rooms.join { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 672 | let joined_room_update = processors::room::sync_v2::update_joined_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 673 | &mut context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 674 | processors::room::RoomCreationData::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 675 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 676 | requested_required_states, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 677 | &mut ambiguity_cache, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 678 | &mut avatar_cache, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 679 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 680 | joined_room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 681 | &mut updated_members_in_room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 682 | processors::notification::Notification::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 683 | &push_rules, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 684 | &mut notifications, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 685 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 686 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 687 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 688 | &e2ee_context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 689 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 690 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 691 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 692 | room_updates.joined.insert(room_id, joined_room_update); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 693 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 694 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 695 | for (room_id, left_room) in response.rooms.leave { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 696 | let left_room_update = processors::room::sync_v2::update_left_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 697 | &mut context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 698 | processors::room::RoomCreationData::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 699 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 700 | requested_required_states, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 701 | &mut ambiguity_cache, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 702 | &mut avatar_cache, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 703 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 704 | left_room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 705 | processors::notification::Notification::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 706 | &push_rules, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 707 | &mut notifications, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 708 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 709 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 710 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 711 | &e2ee_context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 712 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 713 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 714 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 715 | room_updates.left.insert(room_id, left_room_update); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 716 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 717 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 718 | for (room_id, invited_room) in response.rooms.invite { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 719 | let invited_room_update = processors::room::sync_v2::update_invited_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 720 | &mut context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 721 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 722 | &user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 723 | invited_room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 724 | processors::notification::Notification::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 725 | &push_rules, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 726 | &mut notifications, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 727 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 728 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 729 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 730 | &e2ee_context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 731 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 732 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 733 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 734 | room_updates.invited.insert(room_id, invited_room_update); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 735 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 736 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 737 | for (room_id, knocked_room) in response.rooms.knock { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 738 | let knocked_room_update = processors::room::sync_v2::update_knocked_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 739 | &mut context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 740 | &room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 741 | &user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 742 | knocked_room, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 743 | processors::notification::Notification::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 744 | &push_rules, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 745 | &mut notifications, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 746 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 747 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 748 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 749 | &e2ee_context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 750 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 751 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 752 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 753 | room_updates.knocked.insert(room_id, knocked_room_update); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 754 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 755 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 756 | global_account_data_processor.apply(&mut context, &self.state_store).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 757 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 758 | context.state_changes.presence = response |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 759 | .presence |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 760 | .events |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 761 | .iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 762 | .filter_map(|e| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 763 | let event = e.deserialize().ok()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 764 | Some((event.sender, e.clone())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 765 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 766 | .collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 767 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 768 | context.state_changes.ambiguity_maps = ambiguity_cache.cache; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 769 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 770 | processors::changes::save_and_apply( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 771 | context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 772 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 773 | &state_store_guard, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 774 | &self.ignore_user_list_changes, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 775 | Some(response.next_batch.clone()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 776 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 777 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 778 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 779 | let mut context = Context::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 780 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 781 | // Now that all the rooms information have been saved, update the display name |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 782 | // of the updated rooms (which relies on information stored in the database). |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 783 | processors::room::display_name::update_for_rooms( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 784 | &mut context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 785 | &room_updates, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 786 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 787 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 788 | .await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 789 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 790 | // Save the new display name updates if any. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 791 | processors::changes::save_only(context, &self.state_store, &state_store_guard).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 792 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 793 | for (room_id, member_ids) in updated_members_in_room { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 794 | if let Some(room) = self.get_room(&room_id) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 795 | let _ = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 796 | room.room_member_updates_sender.send(RoomMembersUpdate::Partial(member_ids)); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 797 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 798 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 799 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 800 | // Release the state store lock |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 801 | drop(state_store_guard); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 802 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 803 | if enabled!(Level::INFO) { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 804 | info!("Processed a sync response in {:?}", now.map(|now| now.elapsed())); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 805 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 806 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 807 | let response = SyncResponse { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 808 | rooms: room_updates, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 809 | presence: response.presence.events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 810 | account_data: response.account_data.events, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 811 | to_device, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 812 | notifications, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 813 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 814 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 815 | Ok(response) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 816 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 817 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 818 | /// Receive a get member events response and convert it to a deserialized |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 819 | /// `MembersResponse` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 820 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 821 | /// This client-server request must be made without filters to make sure all |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 822 | /// members are received. Otherwise, an error is returned. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 823 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 824 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 825 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 826 | /// * `room_id` - The room id this response belongs to. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 827 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 828 | /// * `response` - The raw response that was received from the server. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 829 | #[instrument(skip_all, fields(?room_id))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 830 | pub async fn receive_all_members( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 831 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 832 | room_id: &RoomId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 833 | request: &api::membership::get_member_events::v3::Request, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 834 | response: &api::membership::get_member_events::v3::Response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 835 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 836 | if request.membership.is_some() || request.not_membership.is_some() || request.at.is_some() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 837 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 838 | // This function assumes all members are loaded at once to optimise how display |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 839 | // name disambiguation works. Using it with partial member list results |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 840 | // would produce incorrect disambiguated display name entries |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 841 | return Err(Error::InvalidReceiveMembersParameters); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 842 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 843 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 844 | let Some(room) = self.state_store.room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 845 | // The room is unknown to us: leave early. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 846 | return Ok(()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 847 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 848 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 849 | let mut chunk = Vec::with_capacity(response.chunk.len()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 850 | let mut context = Context::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 851 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 852 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 853 | let mut user_ids = BTreeSet::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 854 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 855 | let mut ambiguity_map: HashMap<DisplayName, BTreeSet<OwnedUserId>> = Default::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 856 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 857 | for raw_event in &response.chunk { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 858 | let member = match raw_event.deserialize() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 859 | Ok(ev) => ev, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 860 | Err(e) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 861 | let event_id: Option<String> = raw_event.get_field("event_id").ok().flatten(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 862 | debug!(event_id, "Failed to deserialize member event: {e}"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 863 | continue; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 864 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 865 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 866 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 867 | // TODO: All the actions in this loop used to be done only when the membership |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 868 | // event was not in the store before. This was changed with the new room API, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 869 | // because e.g. leaving a room makes members events outdated and they need to be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 870 | // fetched by `members`. Therefore, they need to be overwritten here, even |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 871 | // if they exist. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 872 | // However, this makes a new problem occur where setting the member events here |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 873 | // potentially races with the sync. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 874 | // See <https://github.com/matrix-org/matrix-rust-sdk/issues/1205>. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 875 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 876 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 877 | match member.membership() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 878 | MembershipState::Join | MembershipState::Invite => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 879 | user_ids.insert(member.state_key().to_owned()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 880 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 881 | _ => (), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 882 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 883 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 884 | if let StateEvent::Original(e) = &member |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 885 | && let Some(d) = &e.content.displayname |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 886 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 887 | let display_name = DisplayName::new(d); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 888 | ambiguity_map.entry(display_name).or_default().insert(member.state_key().clone()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 889 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 890 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 891 | let sync_member: SyncRoomMemberEvent = member.clone().into(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 892 | processors::profiles::upsert_or_delete(&mut context, room_id, &sync_member); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 893 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 894 | context |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 895 | .state_changes |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 896 | .state |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 897 | .entry(room_id.to_owned()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 898 | .or_default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 899 | .entry(member.event_type()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 900 | .or_default() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 901 | .insert(member.state_key().to_string(), raw_event.clone().cast()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 902 | chunk.push(member); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 903 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 904 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 905 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 906 | processors::e2ee::tracked_users::update( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 907 | self.olm_machine().await.as_ref(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 908 | room.encryption_state(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 909 | &user_ids, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 910 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 911 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 912 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 913 | context.state_changes.ambiguity_maps.insert(room_id.to_owned(), ambiguity_map); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 914 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 915 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 916 | let state_store_guard = self.state_store_lock().lock().await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 917 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 918 | let mut room_info = room.clone_info(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 919 | room_info.mark_members_synced(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 920 | context.state_changes.add_room(room_info); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 921 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 922 | processors::changes::save_and_apply( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 923 | context, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 924 | &self.state_store, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 925 | &state_store_guard, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 926 | &self.ignore_user_list_changes, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 927 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 928 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 929 | .await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 930 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 931 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 932 | let _ = room.room_member_updates_sender.send(RoomMembersUpdate::FullReload); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 933 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 934 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 935 | if let Some(olm) = self.olm_machine().await.as_ref() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 936 | // With the introduction of MSC4268, it is no longer sufficient to check for |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 937 | // changes to session recipients when we send a message, since we may miss |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 938 | // join/leave pairs in our view of the room state. Instead, we should rotate |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 939 | // the room key whenever we fully reload the member list as a precaution. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 940 | tracing::debug!("Rotating room key due to full member list reload"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 941 | if let Err(e) = olm.discard_room_key(room_id).await { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 942 | tracing::warn!("Error discarding room key: {e:?}"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 943 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 944 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 945 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 946 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 947 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 948 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 949 | /// Receive a successful filter upload response, the filter id will be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 950 | /// stored under the given name in the store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 951 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 952 | /// The filter id can later be retrieved with the [`get_filter`] method. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 953 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 954 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 955 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 956 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 957 | /// * `filter_name` - The name that should be used to persist the filter id |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 958 | /// in the store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 959 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 960 | /// * `response` - The successful filter upload response containing the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 961 | /// filter id. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 962 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 963 | /// [`get_filter`]: #method.get_filter |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 964 | pub async fn receive_filter_upload( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 965 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 966 | filter_name: &str, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 967 | response: &api::filter::create_filter::v3::Response, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 968 | ) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 969 | Ok(self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 970 | .state_store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 971 | .set_kv_data( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 972 | StateStoreDataKey::Filter(filter_name), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 973 | StateStoreDataValue::Filter(response.filter_id.clone()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 974 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 975 | .await?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 976 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 977 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 978 | /// Get the filter id of a previously uploaded filter. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 979 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 980 | /// *Note*: A filter will first need to be uploaded and persisted using |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 981 | /// [`receive_filter_upload`]. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 982 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 983 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 984 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 985 | /// * `filter_name` - The name of the filter that was previously used to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 986 | /// persist the filter. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 987 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 988 | /// [`receive_filter_upload`]: #method.receive_filter_upload |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 989 | pub async fn get_filter(&self, filter_name: &str) -> StoreResult<Option<String>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 990 | let filter = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 991 | .state_store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 992 | .get_kv_data(StateStoreDataKey::Filter(filter_name)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 993 | .await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 994 | .map(|d| d.into_filter().expect("State store data not a filter")); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 995 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 996 | Ok(filter) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 997 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 998 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 999 | /// Get a to-device request that will share a room key with users in a room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1000 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1001 | pub async fn share_room_key(&self, room_id: &RoomId) -> Result<Vec<Arc<ToDeviceRequest>>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1002 | match self.olm_machine().await.as_ref() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1003 | Some(o) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1004 | let Some(room) = self.get_room(room_id) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1005 | return Err(Error::InsufficientData); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1006 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1007 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1008 | let history_visibility = room.history_visibility_or_default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1009 | let Some(room_encryption_event) = room.encryption_settings() else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1010 | return Err(Error::EncryptionNotEnabled); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1011 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1012 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1013 | // Don't share the group session with members that are invited |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1014 | // if the history visibility is set to `Joined` |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1015 | let filter = if history_visibility == HistoryVisibility::Joined { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1016 | RoomMemberships::JOIN |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1017 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1018 | RoomMemberships::ACTIVE |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1019 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1020 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1021 | let members = self.state_store.get_user_ids(room_id, filter).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1022 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1023 | let Some(settings) = EncryptionSettings::from_possibly_redacted( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1024 | room_encryption_event, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1025 | history_visibility, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1026 | self.room_key_recipient_strategy.clone(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1027 | ) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1028 | return Err(Error::EncryptionNotEnabled); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1029 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1030 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1031 | Ok(o.share_room_key(room_id, members.iter().map(Deref::deref), settings).await?) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1032 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1033 | None => panic!("Olm machine wasn't started"), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1034 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1035 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1036 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1037 | /// Get the room with the given room id. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1038 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1039 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1040 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1041 | /// * `room_id` - The id of the room that should be fetched. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1042 | pub fn get_room(&self, room_id: &RoomId) -> Option<Room> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1043 | self.state_store.room(room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1044 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1045 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1046 | /// Forget the room with the given room ID. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1047 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1048 | /// The room will be dropped from the room list and the store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1049 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1050 | /// # Arguments |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1051 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1052 | /// * `room_id` - The id of the room that should be forgotten. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1053 | pub async fn forget_room(&self, room_id: &RoomId) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1054 | // Forget the room in the state store. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1055 | self.state_store.forget_room(room_id).await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1056 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1057 | // Remove the room in the event cache store too. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1058 | match self.event_cache_store().lock().await? { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1059 | // If the lock is clear, we can do the operation as expected. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1060 | // If the lock is dirty, we can ignore to refresh the state, we just need to remove a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1061 | // room. Also, we must not mark the lock as non-dirty because other operations may be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1062 | // critical and may need to refresh the `EventCache`' state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1063 | EventCacheStoreLockState::Clean(guard) | EventCacheStoreLockState::Dirty(guard) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1064 | guard.remove_room(room_id).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1065 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1066 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1067 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1068 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1069 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1070 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1071 | /// Get the olm machine. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1072 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1073 | pub async fn olm_machine(&self) -> RwLockReadGuard<'_, Option<OlmMachine>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1074 | self.olm_machine.read().await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1075 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1076 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1077 | /// Get the push rules. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1078 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1079 | /// Gets the push rules previously processed, otherwise get them from the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1080 | /// store. As a fallback, uses [`Ruleset::server_default`] if the user |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1081 | /// is logged in. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1082 | pub(crate) async fn get_push_rules( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1083 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1084 | global_account_data_processor: &processors::account_data::Global, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1085 | ) -> Result<Ruleset> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1086 | let _timer = timer!(Level::TRACE, "get_push_rules"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1087 | if let Some(event) = global_account_data_processor |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1088 | .push_rules() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1089 | .and_then(|ev| ev.deserialize_as_unchecked::<PushRulesEvent>().ok()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1090 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1091 | Ok(event.content.global) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1092 | } else if let Some(event) = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1093 | .state_store |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1094 | .get_account_data_event_static::<PushRulesEventContent>() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1095 | .await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1096 | .and_then(|ev| ev.deserialize().ok()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1097 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1098 | Ok(event.content.global) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1099 | } else if let Some(session_meta) = self.state_store.session_meta() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1100 | Ok(Ruleset::server_default(&session_meta.user_id)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1101 | } else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1102 | Ok(Ruleset::new()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1103 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1104 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1105 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1106 | /// Returns a subscriber that publishes an event every time the ignore user |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1107 | /// list changes |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1108 | pub fn subscribe_to_ignore_user_list_changes(&self) -> Subscriber<Vec<String>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1109 | self.ignore_user_list_changes.subscribe() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1110 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1111 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1112 | /// Returns a new receiver that gets future room info notable updates. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1113 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1114 | /// Learn more by reading the [`RoomInfoNotableUpdate`] type. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1115 | pub fn room_info_notable_update_receiver(&self) -> broadcast::Receiver<RoomInfoNotableUpdate> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1116 | self.state_store.room_info_notable_update_sender.subscribe() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1117 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1118 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1119 | /// Checks whether the provided `user_id` belongs to an ignored user. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1120 | pub async fn is_user_ignored(&self, user_id: &UserId) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1121 | match self.state_store.get_account_data_event_static::<IgnoredUserListEventContent>().await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1122 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1123 | Ok(Some(raw_ignored_user_list)) => match raw_ignored_user_list.deserialize() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1124 | Ok(current_ignored_user_list) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1125 | current_ignored_user_list.content.ignored_users.contains_key(user_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1126 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1127 | Err(error) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1128 | warn!(?error, "Failed to deserialize the ignored user list event"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1129 | false |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1130 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1131 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1132 | Ok(None) => false, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1133 | Err(error) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1134 | warn!(?error, "Could not get the ignored user list from the state store"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1135 | false |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1136 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1137 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1138 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1139 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1140 | /// Check the record of whether we are waiting for an [MSC4268] key bundle |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1141 | /// for the given room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1142 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1143 | /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1144 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1145 | pub async fn get_pending_key_bundle_details_for_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1146 | &self, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1147 | room_id: &RoomId, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1148 | ) -> Result<Option<RoomPendingKeyBundleDetails>> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1149 | let result = match self.olm_machine().await.as_ref() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1150 | Some(machine) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1151 | machine.store().get_pending_key_bundle_details_for_room(room_id).await? |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1152 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1153 | None => None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1154 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1155 | Ok(result) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1156 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1157 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1158 | /// Close all stores, releasing database connections and file locks. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1159 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1160 | /// In-flight operations will complete before this returns. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1161 | pub async fn close_stores(&self) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1162 | self.state_store.close().await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1163 | self.event_cache_store.close().await.map_err(Error::EventCacheStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1164 | self.media_store.close().await.map_err(Error::MediaStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1165 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1166 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1167 | self.crypto_store.close().await.map_err(Error::CryptoStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1168 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1169 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1170 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1171 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1172 | /// Reopen all stores after a close, re-opening database connections. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1173 | pub async fn reopen_stores(&self) -> Result<()> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1174 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1175 | self.crypto_store.reopen().await.map_err(Error::CryptoStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1176 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1177 | self.media_store.reopen().await.map_err(Error::MediaStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1178 | self.event_cache_store.reopen().await.map_err(Error::EventCacheStore)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1179 | self.state_store.reopen().await?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1180 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1181 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1182 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1183 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1184 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1185 | /// Represent the `required_state` values sent by a sync request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1186 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1187 | /// This is useful to track what state events have been requested when handling |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1188 | /// a response. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1189 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1190 | /// For example, if a sync requests the `m.room.encryption` state event, and the |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1191 | /// server replies with nothing, if means the room **is not** encrypted. Without |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1192 | /// knowing which state event was required by the sync, it is impossible to |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1193 | /// interpret the absence of state event from the server as _the room's |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1194 | /// encryption state is **not encrypted**_ or _the room's encryption state is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1195 | /// **unknown**_. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1196 | #[derive(Debug, Default)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1197 | pub struct RequestedRequiredStates { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1198 | default: Vec<(StateEventType, String)>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1199 | for_rooms: HashMap<OwnedRoomId, Vec<(StateEventType, String)>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1200 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1201 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1202 | impl RequestedRequiredStates { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1203 | /// Create a new `RequestedRequiredStates`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1204 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1205 | /// `default` represents the `required_state` value for all rooms. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1206 | /// `for_rooms` is the `required_state` per room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1207 | pub fn new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1208 | default: Vec<(StateEventType, String)>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1209 | for_rooms: HashMap<OwnedRoomId, Vec<(StateEventType, String)>>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1210 | ) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1211 | Self { default, for_rooms } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1212 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1213 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1214 | /// Get the `required_state` value for a specific room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1215 | pub fn for_room(&self, room_id: &RoomId) -> &[(StateEventType, String)] { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1216 | self.for_rooms.get(room_id).unwrap_or(&self.default) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1217 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1218 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1219 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1220 | impl From<&v5::Request> for RequestedRequiredStates { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1221 | fn from(request: &v5::Request) -> Self { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1222 | // The following information is missing in the MSC4186 at the time of writing |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1223 | // (2025-03-12) but: the `required_state`s from all lists and from all room |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1224 | // subscriptions are combined by doing an union. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1225 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1226 | // Thus, we can do the same here, put the union in `default` and keep |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1227 | // `for_rooms` empty. The `Self::for_room` will automatically do the fallback. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1228 | let mut default = BTreeSet::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1229 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1230 | for list in request.lists.values() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1231 | default.extend(BTreeSet::from_iter(list.room_details.required_state.iter().cloned())); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1232 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1233 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1234 | for room_subscription in request.room_subscriptions.values() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1235 | default.extend(BTreeSet::from_iter(room_subscription.required_state.iter().cloned())); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1236 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1237 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1238 | Self { default: default.into_iter().collect(), for_rooms: HashMap::new() } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1239 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1240 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1241 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1242 | /// An enum that defines what the [`BaseClient`] should consider a DM room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1243 | #[derive(Debug, Clone, Default)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1244 | #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1245 | pub enum DmRoomDefinition { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1246 | /// Standard Matrix spec definition: a room linked to a user in an |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1247 | /// `m.direct` event. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1248 | #[default] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1249 | MatrixSpec, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1250 | /// A room that is direct, as per the spec but also contains at most 2 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1251 | /// active members. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1252 | TwoMembers, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1253 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1254 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1255 | #[cfg(test)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1256 | mod tests { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1257 | use std::collections::HashMap; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1258 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1259 | use assert_matches2::assert_let; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1260 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1261 | use assert_matches2::assert_matches; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1262 | use futures_util::FutureExt as _; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1263 | use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1264 | use matrix_sdk_test::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1265 | BOB, InvitedRoomBuilder, LeftRoomBuilder, SyncResponseBuilder, async_test, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1266 | event_factory::EventFactory, ruma_response_from_json, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1267 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1268 | use ruma::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1269 | api::client::{self as api, sync::sync_events::v5}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1270 | event_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1271 | events::{StateEventType, room::member::MembershipState}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1272 | room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1273 | serde::Raw, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1274 | user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1275 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1276 | use serde_json::{json, value::to_raw_value}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1277 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1278 | use super::{BaseClient, RequestedRequiredStates}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1279 | use crate::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1280 | DmRoomDefinition, RoomDisplayName, RoomState, SessionMeta, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1281 | client::ThreadingSupport, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1282 | store::{RoomLoadSettings, StateStoreExt, StoreConfig}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1283 | test_utils::logged_in_base_client, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1284 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1285 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1286 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1287 | fn test_requested_required_states() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1288 | let room_id_0 = room_id!("!r0"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1289 | let room_id_1 = room_id!("!r1"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1290 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1291 | let requested_required_states = RequestedRequiredStates::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1292 | vec![(StateEventType::RoomAvatar, "".to_owned())], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1293 | HashMap::from([( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1294 | room_id_0.to_owned(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1295 | vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1296 | (StateEventType::RoomMember, "foo".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1297 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1298 | ], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1299 | )]), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1300 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1301 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1302 | // A special set of state events exists for `room_id_0`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1303 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1304 | requested_required_states.for_room(room_id_0), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1305 | &[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1306 | (StateEventType::RoomMember, "foo".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1307 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1308 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1309 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1310 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1311 | // No special list for `room_id_1`, it should return the defaults. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1312 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1313 | requested_required_states.for_room(room_id_1), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1314 | &[(StateEventType::RoomAvatar, "".to_owned()),] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1315 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1316 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1317 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1318 | #[test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1319 | fn test_requested_required_states_from_sync_v5_request() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1320 | let room_id_0 = room_id!("!r0"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1321 | let room_id_1 = room_id!("!r1"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1322 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1323 | // Empty request. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1324 | let mut request = v5::Request::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1325 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1326 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1327 | let requested_required_states = RequestedRequiredStates::from(&request); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1328 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1329 | assert!(requested_required_states.default.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1330 | assert!(requested_required_states.for_rooms.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1331 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1332 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1333 | // One list. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1334 | request.lists.insert("foo".to_owned(), { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1335 | let mut list = v5::request::List::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1336 | list.room_details.required_state = vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1337 | (StateEventType::RoomAvatar, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1338 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1339 | ]; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1340 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1341 | list |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1342 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1343 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1344 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1345 | let requested_required_states = RequestedRequiredStates::from(&request); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1346 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1347 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1348 | requested_required_states.default, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1349 | &[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1350 | (StateEventType::RoomAvatar, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1351 | (StateEventType::RoomEncryption, "".to_owned()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1352 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1353 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1354 | assert!(requested_required_states.for_rooms.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1355 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1356 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1357 | // Two lists. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1358 | request.lists.insert("bar".to_owned(), { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1359 | let mut list = v5::request::List::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1360 | list.room_details.required_state = vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1361 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1362 | (StateEventType::RoomName, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1363 | ]; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1364 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1365 | list |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1366 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1367 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1368 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1369 | let requested_required_states = RequestedRequiredStates::from(&request); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1370 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1371 | // Union of the state events. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1372 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1373 | requested_required_states.default, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1374 | &[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1375 | (StateEventType::RoomAvatar, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1376 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1377 | (StateEventType::RoomName, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1378 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1379 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1380 | assert!(requested_required_states.for_rooms.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1381 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1382 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1383 | // One room subscription. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1384 | request.room_subscriptions.insert(room_id_0.to_owned(), { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1385 | let mut room_subscription = v5::request::RoomSubscription::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1386 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1387 | room_subscription.required_state = vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1388 | (StateEventType::RoomJoinRules, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1389 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1390 | ]; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1391 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1392 | room_subscription |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1393 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1394 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1395 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1396 | let requested_required_states = RequestedRequiredStates::from(&request); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1397 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1398 | // Union of state events, all in `default`, still nothing in `for_rooms`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1399 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1400 | requested_required_states.default, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1401 | &[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1402 | (StateEventType::RoomAvatar, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1403 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1404 | (StateEventType::RoomJoinRules, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1405 | (StateEventType::RoomName, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1406 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1407 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1408 | assert!(requested_required_states.for_rooms.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1409 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1410 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1411 | // Two room subscriptions. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1412 | request.room_subscriptions.insert(room_id_1.to_owned(), { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1413 | let mut room_subscription = v5::request::RoomSubscription::default(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1414 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1415 | room_subscription.required_state = vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1416 | (StateEventType::RoomName, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1417 | (StateEventType::RoomTopic, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1418 | ]; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1419 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1420 | room_subscription |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1421 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1422 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1423 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1424 | let requested_required_states = RequestedRequiredStates::from(&request); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1425 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1426 | // Union of state events, all in `default`, still nothing in `for_rooms`. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1427 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1428 | requested_required_states.default, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1429 | &[ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1430 | (StateEventType::RoomAvatar, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1431 | (StateEventType::RoomEncryption, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1432 | (StateEventType::RoomJoinRules, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1433 | (StateEventType::RoomName, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1434 | (StateEventType::RoomTopic, "".to_owned()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1435 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1436 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1437 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1438 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1439 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1440 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1441 | async fn test_invite_after_leaving() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1442 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1443 | let room_id = room_id!("!test:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1444 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1445 | let client = logged_in_base_client(Some(user_id)).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1446 | let f = EventFactory::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1447 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1448 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1449 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1450 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1451 | .add_left_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1452 | LeftRoomBuilder::new(room_id).add_timeline_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1453 | EventFactory::new() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1454 | .member(user_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1455 | .membership(MembershipState::Leave) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1456 | .display_name("Alice") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1457 | .event_id(event_id!("$994173582443PhrSn:example.org")), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1458 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1459 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1460 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1461 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1462 | assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1463 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1464 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1465 | .add_invited_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1466 | InvitedRoomBuilder::new(room_id).add_state_event( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1467 | f.member(user_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1468 | .sender(user_id!("@example:example.org")) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1469 | .membership(MembershipState::Invite) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1470 | .display_name("Alice"), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1471 | ), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1472 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1473 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1474 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1475 | assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1476 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1477 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1478 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1479 | async fn test_invite_displayname() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1480 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1481 | let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1482 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1483 | let client = logged_in_base_client(Some(user_id)).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1484 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1485 | let response = ruma_response_from_json(&json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1486 | "next_batch": "asdkl;fjasdkl;fj;asdkl;f", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1487 | "device_one_time_keys_count": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1488 | "signed_curve25519": 50u64 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1489 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1490 | "device_unused_fallback_key_types": [ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1491 | "signed_curve25519" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1492 | ], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1493 | "rooms": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1494 | "invite": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1495 | "!ithpyNKDtmhneaTQja:example.org": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1496 | "invite_state": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1497 | "events": [ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1498 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1499 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1500 | "creator": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1501 | "room_version": "9" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1502 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1503 | "sender": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1504 | "state_key": "", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1505 | "type": "m.room.create" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1506 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1507 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1508 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1509 | "join_rule": "invite" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1510 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1511 | "sender": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1512 | "state_key": "", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1513 | "type": "m.room.join_rules" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1514 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1515 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1516 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1517 | "algorithm": "m.megolm.v1.aes-sha2" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1518 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1519 | "sender": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1520 | "state_key": "", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1521 | "type": "m.room.encryption" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1522 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1523 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1524 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1525 | "avatar_url": "mxc://example.org/dcBBDwuWEUrjfrOchvkirUST", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1526 | "displayname": "Kyra", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1527 | "membership": "join" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1528 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1529 | "sender": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1530 | "state_key": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1531 | "type": "m.room.member" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1532 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1533 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1534 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1535 | "avatar_url": "mxc://example.org/ABFEXSDrESxovWwEnCYdNcHT", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1536 | "displayname": "alice", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1537 | "is_direct": true, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1538 | "membership": "invite" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1539 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1540 | "origin_server_ts": 1650878657984u64, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1541 | "sender": "@test:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1542 | "state_key": "@alice:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1543 | "type": "m.room.member", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1544 | "unsigned": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1545 | "age": 14u64 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1546 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1547 | "event_id": "$fLDqltg9Puj-kWItLSFVHPGN4YkgpYQf2qImPzdmgrE" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1548 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1549 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1550 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1551 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1552 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1553 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1554 | })); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1555 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1556 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1557 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1558 | let room = client.get_room(room_id).expect("Room not found"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1559 | assert_eq!(room.state(), RoomState::Invited); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1560 | assert_eq!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1561 | room.compute_display_name().await.expect("fetching display name failed").into_inner(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1562 | RoomDisplayName::Calculated("Kyra".to_owned()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1563 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1564 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1565 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1566 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1567 | async fn test_deserialization_failure() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1568 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1569 | let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1570 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1571 | let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1572 | StoreConfig::new(CrossProcessLockConfig::SingleProcess), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1573 | ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1574 | DmRoomDefinition::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1575 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1576 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1577 | .activate( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1578 | SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1579 | RoomLoadSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1580 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1581 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1582 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1583 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1584 | .unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1585 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1586 | let response = ruma_response_from_json(&json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1587 | "next_batch": "asdkl;fjasdkl;fj;asdkl;f", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1588 | "rooms": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1589 | "join": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1590 | "!ithpyNKDtmhneaTQja:example.org": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1591 | "state": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1592 | "events": [ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1593 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1594 | "invalid": "invalid", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1595 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1596 | { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1597 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1598 | "name": "The room name" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1599 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1600 | "event_id": "$143273582443PhrSn:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1601 | "origin_server_ts": 1432735824653u64, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1602 | "room_id": "!jEsUZKDJdhlrceRyVU:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1603 | "sender": "@example:example.org", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1604 | "state_key": "", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1605 | "type": "m.room.name", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1606 | "unsigned": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1607 | "age": 1234 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1608 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1609 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1610 | ] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1611 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1612 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1613 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1614 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1615 | })); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1616 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1617 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1618 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1619 | .state_store() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1620 | .get_state_event_static::<ruma::events::room::name::RoomNameEventContent>(room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1621 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1622 | .expect("Failed to fetch state event") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1623 | .expect("State event not found") |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1624 | .deserialize() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1625 | .expect("Failed to deserialize state event"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1626 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1627 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1628 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1629 | async fn test_invited_members_arent_ignored() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1630 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1631 | let inviter_user_id = user_id!("@bob:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1632 | let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1633 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1634 | let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1635 | StoreConfig::new(CrossProcessLockConfig::SingleProcess), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1636 | ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1637 | DmRoomDefinition::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1638 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1639 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1640 | .activate( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1641 | SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1642 | RoomLoadSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1643 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1644 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1645 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1646 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1647 | .unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1648 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1649 | // Preamble: let the SDK know about the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1650 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1651 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1652 | .add_joined_room(matrix_sdk_test::JoinedRoomBuilder::new(room_id)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1653 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1654 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1655 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1656 | // When I process the result of a /members request that only contains an invited |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1657 | // member, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1658 | let request = api::membership::get_member_events::v3::Request::new(room_id.to_owned()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1659 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1660 | let raw_member_event = json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1661 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1662 | "avatar_url": "mxc://localhost/fewjilfewjil42", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1663 | "displayname": "Invited Alice", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1664 | "membership": "invite" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1665 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1666 | "event_id": "$151800140517rfvjc:localhost", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1667 | "origin_server_ts": 151800140, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1668 | "room_id": room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1669 | "sender": inviter_user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1670 | "state_key": user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1671 | "type": "m.room.member", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1672 | "unsigned": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1673 | "age": 13374242, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1674 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1675 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1676 | let response = api::membership::get_member_events::v3::Response::new(vec![Raw::from_json( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1677 | to_raw_value(&raw_member_event).unwrap(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1678 | )]); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1679 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1680 | // It's correctly processed, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1681 | client.receive_all_members(room_id, &request, &response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1682 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1683 | let room = client.get_room(room_id).unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1684 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1685 | // And I can get the invited member display name and avatar. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1686 | let member = room.get_member(user_id).await.expect("ok").expect("exists"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1687 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1688 | assert_eq!(member.user_id(), user_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1689 | assert_eq!(member.display_name().unwrap(), "Invited Alice"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1690 | assert_eq!(member.avatar_url().unwrap().to_string(), "mxc://localhost/fewjilfewjil42"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1691 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1692 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1693 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1694 | async fn test_reinvited_members_get_a_display_name() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1695 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1696 | let inviter_user_id = user_id!("@bob:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1697 | let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1698 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1699 | let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1700 | StoreConfig::new(CrossProcessLockConfig::SingleProcess), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1701 | ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1702 | DmRoomDefinition::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1703 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1704 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1705 | .activate( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1706 | SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1707 | RoomLoadSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1708 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1709 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1710 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1711 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1712 | .unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1713 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1714 | // Preamble: let the SDK know about the room, and that the invited user left it. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1715 | let f = EventFactory::new().sender(user_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1716 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1717 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1718 | .add_joined_room( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1719 | matrix_sdk_test::JoinedRoomBuilder::new(room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1720 | .add_state_event(f.member(user_id).leave()), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1721 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1722 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1723 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1724 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1725 | // Now, say that the user has been re-invited. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1726 | let request = api::membership::get_member_events::v3::Request::new(room_id.to_owned()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1727 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1728 | let raw_member_event = json!({ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1729 | "content": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1730 | "avatar_url": "mxc://localhost/fewjilfewjil42", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1731 | "displayname": "Invited Alice", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1732 | "membership": "invite" |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1733 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1734 | "event_id": "$151800140517rfvjc:localhost", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1735 | "origin_server_ts": 151800140, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1736 | "room_id": room_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1737 | "sender": inviter_user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1738 | "state_key": user_id, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1739 | "type": "m.room.member", |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1740 | "unsigned": { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1741 | "age": 13374242, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1742 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1743 | }); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1744 | let response = api::membership::get_member_events::v3::Response::new(vec![Raw::from_json( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1745 | to_raw_value(&raw_member_event).unwrap(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1746 | )]); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1747 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1748 | // It's correctly processed, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1749 | client.receive_all_members(room_id, &request, &response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1750 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1751 | let room = client.get_room(room_id).unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1752 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1753 | // And I can get the invited member display name and avatar. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1754 | let member = room.get_member(user_id).await.expect("ok").expect("exists"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1755 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1756 | assert_eq!(member.user_id(), user_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1757 | assert_eq!(member.display_name().unwrap(), "Invited Alice"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1758 | assert_eq!(member.avatar_url().unwrap().to_string(), "mxc://localhost/fewjilfewjil42"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1759 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1760 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1761 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1762 | async fn test_ignored_user_list_changes() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1763 | let user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1764 | let client = BaseClient::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1765 | StoreConfig::new(CrossProcessLockConfig::SingleProcess), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1766 | ThreadingSupport::Disabled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1767 | DmRoomDefinition::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1768 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1769 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1770 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1771 | .activate( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1772 | SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1773 | RoomLoadSettings::default(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1774 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1775 | None, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1776 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1777 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1778 | .unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1779 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1780 | let mut subscriber = client.subscribe_to_ignore_user_list_changes(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1781 | assert!(subscriber.next().now_or_never().is_none()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1782 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1783 | let f = EventFactory::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1784 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1785 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1786 | .add_global_account_data(f.ignored_user_list([(*BOB).into()])) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1787 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1788 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1789 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1790 | assert_let!(Some(ignored) = subscriber.next().await); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1791 | assert_eq!(ignored, [BOB.to_string()]); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1792 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1793 | // Receive the same response. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1794 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1795 | .add_global_account_data(f.ignored_user_list([(*BOB).into()])) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1796 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1797 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1798 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1799 | // No changes in the ignored list. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1800 | assert!(subscriber.next().now_or_never().is_none()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1801 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1802 | // Now remove Bob from the ignored list. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1803 | let response = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1804 | sync_builder.add_global_account_data(f.ignored_user_list([])).build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1805 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1806 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1807 | assert_let!(Some(ignored) = subscriber.next().await); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1808 | assert!(ignored.is_empty()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1809 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1810 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1811 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1812 | async fn test_is_user_ignored() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1813 | let ignored_user_id = user_id!("@alice:example.org"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1814 | let client = logged_in_base_client(None).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1815 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1816 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1817 | let f = EventFactory::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1818 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1819 | .add_global_account_data(f.ignored_user_list([ignored_user_id.to_owned()])) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1820 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1821 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1822 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1823 | assert!(client.is_user_ignored(ignored_user_id).await); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1824 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1825 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1826 | #[cfg(feature = "e2e-encryption")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1827 | #[async_test] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1828 | async fn test_invite_details_are_set() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1829 | let user_id = user_id!("@alice:localhost"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1830 | let client = logged_in_base_client(Some(user_id)).await; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1831 | let known_room_id = room_id!("!invited:localhost"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1832 | let unknown_room_id = room_id!("!unknown:localhost"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1833 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1834 | let mut sync_builder = SyncResponseBuilder::new(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1835 | let response = sync_builder |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1836 | .add_invited_room(InvitedRoomBuilder::new(known_room_id)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1837 | .build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1838 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1839 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1840 | // Let us first check the initial state, we should have a room in the invite |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1841 | // state. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1842 | let invited_room = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1843 | .get_room(known_room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1844 | .expect("The sync should have created a room in the invited state"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1845 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1846 | assert_eq!(invited_room.state(), RoomState::Invited); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1847 | assert!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1848 | client.get_pending_key_bundle_details_for_room(known_room_id).await.unwrap().is_none() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1849 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1850 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1851 | // Now we join the room. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1852 | let joined_room = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1853 | .room_joined(known_room_id, Some(user_id.to_owned())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1854 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1855 | .expect("We should be able to mark a room as joined"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1856 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1857 | // Yup, we now have some invite details. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1858 | assert_eq!(joined_room.state(), RoomState::Joined); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1859 | assert_matches!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1860 | client.get_pending_key_bundle_details_for_room(known_room_id).await, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1861 | Ok(Some(details)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1862 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1863 | assert_eq!(details.inviter, user_id); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1864 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1865 | // If we didn't know about the room before the join, we assume that there wasn't |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1866 | // an invite and we don't record the timestamp. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1867 | assert!(client.get_room(unknown_room_id).is_none()); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1868 | let unknown_room = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1869 | .room_joined(unknown_room_id, Some(user_id.to_owned())) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1870 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1871 | .expect("We should be able to mark a room as joined"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1872 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1873 | assert_eq!(unknown_room.state(), RoomState::Joined); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1874 | assert!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1875 | client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1876 | .get_pending_key_bundle_details_for_room(unknown_room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1877 | .await |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1878 | .unwrap() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1879 | .is_none() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1880 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1881 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1882 | sync_builder.clear(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1883 | let response = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1884 | sync_builder.add_left_room(LeftRoomBuilder::new(known_room_id)).build_sync_response(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1885 | client.receive_sync_response(response).await.unwrap(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1886 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1887 | // Now that we left the room, we shouldn't have any details anymore. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1888 | let left_room = client |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1889 | .get_room(known_room_id) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1890 | .expect("The sync should have created a room in the invited state"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1891 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1892 | assert_eq!(left_room.state(), RoomState::Left); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1893 | assert!( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1894 | client.get_pending_key_bundle_details_for_room(known_room_id).await.unwrap().is_none() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1895 | ); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1896 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1897 | } |