diff options
Diffstat (limited to 'bin/domain/imag-mail/src/new.rs')
-rw-r--r-- | bin/domain/imag-mail/src/new.rs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/bin/domain/imag-mail/src/new.rs b/bin/domain/imag-mail/src/new.rs new file mode 100644 index 00000000..0d196529 --- /dev/null +++ b/bin/domain/imag-mail/src/new.rs @@ -0,0 +1,219 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2020 Matthias Beyer <mail@beyermatthias.de> and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::path::PathBuf; +use std::collections::BTreeMap; + +use failure::Fallible as Result; +use failure::err_msg; +use toml_query::read::TomlValueReadExt; +use clap::ArgMatches; +use resiter::IterInnerOkOrElse; +use resiter::AndThen; +use resiter::Filter; +use resiter::Map; +use chrono::Local; +use email_format::Email; +use email_format::rfc5322::Parsable; +use handlebars::Handlebars; +use failure::Error; + +use libimagmail::mail::Mail; +use libimagmail::store::MailStore; +use libimagmail::notmuch::connection::NotmuchConnection; +use libimagrt::runtime::Runtime; +use libimagstore::storeid::StoreId; +use libimagstore::iter::get::StoreIdGetIteratorExtension; +use libimagentryedit::edit::edit_in_tmpfile; +use libimaginteraction::format::HandlebarsData; + +use crate::config::MailConfig; + +fn sid_value_of(scmd: &ArgMatches, s: &str) -> Option<Result<StoreId>> { + scmd.value_of(s).map(String::from).map(PathBuf::from).map(StoreId::new) +} + +pub fn new(rt: &Runtime) -> Result<()> { + let config = rt.config() + .ok_or_else(|| err_msg("Configuration missing"))? + .read_partial::<MailConfig>()? + .ok_or_else(|| err_msg("Configuration for \"mail\" missing"))?; + + let scmd = rt.cli().subcommand_matches("new").unwrap(); // safe by main() + let notmuch_path = config.get_notmuch_database_path_or_cli(rt); + debug!("notmuch path: {:?}", notmuch_path); + let notmuch_connection = NotmuchConnection::open(notmuch_path)?; + let store = rt.store().with_connection(¬much_connection); + + let bcc = sid_value_of(&scmd, "bcc").transpose()?; + let cc = sid_value_of(&scmd, "cc").transpose()?; + let to = sid_value_of(&scmd, "to") + .map(|r| r.map(|e| Some(vec![e]))) + .unwrap_or_else(|| rt.ids::<crate::ui::PathProvider>())? + .ok_or_else(|| err_msg("No ids supplied"))?; + let subject = scmd.value_of("subject").map(String::from); + + let in_reply_to = scmd.value_of("in-reply-to") + .map(String::from) + .map(|s| { + match StoreId::new(PathBuf::from(&s)) + .and_then(|sid| store.get(sid))? + { + Some(fle) => Ok(Some(fle)), + None => store.get_mail_by_id(&s), + } + }) + .transpose()? + .flatten(); + + let notmuch_path = config.get_notmuch_database_path_or_cli(rt); + debug!("notmuch path: {:?}", notmuch_path); + + let written_message_id = mk_processed_template(rt, &scmd, &config) + .and_then(|msg| edit_message_validated(rt, msg)) + .and_then(|msg| { + // Write message to maildir and return message id + maildir::Maildir::from(config.get_outgoing_maildir().to_path_buf()) + .store_new(&msg.as_bytes()) + .map_err(Error::from) + })?; + + // + // Writing the valid message to the outgoing maildir + // + + info!("Stored: {}", written_message_id); + debug!("Stored: {} in {}", written_message_id, config.get_outgoing_maildir().display()); + + Ok(()) +} + +pub fn reply_to(rt: &Runtime) -> Result<()> { + let config = rt.config() + .ok_or_else(|| err_msg("Configuration missing"))? + .read_partial::<MailConfig>()? + .ok_or_else(|| err_msg("Configuration for \"mail\" missing"))?; + + let scmd = rt.cli().subcommand_matches("reply-to").unwrap(); // safe by main() + let store = rt.store(); + + let in_reply_to = sid_value_of(&scmd, "in-reply-to") + .map(|r| r.map(|e| Some(vec![e]))) + .unwrap_or_else(|| rt.ids::<crate::ui::PathProvider>())? + .ok_or_else(|| err_msg("No ids supplied"))? + .into_iter() + .map(Ok) + .into_get_iter(rt.store()) + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|m| m.is_mail().map(|b| (b, m))) + .filter_ok(|tpl| tpl.0) + .map_ok(|tpl| tpl.1); + + unimplemented!() +} + +fn mk_processed_template(rt: &Runtime, scmd: &ArgMatches, config: &MailConfig) -> Result<String> { + debug!("Processing the template for the mail..."); + let mut hb_data = BTreeMap::new(); + + hb_data.insert(String::from("message_id"), HandlebarsData::Str(generate_message_id()?)); + hb_data.insert(String::from("date"), HandlebarsData::Str({ + scmd.value_of("date") + .map(String::from) + .unwrap_or_else(|| { + Local::now().to_rfc2822() + }) + })); + + hb_data.insert(String::from("from"), HandlebarsData::Str({ + scmd.value_of("from") + .map(String::from) + .unwrap_or_else(|| { + config.get_from_address().clone() + }) + })); + + hb_data.insert(String::from("to"), HandlebarsData::Str({ + scmd.value_of("to") + .map(String::from) + .ok_or_else(|| { + err_msg("Missing value for field field: 'to'") + })? + })); + + if let Some(in_reply_to) = scmd.value_of("in-reply-to").map(String::from) { + hb_data.insert(String::from("in_reply_to") , HandlebarsData::Str(in_reply_to)); + } + + if let Some(cc) = scmd.value_of("cc").map(String::from) { + hb_data.insert(String::from("cc") , HandlebarsData::Str(cc)); + } + + if let Some(bcc) = scmd.value_of("bcc").map(String::from) { + hb_data.insert(String::from("bcc") , HandlebarsData::Str(bcc)); + } + + hb_data.insert(String::from("subject"), HandlebarsData::Str({ + scmd.value_of("subject") + .map(String::from) + .unwrap_or_else(|| String::new()) + })); + + let template = if *config.get_edit_headers() { + debug!("Template with header header editing"); + config.get_header_template() + } else { + debug!("Template without header editing"); + config.get_default_template() + }; + + process_template(template, &hb_data) +} + +/// +/// Editing the text and validating it +/// +fn edit_message_validated(rt: &Runtime, mut msg: String) -> Result<String> { + edit_in_tmpfile(&rt, &mut msg)?; + + let (mail, remainder) = Email::parse(&msg.as_bytes())?; + debug!("Parsed: {}", mail); + debug!("Remainder: {:?}", remainder); + + if remainder.len() != 0 { + Err(err_msg("Some bytes are not parsed... cannot verify that the mail is correct!"))? + } + + Ok(msg) +} + +fn process_template(template: &str, data: &BTreeMap<String, HandlebarsData>) -> Result<String> { + let mut hb = Handlebars::new(); + hb.register_template_string("format", template)?; + + hb.register_escape_fn(::handlebars::no_escape); + ::libimaginteraction::format::register_all_color_helpers(&mut hb); + ::libimaginteraction::format::register_all_format_helpers(&mut hb); + + hb.render("format", data).map_err(Error::from) +} + +fn generate_message_id() -> Result<String> { + unimplemented!() +} |