]> Untitled Git - lemmy.git/blob - crates/utils/src/email.rs
d9ac1710d86762f8e2760b2b376e702611b0fdac
[lemmy.git] / crates / utils / src / email.rs
1 use crate::{settings::structs::Settings, LemmyError};
2 use lettre::{
3   message::{header, Mailbox, MultiPart, SinglePart},
4   transport::smtp::{
5     authentication::Credentials,
6     client::{Tls, TlsParameters},
7     extension::ClientId,
8   },
9   Address,
10   Message,
11   SmtpTransport,
12   Transport,
13 };
14 use std::str::FromStr;
15 use uuid::Uuid;
16
17 pub fn send_email(
18   subject: &str,
19   to_email: &str,
20   to_username: &str,
21   html: &str,
22   settings: &Settings,
23 ) -> Result<(), LemmyError> {
24   let email_config = settings
25     .email
26     .to_owned()
27     .ok_or_else(|| LemmyError::from_message("no_email_setup"))?;
28   let domain = settings.hostname.to_owned();
29
30   let (smtp_server, smtp_port) = {
31     let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
32     if email_and_port.len() == 1 {
33       return Err(LemmyError::from_message(
34         "email.smtp_server needs a port, IE smtp.xxx.com:465",
35       ));
36     }
37
38     (
39       email_and_port[0],
40       email_and_port[1]
41         .parse::<u16>()
42         .expect("email needs a port"),
43     )
44   };
45
46   let email = Message::builder()
47     .from(
48       email_config
49         .smtp_from_address
50         .parse()
51         .expect("email from address isn't valid"),
52     )
53     .to(Mailbox::new(
54       Some(to_username.to_string()),
55       Address::from_str(to_email).expect("email to address isn't valid"),
56     ))
57     .message_id(Some(format!("{}@{}", Uuid::new_v4(), settings.hostname)))
58     .subject(subject)
59     .multipart(
60       MultiPart::mixed().multipart(
61         MultiPart::alternative()
62           .singlepart(
63             SinglePart::builder()
64               .header(header::ContentType::TEXT_PLAIN)
65               .body(html.to_string()),
66           )
67           .multipart(
68             MultiPart::related().singlepart(
69               SinglePart::builder()
70                 .header(header::ContentType::TEXT_HTML)
71                 .body(html.to_string()),
72             ),
73           ),
74       ),
75     )
76     .expect("email built incorrectly");
77
78   // don't worry about 'dangeous'. it's just that leaving it at the default configuration
79   // is bad.
80   let mut builder = SmtpTransport::builder_dangerous(smtp_server).port(smtp_port);
81
82   // Set the TLS
83   if email_config.use_tls {
84     let tls_config = TlsParameters::new(smtp_server.to_string()).expect("the TLS backend is happy");
85     builder = builder.tls(Tls::Wrapper(tls_config));
86   }
87
88   // Set the creds if they exist
89   if let (Some(username), Some(password)) = (email_config.smtp_login, email_config.smtp_password) {
90     builder = builder.credentials(Credentials::new(username, password));
91   }
92
93   let mailer = builder.hello_name(ClientId::Domain(domain)).build();
94
95   let result = mailer.send(&email);
96
97   match result {
98     Ok(_) => Ok(()),
99     Err(e) => Err(LemmyError::from(e).with_message("email_send_failed")),
100   }
101 }