From ae24c2e0ad609bbc391c9c13818ae2b78ba9830e Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Fri, 8 Sep 2023 22:48:02 -0400 Subject: [PATCH] feat: added the ability to edit files remotely --- Cargo.lock | 69 ++++++++++++++++- sparse-05/sparse-05-client/Cargo.toml | 1 + .../src/commands/connect/commands/download.rs | 1 + .../src/commands/connect/commands/help.rs | 17 +++-- .../src/commands/connect/commands/upload.rs | 5 +- .../src/commands/connect/shell.rs | 74 ++++++++++++++++--- .../src/connection/download_file.rs | 2 - .../src/connection/upload_file.rs | 1 + 8 files changed, 146 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e41e69c..4eda7b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,6 +133,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block-buffer" version = "0.9.0" @@ -189,7 +195,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", @@ -338,6 +344,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -401,6 +418,12 @@ dependencies = [ "anyhow", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "futures" version = "0.3.28" @@ -598,6 +621,12 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "log" version = "0.4.17" @@ -728,7 +757,7 @@ name = "pcap-sys" version = "0.1.0" dependencies = [ "cmake", - "errno", + "errno 0.2.8", "futures", "libc", "packets", @@ -876,6 +905,15 @@ dependencies = [ "libc", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rmp" version = "0.8.12" @@ -913,6 +951,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +dependencies = [ + "bitflags 2.4.0", + "errno 0.3.3", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "semver" version = "0.9.0" @@ -1043,6 +1094,7 @@ dependencies = [ "serde", "sparse-05-common", "structopt", + "tempfile", "tokio", ] @@ -1154,6 +1206,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/sparse-05/sparse-05-client/Cargo.toml b/sparse-05/sparse-05-client/Cargo.toml index f99627f..5f33293 100644 --- a/sparse-05/sparse-05-client/Cargo.toml +++ b/sparse-05/sparse-05-client/Cargo.toml @@ -15,4 +15,5 @@ rmp-serde = "1.1.2" serde = { version = "1.0.188", features = ["derive"] } sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" } structopt = { version = "0.3.26", features = ["paw"] } +tempfile = "3.8.0" tokio = { version = "1.32.0", features = ["io-std", "net", "fs", "macros", "rt"] } diff --git a/sparse-05/sparse-05-client/src/commands/connect/commands/download.rs b/sparse-05/sparse-05-client/src/commands/connect/commands/download.rs index aadef93..3654dc9 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/commands/download.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/commands/download.rs @@ -14,6 +14,7 @@ pub async fn download_file( let mut target_file = fs::OpenOptions::new() .write(true) .create(true) + .truncate(true) .open(&local_path) .await?; diff --git a/sparse-05/sparse-05-client/src/commands/connect/commands/help.rs b/sparse-05/sparse-05-client/src/commands/connect/commands/help.rs index 015700f..a33ece5 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/commands/help.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/commands/help.rs @@ -1,5 +1,4 @@ -use ansi_term::{Color, Style}; -use structopt::StructOpt; +use ansi_term::Style; use crate::commands::connect::shell::SparseCommands; @@ -30,14 +29,22 @@ pub fn print_help(arg: Option) { \n\ Uploads the file at the local file path, overwriting the file at the remote file path" ), + Some(SparseCommands::EditFile { .. }) => println!( + "#edit [remote_file_path]\n\ + \n\ + downloads the file specified to edit locally with the editor specified by the EDITOR environment variable" + ), None => println!( "\n{}{}\n\ \n\ The following shell commands are available:\n\ \n\ - - #sysinfo\t\tprint information about the system you are connecting to - - #help\t\tprints this help page, or alternatively prints info about a command passed as an argument\n\ - - #exit\t\texit from the shell and disconnect from the remote server\n\ + - #sysinfo\t\t\t\tprint information about the system you are connecting to\n\ + - #help\t\t\t\t\tprints this help page, or alternatively prints info about a command passed as an argument\n\ + - #upload [local_file] [remote_file]\tuploads the file located at the local path specified to the remote path\n\ + - #download [remote_file] [local_file]\tdownloads the file from the server at the path specified and saves it to the local path\n\ + - #edit [remote_file]\t\t\tdownloads the remote file specified and opens it locally in an editor\n\ + - #exit\t\t\t\t\texit from the shell and disconnect from the remote server\n\ ", Style::new().bold().paint("SHELL COMMANDS"), Style::new().paint(""), diff --git a/sparse-05/sparse-05-client/src/commands/connect/commands/upload.rs b/sparse-05/sparse-05-client/src/commands/connect/commands/upload.rs index bf2ced3..c934002 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/commands/upload.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/commands/upload.rs @@ -1,9 +1,6 @@ use std::{path::PathBuf, sync::Arc}; -use tokio::{ - fs, - io::{self, AsyncReadExt}, -}; +use tokio::{fs, io::AsyncReadExt}; use sparse_05_common::messages::{ Command, Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE, diff --git a/sparse-05/sparse-05-client/src/commands/connect/shell.rs b/sparse-05/sparse-05-client/src/commands/connect/shell.rs index 0d87c71..5c2d71f 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/shell.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/shell.rs @@ -2,12 +2,16 @@ use std::{ io::{self, Read, Write}, os::fd::AsRawFd, path::PathBuf, - sync::Arc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, thread, }; use sparse_05_common::messages::{Capabilities, Command, Response}; use structopt::StructOpt; +use tempfile::NamedTempFile; use tokio::{ io::{stderr, stdout, AsyncWriteExt}, runtime::Handle, @@ -118,6 +122,36 @@ async fn run_command( Ok(()) } +async fn edit_file(connection: Arc, remote_path: PathBuf) -> anyhow::Result<()> { + let local_path = tempfile::NamedTempFile::new()?.into_temp_path(); + + commands::download::download_file( + Arc::clone(&connection), + remote_path.clone(), + local_path.to_owned(), + ) + .await?; + + let cmd = format!( + "{} {local_path:?}", + std::env::var("VISUAL") + .or(std::env::var("EDITOR")) + .unwrap_or("vim".to_owned()) + ); + + tokio::process::Command::new("sh") + .arg("-c") + .arg(cmd) + .spawn()? + .wait() + .await?; + + commands::upload::upload_file(Arc::clone(&connection), local_path.to_owned(), remote_path) + .await?; + + Ok(()) +} + pub(super) async fn shell( connection: Arc, capabilities: Capabilities, @@ -135,14 +169,24 @@ pub(super) async fn shell( let (stdin_sender, mut stdin_receiver) = channel(64); + let pause = Arc::new(AtomicBool::new(false)); + let pause_2 = Arc::clone(&pause); + let handle = Handle::current(); thread::spawn(move || { let mut stdin_buf = [0u8; 1024]; loop { + if pause_2.load(Ordering::Relaxed) { + std::thread::sleep(std::time::Duration::from_millis(250)); + continue; + } let mut stdin = std::io::stdin(); let Ok(amount) = stdin.read(&mut stdin_buf) else { continue; }; + if pause_2.load(Ordering::Relaxed) { + continue; + } let stdin_buf = stdin_buf[..amount].to_vec(); let stdin_sender = stdin_sender.clone(); handle.spawn(async move { @@ -229,17 +273,25 @@ pub(super) async fn shell( eprintln!("{e:?}") } } - (Ok(SparseCommands::EditFile { remote_path }), _) => {} - _ => { - if let Err(e) = run_command( - &mut stdin_receiver, - input.to_string(), - Arc::clone(&connection), - ) - .await - { + (Ok(SparseCommands::EditFile { remote_path }), _) => { + pause.store(true, Ordering::SeqCst); + if let Err(e) = edit_file(Arc::clone(&connection), remote_path).await { eprintln!("{e:?}"); - }; + } + pause.store(false, Ordering::Relaxed); + } + _ => { + if !input.to_string().trim().is_empty() && !pause.load(Ordering::Relaxed) { + if let Err(e) = run_command( + &mut stdin_receiver, + input.to_string(), + Arc::clone(&connection), + ) + .await + { + eprintln!("{e:?}"); + }; + } } } } diff --git a/sparse-05/sparse-05-server/src/connection/download_file.rs b/sparse-05/sparse-05-server/src/connection/download_file.rs index 8b8044f..7ac0ada 100644 --- a/sparse-05/sparse-05-server/src/connection/download_file.rs +++ b/sparse-05/sparse-05-server/src/connection/download_file.rs @@ -42,8 +42,6 @@ pub(super) fn start_file_download<'a, 'b: 'a>( conninfo .send(conninfo.encrypt_and_sign_resp(Response::StartDownloadFile(id, packet_count))?)?; - let mut current_packet_count = 0; - loop { let mut file_data: Vec> = Vec::with_capacity(FILE_BUFFER_BUFFER_SIZE); let mut buffer = [0u8; FILE_TRANSFER_PACKET_SIZE]; diff --git a/sparse-05/sparse-05-server/src/connection/upload_file.rs b/sparse-05/sparse-05-server/src/connection/upload_file.rs index a08d8fc..5f9e6e8 100644 --- a/sparse-05/sparse-05-server/src/connection/upload_file.rs +++ b/sparse-05/sparse-05-server/src/connection/upload_file.rs @@ -43,6 +43,7 @@ pub(super) fn start_file_upload<'a, 'b: 'a>( let mut target_file = OpenOptions::new() .write(true) .create(true) + .truncate(true) .open(&file_path)?; let mut current_packet_count = 0;