From dd0baa82e9789da23c8f9b06925776c7f80e2568 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 17 Oct 2022 17:40:25 +0300 Subject: [PATCH] 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 --- src/components/mail/view.rs | 87 +++++++++++++++++----------- src/components/mail/view/envelope.rs | 10 ++-- src/components/mail/view/html.rs | 10 ++-- src/mailcap.rs | 3 +- src/main.rs | 13 +++-- 5 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index 36bae497..e7ec7a7b 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -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) + ); } -*/ diff --git a/src/components/mail/view/envelope.rs b/src/components/mail/view/envelope.rs index 9b810935..bb968459 100644 --- a/src/components/mail/view/envelope.rs +++ b/src/components/mail/view/envelope.rs @@ -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 )), )); } diff --git a/src/components/mail/view/html.rs b/src/components/mail/view/html.rs index 34f28fcf..d5334561 100644 --- a/src/components/mail/view/html.rs +++ b/src/components/mail/view/html.rs @@ -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 )), )); } diff --git a/src/mailcap.rs b/src/mailcap.rs index c581d26d..c3caa55f 100644 --- a/src/mailcap.rs +++ b/src/mailcap.rs @@ -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()?; diff --git a/src/main.rs b/src/main.rs index 9a13ec77..391bef2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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()?;