feat: added the ability to edit files remotely

This commit is contained in:
Andrew Rioux 2023-09-08 22:48:02 -04:00
parent eb5e86067b
commit ae24c2e0ad
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
8 changed files with 146 additions and 24 deletions

69
Cargo.lock generated
View File

@ -133,6 +133,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.9.0" version = "0.9.0"
@ -189,7 +195,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"atty", "atty",
"bitflags", "bitflags 1.3.2",
"strsim", "strsim",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
@ -338,6 +344,17 @@ dependencies = [
"winapi", "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]] [[package]]
name = "errno-dragonfly" name = "errno-dragonfly"
version = "0.1.2" version = "0.1.2"
@ -401,6 +418,12 @@ dependencies = [
"anyhow", "anyhow",
] ]
[[package]]
name = "fastrand"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.28" version = "0.3.28"
@ -598,6 +621,12 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linux-raw-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -728,7 +757,7 @@ name = "pcap-sys"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cmake", "cmake",
"errno", "errno 0.2.8",
"futures", "futures",
"libc", "libc",
"packets", "packets",
@ -876,6 +905,15 @@ dependencies = [
"libc", "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]] [[package]]
name = "rmp" name = "rmp"
version = "0.8.12" version = "0.8.12"
@ -913,6 +951,19 @@ dependencies = [
"semver", "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]] [[package]]
name = "semver" name = "semver"
version = "0.9.0" version = "0.9.0"
@ -1043,6 +1094,7 @@ dependencies = [
"serde", "serde",
"sparse-05-common", "sparse-05-common",
"structopt", "structopt",
"tempfile",
"tokio", "tokio",
] ]
@ -1154,6 +1206,19 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"

View File

@ -15,4 +15,5 @@ rmp-serde = "1.1.2"
serde = { version = "1.0.188", features = ["derive"] } serde = { version = "1.0.188", features = ["derive"] }
sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" } sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" }
structopt = { version = "0.3.26", features = ["paw"] } structopt = { version = "0.3.26", features = ["paw"] }
tempfile = "3.8.0"
tokio = { version = "1.32.0", features = ["io-std", "net", "fs", "macros", "rt"] } tokio = { version = "1.32.0", features = ["io-std", "net", "fs", "macros", "rt"] }

View File

@ -14,6 +14,7 @@ pub async fn download_file(
let mut target_file = fs::OpenOptions::new() let mut target_file = fs::OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.truncate(true)
.open(&local_path) .open(&local_path)
.await?; .await?;

View File

@ -1,5 +1,4 @@
use ansi_term::{Color, Style}; use ansi_term::Style;
use structopt::StructOpt;
use crate::commands::connect::shell::SparseCommands; use crate::commands::connect::shell::SparseCommands;
@ -30,14 +29,22 @@ pub fn print_help(arg: Option<SparseCommands>) {
\n\ \n\
Uploads the file at the local file path, overwriting the file at the remote file path" 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!( None => println!(
"\n{}{}\n\ "\n{}{}\n\
\n\ \n\
The following shell commands are available:\n\ The following shell commands are available:\n\
\n\ \n\
- #sysinfo\t\tprint information about the system you are connecting to - #sysinfo\t\t\t\tprint information about the system you are connecting to\n\
- #help\t\tprints this help page, or alternatively prints info about a command passed as an argument\n\ - #help\t\t\t\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\ - #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().bold().paint("SHELL COMMANDS"),
Style::new().paint(""), Style::new().paint(""),

View File

@ -1,9 +1,6 @@
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
use tokio::{ use tokio::{fs, io::AsyncReadExt};
fs,
io::{self, AsyncReadExt},
};
use sparse_05_common::messages::{ use sparse_05_common::messages::{
Command, Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE, Command, Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE,

View File

@ -2,12 +2,16 @@ use std::{
io::{self, Read, Write}, io::{self, Read, Write},
os::fd::AsRawFd, os::fd::AsRawFd,
path::PathBuf, path::PathBuf,
sync::Arc, sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread, thread,
}; };
use sparse_05_common::messages::{Capabilities, Command, Response}; use sparse_05_common::messages::{Capabilities, Command, Response};
use structopt::StructOpt; use structopt::StructOpt;
use tempfile::NamedTempFile;
use tokio::{ use tokio::{
io::{stderr, stdout, AsyncWriteExt}, io::{stderr, stdout, AsyncWriteExt},
runtime::Handle, runtime::Handle,
@ -118,6 +122,36 @@ async fn run_command(
Ok(()) Ok(())
} }
async fn edit_file(connection: Arc<Connection>, 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( pub(super) async fn shell(
connection: Arc<Connection>, connection: Arc<Connection>,
capabilities: Capabilities, capabilities: Capabilities,
@ -135,14 +169,24 @@ pub(super) async fn shell(
let (stdin_sender, mut stdin_receiver) = channel(64); 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(); let handle = Handle::current();
thread::spawn(move || { thread::spawn(move || {
let mut stdin_buf = [0u8; 1024]; let mut stdin_buf = [0u8; 1024];
loop { loop {
if pause_2.load(Ordering::Relaxed) {
std::thread::sleep(std::time::Duration::from_millis(250));
continue;
}
let mut stdin = std::io::stdin(); let mut stdin = std::io::stdin();
let Ok(amount) = stdin.read(&mut stdin_buf) else { let Ok(amount) = stdin.read(&mut stdin_buf) else {
continue; continue;
}; };
if pause_2.load(Ordering::Relaxed) {
continue;
}
let stdin_buf = stdin_buf[..amount].to_vec(); let stdin_buf = stdin_buf[..amount].to_vec();
let stdin_sender = stdin_sender.clone(); let stdin_sender = stdin_sender.clone();
handle.spawn(async move { handle.spawn(async move {
@ -229,8 +273,15 @@ pub(super) async fn shell(
eprintln!("{e:?}") eprintln!("{e:?}")
} }
} }
(Ok(SparseCommands::EditFile { remote_path }), _) => {} (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( if let Err(e) = run_command(
&mut stdin_receiver, &mut stdin_receiver,
input.to_string(), input.to_string(),
@ -243,6 +294,7 @@ pub(super) async fn shell(
} }
} }
} }
}
Ok(()) Ok(())
} }

View File

@ -42,8 +42,6 @@ pub(super) fn start_file_download<'a, 'b: 'a>(
conninfo conninfo
.send(conninfo.encrypt_and_sign_resp(Response::StartDownloadFile(id, packet_count))?)?; .send(conninfo.encrypt_and_sign_resp(Response::StartDownloadFile(id, packet_count))?)?;
let mut current_packet_count = 0;
loop { loop {
let mut file_data: Vec<Vec<u8>> = Vec::with_capacity(FILE_BUFFER_BUFFER_SIZE); let mut file_data: Vec<Vec<u8>> = Vec::with_capacity(FILE_BUFFER_BUFFER_SIZE);
let mut buffer = [0u8; FILE_TRANSFER_PACKET_SIZE]; let mut buffer = [0u8; FILE_TRANSFER_PACKET_SIZE];

View File

@ -43,6 +43,7 @@ pub(super) fn start_file_upload<'a, 'b: 'a>(
let mut target_file = OpenOptions::new() let mut target_file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.truncate(true)
.open(&file_path)?; .open(&file_path)?;
let mut current_packet_count = 0; let mut current_packet_count = 0;