]> Untitled Git - lemmy.git/blob - crates/utils/src/email.rs
62cbe3c5e046ad890a352db1eefaed61db221bcd
[lemmy.git] / crates / utils / src / email.rs
1 use crate::{error::LemmyError, settings::structs::Settings};
2 use html2text;
3 use lettre::{
4   message::{Mailbox, MultiPart},
5   transport::smtp::{authentication::Credentials, extension::ClientId},
6   Address,
7   Message,
8   SmtpTransport,
9   Transport,
10 };
11 use std::str::FromStr;
12 use uuid::Uuid;
13
14 pub mod translations {
15   rosetta_i18n::include_translations!();
16 }
17
18 pub fn send_email(
19   subject: &str,
20   to_email: &str,
21   to_username: &str,
22   html: &str,
23   settings: &Settings,
24 ) -> Result<(), LemmyError> {
25   let email_config = settings
26     .email
27     .clone()
28     .ok_or_else(|| LemmyError::from_message("no_email_setup"))?;
29   let domain = settings.hostname.clone();
30
31   let (smtp_server, smtp_port) = {
32     let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
33     let email = *email_and_port
34       .first()
35       .ok_or_else(|| LemmyError::from_message("missing an email"))?;
36     let port = email_and_port
37       .get(1)
38       .ok_or_else(|| {
39         LemmyError::from_message("email.smtp_server needs a port, IE smtp.xxx.com:465")
40       })?
41       .parse::<u16>()?;
42
43     (email, port)
44   };
45
46   // use usize::MAX as the line wrap length, since lettre handles the wrapping for us
47   let plain_text = html2text::from_read(html.as_bytes(), usize::MAX);
48
49   let email = Message::builder()
50     .from(
51       email_config
52         .smtp_from_address
53         .parse()
54         .expect("email from address isn't valid"),
55     )
56     .to(Mailbox::new(
57       Some(to_username.to_string()),
58       Address::from_str(to_email).expect("email to address isn't valid"),
59     ))
60     .message_id(Some(format!("<{}@{}>", Uuid::new_v4(), settings.hostname)))
61     .subject(subject)
62     .multipart(MultiPart::alternative_plain_html(
63       plain_text,
64       html.to_string(),
65     ))
66     .expect("email built incorrectly");
67
68   // don't worry about 'dangeous'. it's just that leaving it at the default configuration
69   // is bad.
70
71   // Set the TLS
72   let builder_dangerous = SmtpTransport::builder_dangerous(smtp_server).port(smtp_port);
73
74   let mut builder = match email_config.tls_type.as_str() {
75     "starttls" => SmtpTransport::starttls_relay(smtp_server)?,
76     "tls" => SmtpTransport::relay(smtp_server)?,
77     _ => builder_dangerous,
78   };
79
80   // Set the creds if they exist
81   let smtp_password = std::env::var("LEMMY_SMTP_PASSWORD")
82     .ok()
83     .or(email_config.smtp_password);
84
85   if let (Some(username), Some(password)) = (email_config.smtp_login, smtp_password) {
86     builder = builder.credentials(Credentials::new(username, password));
87   }
88
89   let mailer = builder.hello_name(ClientId::Domain(domain)).build();
90
91   let result = mailer.send(&email);
92
93   match result {
94     Ok(_) => Ok(()),
95     Err(e) => Err(LemmyError::from_error_message(e, "email_send_failed")),
96   }
97 }