From ed0974eba328d6d0b04100447c74ecfd1f5897f1 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Wed, 23 Feb 2022 19:38:35 +0100 Subject: [PATCH] re-integrate sled and indexeddb stores into base --- crates/matrix-sdk-sled/src/state_store.rs | 6 +- crates/matrix-sdk/Cargo.toml | 12 ++- crates/matrix-sdk/examples/autojoin.rs | 4 +- crates/matrix-sdk/examples/command_bot.rs | 4 +- crates/matrix-sdk/src/client.rs | 13 ++- crates/matrix-sdk/src/config/client.rs | 118 ++++++++++++++-------- crates/matrix-sdk/src/config/mod.rs | 2 +- crates/matrix-sdk/src/error.rs | 7 ++ 8 files changed, 103 insertions(+), 63 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 3e1d19f67..e7b841f6b 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -311,10 +311,10 @@ impl SledStore { }) } - pub fn open() -> Result { - let db = Config::new().temporary(true).open()?; + pub fn open() -> StoreResult { + let db = Config::new().temporary(true).open().map_err(|e| StoreError::Backend(anyhow!(e)))?; - SledStore::open_helper(db, None, None) + SledStore::open_helper(db, None, None).map_err(|e| e.into()) } pub fn open_with_passphrase(path: impl AsRef, passphrase: &str) -> Result { diff --git a/crates/matrix-sdk/Cargo.toml b/crates/matrix-sdk/Cargo.toml index e45067730..16c83ae5c 100644 --- a/crates/matrix-sdk/Cargo.toml +++ b/crates/matrix-sdk/Cargo.toml @@ -24,12 +24,12 @@ default = [ "native-tls" ] -indexeddb_stores = [] +indexeddb_stores = ["matrix-sdk-indexeddb"] encryption = ["matrix-sdk-base/encryption"] qrcode = ["encryption", "matrix-sdk-base/qrcode"] # TODO merge those two sled features -sled_state_store = [] -sled_cryptostore = [] +sled_state_store = ["matrix-sdk-sled"] +sled_cryptostore = ["matrix-sdk-sled", "encryption"] markdown = ["ruma/markdown"] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] @@ -67,7 +67,11 @@ thiserror = "1.0.25" tracing = "0.1.26" url = "2.2.2" zeroize = "1.3.0" -async-stream = "0.3.2" +async-stream = "0.3.2" + +# +matrix-sdk-sled = { path = "../matrix-sdk-sled", optional = true } +matrix-sdk-indexeddb = { path = "../matrix-sdk-indexeddb", optional = true } [dependencies.image] version = "0.24.0" diff --git a/crates/matrix-sdk/examples/autojoin.rs b/crates/matrix-sdk/examples/autojoin.rs index 7152db9d6..b0be0dd94 100644 --- a/crates/matrix-sdk/examples/autojoin.rs +++ b/crates/matrix-sdk/examples/autojoin.rs @@ -1,7 +1,7 @@ use std::{env, process::exit}; use matrix_sdk::{ - config::{ClientConfig, SyncSettings}, + config::{ClientConfig, default_store_with_name, SyncSettings}, room::Room, ruma::events::room::member::StrippedRoomMemberEvent, Client, @@ -48,7 +48,7 @@ async fn login_and_sync( let mut home = dirs::home_dir().expect("no home directory found"); home.push("autojoin_bot"); - let client_config = ClientConfig::new().store_path(home); + let client_config = ClientConfig::new()?.state_store(default_store_with_name(home.to_str().expect("home dir path must be utf-8"), None)?); let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL"); let client = Client::new_with_config(homeserver_url, client_config).await.unwrap(); diff --git a/crates/matrix-sdk/examples/command_bot.rs b/crates/matrix-sdk/examples/command_bot.rs index 2a17cedd1..d63fb9ca3 100644 --- a/crates/matrix-sdk/examples/command_bot.rs +++ b/crates/matrix-sdk/examples/command_bot.rs @@ -1,7 +1,7 @@ use std::{env, process::exit}; use matrix_sdk::{ - config::{ClientConfig, SyncSettings}, + config::{ClientConfig, SyncSettings, default_store_with_name}, room::Room, ruma::events::room::message::{ MessageType, RoomMessageEventContent, SyncRoomMessageEvent, TextMessageEventContent, @@ -41,7 +41,7 @@ async fn login_and_sync( let mut home = dirs::home_dir().expect("no home directory found"); home.push("party_bot"); - let client_config = ClientConfig::new().store_path(home); + let client_config = ClientConfig::new()?.state_store(default_store_with_name(home.to_str().expect("home dir path must be utf-8"), None)?); let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL"); // create a new Client with the given homeserver url and config diff --git a/crates/matrix-sdk/src/client.rs b/crates/matrix-sdk/src/client.rs index 4f15936f7..fe10a9210 100644 --- a/crates/matrix-sdk/src/client.rs +++ b/crates/matrix-sdk/src/client.rs @@ -166,7 +166,7 @@ impl Client { /// /// * `homeserver_url` - The homeserver that the client should connect to. pub async fn new(homeserver_url: Url) -> Result { - let config = ClientConfig::new(); + let config = ClientConfig::new()?; Client::new_with_config(homeserver_url, config).await } @@ -245,7 +245,7 @@ impl Client { /// /// [spec]: https://spec.matrix.org/unstable/client-server-api/#well-known-uri pub async fn new_from_user_id(user_id: &UserId) -> Result { - let config = ClientConfig::new(); + let config = ClientConfig::new()?; Client::new_from_user_id_with_config(user_id, config).await } @@ -2371,7 +2371,7 @@ pub(crate) mod test { device_id: device_id!("DEVICEID").to_owned(), }; let homeserver = url::Url::parse(&mockito::server_url()).unwrap(); - let config = ClientConfig::new().request_config(RequestConfig::new().disable_retry()); + let config = ClientConfig::new().unwrap().request_config(RequestConfig::new().disable_retry()); let client = Client::new_with_config(homeserver, config).await.unwrap(); client.restore_login(session).await.unwrap(); @@ -2469,7 +2469,7 @@ pub(crate) mod test { #[async_test] async fn login_with_discovery() { let homeserver = Url::from_str(&mockito::server_url()).unwrap(); - let config = ClientConfig::new().use_discovery_response(); + let config = ClientConfig::new().unwrap().use_discovery_response(); let client = Client::new_with_config(homeserver, config).await.unwrap(); @@ -2489,7 +2489,7 @@ pub(crate) mod test { #[async_test] async fn login_no_discovery() { let homeserver = Url::from_str(&mockito::server_url()).unwrap(); - let config = ClientConfig::new().use_discovery_response(); + let config = ClientConfig::new().unwrap().use_discovery_response(); let client = Client::new_with_config(homeserver.clone(), config).await.unwrap(); @@ -2620,8 +2620,7 @@ pub(crate) mod test { // test store reloads with correct room state from the sled store let path = tempfile::tempdir().unwrap(); - let config = ClientConfig::default() - .store_path(path) + let config = ClientConfig::with_named_store(path.into_path().to_str().unwrap(), None).unwrap() .request_config(RequestConfig::new().disable_retry()); let joined_client = Client::new_with_config(homeserver, config).await.unwrap(); joined_client.restore_login(session).await.unwrap(); diff --git a/crates/matrix-sdk/src/config/client.rs b/crates/matrix-sdk/src/config/client.rs index e6d4b6fcc..2a5274f4a 100644 --- a/crates/matrix-sdk/src/config/client.rs +++ b/crates/matrix-sdk/src/config/client.rs @@ -20,7 +20,7 @@ use std::{ }; use http::header::InvalidHeaderValue; -use matrix_sdk_base::BaseClientConfig; +use matrix_sdk_base::{BaseClientConfig, StateStore}; use crate::{config::RequestConfig, HttpSend, Result}; @@ -35,7 +35,7 @@ use crate::{config::RequestConfig, HttpSend, Result}; /// use matrix_sdk::config::ClientConfig; /// // To pass all the request through mitmproxy set the proxy and disable SSL /// // verification -/// let client_config = ClientConfig::new() +/// let client_config = ClientConfig::new()? /// .proxy("http://localhost:8080")? /// .disable_ssl_verification(); /// # matrix_sdk::Result::<()>::Ok(()) @@ -57,7 +57,7 @@ use crate::{config::RequestConfig, HttpSend, Result}; /// .no_proxy() /// .user_agent("MyApp/v3.0"); /// -/// let client_config = ClientConfig::new() +/// let client_config = ClientConfig::new()? /// .client(Arc::new(builder.build()?)); /// # matrix_sdk::Result::<()>::Ok(()) /// ``` @@ -89,11 +89,74 @@ impl Debug for ClientConfig { } } +#[cfg(feature = "sled_state_store")] +mod store_helpers { + use super::Result; + use matrix_sdk_sled::StateStore; + + /// Build the sled Store with the default settings - as a temporary storage + /// + pub fn default_store() -> Result> { + Ok(Box::new(StateStore::open()?)) + } + + /// Build a sled store at `name` (being a relative or full path), and open + /// the store with the given passphrase (if given) for encryption + pub fn default_store_with_name(name: &str, passphrase: Option<&str>) -> Result> { + unimplemented!{} + } +} + +#[cfg(feature = "indexeddb_state_store")] +mod store_helpers { + use super::Result; + use matrix_sdk_sled::StateStore; + + /// Open the IndexedDB store with the default name, unencrypted + pub fn default_store() -> Result> { + Ok(Box::new(StateStore::default())) + } + + /// Open the indexeddb store at `name` (IndexedDB Database name), and open + /// the store with the given passphrase (if given) for encryption + pub fn default_store_with_name(name: &str, passphrase: Option<&str>) -> Result> { + unimplemented!{} + } +} + +#[cfg(not(any(feature = "indexeddb_state_store", feature = "sled_state_store")))] +mod store_helpers { + use super::Result; + use matrix_sdk_base::state::MemoryStore as StateStore; + /// Open a new in-memory StateStore + pub fn default_store() -> Result> { + Ok(Box::new(StateStore::default())) + } + + /// Alias for `default_store` - in Memory Stores are never named + pub fn default_store_with_name(_name: &str, _passphrase: Option<&str>) -> Result> { + Ok(Box::new(StateStore::default())) + } +} + +#[allow(dead_code)] +pub use store_helpers::{default_store, default_store_with_name}; + + impl ClientConfig { /// Create a new default `ClientConfig`. #[must_use] - pub fn new() -> Self { - Default::default() + pub fn new() -> Result { + let mut d = Self::default(); + d.base_config = d.base_config.state_store(default_store()?); + Ok(d) + } + + /// Create a new ClientConfig with a named state store, encrypted with the given passphrase (if any) + pub fn with_named_store(name: &str, passphrase: Option<&str>) -> Result { + let mut d = Self::default(); + d.base_config = d.base_config.state_store(default_store_with_name(name, passphrase)?); + Ok(d) } /// Set the proxy through which all the HTTP requests should go. @@ -109,7 +172,7 @@ impl ClientConfig { /// ``` /// use matrix_sdk::{Client, config::ClientConfig}; /// - /// let client_config = ClientConfig::new() + /// let client_config = ClientConfig::new()? /// .proxy("http://localhost:8080")?; /// /// # Result::<_, matrix_sdk::Error>::Ok(()) @@ -133,45 +196,12 @@ impl ClientConfig { Ok(self) } - ///// Set a custom implementation of a `StateStore`. - ///// - ///// The state store should be opened before being set. - //#[must_use] - //pub fn state_store(mut self, store: Box) -> Self { - // self.base_config = self.base_config.state_store(store); - // self - //} - - /// Set the path for storage. + /// Set a custom implementation of a `StateStore`. /// - /// # Arguments - /// - /// * `path` - The path where the stores should save data in. It is the - /// callers responsibility to make sure that the path exists. - /// - /// In the default configuration, and if the corresponding features - /// (`sled_state_store` and `sled_cryptostore`) are enabled, the client will - /// open default implementations for the crypto store and the state store. - /// It will use the given path to open the stores. If no path is provided an - /// in-memory store will be opened. - #[must_use] - pub fn store_path(mut self, path: impl AsRef) -> Self { - self.base_config = self.base_config.store_path(path); - self - } - - /// Set the passphrase to encrypt the crypto store. - /// - /// # Argument - /// - /// * `passphrase` - The passphrase that will be used to encrypt the data in - /// the cryptostore. - /// - /// This is only used if no custom cryptostore is set. - #[must_use] - pub fn passphrase(mut self, passphrase: String) -> Self { - self.base_config = self.base_config.passphrase(passphrase); - self + /// The state store should be opened before being set. + pub fn state_store(mut self, store: Box) -> Self { + self.base_config = self.base_config.state_store(store); + self } /// Set the default timeout, fail and retry behavior for all HTTP requests. diff --git a/crates/matrix-sdk/src/config/mod.rs b/crates/matrix-sdk/src/config/mod.rs index 48830e78f..fad4a7d51 100644 --- a/crates/matrix-sdk/src/config/mod.rs +++ b/crates/matrix-sdk/src/config/mod.rs @@ -20,6 +20,6 @@ mod client; mod request; mod sync; -pub use client::ClientConfig; +pub use client::{ClientConfig, default_store, default_store_with_name}; pub use request::RequestConfig; pub use sync::SyncSettings; diff --git a/crates/matrix-sdk/src/error.rs b/crates/matrix-sdk/src/error.rs index 00a0e7595..7c9f639a6 100644 --- a/crates/matrix-sdk/src/error.rs +++ b/crates/matrix-sdk/src/error.rs @@ -109,6 +109,11 @@ pub enum Error { #[error("the queried endpoint requires authentication but was called before logging in")] AuthenticationRequired, + /// Attempting to restore a session after the olm-machine has already been set up fails + #[cfg(feature = "encryption")] + #[error("The olm machine has already been initialized")] + BadCryptoStoreState, + /// An error de/serializing type for the `StateStore` #[error(transparent)] SerdeJson(#[from] JsonError), @@ -250,6 +255,8 @@ impl From for Error { #[cfg(feature = "encryption")] SdkBaseError::CryptoStore(e) => Self::CryptoStoreError(e), #[cfg(feature = "encryption")] + SdkBaseError::BadCryptoStoreState => Self::BadCryptoStoreState, + #[cfg(feature = "encryption")] SdkBaseError::OlmError(e) => Self::OlmError(e), #[cfg(feature = "encryption")] SdkBaseError::MegolmError(e) => Self::MegolmError(e),