diff --git a/Cargo.lock b/Cargo.lock index b48d6697..e7ec3ea7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -938,6 +938,7 @@ dependencies = [ "serde", "serde_json", "sourceview5", + "thiserror", "tokio", "tracing-subscriber", "url", @@ -2207,7 +2208,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "matrix-qrcode" version = "0.2.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "base64", "byteorder", @@ -2222,7 +2223,7 @@ dependencies = [ [[package]] name = "matrix-sdk" version = "0.4.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "anymap2", "async-stream", @@ -2252,7 +2253,7 @@ dependencies = [ [[package]] name = "matrix-sdk-base" version = "0.4.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "anyhow", "async-stream", @@ -2279,7 +2280,7 @@ dependencies = [ [[package]] name = "matrix-sdk-common" version = "0.4.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "async-lock", "async-trait", @@ -2295,7 +2296,7 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" version = "0.4.1" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "aes", "aes-gcm", @@ -2325,7 +2326,7 @@ dependencies = [ [[package]] name = "matrix-sdk-sled" version = "0.1.0" -source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#88f1552a70521ca969e2759f484ab46d76c33a5e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk.git#c83f8f2a45c83b2e2ec6e1bb08425ef036c54e97" dependencies = [ "anyhow", "async-stream", diff --git a/Cargo.toml b/Cargo.toml index d839503e..0d38a5bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ image = { version = "0.23", default-features = false, features = ["png"] } regex = "1.5.4" mime_guess = "2.0.3" num_enum = "0.5.6" +thiserror = "1.0.25" [dependencies.sourceview] package = "sourceview5" diff --git a/src/login.rs b/src/login.rs index 9498be5d..fd6a2d5a 100644 --- a/src/login.rs +++ b/src/login.rs @@ -6,7 +6,7 @@ use matrix_sdk::{ config::RequestConfig, ruma::{ api::client::discover::get_supported_versions, identifiers::Error as IdentifierError, - ServerName, UserId, + ServerName, }, Client, Result as MatrixResult, }; @@ -294,11 +294,11 @@ impl Login { fn try_autodiscovery(&self) { let server = build_server_name(self.imp().homeserver_entry.text().as_str()).unwrap(); - let mxid = UserId::parse_with_server_name("user", &server).unwrap(); self.freeze(); - let handle = spawn_tokio!(async move { Client::new_from_user_id(&mxid).await }); + let handle = + spawn_tokio!(async move { Client::builder().server_name(&server).build().await }); spawn!( glib::PRIORITY_DEFAULT_IDLE, @@ -381,13 +381,19 @@ impl Login { let homeserver = self.homeserver().unwrap(); let username = priv_.username_entry.text().to_string(); let password = priv_.password_entry.text().to_string(); + let autodiscovery = self.autodiscovery(); self.freeze(); let session = Session::new(); self.set_handler_for_prepared_session(&session); - session.login_with_password(homeserver, username, password, self.autodiscovery()); + spawn!( + glib::PRIORITY_DEFAULT_IDLE, + clone!(@weak session => async move { + session.login_with_password(homeserver, username, password, autodiscovery).await; + }) + ); priv_.current_session.replace(Some(session)); } diff --git a/src/session/account_settings/devices_page/device_list.rs b/src/session/account_settings/devices_page/device_list.rs index f95dd1b3..ec3ad8dd 100644 --- a/src/session/account_settings/devices_page/device_list.rs +++ b/src/session/account_settings/devices_page/device_list.rs @@ -204,7 +204,7 @@ impl DeviceList { let handle = spawn_tokio!(async move { let user_id = client.user_id().await.unwrap(); - let crypto_devices = client.get_user_devices(&user_id).await; + let crypto_devices = client.encryption().get_user_devices(&user_id).await; let crypto_devices = match crypto_devices { Ok(crypto_devices) => crypto_devices, diff --git a/src/session/content/verification/session_verification.rs b/src/session/content/verification/session_verification.rs index 8e086abe..7e5dbb26 100644 --- a/src/session/content/verification/session_verification.rs +++ b/src/session/content/verification/session_verification.rs @@ -294,9 +294,9 @@ impl SessionVerification { .authenticate(move |client, auth_data| async move { if let Some(auth) = auth_data { let auth = Some(auth.as_matrix_auth_data()); - client.bootstrap_cross_signing(auth).await + client.encryption().bootstrap_cross_signing(auth).await } else { - client.bootstrap_cross_signing(None).await + client.encryption().bootstrap_cross_signing(None).await } }) .await; diff --git a/src/session/mod.rs b/src/session/mod.rs index 4f2768ae..67a5c857 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -10,7 +10,7 @@ mod sidebar; mod user; pub mod verification; -use std::{convert::TryFrom, fs, time::Duration}; +use std::{convert::TryFrom, fs, path::Path, time::Duration}; use adw::subclass::prelude::BinImpl; use futures::StreamExt; @@ -24,7 +24,7 @@ use gtk::{ }; use log::{debug, error, warn}; use matrix_sdk::{ - config::{ClientConfig, RequestConfig, SyncSettings}, + config::{RequestConfig, SyncSettings}, deserialized_responses::SyncResponse, ruma::{ api::{ @@ -38,10 +38,11 @@ use matrix_sdk::{ assign, identifiers::RoomId, }, - store::make_store_config, - Client, HttpError, + store::{make_store_config, OpenStoreError}, + Client, ClientBuildError, Error, HttpError, }; use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use thiserror::Error; use tokio::task::JoinHandle; use url::Url; @@ -67,6 +68,26 @@ use crate::{ spawn, spawn_tokio, UserFacingError, Window, }; +#[derive(Error, Debug)] +pub enum ClientSetupError { + #[error(transparent)] + Store(#[from] OpenStoreError), + #[error(transparent)] + Client(#[from] ClientBuildError), + #[error(transparent)] + Sdk(#[from] Error), +} + +impl UserFacingError for ClientSetupError { + fn to_user_facing(self) -> String { + match self { + ClientSetupError::Store(err) => err.to_user_facing(), + ClientSetupError::Client(err) => err.to_user_facing(), + ClientSetupError::Sdk(err) => err.to_user_facing(), + } + } +} + mod imp { use std::cell::{Cell, RefCell}; @@ -283,7 +304,12 @@ impl Session { } } - pub fn login_with_password( + fn toggle_room_search(&self) { + let room_search = self.imp().sidebar.room_search_bar(); + room_search.set_search_mode(!room_search.is_search_mode()); + } + + pub async fn login_with_password( &self, homeserver: Url, username: String, @@ -291,33 +317,22 @@ impl Session { use_discovery: bool, ) { self.imp().logout_on_dispose.set(true); + let mut path = glib::user_data_dir(); path.push(glib::uuid_string_random().as_str()); + let passphrase: String = { + let mut rng = thread_rng(); + (&mut rng) + .sample_iter(Alphanumeric) + .take(30) + .map(char::from) + .collect() + }; + let handle = spawn_tokio!(async move { - let passphrase: String = { - let mut rng = thread_rng(); - (&mut rng) - .sample_iter(Alphanumeric) - .take(30) - .map(char::from) - .collect() - }; - let store_config = make_store_config(path.as_path(), Some(&passphrase)) - .map_err::(Into::into)?; - let config = ClientConfig::with_store_config(store_config) - // force_auth option to solve an issue with some servers configuration to require - // auth for profiles: - // https://gitlab.gnome.org/GNOME/fractal/-/issues/934 - .request_config(RequestConfig::new().retry_limit(2).force_auth()); - - let config = if use_discovery { - config.use_discovery_response() - } else { - config - }; + let client = create_client(&homeserver, &path, &passphrase, use_discovery).await?; - let client = Client::new_with_config(homeserver.clone(), config).await?; let response = client .login(&username, &password, None, Some("Fractal Next")) .await; @@ -338,36 +353,24 @@ impl Session { Err(error) => { // Remove the store created by Client::new() fs::remove_dir_all(path).unwrap(); - Err(error) + Err(error.into()) } } }); - spawn!( - glib::PRIORITY_DEFAULT_IDLE, - clone!(@weak self as obj => async move { - obj.handle_login_result(handle.await.unwrap(), true).await; - }) - ); - } - - fn toggle_room_search(&self) { - let room_search = self.imp().sidebar.room_search_bar(); - room_search.set_search_mode(!room_search.is_search_mode()); + self.handle_login_result(handle.await.unwrap(), true).await; } - pub fn login_with_previous_session(&self, session: StoredSession) { + pub async fn login_with_previous_session(&self, session: StoredSession) { let handle = spawn_tokio!(async move { - let store_config = - make_store_config(session.path.as_path(), Some(&session.secret.passphrase)) - .map_err::(Into::into)?; - let config = ClientConfig::with_store_config(store_config) - // force_auth option to solve an issue with some servers configuration to require - // auth for profiles: - // https://gitlab.gnome.org/GNOME/fractal/-/issues/934 - .request_config(RequestConfig::new().retry_limit(2).force_auth()); - - let client = Client::new_with_config(session.homeserver.clone(), config).await?; + let client = create_client( + &session.homeserver, + &session.path, + &session.secret.passphrase, + false, + ) + .await?; + client .restore_login(matrix_sdk::Session { user_id: session.user_id.clone(), @@ -376,19 +379,15 @@ impl Session { }) .await .map(|_| (client, session)) + .map_err(Into::into) }); - spawn!( - glib::PRIORITY_DEFAULT_IDLE, - clone!(@weak self as obj => async move { - obj.handle_login_result(handle.await.unwrap(), false).await; - }) - ); + self.handle_login_result(handle.await.unwrap(), false).await; } async fn handle_login_result( &self, - result: Result<(Client, StoredSession), matrix_sdk::Error>, + result: Result<(Client, StoredSession), ClientSetupError>, store_session: bool, ) { let priv_ = self.imp(); @@ -487,8 +486,9 @@ impl Session { self.imp().is_ready.set(true); + let encryption = client.encryption(); let has_cross_signing_keys = spawn_tokio!(async move { - if let Some(cross_signing_status) = client.cross_signing_status().await { + if let Some(cross_signing_status) = encryption.cross_signing_status().await { cross_signing_status.has_master && cross_signing_status.has_self_signing && cross_signing_status.has_user_signing @@ -497,11 +497,11 @@ impl Session { } }); - let client = self.client(); + let encryption = client.encryption(); let need_new_identity = spawn_tokio!(async move { // If there is an error just assume we don't need a new identity since // we will try again during the session verification - client + encryption .get_user_identity(&user_id) .await .map_or(false, |identity| identity.is_none()) @@ -511,9 +511,9 @@ impl Session { let priv_ = obj.imp(); if !has_cross_signing_keys.await.unwrap() { if need_new_identity.await.unwrap() { - let client = obj.client(); + let encryption = obj.client().encryption(); - let handle = spawn_tokio!(async move { client.bootstrap_cross_signing(None).await }); + let handle = spawn_tokio!(async move { encryption.bootstrap_cross_signing(None).await }); if handle.await.is_ok() { priv_.stack.set_visible_child(&*priv_.content); if let Some(window) = obj.parent_window() { @@ -791,3 +791,23 @@ impl Default for Session { Self::new() } } + +async fn create_client( + homeserver: &Url, + path: &Path, + passphrase: &str, + use_discovery: bool, +) -> Result { + let store_config = make_store_config(path, Some(passphrase))?; + Client::builder() + .homeserver_url(homeserver) + .store_config(store_config) + // force_auth option to solve an issue with some servers configuration to require + // auth for profiles: + // https://gitlab.gnome.org/GNOME/fractal/-/issues/934 + .request_config(RequestConfig::new().retry_limit(2).force_auth()) + .respect_login_well_known(use_discovery) + .build() + .await + .map_err(Into::into) +} diff --git a/src/session/user.rs b/src/session/user.rs index cfeef041..442a13cc 100644 --- a/src/session/user.rs +++ b/src/session/user.rs @@ -167,9 +167,9 @@ impl User { } pub async fn crypto_identity(&self) -> Option { - let client = self.session().client(); + let encryption = self.session().client().encryption(); let user_id = self.user_id(); - let handle = spawn_tokio!(async move { client.get_user_identity(&user_id).await }); + let handle = spawn_tokio!(async move { encryption.get_user_identity(&user_id).await }); match handle.await.unwrap() { Ok(identity) => identity, diff --git a/src/session/verification/identity_verification.rs b/src/session/verification/identity_verification.rs index 63d55a59..d8abcd37 100644 --- a/src/session/verification/identity_verification.rs +++ b/src/session/verification/identity_verification.rs @@ -774,7 +774,7 @@ macro_rules! wait { { loop { // FIXME: add method to the sdk to check if a SAS verification was started - if let Some(Verification::SasV1(sas)) = $this.client.get_verification($this.request.other_user_id(), $this.request.flow_id()).await { + if let Some(Verification::SasV1(sas)) = $this.client.encryption().get_verification($this.request.other_user_id(), $this.request.flow_id()).await { return $this.continue_sas(sas).await; } @@ -893,7 +893,10 @@ impl Context { sync_receiver: mpsc::Receiver, supported_methods: SupportedMethods, ) -> Option { - let request = client.get_verification_request(user_id, flow_id).await?; + let request = client + .encryption() + .get_verification_request(user_id, flow_id) + .await?; Some(Self { client, diff --git a/src/user_facing_error.rs b/src/user_facing_error.rs index 1b0162cd..cc3a2299 100644 --- a/src/user_facing_error.rs +++ b/src/user_facing_error.rs @@ -4,7 +4,8 @@ use matrix_sdk::{ client::error::ErrorKind::{Forbidden, LimitExceeded, UserDeactivated}, error::{FromHttpResponseError, ServerError}, }, - Error, HttpError, + store::OpenStoreError, + ClientBuildError, Error, HttpError, }; pub trait UserFacingError { @@ -57,3 +58,20 @@ impl UserFacingError for Error { } } } + +impl UserFacingError for OpenStoreError { + fn to_user_facing(self) -> String { + gettext("Could not open the store.") + } +} + +impl UserFacingError for ClientBuildError { + fn to_user_facing(self) -> String { + match self { + ClientBuildError::Url(_) => gettext("This is not a valid URL"), + ClientBuildError::Http(err) => err.to_user_facing(), + ClientBuildError::SledStore(err) => err.to_user_facing(), + _ => gettext("An unknown error occurred."), + } + } +} diff --git a/src/window.rs b/src/window.rs index 35edf185..0ede9eb8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -172,7 +172,12 @@ impl Window { } else { for stored_session in sessions { let session = Session::new(); - session.login_with_previous_session(stored_session); + spawn!( + glib::PRIORITY_DEFAULT_IDLE, + clone!(@weak session => async move { + session.login_with_previous_session(stored_session).await; + }) + ); self.add_session(&session); } }