rnotifylib/destination/kinds/
mail.rs1use std::error::Error;
2use lettre::message::{Mailbox, SinglePart};
3use lettre::{SmtpTransport, Transport};
4use lettre::transport::smtp::authentication;
5use serde::{Serialize, Deserialize};
6use crate::destination::{MessageDestination, SerializableDestination};
7use crate::message::{Message, MessageDetail};
8use crate::util::html::HtmlMessageDetail;
9
10#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
11pub struct MailDestination {
12 from: Mailbox,
13 relay: Relay,
14 to: Mailbox,
15 reply_to: Option<Mailbox>,
16}
17
18#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
19pub struct Relay {
20 url: String,
21 #[serde(default = "default_port")]
22 port: u16,
23 #[serde(default = "bool::default")]
24 start_tls_relay: bool,
25 username: String,
26 password: String,
27}
28
29fn default_port() -> u16 {
30 return lettre::transport::smtp::SMTP_PORT;
31}
32
33impl MessageDestination for MailDestination {
34 fn send(&self, message: &Message) -> Result<(), Box<dyn Error>> {
35 println!("Message destination.");
36 let mut message_builder = lettre::Message::builder()
37 .from(self.from.clone())
38 .to(self.to.clone());
39
40 if let Some(reply_to) = self.reply_to.clone() {
41 message_builder = message_builder.reply_to(reply_to);
42 }
43 if let Some(title) = message.get_title() {
44 message_builder = message_builder.subject(title);
45 }
46
47 let email = message_builder.singlepart(create_body(message.get_message_detail()))?;
48
49 let creds = authentication::Credentials::new(self.relay.username.clone(), self.relay.password.clone());
50 let mailer =
51 if self.relay.start_tls_relay { SmtpTransport::starttls_relay(&self.relay.url) } else { SmtpTransport::relay(&self.relay.url) }?.port(self.relay.port)
52 .credentials(creds)
53 .build();
54
55 mailer.send(&email)?;
56 println!("Mail successfully sent.");
57 Ok(())
58 }
59}
60
61#[typetag::serde(name = "Mail")]
62impl SerializableDestination for MailDestination {
63 fn as_message_destination(&self) -> &dyn MessageDestination {
64 self
65 }
66}
67
68fn create_body(detail: &MessageDetail) -> SinglePart {
69 match detail {
70 MessageDetail::Raw(raw) => {
71 SinglePart::plain(raw.to_owned())
72 }
73 MessageDetail::Formatted(formatted) => {
74 SinglePart::html(formatted.create_html())
75 }
76 }
77}