diff --git a/src/configuration.rs b/src/configuration.rs index 1565d2e..ba07b1a 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -14,11 +14,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use crate::passwords::Password; +use crate::passwords::{Password, try_retrieve_password_from_keyring}; use jid::BareJid; use keyring::Entry; -use log::error; +use log::{error, info}; use serde_derive::{Deserialize, Serialize}; +use crate::types::LoginCredentials; /// The configuration struct containing all the configuration options. #[derive(Default, Debug, Serialize, Deserialize)] @@ -70,3 +71,22 @@ pub fn load_config() -> Configuration { } } } + +/// Retrieve the full login credentials from the config and keyring, if they exist. +pub fn try_retrieve_credentials(config: &Configuration) -> Option { + if let (Some(user), Some(nick)) = (&config.stored_username, &config.stored_default_nick) { + if let Some(password) = try_retrieve_password_from_keyring(&user) { + Some(LoginCredentials { + username: user.clone(), + default_nick: nick.clone(), + password, + }) + } else { + info!("No stored password found; will not try to log in."); + None + } + } else { + info!("No stored username or default nick found; will not try to log in."); + None + } +} \ No newline at end of file diff --git a/src/widgets/login_screen.rs b/src/widgets/login_screen.rs index dfa72f3..1f0adf6 100644 --- a/src/widgets/login_screen.rs +++ b/src/widgets/login_screen.rs @@ -19,6 +19,11 @@ use dioxus::hooks::use_state; use dioxus::prelude::*; use std::fmt::Debug; +use std::str::FromStr; +use jid::BareJid; +use log::error; +use crate::passwords::Password; +use crate::types::LoginCredentials; #[derive(Debug, Clone, PartialEq)] pub enum LoginStatus { @@ -49,6 +54,32 @@ impl Debug for LoginAttempt { } } +pub fn validate_login_attempt(x: LoginAttempt) -> Result { + let LoginAttempt { + username, + default_nick, + password, + } = x; + + // First, validate the username as a jid: + let jid = match BareJid::from_str(&username) { + Ok(x) => x, + Err(e) => { + error!("Invalid JID: {}", e); + return Err(format!("Invalid JID: {}", e)); + } + }; + + // Wrap the password in a Password struct. + let password = Password(password); + + Ok(LoginCredentials { + username: jid, + default_nick, + password, + }) +} + /// The props for the login screen widget, including: /// * The xmpp address to pre-fill in the username field. /// * The default nick to pre-fill in the default nick field. diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index fd11da8..3d72a37 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -23,6 +23,7 @@ use crate::widgets::room_view::RoomView; use crate::widgets::sidebar::SideBar; use crate::xmpp_interface::NetworkCommand; use dioxus::core::{Element, Scope}; +use crate::widgets::login_screen::validate_login_attempt; use dioxus::prelude::*; use futures_util::StreamExt; @@ -30,7 +31,7 @@ use jid::BareJid; use log::{error, info}; use std::collections::HashMap; -use crate::configuration::{load_config, Configuration}; +use crate::configuration::{load_config, Configuration, try_retrieve_credentials}; use crate::xmpp_interface; use crate::configuration::store_login_details; @@ -46,24 +47,6 @@ pub mod room_view; pub mod send_message; pub mod sidebar; -fn try_retrieve_credentials(config: &Configuration) -> Option { - if let (Some(user), Some(nick)) = (&config.stored_username, &config.stored_default_nick) { - if let Some(password) = try_retrieve_password_from_keyring(&user) { - Some(LoginCredentials { - username: user.clone(), - default_nick: nick.clone(), - password, - }) - } else { - info!("No stored password found; will not try to log in."); - None - } - } else { - info!("No stored username or default nick found; will not try to log in."); - None - } -} - pub fn App(cx: Scope) -> Element { let messages = use_ref(cx, || HashMap::new()); let current_room = use_state(cx, || None::); @@ -103,34 +86,18 @@ pub fn App(cx: Scope) -> Element { cached_nick: config.read().stored_default_nick.clone().unwrap_or("".to_string()), login_state: connection_status.current().as_ref().clone(), on_login_attempt: move |x: LoginAttempt| { - - let LoginAttempt { username, default_nick, password } = x; - - // First, validate the username as a jid: - let jid = match BareJid::from_str(&username) { - Ok(x) => x, - Err(e) => { - error!("Invalid JID: {}", e); - return; - } - }; - - // Wrap the password in a Password struct. - let password = Password(password); - - // Store the login details in the config and keyring for auto-login next time. - config.with_mut(|c| { - store_login_details(jid.clone(), default_nick.clone(), &password, c); - }); - - // Finally, send the login command to the xmpp interface. - coroutine.send(NetworkCommand::TryLogin { - credentials: LoginCredentials { - username: jid, - default_nick, - password, + // Validate the login attempt. + match validate_login_attempt(x) { + Ok(x) => { + config.with_mut(|c| { + store_login_details(x.username.clone(), x.default_nick.clone(), &x.password, c); + }); + coroutine.send(NetworkCommand::TryLogin { credentials: x }); }, - }); + Err(e) => { + error!("Invalid login attempt: {}", e); + } + } }, } } @@ -138,7 +105,6 @@ pub fn App(cx: Scope) -> Element { } else { // We're logged in; show a sidebar and a room view. - rsx!{ // Sidebar.