diff --git a/src/activity_manager.rs b/src/activity_manager.rs index 99cac71..52132dc 100644 --- a/src/activity_manager.rs +++ b/src/activity_manager.rs @@ -26,7 +26,7 @@ * SOFTWARE. */ // Deps -use crate::filetransfer::FileTransferProtocol; +use crate::filetransfer::{FileTransferParams, FileTransferProtocol}; use crate::host::{HostError, Localhost}; use crate::system::config_client::ConfigClient; use crate::system::environment; @@ -35,7 +35,7 @@ use crate::ui::activities::{ auth::AuthActivity, filetransfer::FileTransferActivity, setup::SetupActivity, Activity, ExitReason, }; -use crate::ui::context::{Context, FileTransferParams}; +use crate::ui::context::Context; // Namespaces use std::path::{Path, PathBuf}; @@ -87,27 +87,9 @@ impl ActivityManager { /// ### set_filetransfer_params /// /// Set file transfer params - pub fn set_filetransfer_params( - &mut self, - address: String, - port: u16, - protocol: FileTransferProtocol, - username: Option, - password: Option, - entry_directory: Option, - ) { + pub fn set_filetransfer_params(&mut self, params: FileTransferParams) { // Put params into the context - self.context - .as_mut() - .unwrap() - .set_ftparams(FileTransferParams { - address, - port, - protocol, - username, - password, - entry_directory, - }); + self.context.as_mut().unwrap().set_ftparams(params); } /// ### run diff --git a/src/filetransfer/mod.rs b/src/filetransfer/mod.rs index a58a191..fc5e7f6 100644 --- a/src/filetransfer/mod.rs +++ b/src/filetransfer/mod.rs @@ -34,9 +34,12 @@ use thiserror::Error; use wildmatch::WildMatch; // exports pub mod ftp_transfer; +pub mod params; pub mod scp_transfer; pub mod sftp_transfer; +pub use params::FileTransferParams; + /// ## FileTransferProtocol /// /// This enum defines the different transfer protocol available in termscp diff --git a/src/main.rs b/src/main.rs index cb5a095..2797cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ mod utils; // namespaces use activity_manager::{ActivityManager, NextActivity}; -use filetransfer::FileTransferProtocol; +use filetransfer::FileTransferParams; use system::logging; enum Task { @@ -97,12 +97,7 @@ struct Args { } struct RunOpts { - address: Option, - port: u16, - username: Option, - password: Option, - remote_wrkdir: Option, - protocol: FileTransferProtocol, + remote: Option, ticks: Duration, log_enabled: bool, task: Task, @@ -111,12 +106,7 @@ struct RunOpts { impl Default for RunOpts { fn default() -> Self { Self { - address: None, - port: 22, - username: None, - password: None, - remote_wrkdir: None, - protocol: FileTransferProtocol::Sftp, + remote: None, ticks: Duration::from_millis(10), log_enabled: true, task: Task::Activity(NextActivity::Authentication), @@ -176,10 +166,6 @@ fn parse_args(args: Args) -> Result { if args.quiet { run_opts.log_enabled = false; } - // Match password - if let Some(passwd) = args.password { - run_opts.password = Some(passwd); - } // Match ticks run_opts.ticks = Duration::from_millis(args.ticks); // @! extra modes @@ -191,13 +177,13 @@ fn parse_args(args: Args) -> Result { if let Some(remote) = args.positional.get(0) { // Parse address match utils::parser::parse_remote_opt(remote.as_str()) { - Ok(host_opts) => { + Ok(mut remote) => { + // If password is provided, set password + if let Some(passwd) = args.password { + remote = remote.password(Some(passwd)); + } // Set params - run_opts.address = Some(host_opts.hostname); - run_opts.port = host_opts.port; - run_opts.protocol = host_opts.protocol; - run_opts.username = host_opts.username; - run_opts.remote_wrkdir = host_opts.wrkdir; + run_opts.remote = Some(remote); // In this case the first activity will be FileTransfer run_opts.task = Task::Activity(NextActivity::FileTransfer); } @@ -222,11 +208,11 @@ fn parse_args(args: Args) -> Result { /// Read password from tty if address is specified fn read_password(run_opts: &mut RunOpts) -> Result<(), String> { // Initialize client if necessary - if run_opts.address.is_some() { - debug!("User has specified remote options: address: {:?}, port: {:?}, protocol: {:?}, user: {:?}, password: {}", run_opts.address, run_opts.port, run_opts.protocol, run_opts.username, utils::fmt::shadow_password(run_opts.password.as_deref().unwrap_or(""))); - if run_opts.password.is_none() { + if let Some(remote) = run_opts.remote.as_mut() { + debug!("User has specified remote options: address: {:?}, port: {:?}, protocol: {:?}, user: {:?}, password: {}", remote.address, remote.port, remote.protocol, remote.username, utils::fmt::shadow_password(remote.password.as_deref().unwrap_or(""))); + if remote.password.is_none() { // Ask password if unspecified - run_opts.password = match rpassword::read_password_from_tty(Some("Password: ")) { + remote.password = match rpassword::read_password_from_tty(Some("Password: ")) { Ok(p) => { if p.is_empty() { None @@ -278,15 +264,8 @@ fn run(mut run_opts: RunOpts) -> i32 { } }; // Set file transfer params if set - if let Some(address) = run_opts.address.take() { - manager.set_filetransfer_params( - address, - run_opts.port, - run_opts.protocol, - run_opts.username, - run_opts.password, - run_opts.remote_wrkdir, - ); + if let Some(remote) = run_opts.remote.take() { + manager.set_filetransfer_params(remote); } manager.run(activity); 0 diff --git a/src/ui/activities/auth/mod.rs b/src/ui/activities/auth/mod.rs index 72121e2..d956412 100644 --- a/src/ui/activities/auth/mod.rs +++ b/src/ui/activities/auth/mod.rs @@ -34,9 +34,8 @@ mod view; // locals use super::{Activity, Context, ExitReason}; use crate::config::themes::Theme; -use crate::filetransfer::FileTransferProtocol; +use crate::filetransfer::{FileTransferParams, FileTransferProtocol}; use crate::system::bookmarks_client::BookmarksClient; -use crate::ui::context::FileTransferParams; use crate::utils::git; // Includes diff --git a/src/ui/context.rs b/src/ui/context.rs index 9cb001c..5750ef1 100644 --- a/src/ui/context.rs +++ b/src/ui/context.rs @@ -28,7 +28,7 @@ // Locals use super::input::InputHandler; use super::store::Store; -use crate::filetransfer::FileTransferProtocol; +use crate::filetransfer::FileTransferParams; use crate::system::config_client::ConfigClient; use crate::system::theme_provider::ThemeProvider; @@ -37,7 +37,6 @@ use crossterm::event::DisableMouseCapture; use crossterm::execute; use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen}; use std::io::{stdout, Stdout}; -use std::path::PathBuf; use tuirealm::tui::backend::CrosstermBackend; use tuirealm::tui::Terminal; @@ -56,19 +55,6 @@ pub struct Context { error: Option, } -/// ### FileTransferParams -/// -/// Holds connection parameters for file transfers -#[derive(Clone)] -pub struct FileTransferParams { - pub address: String, - pub port: u16, - pub protocol: FileTransferProtocol, - pub username: Option, - pub password: Option, - pub entry_directory: Option, -} - impl Context { /// ### new /// @@ -198,34 +184,3 @@ impl Drop for Context { self.leave_alternate_screen(); } } - -impl Default for FileTransferParams { - fn default() -> Self { - Self { - address: String::new(), - port: 22, - protocol: FileTransferProtocol::Sftp, - username: None, - password: None, - entry_directory: None, - } - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - use pretty_assertions::assert_eq; - - #[test] - fn test_ui_context_ft_params() { - let params: FileTransferParams = FileTransferParams::default(); - assert_eq!(params.address.as_str(), ""); - assert_eq!(params.port, 22); - assert_eq!(params.protocol, FileTransferProtocol::Sftp); - assert!(params.username.is_none()); - assert!(params.password.is_none()); - } -} diff --git a/src/utils/parser.rs b/src/utils/parser.rs index 17a7382..1cb15c5 100644 --- a/src/utils/parser.rs +++ b/src/utils/parser.rs @@ -26,7 +26,7 @@ * SOFTWARE. */ // Locals -use crate::filetransfer::FileTransferProtocol; +use crate::filetransfer::{FileTransferParams, FileTransferProtocol}; #[cfg(not(test))] // NOTE: don't use configuration during tests use crate::system::config_client::ConfigClient; #[cfg(not(test))] // NOTE: don't use configuration during tests @@ -75,14 +75,6 @@ lazy_static! { static ref COLOR_RGB_REGEX: Regex = Regex::new(r"^(rgb)?\(?([01]?\d\d?|2[0-4]\d|25[0-5])(\W+)([01]?\d\d?|2[0-4]\d|25[0-5])\W+(([01]?\d\d?|2[0-4]\d|25[0-5])\)?)").unwrap(); } -pub struct RemoteOptions { - pub hostname: String, - pub port: u16, - pub protocol: FileTransferProtocol, - pub username: Option, - pub wrkdir: Option, -} - /// ### parse_remote_opt /// /// Parse remote option string. Returns in case of success a RemoteOptions struct @@ -101,8 +93,7 @@ pub struct RemoteOptions { /// - sftp://172.26.104.1:4022 /// - sftp://172.26.104.1 /// - ... -/// -pub fn parse_remote_opt(remote: &str) -> Result { +pub fn parse_remote_opt(remote: &str) -> Result { // Set protocol to default protocol #[cfg(not(test))] // NOTE: don't use configuration during tests let mut protocol: FileTransferProtocol = match environment::init_config_dir() { @@ -152,7 +143,7 @@ pub fn parse_remote_opt(remote: &str) -> Result { }, }; // Get address - let hostname: String = match groups.get(3) { + let address: String = match groups.get(3) { Some(group) => group.as_str().to_string(), None => return Err(String::from("Missing address")), }; @@ -164,14 +155,13 @@ pub fn parse_remote_opt(remote: &str) -> Result { }; } // Get workdir - let wrkdir: Option = groups.get(5).map(|group| PathBuf::from(group.as_str())); - Ok(RemoteOptions { - hostname, - port, - protocol, - username, - wrkdir, - }) + let entry_directory: Option = + groups.get(5).map(|group| PathBuf::from(group.as_str())); + Ok(FileTransferParams::new(address) + .port(port) + .protocol(protocol) + .username(username) + .entry_directory(entry_directory)) } None => Err(String::from("Bad remote host syntax!")), } @@ -476,107 +466,110 @@ mod tests { #[test] fn test_utils_parse_remote_opt() { // Base case - let result: RemoteOptions = parse_remote_opt(&String::from("172.26.104.1")) + let result: FileTransferParams = parse_remote_opt(&String::from("172.26.104.1")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 22); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert!(result.username.is_some()); // User case - let result: RemoteOptions = parse_remote_opt(&String::from("root@172.26.104.1")) + let result: FileTransferParams = parse_remote_opt(&String::from("root@172.26.104.1")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 22); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert_eq!(result.username.unwrap(), String::from("root")); - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // User + port - let result: RemoteOptions = parse_remote_opt(&String::from("root@172.26.104.1:8022")) + let result: FileTransferParams = parse_remote_opt(&String::from("root@172.26.104.1:8022")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 8022); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert_eq!(result.username.unwrap(), String::from("root")); - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // Port only - let result: RemoteOptions = parse_remote_opt(&String::from("172.26.104.1:4022")) + let result: FileTransferParams = parse_remote_opt(&String::from("172.26.104.1:4022")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 4022); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert!(result.username.is_some()); - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // Protocol - let result: RemoteOptions = parse_remote_opt(&String::from("ftp://172.26.104.1")) + let result: FileTransferParams = parse_remote_opt(&String::from("ftp://172.26.104.1")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 21); // Fallback to ftp default assert_eq!(result.protocol, FileTransferProtocol::Ftp(false)); assert!(result.username.is_none()); // Doesn't fall back - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // Protocol - let result: RemoteOptions = parse_remote_opt(&String::from("sftp://172.26.104.1")) + let result: FileTransferParams = parse_remote_opt(&String::from("sftp://172.26.104.1")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 22); // Fallback to sftp default assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert!(result.username.is_some()); // Doesn't fall back - assert!(result.wrkdir.is_none()); - let result: RemoteOptions = parse_remote_opt(&String::from("scp://172.26.104.1")) + assert!(result.entry_directory.is_none()); + let result: FileTransferParams = parse_remote_opt(&String::from("scp://172.26.104.1")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 22); // Fallback to scp default assert_eq!(result.protocol, FileTransferProtocol::Scp); assert!(result.username.is_some()); // Doesn't fall back - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // Protocol + user - let result: RemoteOptions = parse_remote_opt(&String::from("ftps://anon@172.26.104.1")) - .ok() - .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + let result: FileTransferParams = + parse_remote_opt(&String::from("ftps://anon@172.26.104.1")) + .ok() + .unwrap(); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 21); // Fallback to ftp default assert_eq!(result.protocol, FileTransferProtocol::Ftp(true)); assert_eq!(result.username.unwrap(), String::from("anon")); - assert!(result.wrkdir.is_none()); + assert!(result.entry_directory.is_none()); // Path - let result: RemoteOptions = parse_remote_opt(&String::from("root@172.26.104.1:8022:/var")) - .ok() - .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + let result: FileTransferParams = + parse_remote_opt(&String::from("root@172.26.104.1:8022:/var")) + .ok() + .unwrap(); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 8022); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert_eq!(result.username.unwrap(), String::from("root")); - assert_eq!(result.wrkdir.unwrap(), PathBuf::from("/var")); + assert_eq!(result.entry_directory.unwrap(), PathBuf::from("/var")); // Port only - let result: RemoteOptions = parse_remote_opt(&String::from("172.26.104.1:home")) + let result: FileTransferParams = parse_remote_opt(&String::from("172.26.104.1:home")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 22); assert_eq!(result.protocol, FileTransferProtocol::Sftp); assert!(result.username.is_some()); - assert_eq!(result.wrkdir.unwrap(), PathBuf::from("home")); + assert_eq!(result.entry_directory.unwrap(), PathBuf::from("home")); // All together now - let result: RemoteOptions = + let result: FileTransferParams = parse_remote_opt(&String::from("ftp://anon@172.26.104.1:8021:/tmp")) .ok() .unwrap(); - assert_eq!(result.hostname, String::from("172.26.104.1")); + assert_eq!(result.address, String::from("172.26.104.1")); assert_eq!(result.port, 8021); // Fallback to ftp default assert_eq!(result.protocol, FileTransferProtocol::Ftp(false)); assert_eq!(result.username.unwrap(), String::from("anon")); - assert_eq!(result.wrkdir.unwrap(), PathBuf::from("/tmp")); + assert_eq!(result.entry_directory.unwrap(), PathBuf::from("/tmp")); // bad syntax - assert!(parse_remote_opt(&String::from("omar://172.26.104.1")).is_err()); // Bad protocol - assert!(parse_remote_opt(&String::from("omar://172.26.104.1:650000")).is_err()); + // Bad protocol + assert!(parse_remote_opt(&String::from("omar://172.26.104.1")).is_err()); // Bad port + assert!(parse_remote_opt(&String::from("scp://172.26.104.1:650000")).is_err()); } #[test]