diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-02-04 15:52:12 +0200 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-02-04 17:29:55 +0200 |
commit | 8b6ea8de9a89b3ad42276eb7835992f7b875128b (patch) | |
tree | 864a7136b4c23d76761ee3e7d034307f8c302838 /src/mailcap.rs | |
parent | 6fcc792b83f715644c041f728049de65f7da2e38 (diff) |
Remove ui crate
Merge ui crate with root crate.
In preparation for uploading `meli` as a separate crate on crates.io.
Workspace crates will need to be published as well and having a separate
`ui` crate and binary perhaps doesn't make sense anymore.
Diffstat (limited to 'src/mailcap.rs')
-rw-r--r-- | src/mailcap.rs | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/mailcap.rs b/src/mailcap.rs new file mode 100644 index 00000000..0973bcfa --- /dev/null +++ b/src/mailcap.rs @@ -0,0 +1,240 @@ +/* + * meli + * + * Copyright 2019 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! Find mailcap entries to execute attachments. + */ +use crate::split_command; +use crate::state::Context; +use crate::types::{create_temp_file, ForkType, UIEvent}; +use fnv::FnvHashMap; +use melib::attachments::decode; +use melib::{email::Attachment, MeliError, Result}; +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use text_processing::GlobMatch; + +pub struct MailcapEntry { + command: String, + /* Pass to pager */ + copiousoutput: bool, +} + +impl MailcapEntry { + pub fn execute(a: &Attachment, context: &mut Context) -> Result<()> { + /* lookup order: + * $XDG_CONFIG_HOME/meli/mailcap:$XDG_CONFIG_HOME/.mailcap:$HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap + */ + let xdg_dirs = + xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?; + let mut mailcap_path = xdg_dirs + .place_config_file("mailcap") + .map_err(|e| MeliError::new(e.to_string()))?; + if !mailcap_path.exists() { + mailcap_path = xdg::BaseDirectories::new() + .map_err(|e| MeliError::new(e.to_string()))? + .place_config_file("mailcap")?; + if !mailcap_path.exists() { + if let Ok(home) = std::env::var("HOME") { + mailcap_path = PathBuf::from(format!("{}/.mailcap", home)); + } + if !mailcap_path.exists() { + mailcap_path = PathBuf::from("/etc/mailcap"); + if !mailcap_path.exists() { + mailcap_path = PathBuf::from("/usr/etc/mailcap"); + if !mailcap_path.exists() { + mailcap_path = PathBuf::from("/usr/local/etc/mailcap"); + } + if !mailcap_path.exists() { + return Err(MeliError::new("No mailcap file found.")); + } + } + } + } + } + + let mut hash_map = FnvHashMap::default(); + let mut content = String::new(); + + std::fs::File::open(mailcap_path.as_path())?.read_to_string(&mut content)?; + let content_type = a.content_type().to_string(); + + let mut result = None; + let mut lines_iter = content.lines(); + while let Some(l) = lines_iter.next() { + let l = l.trim(); + if l.starts_with("#") { + continue; + } + if l.is_empty() { + continue; + } + + if l.ends_with("\\") { + let l = format!("{}{}", &l[..l.len() - 2], lines_iter.next().unwrap()); + let mut parts_iter = l.split(";"); + let key = parts_iter.next().unwrap(); + let cmd = parts_iter.next().unwrap(); + //let flags = parts_iter.next().unwrap(); + if key.starts_with(&content_type) || key.matches_glob(&content_type) { + let mut copiousoutput = false; + while let Some(flag) = parts_iter.next() { + if flag.trim() == "copiousoutput" { + copiousoutput = true; + } else { + debug!("unknown mailcap flag: {}", flag); + } + } + + result = Some(MailcapEntry { + command: cmd.to_string(), + copiousoutput, + }); + break; + } + hash_map.insert(key.to_string(), cmd.to_string()); + } else { + let mut parts_iter = l.split(";"); + let key = parts_iter.next().unwrap(); + let cmd = parts_iter.next().unwrap(); + //let flags = parts_iter.next().unwrap(); + if key.starts_with(&content_type) || key.matches_glob(&content_type) { + let mut copiousoutput = false; + while let Some(flag) = parts_iter.next() { + if flag.trim() == "copiousoutput" { + copiousoutput = true; + } else { + debug!("unknown mailcap flag: {}", flag); + } + } + + result = Some(MailcapEntry { + command: cmd.to_string(), + copiousoutput, + }); + break; + } + hash_map.insert(key.to_string(), cmd.to_string()); + } + } + + match result { + None => Err(MeliError::new("Not found")), + Some(MailcapEntry { + command, + copiousoutput, + }) => { + let parts = split_command!(command); + let (cmd, args) = (parts[0], &parts[1..]); + let mut f = None; + let mut needs_stdin = true; + let params = a.parameters(); + /* TODO: See mailcap(5) + * - replace "\%" with "%" and unescape other blackslash uses. + * - "%n" and "%F". + * - test=xxx field. + */ + let args = args + .iter() + .map(|arg| match *arg { + "%s" => { + needs_stdin = false; + let _f = create_temp_file(&decode(a, None), None, None, true); + let p = _f.path().display().to_string(); + f = Some(_f); + p + } + "%t" => a.content_type().to_string(), + param if param.starts_with("%{") && param.ends_with("}") => { + let param = ¶m["%{".len()..param.len() - 1]; + if let Some(v) = params.iter().find(|(k, _)| *k == param.as_bytes()) { + String::from_utf8_lossy(v.1).into() + } else if param == "charset" { + String::from("utf-8") + } else { + String::new() + } + } + a => a.to_string(), + }) + .collect::<Vec<String>>(); + { + context.input_kill(); + } + if copiousoutput { + let out = if needs_stdin { + let mut child = Command::new(cmd) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()?; + + child.stdin.as_mut().unwrap().write_all(&decode(a, None))?; + child.wait_with_output()?.stdout + } else { + let child = Command::new(cmd) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()?; + + child.wait_with_output()?.stdout + }; + let pager_cmd = if let Ok(v) = std::env::var("PAGER") { + std::borrow::Cow::from(v) + } else { + std::borrow::Cow::from("less") + }; + + let mut pager = Command::new(pager_cmd.as_ref()) + .stdin(Stdio::piped()) + .stdout(Stdio::inherit()) + .spawn()?; + pager.stdin.as_mut().unwrap().write_all(&out)?; + debug!(pager.wait_with_output()?.stdout); + } else { + if needs_stdin { + let mut child = Command::new(cmd) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::inherit()) + .spawn()?; + + child.stdin.as_mut().unwrap().write_all(&decode(a, None))?; + debug!(child.wait_with_output()?.stdout); + } else { + let child = Command::new(cmd) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .spawn()?; + + debug!(child.wait_with_output()?.stdout); + } + } + context.replies.push_back(UIEvent::Fork(ForkType::Finished)); + context.restore_input(); + Ok(()) + } + } + } +} |