mod html;
mod pgp;
use std::{
error::Error,
io::{Read, Write, stdin, stdout},
};
use clap::Parser;
use html::generate_html;
use m2::Message;
use mail_builder::{
MessageBuilder,
headers::content_type::ContentType,
mime::{BodyPart, MimePart},
};
#[derive(Debug, Parser)]
#[command(name = "m2proc", author, version)]
pub struct Args {
#[arg(long, default_value_t = false)]
sign: bool,
#[arg(long, default_value_t = false)]
no_html: bool,
}
pub fn preprocess<R: Read, W: Write>(
mut r: R,
w: W,
sign: bool,
gen_html: bool,
) -> Result<(), Box<dyn Error>> {
let msg = Message::from_reader(&mut r)?;
let mut builder = msg.builder();
if gen_html {
builder = multipart_html(builder);
}
if sign {
builder = sign_builder(builder)?;
}
Ok(builder.write_to(w)?)
}
pub fn multipart_html(mut builder: MessageBuilder) -> MessageBuilder {
let plain_ctype = ContentType::new("text/plain")
.attribute("charset", "utf-8")
.attribute("format", "flowed");
let html_ctype = ContentType::new("text/html").attribute("charset", "utf-8");
if let Some(ref mut body) = builder.body {
let plain_body: String = match &body.contents {
BodyPart::Text(s) => s.to_string(),
_ => "".to_string(),
};
let html_body = generate_html(plain_body.clone());
builder.headers.append(&mut body.headers);
builder = builder.body(MimePart::new(
"multipart/alternative",
vec![
MimePart::new(plain_ctype.clone(), plain_body),
MimePart::new(html_ctype.clone(), html_body),
],
));
}
builder
}
pub fn sign_builder(msg_builder: MessageBuilder) -> Result<MessageBuilder, Box<dyn Error>> {
let body = msg_builder.clone().body.ok_or("no body to sign")?;
let mut mail_bytes = Vec::new();
body.clone().write_part(&mut mail_bytes)?;
let mut signature = Vec::<u8>::new();
pgp::sign(mail_bytes.trim_ascii(), &mut signature)?;
let signature = MimePart::new(
"application/pgp-signature",
String::from_utf8_lossy(&signature).into_owned(),
)
.transfer_encoding("7bit")
.attachment("signature.asc");
let content_type = ContentType {
c_type: "multipart/signed".into(),
attributes: vec![
("micalg".into(), "pgp-sha1".into()),
("protocol".into(), "application/pgp-signature".into()),
],
};
Ok(msg_builder.body(MimePart::new(content_type, vec![body, signature])))
}
pub fn main() {
let args = Args::parse();
if let Err(e) = preprocess(stdin(), stdout(), args.sign, !args.no_html) {
eprintln!("{e}");
}
}