Spawn user-given command strings with sh -c ".."

If given string contains arguments, Command::new(string) will fail.

Reported in #159 https://git.meli.delivery/meli/meli/issues/159
This commit is contained in:
Manos Pitsidianakis 2022-10-17 17:40:25 +03:00
parent 0ef4dde939
commit dd0baa82e9
5 changed files with 71 additions and 52 deletions

View File

@ -2223,13 +2223,13 @@ impl Component for MailView {
None, None,
true, true,
); );
let (exec_cmd, argument) = desktop_exec_to_command( let exec_cmd = desktop_exec_to_command(
&command, &command,
p.path.display().to_string(), p.path.display().to_string(),
false, false,
); );
match Command::new(&exec_cmd) match Command::new("sh")
.arg(&argument) .args(&["-c", &exec_cmd])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
@ -2241,8 +2241,8 @@ impl Component for MailView {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}", "Failed to start `{}`: {}",
&exec_cmd, &argument, err &exec_cmd, err
)), )),
)); ));
} }
@ -2790,49 +2790,66 @@ fn save_attachment(path: &std::path::Path, bytes: &[u8]) -> Result<()> {
Ok(()) Ok(())
} }
fn desktop_exec_to_command(command: &str, path: String, is_url: bool) -> (String, String) { fn desktop_exec_to_command(command: &str, path: String, is_url: bool) -> String {
/* Purge unused field codes */ /* Purge unused field codes */
let command = command let command = command
.replace("%i", "") .replace("%i", "")
.replace("%c", "") .replace("%c", "")
.replace("%k", ""); .replace("%k", "");
if let Some(pos) = command.find("%f").or_else(|| command.find("%F")) { if command.contains("%f") {
(command[0..pos].trim().to_string(), path) command.replacen("%f", &path.replace(' ', "\\ "), 1)
} else if let Some(pos) = command.find("%u").or_else(|| command.find("%U")) { } else if command.contains("%F") {
command.replacen("%F", &path.replace(' ', "\\ "), 1)
} else if command.contains("%u") || command.contains("%U") {
let from_pattern = if command.contains("%u") { "%u" } else { "%U" };
if is_url { if is_url {
(command[0..pos].trim().to_string(), path) command.replacen(from_pattern, &path, 1)
} else { } else {
( command.replacen(
command[0..pos].trim().to_string(), from_pattern,
format!("file://{}", path), &format!("file://{}", path).replace(' ', "\\ "),
1,
) )
} }
} else if is_url {
format!("{} {}", command, path)
} else { } else {
(command, path) format!("{} {}", command, path.replace(' ', "\\ "))
} }
} }
/*
#[test] #[test]
fn test_desktop_exec() { fn test_desktop_exec() {
for cmd in [ assert_eq!(
"ristretto %F", "ristretto /tmp/file".to_string(),
"/usr/lib/firefox-esr/firefox-esr %u", desktop_exec_to_command("ristretto %F", "/tmp/file".to_string(), false)
"/usr/bin/vlc --started-from-file %U", );
"zathura %U", assert_eq!(
] "/usr/lib/firefox-esr/firefox-esr file:///tmp/file".to_string(),
.iter() desktop_exec_to_command(
{ "/usr/lib/firefox-esr/firefox-esr %u",
println!( "/tmp/file".to_string(),
"cmd = {} output = {:?}, is_url = false", false
cmd, )
desktop_exec_to_command(cmd, "/tmp/file".to_string(), false) );
); assert_eq!(
println!( "/usr/lib/firefox-esr/firefox-esr www.example.com".to_string(),
"cmd = {} output = {:?}, is_url = true", desktop_exec_to_command(
cmd, "/usr/lib/firefox-esr/firefox-esr %u",
desktop_exec_to_command(cmd, "www.example.com".to_string(), true) "www.example.com".to_string(),
); true
} )
);
assert_eq!(
"/usr/bin/vlc --started-from-file www.example.com".to_string(),
desktop_exec_to_command(
"/usr/bin/vlc --started-from-file %U",
"www.example.com".to_string(),
true
)
);
assert_eq!(
"zathura --fork file:///tmp/file".to_string(),
desktop_exec_to_command("zathura --fork %U", "file:///tmp/file".to_string(), true)
);
} }
*/

View File

@ -403,13 +403,13 @@ impl Component for EnvelopeView {
None, None,
true, true,
); );
let (exec_cmd, argument) = super::desktop_exec_to_command( let exec_cmd = super::desktop_exec_to_command(
&command, &command,
p.path.display().to_string(), p.path.display().to_string(),
false, false,
); );
match Command::new(&exec_cmd) match Command::new("sh")
.arg(&argument) .args(&["-c", &exec_cmd])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
@ -421,8 +421,8 @@ impl Component for EnvelopeView {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}", "Failed to start `{}`: {}",
&exec_cmd, &argument, err &exec_cmd, err
)), )),
)); ));
} }

View File

@ -151,10 +151,10 @@ impl Component for HtmlView {
}; };
if let Some(command) = command { if let Some(command) = command {
let p = create_temp_file(&self.bytes, None, None, true); let p = create_temp_file(&self.bytes, None, None, true);
let (exec_cmd, argument) = let exec_cmd =
super::desktop_exec_to_command(&command, p.path.display().to_string(), false); super::desktop_exec_to_command(&command, p.path.display().to_string(), false);
match Command::new(&exec_cmd) match Command::new("sh")
.arg(&argument) .args(&["-c", &exec_cmd])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
@ -166,8 +166,8 @@ impl Component for HtmlView {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}", "Failed to start `{}`: {}",
&exec_cmd, &argument, err &exec_cmd, err
)), )),
)); ));
} }

View File

@ -214,7 +214,8 @@ impl MailcapEntry {
std::borrow::Cow::from("less") std::borrow::Cow::from("less")
}; };
let mut pager = Command::new(pager_cmd.as_ref()) let mut pager = Command::new("sh")
.args(["-c", pager_cmd.as_ref()])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn()?; .spawn()?;

View File

@ -233,12 +233,13 @@ fn run_app(opt: Opt) -> Result<()> {
} }
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let mut handle = let mut handle = Command::new("sh")
Command::new(std::env::var("PAGER").unwrap_or_else(|_| "more".to_string())) .arg("-c")
.stdin(Stdio::piped()) .arg(std::env::var("PAGER").unwrap_or_else(|_| "more".to_string()))
.stdout(Stdio::inherit()) .stdin(Stdio::piped())
.stderr(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn()?; .stderr(Stdio::inherit())
.spawn()?;
handle.stdin.take().unwrap().write_all(v.as_bytes())?; handle.stdin.take().unwrap().write_all(v.as_bytes())?;
handle.wait()?; handle.wait()?;