use std::{
error::Error,
io::{Read, stdin},
};
use clap::Parser;
use mail_parser::{Addr, Address, Message, MessageParser};
use mail_send::{SmtpClientBuilder, smtp::message::Message as SmtpMessage};
#[derive(Parser)]
struct Args {
#[arg(short, long)]
username: String,
#[arg(short, long)]
password: String,
#[arg(short, long)]
host: String,
#[arg(short, long, default_value_t = 465)]
port: u16,
}
async fn run() -> Result<(), Box<dyn Error>> {
let args = Args::try_parse()?;
let mut bytes = Vec::new();
stdin().read_to_end(&mut bytes)?;
let msg = MessageParser::new()
.parse(&bytes)
.ok_or("could not send email as it is invalid: no headers found")?;
eprintln!("Sending over SMTP...");
SmtpClientBuilder::new(args.host, args.port)
.credentials((args.username, args.password))
.connect()
.await?
.send(parsed_msg_to_smtp_msg(&msg)?)
.await?;
Ok(())
}
#[tokio::main]
async fn main() {
if let Err(e) = run().await {
eprintln!("m2send: {e}");
}
}
fn parsed_msg_to_smtp_msg<'a>(msg: &'a Message<'a>) -> Result<SmtpMessage<'a>, &'static str> {
let from = msg
.from()
.and_then(Address::first)
.and_then(Addr::address)
.ok_or("missing sender")?;
let to = msg
.to()
.and_then(Address::as_list)
.unwrap_or_default()
.iter()
.flat_map(Addr::address);
let cc = msg
.cc()
.and_then(Address::as_list)
.unwrap_or_default()
.iter()
.flat_map(Addr::address);
let bcc = msg
.bcc()
.and_then(Address::as_list)
.unwrap_or_default()
.iter()
.flat_map(Addr::address);
Ok(SmtpMessage::new(
from,
to.chain(cc).chain(bcc),
msg.raw_message.as_ref(),
))
}