use async_std::task;
use async_tls::TlsConnector;
use enqueuemail::connector;
use enqueuemail::page_content;
use enqueuemail::page_title_from_html;
use enqueuemail::Jump;
use enqueuemail::Options;
use enqueuemail::Parts;
use env_logger::Env;
use std::boxed::Box;
use std::convert::TryFrom;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::stderr;
use std::io::stdout;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use structopt::StructOpt;
#[macro_use]
extern crate log;
#[deny(missing_docs, unused_variables)]
const HOME: &str = "HOME";
const QUEUE_DIR: &str = ".msmtp.queue";
const MAX_REDIRECTS: u8 = 10;
const KEY_ENQUEUE_MAIL_FROM: &str = "ENQUEUE_MAIL_FROM";
const KEY_ENQUEUE_MAIL_TO: &str = "ENQUEUE_MAIL_TO";
const KEY_ENQUEUE_MAIL_CC: &str = "ENQUEUE_MAIL_CC";
const KEY_ENQUEUE_MAIL_FCC: &str = "ENQUEUE_MAIL_FCC";
const KEY_ENQUEUE_MAIL_ACCOUNT: &str = "ENQUEUE_MAIL_ACCOUNT";
fn main() -> Result<(), Box<dyn Error>> {
env_logger::from_env(Env::default().default_filter_or("warn")).init();
let options = Options::from_args();
let parts: Parts = Parts::try_from(&options)?;
let connector: TlsConnector = task::block_on(async { connector(&options.cafile).await })?;
let mut jumps: u8 = MAX_REDIRECTS;
let pc: Vec<u8> = task::block_on(async {
let parts: &mut Parts = &mut parts.clone();
loop {
match page_content(&connector, &parts).await {
Err(e) => {
panic!(e.to_string());
}
Ok(Jump::Next(url)) if jumps != 0 => {
jumps -= 1;
*parts = Parts::try_from(url.as_str())
.expect("could not derive the URL parts from the Redirect location");
}
Ok(Jump::Next(_)) => {
panic!("too many redirects");
}
Ok(Jump::Landing(page)) => break page,
};
}
});
let page_title: String = match page_title_from_html(&pc) {
Ok(Some(t)) => t,
Ok(None) => parts.domain,
Err(_) => panic!("unparseable page title"),
};
info!("page title: {}", &page_title);
let home: String = match env::var(HOME) {
Ok(val) => val,
Err(_) => panic!("$HOME must be defined in the ENV"),
};
let filename: String = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n.as_secs().to_string(),
Err(_) => panic!("SystemTime before UNIX EPOCH!"),
};
let mail_filename = format!("{}.mail", filename);
let mail_path = Path::new(&home).join(QUEUE_DIR).join(&mail_filename);
let mail_from: String = match env::var(KEY_ENQUEUE_MAIL_FROM) {
Ok(val) => val,
Err(_) => panic!(format!(
"missing ENV configuration: {}",
KEY_ENQUEUE_MAIL_FROM
)),
};
let mail_to: String = match env::var(KEY_ENQUEUE_MAIL_TO) {
Ok(val) => val,
Err(_) => panic!(format!(
"missing ENV configuration: {}",
KEY_ENQUEUE_MAIL_TO
)),
};
let mail_cc: String = match env::var(KEY_ENQUEUE_MAIL_CC) {
Ok(val) => val,
Err(_) => panic!(format!(
"missing ENV configuration: {}",
KEY_ENQUEUE_MAIL_CC
)),
};
let mail_fcc: String = match env::var(KEY_ENQUEUE_MAIL_FCC) {
Ok(val) => val,
Err(_) => panic!(format!(
"missing ENV configuration: {}",
KEY_ENQUEUE_MAIL_FCC
)),
};
let mail_account: String = match env::var(KEY_ENQUEUE_MAIL_ACCOUNT) {
Ok(val) => format!("-a default {}\n", val),
Err(_) => panic!(format!(
"missing ENV configuration: {}",
KEY_ENQUEUE_MAIL_ACCOUNT
)),
};
let mut file = File::create(mail_path)
.map_err(|e| format!("Problem opening the file {:#?}: {}", &mail_filename, e))?;
let mail_body: String = format!(
"From: {}\n\
To: {}\n\
Cc: {}\n\
Subject: [link] {}\n\
Fcc: {}\n\
Bcc:\n\
\n\
title: {}\n\
URL: {}\n\
link: [[{}][{}]]\n\
---\n",
&mail_from,
&mail_to,
&mail_cc,
page_title,
&mail_fcc,
page_title,
&parts.url.as_str(),
&parts.url.as_str(),
page_title,
);
debug!("mail body: \n{}", &mail_body);
file.write_all(mail_body.as_bytes())?;
let msmtp_filename = format!("{}.msmtp", filename);
let mut file = File::create(Path::new(&home).join(QUEUE_DIR).join(&msmtp_filename))
.map_err(|e| format!("Problem opening the file {:#?}: {}", &msmtp_filename, e))?;
file.write_all(&mail_account.into_bytes())?;
if options.run {
let output = Command::new("msmtp-queue")
.args(&["-r"])
.output()
.expect("command failed to start");
return if output.status.success() {
stdout()
.write_all(&output.stdout)
.expect("failed to write on STDOUT");
Ok(())
} else {
stderr()
.write_all(&output.stderr)
.expect("failed to write on STDERR");
let e: Box<dyn Error> = From::from("queue flush failed");
Err(e)
};
}
Ok(())
}