Skip to content

Commit

Permalink
feat: cli args for double remote
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Oct 6, 2024
1 parent 418b132 commit 4d4c711
Show file tree
Hide file tree
Showing 23 changed files with 742 additions and 221 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@

Released on

- [**Multi Host support**](https://github.com/veeso/termscp/issues/285):
- Now it is possible to work on two different remotes `remote A -> remote B` instead of just `localhost -> remote`
- Cli arguments now accept an additional `remote-args` for the left panel.
- For more details read this issue <https://github.com/veeso/termscp/issues/285>.
- [Issue 290](https://github.com/veeso/termscp/issues/290): Password prompt was broken

## 0.15.0
Expand Down
4 changes: 2 additions & 2 deletions docs/de/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@

termscp kann mit den folgenden Optionen gestartet werden:

`termscp [Optionen]... [protokoll://benutzer@adresse:port:arbeitsverzeichnis] [lokales-arbeitsverzeichnis]`
`termscp [Optionen]... [protokoll://benutzer@adresse:port:arbeitsverzeichnis] [protokoll://benutzer@adresse:port:arbeitsverzeichnis] [lokales-arbeitsverzeichnis]`

ODER

`termscp [Optionen]... -b [Lesezeichen-Name] [lokales-arbeitsverzeichnis]`
`termscp [Optionen]... -b [Lesezeichen-Name] -b [Lesezeichen-Name] [lokales-arbeitsverzeichnis]`

- `-P, --password <Passwort>` wenn Adresse angegeben wird, ist das Passwort dieses Argument
- `-b, --address-as-bookmark` löst das Adressargument als Lesezeichenname auf
Expand Down
4 changes: 2 additions & 2 deletions docs/es/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@

termscp se puede iniciar con las siguientes opciones:

`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
`termscp [options]... [protocol://user@address:port:wrkdir] [protocol://user@address:port:wrkdir] [local-wrkdir]`

OR

`termscp [options]... -b [bookmark-name] [local-wrkdir]`
`termscp [options]... -b [bookmark-name] -b [bookmark-name] [local-wrkdir]`

- `-P, --password <password>` si se proporciona la dirección, la contraseña será este argumento
- `-b, --address-as-bookmark` resuelve el argumento de la dirección como un nombre de marcador
Expand Down
4 changes: 2 additions & 2 deletions docs/fr/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

termscp peut être démarré avec les options suivantes :

`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
`termscp [options]... [protocol://user@address:port:wrkdir] [protocol://user@address:port:wrkdir] [local-wrkdir]`

ou

`termscp [options]... -b [bookmark-name] [local-wrkdir]`
`termscp [options]... -b [bookmark-name] -b [bookmark-name] [local-wrkdir]`

- `-P, --password <password>` si l'adresse est fournie, le mot de passe sera cet argument
- `-b, --address-as-bookmark` résoudre l'argument d'adresse en tant que nom de signet
Expand Down
4 changes: 2 additions & 2 deletions docs/it/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

termscp può essere lanciato con questi argomenti:

`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
`termscp [options]... [protocol://user@address:port:wrkdir] [protocol://user@address:port:wrkdir] [local-wrkdir]`

O

`termscp [options]... -b [bookmark-name] [local-wrkdir]`
`termscp [options]... -b [bookmark-name] -b [bookmark-name] [local-wrkdir]`

- `-P, --password <password>` Se viene fornito l'argomento indirizzo, questa sarà la password utilizzata per autenticarsi
- `-b, --address-as-bookmark` risolve l'argomento indirizzo come nome di un segnalibro
Expand Down
8 changes: 5 additions & 3 deletions docs/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@

termscp can be started with the following options:

`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
`termscp [options]... [protocol://user@address:port:wrkdir] [protocol://user@address:port:wrkdir] [local-wrkdir]`

OR

`termscp [options]... -b [bookmark-name] [local-wrkdir]`
`termscp [options]... -b [bookmark-name] -b [bookmark-name] [local-wrkdir]`

- `-P, --password <password>` if address is provided, password will be this argument
AND any combination of the two

- `-P, --password <password>` if address is provided, password will be this argument. A password *can* be specified for each remote provided. The order must be the same of the address argument. The use of this parameter is discouraged.
- `-b, --address-as-bookmark` resolve address argument as a bookmark name
- `-q, --quiet` Disable logging
- `-v, --version` Print version info
Expand Down
4 changes: 2 additions & 2 deletions docs/ptbr/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@

O termscp pode ser iniciado com as seguintes opções:

`termscp [opções]... [protocol://usuário@endereço:porta:diretório-trabalho] [diretório-trabalho-local]`
`termscp [opções]... [protocol://usuário@endereço:porta:diretório-trabalho] [protocol://usuário@endereço:porta:diretório-trabalho] [diretório-trabalho-local]`

OU

`termscp [opções]... -b [nome-do-favorito] [diretório-trabalho-local]`
`termscp [opções]... -b [nome-do-favorito] -b [nome-do-favorito] [diretório-trabalho-local]`

- `-P, --password <senha>` se o endereço for fornecido, a senha será este argumento
- `-b, --address-as-bookmark` resolve o argumento do endereço como um nome de favorito
Expand Down
4 changes: 2 additions & 2 deletions docs/zh-CN/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

termscp启动时可以使用以下选项:

`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
`termscp [options]... [protocol://user@address:port:wrkdir] [protocol://user@address:port:wrkdir] [local-wrkdir]`

或作为

`termscp [options]... -b [bookmark-name] [local-wrkdir]`
`termscp [options]... -b [bookmark-name] -b [bookmark-name] [local-wrkdir]`

- `-P, --password <password>` 登陆密码
- `-b, --address-as-bookmark` 将地址参数解析为书签名称
Expand Down
178 changes: 144 additions & 34 deletions src/activity_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
//!
//! `activity_manager` is the module which provides run methods and handling for activities

// Deps
// Namespaces
use std::env;
use std::path::PathBuf;
use std::time::Duration;

use remotefs_ssh::SshKeyStorage as SshKeyStorageTrait;

use crate::filetransfer::{FileTransferParams, FileTransferProtocol, HostBridgeParams};
use crate::cli::{Remote, RemoteArgs};
use crate::filetransfer::{
FileTransferParams, FileTransferProtocol, HostBridgeParams, ProtocolParams,
};
use crate::host::HostError;
use crate::system::bookmarks_client::BookmarksClient;
use crate::system::config_client::ConfigClient;
Expand All @@ -30,6 +32,16 @@ pub enum NextActivity {
SetupActivity,
}

pub enum Host {
HostBridge,
Remote,
}

pub enum HostParams {
HostBridge(HostBridgeParams),
Remote(FileTransferParams),
}

/// The activity manager takes care of running activities and handling them until the application has ended
pub struct ActivityManager {
context: Option<Context>,
Expand Down Expand Up @@ -62,24 +74,114 @@ impl ActivityManager {
})
}

/// Configure remote args
pub fn configure_remote_args(&mut self, remote_args: RemoteArgs) -> Result<(), String> {
// Set for host bridge
match remote_args.host_bridge {
Remote::Bookmark(params) => self.resolve_bookmark_name(
Host::HostBridge,
&params.name,
params.password.as_deref(),
),
Remote::Host(host_params) => self.set_host_params(
HostParams::HostBridge(HostBridgeParams::Remote(
host_params.file_transfer_params.protocol,
host_params.file_transfer_params.params,
)),
host_params.password.as_deref(),
),
Remote::None => self.set_host_params(
HostParams::HostBridge(HostBridgeParams::Localhost(
env::current_dir()
.map_err(|e| format!("Could not get current directory: {e}"))?,
)),
None,
),
}?;

// set remote
match remote_args.remote {
Remote::Bookmark(params) => {
self.resolve_bookmark_name(Host::Remote, &params.name, params.password.as_deref())
}
Remote::Host(host_params) => self.set_host_params(
HostParams::Remote(host_params.file_transfer_params),
host_params.password.as_deref(),
),
Remote::None => Ok(()),
}
}

/// Set file transfer params
pub fn set_filetransfer_params(
pub fn set_host_params(
&mut self,
host: HostParams,
password: Option<&str>,
) -> Result<(), String> {
let (remote_local_path, remote_remote_path) = match &host {
HostParams::Remote(params) => (params.local_path.clone(), params.remote_path.clone()),
_ => (None, None),
};

let mut remote_params = match &host {
HostParams::HostBridge(HostBridgeParams::Remote(protocol, protocol_params)) => {
Some((*protocol, protocol_params.clone()))
}
HostParams::HostBridge(HostBridgeParams::Localhost(_)) => None,
HostParams::Remote(ft_params) => Some((ft_params.protocol, ft_params.params.clone())),
};

// Put params into the context
if let Some((protocol, params)) = remote_params.as_mut() {
self.resolve_password_for_protocol_params(*protocol, params, password)?;
}

match host {
HostParams::HostBridge(HostBridgeParams::Localhost(path)) => {
self.context
.as_mut()
.unwrap()
.set_host_bridge_params(HostBridgeParams::Localhost(path));
}
HostParams::HostBridge(HostBridgeParams::Remote(_, _)) => {
let (protocol, params) = remote_params.unwrap();
self.context
.as_mut()
.unwrap()
.set_host_bridge_params(HostBridgeParams::Remote(protocol, params));
}
HostParams::Remote(_) => {
let (protocol, params) = remote_params.unwrap();
let params = FileTransferParams {
local_path: remote_local_path,
remote_path: remote_remote_path,
protocol,
params,
};
self.context.as_mut().unwrap().set_remote_params(params);
}
}
Ok(())
}

fn resolve_password_for_protocol_params(
&mut self,
mut params: FileTransferParams,
protocol: FileTransferProtocol,
params: &mut ProtocolParams,
password: Option<&str>,
) -> Result<(), String> {
// Set password if provided
if params.password_missing() {
if let Some(password) = password {
params.set_default_secret(password.to_string());
} else if matches!(
params.protocol,
protocol,
FileTransferProtocol::Scp | FileTransferProtocol::Sftp,
) && params.params.generic_params().is_some()
) && params.generic_params().is_some()
{
// * if protocol is SCP or SFTP check whether a SSH key is registered for this remote, in case not ask password
let storage = SshKeyStorage::from(self.context.as_ref().unwrap().config());
let generic_params = params.params.generic_params().unwrap();
let generic_params = params.generic_params().unwrap();
if storage
.resolve(
&generic_params.address,
Expand All @@ -94,27 +196,27 @@ impl ActivityManager {
"storage could not find any suitable key for {}... prompting for password",
generic_params.address
);
self.prompt_password(&mut params)?;
self.prompt_password(params)?;
} else {
debug!(
"a key is already set for {}; password is not required",
generic_params.address
);
}
} else {
self.prompt_password(&mut params)?;
self.prompt_password(params)?;
}
}
// Put params into the context
self.context.as_mut().unwrap().set_ftparams(params);

Ok(())
}

/// Prompt user for password to set into params.
fn prompt_password(&mut self, params: &mut FileTransferParams) -> Result<(), String> {
fn prompt_password(&mut self, params: &mut ProtocolParams) -> Result<(), String> {
let ctx = self.context.as_mut().unwrap();
let prompt = format!("Password for {}: ", params.host_name());

match tty::read_secret_from_tty(ctx.terminal(), "Password: ") {
match tty::read_secret_from_tty(ctx.terminal(), prompt) {
Err(err) => Err(format!("Could not read password: {err}")),
Ok(Some(secret)) => {
debug!(
Expand All @@ -132,16 +234,28 @@ impl ActivityManager {
/// Returns error if bookmark is not found
pub fn resolve_bookmark_name(
&mut self,
host: Host,
bookmark_name: &str,
password: Option<&str>,
) -> Result<(), String> {
if let Some(bookmarks_client) = self.context.as_mut().unwrap().bookmarks_client_mut() {
match bookmarks_client.get_bookmark(bookmark_name) {
None => Err(format!(
r#"Could not resolve bookmark name: "{bookmark_name}" no such bookmark"#
)),
Some(params) => self.set_filetransfer_params(params, password),
}
let params = match bookmarks_client.get_bookmark(bookmark_name) {
None => {
return Err(format!(
r#"Could not resolve bookmark name: "{bookmark_name}" no such bookmark"#
))
}
Some(params) => params,
};

let params = match host {
Host::Remote => HostParams::Remote(params),
Host::HostBridge => {
HostParams::HostBridge(HostBridgeParams::Remote(params.protocol, params.params))
}
};

self.set_host_params(params, password)
} else {
Err(String::from(
"Could not resolve bookmark name: bookmarks client not initialized",
Expand Down Expand Up @@ -235,28 +349,24 @@ impl ActivityManager {
return None;
}
};

let host_bridge_params = match ctx.host_bridge_params() {
Some(params) => params.clone(),
None => {
error!("Failed to start FileTransferActivity: host bridge params is None");
return None;
}
};

// If ft params is None, return None
let remote_params: &FileTransferParams = match ctx.ft_params() {
let remote_params: &FileTransferParams = match ctx.remote_params() {
Some(ft_params) => ft_params,
None => {
error!("Failed to start FileTransferActivity: file transfer params is None");
return None;
}
};

// get local path:
// - if set in file transfer params, get it from there
// - otherwise is env current dir
// - otherwise is /
let local_wrkdir = remote_params
.local_path
.clone()
.or(std::env::current_dir().ok())
.unwrap_or(PathBuf::from("/"));

// TODO: get host params from prev activity
let host_bridge_params = HostBridgeParams::Localhost(local_wrkdir);

let mut activity: FileTransferActivity =
FileTransferActivity::new(host_bridge_params, remote_params, self.ticks);
// Prepare result
Expand Down
Loading

0 comments on commit 4d4c711

Please sign in to comment.