diff --git a/bindings/matrix-sdk-ffi/src/notification_settings.rs b/bindings/matrix-sdk-ffi/src/notification_settings.rs index a4cfa77c9..81cbf6b84 100644 --- a/bindings/matrix-sdk-ffi/src/notification_settings.rs +++ b/bindings/matrix-sdk-ffi/src/notification_settings.rs @@ -319,6 +319,13 @@ impl NotificationSettings { } } + /// Check whether [MSC 4028 push rule][rule] is enabled on the homeserver. + /// + /// [rule]: https://github.com/matrix-org/matrix-spec-proposals/blob/giomfo/push_encrypted_events/proposals/4028-push-all-encrypted-events-except-for-muted-rooms.md + pub async fn can_homeserver_push_encrypted_event_to_device(&self) -> bool { + self.sdk_client.can_homeserver_push_encrypted_event_to_device().await.unwrap() + } + /// Set whether user mentions are enabled. pub async fn set_user_mention_enabled( &self, diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index c000075ba..053d68b94 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -508,6 +508,7 @@ impl ClientBuilder { http_client, base_client, self.server_versions, + None, self.respect_login_well_known, event_cache, #[cfg(feature = "e2e-encryption")] diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 87c43fd1e..e372634ed 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -235,6 +235,9 @@ pub(crate) struct ClientInner { /// The Matrix versions the server supports (well-known ones only) server_versions: OnceCell>, + /// The unstable features and their on/off state on the server + unstable_features: OnceCell>, + /// Collection of locks individual client methods might want to use, either /// to ensure that only a single call to a method happens at once or to /// deduplicate multiple calls to a method. @@ -292,6 +295,7 @@ impl ClientInner { http_client: HttpClient, base_client: BaseClient, server_versions: Option>, + unstable_features: Option>, respect_login_well_known: bool, event_cache: OnceCell, #[cfg(feature = "e2e-encryption")] encryption_settings: EncryptionSettings, @@ -305,6 +309,7 @@ impl ClientInner { base_client, locks: Default::default(), server_versions: OnceCell::new_with(server_versions), + unstable_features: OnceCell::new_with(unstable_features), typing_notice_times: Default::default(), event_handlers: Default::default(), notification_handlers: Default::default(), @@ -1401,6 +1406,67 @@ impl Client { Ok(server_versions) } + /// Fetch unstable_features from homeserver + async fn request_unstable_features(&self) -> HttpResult> { + let unstable_features: BTreeMap = self + .inner + .http_client + .send( + get_supported_versions::Request::new(), + None, + self.homeserver().to_string(), + None, + &[MatrixVersion::V1_0], + Default::default(), + ) + .await? + .unstable_features; + + Ok(unstable_features) + } + + /// Get unstable features from `request_unstable_features` or cache + /// + /// # Examples + /// + /// ```no_run + /// # use matrix_sdk::{Client, config::SyncSettings}; + /// # use url::Url; + /// # async { + /// # let homeserver = Url::parse("http://localhost:8080")?; + /// # let mut client = Client::new(homeserver).await?; + /// let unstable_features = client.unstable_features().await?; + /// let msc_x = unstable_features.get("msc_x").unwrap_or(&false); + /// # anyhow::Ok(()) }; + /// ``` + pub async fn unstable_features(&self) -> HttpResult<&BTreeMap> { + let unstable_features = self + .inner + .unstable_features + .get_or_try_init(|| self.request_unstable_features()) + .await?; + + Ok(unstable_features) + } + + /// Check whether MSC 4028 is enabled on the homeserver. + /// + /// # Examples + /// + /// ```no_run + /// # use matrix_sdk::{Client, config::SyncSettings}; + /// # use url::Url; + /// # async { + /// # let homeserver = Url::parse("http://localhost:8080")?; + /// # let mut client = Client::new(homeserver).await?; + /// let msc4028_enabled = + /// client.can_homeserver_push_encrypted_event_to_device().await?; + /// # anyhow::Ok(()) }; + /// ``` + pub async fn can_homeserver_push_encrypted_event_to_device(&self) -> HttpResult { + Ok(self.unstable_features().await?.get("org.matrix.msc4028").copied().unwrap_or(false)) + } + /// Get information of all our own devices. /// /// # Examples @@ -2006,6 +2072,7 @@ impl Client { self.inner.http_client.clone(), self.inner.base_client.clone_with_in_memory_state_store(), self.inner.server_versions.get().cloned(), + self.inner.unstable_features.get().cloned(), self.inner.respect_login_well_known, self.inner.event_cache.clone(), #[cfg(feature = "e2e-encryption")] @@ -2269,4 +2336,39 @@ pub(crate) mod tests { assert_eq!(result.avatar_url.clone().unwrap().to_string(), "mxc://example.me/someid"); assert!(!response.limited); } + + #[async_test] + async fn test_request_unstable_features() { + let server = MockServer::start().await; + let client = logged_in_client(Some(server.uri())).await; + + Mock::given(method("GET")) + .and(path("_matrix/client/versions")) + .respond_with( + ResponseTemplate::new(200).set_body_json(&*test_json::api_responses::VERSIONS), + ) + .mount(&server) + .await; + let unstable_features = client.request_unstable_features().await.unwrap(); + + assert_eq!(unstable_features.get("org.matrix.e2e_cross_signing"), Some(&true)); + assert_eq!(unstable_features, client.unstable_features().await.unwrap().clone()); + } + + #[async_test] + async fn test_can_homeserver_push_encrypted_event_to_device() { + let server = MockServer::start().await; + let client = logged_in_client(Some(server.uri())).await; + + Mock::given(method("GET")) + .and(path("_matrix/client/versions")) + .respond_with( + ResponseTemplate::new(200).set_body_json(&*test_json::api_responses::VERSIONS), + ) + .mount(&server) + .await; + + let msc4028_enabled = client.can_homeserver_push_encrypted_event_to_device().await.unwrap(); + assert!(msc4028_enabled); + } } diff --git a/testing/matrix-sdk-test/src/test_json/api_responses.rs b/testing/matrix-sdk-test/src/test_json/api_responses.rs index d53469771..8d052f209 100644 --- a/testing/matrix-sdk-test/src/test_json/api_responses.rs +++ b/testing/matrix-sdk-test/src/test_json/api_responses.rs @@ -324,7 +324,8 @@ pub static VERSIONS: Lazy = Lazy::new(|| { ], "unstable_features": { "org.matrix.label_based_filtering":true, - "org.matrix.e2e_cross_signing":true + "org.matrix.e2e_cross_signing":true, + "org.matrix.msc4028":true } }) });