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"
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"

View File

@ -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"] }

View File

@ -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?;

View File

@ -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<SparseCommands>) {
\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(""),

View File

@ -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,

View File

@ -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<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(
connection: Arc<Connection>,
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:?}");
};
}
}
}
}

View File

@ -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<u8>> = Vec::with_capacity(FILE_BUFFER_BUFFER_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()
.write(true)
.create(true)
.truncate(true)
.open(&file_path)?;
let mut current_packet_count = 0;