matrix-sdk: Move most of the configuration to the base client.
parent
7edb42b75c
commit
ba66ee214f
|
@ -20,7 +20,7 @@ sqlite-cryptostore = ["matrix-sdk-base/sqlite-cryptostore"]
|
||||||
http = "0.2.1"
|
http = "0.2.1"
|
||||||
reqwest = "0.10.4"
|
reqwest = "0.10.4"
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
thiserror = "1.0.18"
|
thiserror = "1.0.19"
|
||||||
tracing = "0.1.14"
|
tracing = "0.1.14"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
futures-timer = { version = "3.0.2", features = ["wasm-bindgen"] }
|
futures-timer = { version = "3.0.2", features = ["wasm-bindgen"] }
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::path::Path;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -47,10 +48,7 @@ use crate::api;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use crate::VERSION;
|
use crate::VERSION;
|
||||||
use crate::{Error, EventEmitter, Result};
|
use crate::{Error, EventEmitter, Result};
|
||||||
use matrix_sdk_base::BaseClient;
|
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
|
||||||
use matrix_sdk_base::Room;
|
|
||||||
use matrix_sdk_base::Session;
|
|
||||||
use matrix_sdk_base::StateStore;
|
|
||||||
|
|
||||||
const DEFAULT_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
|
const DEFAULT_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
|
@ -104,7 +102,7 @@ pub struct ClientConfig {
|
||||||
proxy: Option<reqwest::Proxy>,
|
proxy: Option<reqwest::Proxy>,
|
||||||
user_agent: Option<HeaderValue>,
|
user_agent: Option<HeaderValue>,
|
||||||
disable_ssl_verification: bool,
|
disable_ssl_verification: bool,
|
||||||
state_store: Option<Box<dyn StateStore>>,
|
base_config: BaseClientConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
|
@ -166,7 +164,36 @@ impl ClientConfig {
|
||||||
///
|
///
|
||||||
/// The state store should be opened before being set.
|
/// The state store should be opened before being set.
|
||||||
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
|
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
|
||||||
self.state_store = Some(store);
|
self.base_config = self.base_config.state_store(store);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the path for storage.
|
||||||
|
///
|
||||||
|
/// # 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 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 no store will
|
||||||
|
/// be opened
|
||||||
|
pub fn store_path<P: AsRef<Path>>(mut self, path: P) -> 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.
|
||||||
|
pub fn passphrase(mut self, passphrase: String) -> Self {
|
||||||
|
self.base_config = self.base_config.passphrase(passphrase);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,11 +320,7 @@ impl Client {
|
||||||
|
|
||||||
let http_client = http_client.build()?;
|
let http_client = http_client.build()?;
|
||||||
|
|
||||||
let base_client = if let Some(store) = config.state_store {
|
let base_client = BaseClient::new_with_config(config.base_config)?;
|
||||||
BaseClient::new_with_state_store(store)?
|
|
||||||
} else {
|
|
||||||
BaseClient::new()?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
homeserver,
|
homeserver,
|
||||||
|
@ -371,38 +394,6 @@ impl Client {
|
||||||
self.base_client.get_left_room(room_id).await
|
self.base_client.get_left_room(room_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This allows `Client` to manually sync state with the provided `StateStore`.
|
|
||||||
///
|
|
||||||
/// Returns true when a successful `StateStore` sync has completed.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use matrix_sdk::{Client, ClientConfig, JsonStore, RoomBuilder};
|
|
||||||
/// # use matrix_sdk::api::r0::room::Visibility;
|
|
||||||
/// # use url::Url;
|
|
||||||
///
|
|
||||||
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
||||||
/// let store = JsonStore::open("path/to/store").unwrap();
|
|
||||||
/// let config = ClientConfig::new().state_store(Box::new(store));
|
|
||||||
/// let mut client = Client::new(homeserver).unwrap();
|
|
||||||
/// # use futures::executor::block_on;
|
|
||||||
/// # block_on(async {
|
|
||||||
/// let _ = client.login("name", "password", None, None).await.unwrap();
|
|
||||||
/// // returns true when a state store sync is successful
|
|
||||||
/// assert!(client.sync_with_state_store().await.unwrap());
|
|
||||||
/// // now state is restored without a request to the server
|
|
||||||
/// let mut names = vec![];
|
|
||||||
/// for r in client.joined_rooms().read().await.values() {
|
|
||||||
/// names.push(r.read().await.display_name());
|
|
||||||
/// }
|
|
||||||
/// assert_eq!(vec!["room".to_string(), "names".to_string()], names)
|
|
||||||
/// # });
|
|
||||||
/// ```
|
|
||||||
pub async fn sync_with_state_store(&self) -> Result<bool> {
|
|
||||||
Ok(self.base_client.sync_with_state_store().await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This allows `Client` to manually store `Room` state with the provided
|
/// This allows `Client` to manually store `Room` state with the provided
|
||||||
/// `StateStore`.
|
/// `StateStore`.
|
||||||
///
|
///
|
||||||
|
@ -477,8 +468,6 @@ impl Client {
|
||||||
self.send(request).await
|
self.send(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO enable this once Ruma supports proper serialization of the query
|
|
||||||
// string.
|
|
||||||
/// Join a room by `RoomId`.
|
/// Join a room by `RoomId`.
|
||||||
///
|
///
|
||||||
/// Returns a `join_room_by_id_or_alias::Response` consisting of the
|
/// Returns a `join_room_by_id_or_alias::Response` consisting of the
|
||||||
|
@ -1441,8 +1430,6 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO enable this once Ruma supports proper serialization of the query
|
|
||||||
// string.
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn join_room_by_id_or_alias() {
|
async fn join_room_by_id_or_alias() {
|
||||||
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
||||||
|
@ -1798,7 +1785,7 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EventId::try_from("$h29iv0s8:example.com").ok(),
|
EventId::try_from("$h29iv0s8:example.com").unwrap(),
|
||||||
response.event_id
|
response.event_id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,13 @@ sqlite-cryptostore = ["matrix-sdk-crypto/sqlite-cryptostore"]
|
||||||
async-trait = "0.1.31"
|
async-trait = "0.1.31"
|
||||||
serde = "1.0.110"
|
serde = "1.0.110"
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
|
zeroize = "1.1.0"
|
||||||
|
|
||||||
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
||||||
matrix-sdk-crypto = { version = "0.1.0", path = "../matrix_sdk_crypto", optional = true }
|
matrix-sdk-crypto = { version = "0.1.0", path = "../matrix_sdk_crypto", optional = true }
|
||||||
|
|
||||||
# Misc dependencies
|
# Misc dependencies
|
||||||
thiserror = "1.0.18"
|
thiserror = "1.0.19"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
|
|
|
@ -17,10 +17,10 @@ use std::collections::HashMap;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
#[cfg(feature = "encryption")]
|
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
use crate::api::r0 as api;
|
use crate::api::r0 as api;
|
||||||
|
@ -55,8 +55,10 @@ use crate::api::r0::to_device::send_event_to_device;
|
||||||
use crate::events::room::{encrypted::EncryptedEventContent, message::MessageEventContent};
|
use crate::events::room::{encrypted::EncryptedEventContent, message::MessageEventContent};
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use crate::identifiers::DeviceId;
|
use crate::identifiers::DeviceId;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use crate::JsonStore;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use matrix_sdk_crypto::{OlmMachine, OneTimeKeys};
|
use matrix_sdk_crypto::{CryptoStore, OlmMachine, OneTimeKeys};
|
||||||
|
|
||||||
pub type Token = String;
|
pub type Token = String;
|
||||||
|
|
||||||
|
@ -115,11 +117,13 @@ pub struct BaseClient {
|
||||||
///
|
///
|
||||||
/// There is a default implementation `JsonStore` that saves JSON to disk.
|
/// There is a default implementation `JsonStore` that saves JSON to disk.
|
||||||
state_store: Arc<RwLock<Option<Box<dyn StateStore>>>>,
|
state_store: Arc<RwLock<Option<Box<dyn StateStore>>>>,
|
||||||
/// Does the `Client` need to sync with the state store.
|
|
||||||
needs_state_store_sync: Arc<AtomicBool>,
|
|
||||||
|
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
olm: Arc<Mutex<Option<OlmMachine>>>,
|
olm: Arc<Mutex<Option<OlmMachine>>>,
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
cryptostore: Arc<Mutex<Option<Box<dyn CryptoStore>>>>,
|
||||||
|
store_path: Arc<Option<PathBuf>>,
|
||||||
|
store_passphrase: Arc<Zeroizing<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
|
@ -136,31 +140,99 @@ impl fmt::Debug for BaseClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration for the creation of the `BaseClient`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use matrix_sdk_base::BaseClientConfig;
|
||||||
|
///
|
||||||
|
/// let client_config = BaseClientConfig::new()
|
||||||
|
/// .store_path("/home/example/matrix-sdk-client")
|
||||||
|
/// .passphrase("test-passphrase".to_owned());
|
||||||
|
/// ```
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BaseClientConfig {
|
||||||
|
state_store: Option<Box<dyn StateStore>>,
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
crypto_store: Option<Box<dyn CryptoStore>>,
|
||||||
|
store_path: Option<PathBuf>,
|
||||||
|
passphrase: Option<Zeroizing<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
|
impl std::fmt::Debug for BaseClientConfig {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> StdResult<(), std::fmt::Error> {
|
||||||
|
fmt.debug_struct("BaseClientConfig").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BaseClientConfig {
|
||||||
|
/// Create a new default `BaseClientConfig`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a custom implementation of a `StateStore`.
|
||||||
|
///
|
||||||
|
/// The state store should be opened before being set.
|
||||||
|
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
|
||||||
|
self.state_store = Some(store);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a custom implementation of a `CryptoStore`.
|
||||||
|
///
|
||||||
|
/// The crypto store should be opened before being set.
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
pub fn crypto_store(mut self, store: Box<dyn CryptoStore>) -> Self {
|
||||||
|
self.crypto_store = Some(store);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the path for storage.
|
||||||
|
///
|
||||||
|
/// # 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 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 no store will
|
||||||
|
/// be opened
|
||||||
|
pub fn store_path<P: AsRef<Path>>(mut self, path: P) -> Self {
|
||||||
|
self.store_path = Some(path.as_ref().into());
|
||||||
|
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.
|
||||||
|
pub fn passphrase(mut self, passphrase: String) -> Self {
|
||||||
|
self.passphrase = Some(Zeroizing::new(passphrase));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseClient {
|
impl BaseClient {
|
||||||
/// Create a new client.
|
/// Create a new default client.
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `session` - An optional session if the user already has one from a
|
|
||||||
/// previous login call.
|
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
BaseClient::new_helper(None)
|
BaseClient::new_with_config(BaseClientConfig::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new client.
|
/// Create a new client.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `session` - An optional session if the user already has one from a
|
/// * `config` - An optional session if the user already has one from a
|
||||||
/// previous login call.
|
/// previous login call.
|
||||||
///
|
pub fn new_with_config(config: BaseClientConfig) -> Result<Self> {
|
||||||
/// * `store` - An open state store implementation that will be used through
|
|
||||||
/// the lifetime of the client.
|
|
||||||
pub fn new_with_state_store(store: Box<dyn StateStore>) -> Result<Self> {
|
|
||||||
BaseClient::new_helper(Some(store))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_helper(store: Option<Box<dyn StateStore>>) -> Result<Self> {
|
|
||||||
Ok(BaseClient {
|
Ok(BaseClient {
|
||||||
session: Arc::new(RwLock::new(None)),
|
session: Arc::new(RwLock::new(None)),
|
||||||
sync_token: Arc::new(RwLock::new(None)),
|
sync_token: Arc::new(RwLock::new(None)),
|
||||||
|
@ -170,10 +242,17 @@ impl BaseClient {
|
||||||
ignored_users: Arc::new(RwLock::new(Vec::new())),
|
ignored_users: Arc::new(RwLock::new(Vec::new())),
|
||||||
push_ruleset: Arc::new(RwLock::new(None)),
|
push_ruleset: Arc::new(RwLock::new(None)),
|
||||||
event_emitter: Arc::new(RwLock::new(None)),
|
event_emitter: Arc::new(RwLock::new(None)),
|
||||||
state_store: Arc::new(RwLock::new(store)),
|
state_store: Arc::new(RwLock::new(config.state_store)),
|
||||||
needs_state_store_sync: Arc::new(AtomicBool::from(true)),
|
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
olm: Arc::new(Mutex::new(None)),
|
olm: Arc::new(Mutex::new(None)),
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
cryptostore: Arc::new(Mutex::new(config.crypto_store)),
|
||||||
|
store_path: Arc::new(config.store_path),
|
||||||
|
store_passphrase: Arc::new(
|
||||||
|
config
|
||||||
|
.passphrase
|
||||||
|
.unwrap_or_else(|| Zeroizing::new("DEFAULT_PASSPHRASE".to_owned())),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,55 +276,55 @@ impl BaseClient {
|
||||||
*self.event_emitter.write().await = Some(emitter);
|
*self.event_emitter.write().await = Some(emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the state store has been loaded into the client.
|
|
||||||
pub fn is_state_store_synced(&self) -> bool {
|
|
||||||
!self.needs_state_store_sync.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When a client is provided the state store will load state from the `StateStore`.
|
/// When a client is provided the state store will load state from the `StateStore`.
|
||||||
///
|
///
|
||||||
/// Returns `true` when a state store sync has successfully completed.
|
/// Returns `true` when a state store sync has successfully completed.
|
||||||
pub async fn sync_with_state_store(&self) -> Result<bool> {
|
async fn sync_with_state_store(&self, session: &Session) -> Result<bool> {
|
||||||
let store = self.state_store.read().await;
|
let store = self.state_store.read().await;
|
||||||
if let Some(store) = store.as_ref() {
|
|
||||||
if let Some(sess) = self.session.read().await.as_ref() {
|
|
||||||
if let Some(client_state) = store.load_client_state(sess).await? {
|
|
||||||
let ClientState {
|
|
||||||
sync_token,
|
|
||||||
ignored_users,
|
|
||||||
push_ruleset,
|
|
||||||
} = client_state;
|
|
||||||
*self.sync_token.write().await = sync_token;
|
|
||||||
*self.ignored_users.write().await = ignored_users;
|
|
||||||
*self.push_ruleset.write().await = push_ruleset;
|
|
||||||
} else {
|
|
||||||
// return false and continues with a sync request then save the state and create
|
|
||||||
// and populate the files during the sync
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let AllRooms {
|
let loaded = if let Some(store) = store.as_ref() {
|
||||||
mut joined,
|
if let Some(client_state) = store.load_client_state(session).await? {
|
||||||
mut invited,
|
let ClientState {
|
||||||
mut left,
|
sync_token,
|
||||||
} = store.load_all_rooms().await?;
|
ignored_users,
|
||||||
*self.joined_rooms.write().await = joined
|
push_ruleset,
|
||||||
.drain()
|
} = client_state;
|
||||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
*self.sync_token.write().await = sync_token;
|
||||||
.collect();
|
*self.ignored_users.write().await = ignored_users;
|
||||||
*self.invited_rooms.write().await = invited
|
*self.push_ruleset.write().await = push_ruleset;
|
||||||
.drain()
|
} else {
|
||||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
// return false and continues with a sync request then save the state and create
|
||||||
.collect();
|
// and populate the files during the sync
|
||||||
*self.left_rooms.write().await = left
|
return Ok(false);
|
||||||
.drain()
|
|
||||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.needs_state_store_sync.store(false, Ordering::Relaxed);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(!self.needs_state_store_sync.load(Ordering::Relaxed))
|
let AllRooms {
|
||||||
|
mut joined,
|
||||||
|
mut invited,
|
||||||
|
mut left,
|
||||||
|
} = store.load_all_rooms().await?;
|
||||||
|
|
||||||
|
*self.joined_rooms.write().await = joined
|
||||||
|
.drain()
|
||||||
|
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
*self.invited_rooms.write().await = invited
|
||||||
|
.drain()
|
||||||
|
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
*self.left_rooms.write().await = left
|
||||||
|
.drain()
|
||||||
|
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When a client is provided the state store will load state from the `StateStore`.
|
/// When a client is provided the state store will load state from the `StateStore`.
|
||||||
|
@ -303,9 +382,54 @@ impl BaseClient {
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
{
|
{
|
||||||
let mut olm = self.olm.lock().await;
|
let mut olm = self.olm.lock().await;
|
||||||
*olm = Some(OlmMachine::new(&session.user_id, &session.device_id));
|
let store = self.cryptostore.lock().await.take();
|
||||||
|
|
||||||
|
if let Some(store) = store {
|
||||||
|
*olm = Some(
|
||||||
|
OlmMachine::new_with_store(
|
||||||
|
session.user_id.to_owned(),
|
||||||
|
session.device_id.to_owned(),
|
||||||
|
store,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
} else if let Some(path) = self.store_path.as_ref() {
|
||||||
|
#[cfg(feature = "sqlite-cryptostore")]
|
||||||
|
{
|
||||||
|
*olm = Some(
|
||||||
|
OlmMachine::new_with_default_store(
|
||||||
|
&session.user_id,
|
||||||
|
&session.device_id,
|
||||||
|
path,
|
||||||
|
&self.store_passphrase,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "sqlite-cryptostore"))]
|
||||||
|
{
|
||||||
|
*olm = Some(OlmMachine::new(&session.user_id, &session.device_id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*olm = Some(OlmMachine::new(&session.user_id, &session.device_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.sync_with_state_store().await?;
|
|
||||||
|
// If there wasn't a state store opened, try to open the default one if
|
||||||
|
// a store path was provided.
|
||||||
|
if self.state_store.read().await.is_none() {
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
if let Some(path) = &*self.store_path {
|
||||||
|
let store = JsonStore::open(path)?;
|
||||||
|
*self.state_store.write().await = Some(Box::new(store));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sync_with_state_store(&session).await?;
|
||||||
|
|
||||||
*self.session.write().await = Some(session);
|
*self.session.write().await = Some(session);
|
||||||
|
|
||||||
|
@ -713,8 +837,6 @@ impl BaseClient {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `response` - The response that we received after a successful sync.
|
/// * `response` - The response that we received after a successful sync.
|
||||||
///
|
|
||||||
/// * `did_update` - Signals to the `StateStore` if the client state needs updating.
|
|
||||||
pub async fn receive_sync_response(
|
pub async fn receive_sync_response(
|
||||||
&self,
|
&self,
|
||||||
response: &mut api::sync::sync_events::Response,
|
response: &mut api::sync::sync_events::Response,
|
||||||
|
|
|
@ -45,7 +45,7 @@ mod models;
|
||||||
mod session;
|
mod session;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
pub use client::{BaseClient, RoomState, RoomStateType};
|
pub use client::{BaseClient, BaseClientConfig, RoomState, RoomStateType};
|
||||||
pub use event_emitter::{EventEmitter, SyncRoom};
|
pub use event_emitter::{EventEmitter, SyncRoom};
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
pub use matrix_sdk_crypto::{Device, TrustState};
|
pub use matrix_sdk_crypto::{Device, TrustState};
|
||||||
|
|
|
@ -205,7 +205,7 @@ mod test {
|
||||||
|
|
||||||
use crate::api::r0::sync::sync_events::Response as SyncResponse;
|
use crate::api::r0::sync::sync_events::Response as SyncResponse;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
use crate::{BaseClient, Session};
|
use crate::{BaseClient, BaseClientConfig, Session};
|
||||||
|
|
||||||
fn sync_response(file: &str) -> SyncResponse {
|
fn sync_response(file: &str) -> SyncResponse {
|
||||||
let mut file = File::open(file).unwrap();
|
let mut file = File::open(file).unwrap();
|
||||||
|
@ -360,7 +360,8 @@ mod test {
|
||||||
|
|
||||||
// a sync response to populate our JSON store
|
// a sync response to populate our JSON store
|
||||||
let store = Box::new(JsonStore::open(path).unwrap());
|
let store = Box::new(JsonStore::open(path).unwrap());
|
||||||
let client = BaseClient::new_with_state_store(store).unwrap();
|
let client =
|
||||||
|
BaseClient::new_with_config(BaseClientConfig::new().state_store(store)).unwrap();
|
||||||
client.restore_login(session.clone()).await.unwrap();
|
client.restore_login(session.clone()).await.unwrap();
|
||||||
|
|
||||||
let mut response = sync_response("../test_data/sync.json");
|
let mut response = sync_response("../test_data/sync.json");
|
||||||
|
@ -370,9 +371,9 @@ mod test {
|
||||||
|
|
||||||
// now syncing the client will update from the state store
|
// now syncing the client will update from the state store
|
||||||
let store = Box::new(JsonStore::open(path).unwrap());
|
let store = Box::new(JsonStore::open(path).unwrap());
|
||||||
let client = BaseClient::new_with_state_store(store).unwrap();
|
let client =
|
||||||
|
BaseClient::new_with_config(BaseClientConfig::new().state_store(store)).unwrap();
|
||||||
client.restore_login(session.clone()).await.unwrap();
|
client.restore_login(session.clone()).await.unwrap();
|
||||||
client.sync_with_state_store().await.unwrap();
|
|
||||||
|
|
||||||
// assert the synced client and the logged in client are equal
|
// assert the synced client and the logged in client are equal
|
||||||
assert_eq!(*client.session().read().await, Some(session));
|
assert_eq!(*client.session().read().await, Some(session));
|
||||||
|
|
|
@ -13,7 +13,7 @@ version = "0.1.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
js_int = "0.1.5"
|
js_int = "0.1.5"
|
||||||
ruma-api = "0.16.1"
|
ruma-api = "0.16.1"
|
||||||
ruma-client-api = "0.8.0"
|
ruma-client-api = "0.9.0"
|
||||||
ruma-events = "0.21.2"
|
ruma-events = "0.21.2"
|
||||||
ruma-identifiers = "0.16.1"
|
ruma-identifiers = "0.16.1"
|
||||||
instant = { version = "0.1.4", features = ["wasm-bindgen", "now"] }
|
instant = { version = "0.1.4", features = ["wasm-bindgen", "now"] }
|
||||||
|
|
|
@ -27,10 +27,10 @@ zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
# Misc dependencies
|
# Misc dependencies
|
||||||
thiserror = "1.0.18"
|
thiserror = "1.0.19"
|
||||||
tracing = "0.1.14"
|
tracing = "0.1.14"
|
||||||
atomic = "0.4.5"
|
atomic = "0.4.5"
|
||||||
dashmap = "3.11.1"
|
dashmap = "3.11.2"
|
||||||
|
|
||||||
[dependencies.tracing-futures]
|
[dependencies.tracing-futures]
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|
|
@ -141,9 +141,9 @@ impl OlmMachine {
|
||||||
/// * `store` - A `Cryptostore` implementation that will be used to store
|
/// * `store` - A `Cryptostore` implementation that will be used to store
|
||||||
/// the encryption keys.
|
/// the encryption keys.
|
||||||
pub async fn new_with_store(
|
pub async fn new_with_store(
|
||||||
user_id: &UserId,
|
user_id: UserId,
|
||||||
device_id: &str,
|
device_id: String,
|
||||||
mut store: impl CryptoStore + 'static,
|
mut store: Box<dyn CryptoStore>,
|
||||||
) -> StoreError<Self> {
|
) -> StoreError<Self> {
|
||||||
let account = match store.load_account().await? {
|
let account = match store.load_account().await? {
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
|
@ -161,7 +161,7 @@ impl OlmMachine {
|
||||||
device_id: device_id.to_owned(),
|
device_id: device_id.to_owned(),
|
||||||
account,
|
account,
|
||||||
uploaded_signed_key_count: None,
|
uploaded_signed_key_count: None,
|
||||||
store: Box::new(store),
|
store,
|
||||||
outbound_group_sessions: HashMap::new(),
|
outbound_group_sessions: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -181,12 +181,12 @@ impl OlmMachine {
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
device_id: &str,
|
device_id: &str,
|
||||||
path: P,
|
path: P,
|
||||||
passphrase: String,
|
passphrase: &str,
|
||||||
) -> StoreError<Self> {
|
) -> StoreError<Self> {
|
||||||
let store =
|
let store =
|
||||||
SqliteStore::open_with_passphrase(&user_id, device_id, path, passphrase).await?;
|
SqliteStore::open_with_passphrase(&user_id, device_id, path, passphrase).await?;
|
||||||
|
|
||||||
OlmMachine::new_with_store(user_id, device_id, store).await
|
OlmMachine::new_with_store(user_id.to_owned(), device_id.to_owned(), Box::new(store)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The unique user id that owns this identity.
|
/// The unique user id that owns this identity.
|
||||||
|
|
|
@ -91,9 +91,15 @@ impl SqliteStore {
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
device_id: &str,
|
device_id: &str,
|
||||||
path: P,
|
path: P,
|
||||||
passphrase: String,
|
passphrase: &str,
|
||||||
) -> Result<SqliteStore> {
|
) -> Result<SqliteStore> {
|
||||||
SqliteStore::open_helper(user_id, device_id, path, Some(Zeroizing::new(passphrase))).await
|
SqliteStore::open_helper(
|
||||||
|
user_id,
|
||||||
|
device_id,
|
||||||
|
path,
|
||||||
|
Some(Zeroizing::new(passphrase.to_owned())),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_to_url(path: &Path) -> Result<Url> {
|
fn path_to_url(path: &Path) -> Result<Url> {
|
||||||
|
@ -801,14 +807,9 @@ mod test {
|
||||||
let user_id = &UserId::try_from(USER_ID).unwrap();
|
let user_id = &UserId::try_from(USER_ID).unwrap();
|
||||||
|
|
||||||
let store = if let Some(passphrase) = passphrase {
|
let store = if let Some(passphrase) = passphrase {
|
||||||
SqliteStore::open_with_passphrase(
|
SqliteStore::open_with_passphrase(&user_id, DEVICE_ID, tmpdir_path, passphrase)
|
||||||
&user_id,
|
.await
|
||||||
DEVICE_ID,
|
.expect("Can't create a passphrase protected store")
|
||||||
tmpdir_path,
|
|
||||||
passphrase.to_owned(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("Can't create a passphrase protected store")
|
|
||||||
} else {
|
} else {
|
||||||
SqliteStore::open(&user_id, DEVICE_ID, tmpdir_path)
|
SqliteStore::open(&user_id, DEVICE_ID, tmpdir_path)
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in New Issue