use std::{
fs,
io::{BufRead, BufReader},
};
use anyhow::{Context, Error, Result};
use duct::cmd;
use mail_parser::MessageParser;
use crate::model::Mua;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Message {
#[serde(skip_serializing, skip_deserializing)]
pub raw: String,
pub id: String,
pub subject: String,
pub sender: String,
pub sender_addr: String,
pub recipient: String,
pub recipient_addr: String,
pub at: String,
}
impl Message {
pub fn new(raw: String) -> Result<Message> {
let (id, subject, sender, sender_addr, recipient, recipient_addr) = Message::details(&raw)?;
let at = String::from("not scheduled");
Ok(Message {
raw,
id,
subject,
sender,
sender_addr,
recipient,
recipient_addr,
at,
})
}
fn details(raw: &String) -> Result<(String, String, String, String, String, String)> {
let rfc5322 = MessageParser::default()
.parse(raw)
.context("Unable to parse input as an RFC 5322 MIME message")?;
let id = String::from(
rfc5322
.message_id()
.context("There appears to be no unique message ID")?,
);
let subject = String::from(rfc5322.subject().unwrap_or("[no subject]"));
let sender = rfc5322
.from()
.context("From field is missing from RFC 5322 MIME message")?
.first()
.context("There appears to be no senders")?;
let sender_addr = String::from(sender.address().context("Sender has no address")?);
let sender = String::from(sender.name().unwrap_or(""));
let recipient = rfc5322
.to()
.context("To field is missing from RFC 5322 MIME message")?
.first()
.context("There appears to be no recipients")?;
let recipient_addr = String::from(recipient.address().context("Recipient has no address")?);
let recipient = String::from(recipient.name().unwrap_or(""));
Ok((id, subject, sender, sender_addr, recipient, recipient_addr))
}
pub fn enqueue(&self) -> Result<(String, String)> {
let echo_cmd = String::from(format!("echo $'{}' | mnsend", self.id));
let mut args = vec!["-q", "m"];
self.at.split(" ").for_each(|arg| args.push(arg));
let cmd = cmd("at", &args);
let reader = cmd.stdin_bytes(echo_cmd).stderr_to_stdout().reader()?;
let reader = BufReader::new(reader);
let job = match reader.lines().last() {
Some(job) => job?,
None => {
return Err(Error::msg(
"Invalid time, could not schedule mail for delivery",
));
}
};
let split = job.splitn(4, " ").collect::<Vec<&str>>();
let jobid = String::from(split[1]);
let at = String::from(split[3].replace(" ", " "));
Ok((jobid, at))
}
pub fn send(&mut self) -> Result<()> {
let (rc, account, path) = Mua::search(self)?;
cmd!("neomutt", "-F", rc, "-F", account, "-H", "-")
.stdin_bytes(self.raw.as_bytes())
.run()?;
fs::remove_file(path)?;
Ok(())
}
}