2024-08-05 19:29:02 +02:00
|
|
|
use std::error::Error;
|
|
|
|
|
2024-08-05 19:57:12 +02:00
|
|
|
use colored::Colorize;
|
2024-08-05 19:29:02 +02:00
|
|
|
use lettre::{Message, SmtpTransport, Transport};
|
2024-08-05 04:45:34 +02:00
|
|
|
use lettre::message::{header, SinglePart};
|
|
|
|
use lettre::transport::smtp::authentication::Credentials;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2024-08-05 19:29:02 +02:00
|
|
|
use crate::config_spec::{CfgField, CfgGroup};
|
|
|
|
|
2024-08-05 04:45:34 +02:00
|
|
|
pub const CONFIG: CfgGroup<'static> = CfgGroup {
|
|
|
|
name: "email",
|
|
|
|
description: "Sending emails.",
|
|
|
|
fields: &[
|
|
|
|
CfgField::Default {
|
|
|
|
key: "server",
|
|
|
|
default: "mail.berlin.ccc.de",
|
|
|
|
description: "SMTP server used for sending emails.",
|
|
|
|
},
|
|
|
|
CfgField::Default {
|
|
|
|
key: "user",
|
|
|
|
default: "plenum-bot@berlin.ccc.de",
|
|
|
|
description: "User name used for authenticating with the mail server.",
|
|
|
|
},
|
|
|
|
CfgField::Password {
|
|
|
|
key: "password",
|
|
|
|
description: "Password for authenticating with the mail server.",
|
|
|
|
},
|
|
|
|
CfgField::Default {
|
2024-08-05 19:29:02 +02:00
|
|
|
key: "from",
|
2024-08-05 04:45:34 +02:00
|
|
|
default: "Plenumsbot <plenum-bot@berlin.ccc.de>",
|
|
|
|
description: "Email address to use for \"From:\".",
|
|
|
|
},
|
|
|
|
CfgField::Default {
|
|
|
|
key: "to",
|
|
|
|
default: "CCCB Intern <intern@berlin.ccc.de>",
|
|
|
|
description: "Recipient of the emails sent.",
|
|
|
|
},
|
|
|
|
CfgField::Optional {
|
|
|
|
key: "last-message-id",
|
|
|
|
description:
|
|
|
|
"Message-Id of last initial announcement to send In-Reply-To (if applicable).",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Email {
|
|
|
|
server: String,
|
|
|
|
credentials: Credentials,
|
|
|
|
message_id_suffix: String,
|
|
|
|
is_dry_run: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SimpleEmail {
|
|
|
|
base: Email,
|
|
|
|
from: String,
|
|
|
|
to: String,
|
|
|
|
in_reply_to: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SimpleEmail {
|
|
|
|
pub fn new(base: Email, from: &str, to: &str, in_reply_to: Option<String>) -> Self {
|
|
|
|
Self { base: base.clone(), from: from.to_string(), to: to.to_string(), in_reply_to }
|
|
|
|
}
|
|
|
|
pub fn send_email(&self, subject: String, body: String) -> Result<String, Box<dyn Error>> {
|
|
|
|
self.base.send_email(
|
|
|
|
self.from.clone(),
|
|
|
|
self.to.clone(),
|
|
|
|
subject,
|
|
|
|
body,
|
|
|
|
self.in_reply_to.clone(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Email {
|
|
|
|
pub fn new(server: &str, user: &str, pass: &str, is_dry_run: bool) -> Self {
|
|
|
|
let message_id_suffix = user.split('@').next_back().unwrap_or(&server).to_string();
|
|
|
|
let credentials = Credentials::new(user.to_string(), pass.to_string());
|
|
|
|
Self { server: server.to_string(), credentials, message_id_suffix, is_dry_run }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn send_email(
|
|
|
|
&self, from: String, to: String, subject: String, body: String, in_reply_to: Option<String>,
|
|
|
|
) -> Result<String, Box<dyn Error>> {
|
|
|
|
let message_id = Uuid::new_v4().to_string() + &self.message_id_suffix;
|
|
|
|
let email = Message::builder()
|
|
|
|
.from(from.parse().unwrap())
|
|
|
|
.to(to.parse().unwrap())
|
|
|
|
.message_id(Some(message_id.clone()));
|
|
|
|
let email =
|
|
|
|
if in_reply_to.is_some() { email.in_reply_to(in_reply_to.unwrap()) } else { email }
|
|
|
|
.subject(subject)
|
|
|
|
.singlepart(
|
|
|
|
SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
if !self.is_dry_run {
|
|
|
|
let mailer = SmtpTransport::starttls_relay(&self.server)?
|
|
|
|
.credentials(self.credentials.clone())
|
|
|
|
.build();
|
|
|
|
mailer.send(&email)?;
|
|
|
|
Ok(message_id)
|
|
|
|
} else {
|
|
|
|
println!(
|
2024-08-05 19:57:12 +02:00
|
|
|
"{}\n(raw message:)\n{}",
|
|
|
|
"[DRY RUN - NOT sending email]".yellow(),
|
|
|
|
std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))").blue()
|
2024-08-05 04:45:34 +02:00
|
|
|
);
|
|
|
|
Ok("dummy-message-id@localhost".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|