chimes_utils/utils/
mail.rs1use crate::{global_app_data_resizing, AppConfig};
2use lettre::message::Mailbox;
3use lettre::transport::smtp::authentication::Credentials;
4use lettre::transport::smtp::SmtpTransport;
5use lettre::{Message, Transport};
6use std::mem::MaybeUninit;
7use std::sync::{Arc, Once};
8use std::time::Duration;
9use tokio::sync::{Notify, RwLock};
10
11#[derive(Debug, Clone, Default)]
12pub struct EmailBody {
13 pub email_receiver: String,
14 pub mine_email: String,
15 pub subject: String,
16 pub content: String,
17 pub html_email: bool,
18}
19
20impl EmailBody {
21 fn send(&self, mailer: &mut SmtpTransport) {
22 send_email(
23 mailer,
24 &self.email_receiver,
25 &self.mine_email,
26 &self.subject,
27 &self.content,
28 self.html_email,
29 );
30 }
31}
32
33#[derive(Debug, Clone, Default)]
34pub struct EmailServerConfig {
35 pub smtp_server: String,
36 pub password: String,
37 pub port: String,
38 pub mine_email: String,
39 pub ssl: bool,
40}
41#[derive(Debug, Clone, Default)]
42pub struct EmailSendingQueue {
43 queue: Vec<EmailBody>,
44 client_conf: EmailServerConfig,
45 started: bool,
46 notify: Arc<Notify>,
47 lock: Arc<RwLock<u32>>,
48}
49
50impl EmailSendingQueue {
51 pub fn new() -> Self {
52 Self {
53 queue: vec![],
54 client_conf: EmailServerConfig {
55 smtp_server: String::new(),
56 password: String::new(),
57 port: String::new(),
58 mine_email: String::new(),
59 ssl: false,
60 },
61 started: false,
62 notify: Arc::new(Notify::new()),
63 lock: Arc::new(RwLock::new(2)),
64 }
65 }
66
67 pub fn config(
68 &mut self,
69 smtp_server: String,
70 password: String,
71 port: String,
72 mine_email: String,
73 ssl: bool,
74 ) {
75 self.client_conf.smtp_server = smtp_server;
76 self.client_conf.password = password.clone();
77 self.client_conf.mine_email = mine_email.clone();
78 self.client_conf.port = port;
79 self.client_conf.ssl = ssl;
80 }
81
82 pub async fn queue_send(&mut self, emb: &EmailBody) {
83 let ck = self.lock.clone();
84 let mut clemb = emb.clone();
85 clemb.mine_email = self.client_conf.mine_email.clone();
86 let mu = ck.write().await;
87 self.queue.push(clemb);
88 drop(mu);
89 self.notify.notify_one();
90 }
91
92 fn start(&'static mut self) {
96 if !self.started {
97 self.started = true;
98 start_email_queue(self);
99 }
100 }
101
102 fn shutdown(&mut self) {
106 if self.started {
107 self.started = false;
108 self.notify.notify_waiters();
109 }
111 }
112}
113
114fn start_email_queue(msq: &'static mut EmailSendingQueue) {
115 tokio::spawn(async move {
116 let creds = Credentials::new(
117 msq.client_conf.mine_email.clone(),
118 msq.client_conf.password.clone(),
119 );
120
121 log::info!("The email sending queue was created and processing.");
122
123 let notified = msq.notify.clone();
124 let rclock = msq.lock.clone();
125
126 while msq.started {
127 let mut fst = msq.queue.pop();
129 if fst.is_some() {
130 let mut mailer = SmtpTransport::starttls_relay(&msq.client_conf.smtp_server)
131 .unwrap()
132 .credentials(creds.clone())
133 .build();
134
135 while fst.is_some() {
136 let email = fst.unwrap();
137 email.send(&mut mailer);
138
139 {
140 let mu = rclock.read().await;
141 fst = msq.queue.pop();
142 drop(mu);
143 }
144 }
145 } else {
146 match tokio::time::timeout(Duration::from_secs(60), notified.notified()).await {
147 Ok(_) => {
148 log::info!("Received a new email notification. The loop will continues.");
149 }
150 Err(err) => {
151 global_app_data_resizing();
152 log::info!("Timeout {}", err);
153 }
154 }
155 }
156 }
157 });
158}
159
160fn send_email(
161 mailer: &mut SmtpTransport,
162 email_receiver: &str,
163 mine_email: &str,
164 subject: &str,
165 content: &str,
166 html_email: bool,
167) {
168 let from: Mailbox = match mine_email.parse() {
169 Ok(t) => t,
170 Err(err) => {
171 log::info!("Error for parse mine email address. {}", err);
172 return;
173 }
174 };
175 let to: Mailbox = match email_receiver.parse() {
176 Ok(t) => t,
177 Err(err) => {
178 log::info!("Error for parse mine email address. {}", err);
179 return;
180 }
181 };
182 let email_build = if html_email {
183 Message::builder()
184 .from(from)
185 .to(to)
186 .subject(subject.to_string())
187 .body(content.to_string())
188 .unwrap()
189 } else {
190 Message::builder()
191 .from(from)
192 .to(to)
193 .subject(subject)
194 .body(content.to_string())
195 .unwrap()
196 };
197
198 match mailer.send(&email_build) {
199 Ok(_) => {
200 log::info!("Mail was sent.");
201 }
202 Err(err) => {
203 log::info!("Could not send email: {:?}", err);
204 }
205 }
206}
207
208pub fn get_email_queue() -> &'static mut EmailSendingQueue {
209 static mut STATIC_ESQ: MaybeUninit<EmailSendingQueue> = MaybeUninit::uninit();
211 static ONCE: Once = Once::new();
213
214 ONCE.call_once(|| unsafe {
215 let conf = AppConfig::get().lock().unwrap().to_owned();
217 let emailconf = conf.email_conf.clone();
218
219 async_std::task::block_on(async {
220 let mut esq = EmailSendingQueue::new();
221 esq.config(
222 emailconf.smtp_server,
223 emailconf.password,
224 emailconf.port,
225 emailconf.mine_email,
226 emailconf.ssl,
227 );
228 STATIC_ESQ.as_mut_ptr().write(esq);
229 });
230 });
231 unsafe { &mut *STATIC_ESQ.as_mut_ptr() }
232}
233
234pub fn start_email_queue_thread() {
235 get_email_queue().start();
236}
237
238pub fn stop_email_queue_thread() {
239 get_email_queue().shutdown();
240}