summaryrefslogtreecommitdiffstats
path: root/bin/domain/imag-mail/src/new.rs
diff options
context:
space:
mode:
Diffstat (limited to 'bin/domain/imag-mail/src/new.rs')
-rw-r--r--bin/domain/imag-mail/src/new.rs219
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(&notmuch_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!()
+}