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

View File

@ -151,10 +151,10 @@ impl Component for HtmlView {
};
if let Some(command) = command {
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);
match Command::new(&exec_cmd)
.arg(&argument)
match Command::new("sh")
.args(&["-c", &exec_cmd])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
@ -166,8 +166,8 @@ impl Component for HtmlView {
Err(err) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}",
&exec_cmd, &argument, err
"Failed to start `{}`: {}",
&exec_cmd, err
)),
));
}

View File

@ -214,7 +214,8 @@ impl MailcapEntry {
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())
.stdout(Stdio::inherit())
.spawn()?;

View File

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