use crate::{global_app_data_resizing, AppConfig};
use lettre::message::Mailbox;
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::SmtpTransport;
use lettre::{Message, Transport};
use std::mem::MaybeUninit;
use std::sync::{Arc, Once};
use std::time::Duration;
use tokio::sync::{Notify, RwLock};
#[derive(Debug, Clone, Default)]
pub struct EmailBody {
pub email_receiver: String,
pub mine_email: String,
pub subject: String,
pub content: String,
pub html_email: bool,
}
impl EmailBody {
fn send(&self, mailer: &mut SmtpTransport) {
send_email(
mailer,
&self.email_receiver,
&self.mine_email,
&self.subject,
&self.content,
self.html_email,
);
}
}
#[derive(Debug, Clone, Default)]
pub struct EmailServerConfig {
pub smtp_server: String,
pub password: String,
pub port: String,
pub mine_email: String,
pub ssl: bool,
}
#[derive(Debug, Clone, Default)]
pub struct EmailSendingQueue {
queue: Vec<EmailBody>,
client_conf: EmailServerConfig,
started: bool,
notify: Arc<Notify>,
lock: Arc<RwLock<u32>>,
}
impl EmailSendingQueue {
pub fn new() -> Self {
Self {
queue: vec![],
client_conf: EmailServerConfig {
smtp_server: String::new(),
password: String::new(),
port: String::new(),
mine_email: String::new(),
ssl: false,
},
started: false,
notify: Arc::new(Notify::new()),
lock: Arc::new(RwLock::new(2)),
}
}
pub fn config(
&mut self,
smtp_server: String,
password: String,
port: String,
mine_email: String,
ssl: bool,
) {
self.client_conf.smtp_server = smtp_server;
self.client_conf.password = password.clone();
self.client_conf.mine_email = mine_email.clone();
self.client_conf.port = port;
self.client_conf.ssl = ssl;
}
pub async fn queue_send(&mut self, emb: &EmailBody) {
let ck = self.lock.clone();
let mut clemb = emb.clone();
clemb.mine_email = self.client_conf.mine_email.clone();
let mu = ck.write().await;
self.queue.push(clemb);
drop(mu);
self.notify.notify_one();
}
fn start(&'static mut self) {
if !self.started {
self.started = true;
start_email_queue(self);
}
}
fn shutdown(&mut self) {
if self.started {
self.started = false;
self.notify.notify_waiters();
}
}
}
fn start_email_queue(msq: &'static mut EmailSendingQueue) {
tokio::spawn(async move {
let creds = Credentials::new(
msq.client_conf.mine_email.clone(),
msq.client_conf.password.clone(),
);
log::info!("The email sending queue was created and processing.");
let notified = msq.notify.clone();
let rclock = msq.lock.clone();
while msq.started {
let mut fst = msq.queue.pop();
if fst.is_some() {
let mut mailer = SmtpTransport::starttls_relay(&msq.client_conf.smtp_server)
.unwrap()
.credentials(creds.clone())
.build();
while fst.is_some() {
let email = fst.unwrap();
email.send(&mut mailer);
{
let mu = rclock.read().await;
fst = msq.queue.pop();
drop(mu);
}
}
} else {
match tokio::time::timeout(Duration::from_secs(60), notified.notified()).await {
Ok(_) => {
log::info!("Received a new email notification. The loop will continues.");
}
Err(err) => {
global_app_data_resizing();
log::info!("Timeout {}", err);
}
}
}
}
});
}
fn send_email(
mailer: &mut SmtpTransport,
email_receiver: &str,
mine_email: &str,
subject: &str,
content: &str,
html_email: bool,
) {
let from: Mailbox = match mine_email.parse() {
Ok(t) => t,
Err(err) => {
log::info!("Error for parse mine email address. {}", err);
return;
}
};
let to: Mailbox = match email_receiver.parse() {
Ok(t) => t,
Err(err) => {
log::info!("Error for parse mine email address. {}", err);
return;
}
};
let email_build = if html_email {
Message::builder()
.from(from)
.to(to)
.subject(subject.to_string())
.body(content.to_string())
.unwrap()
} else {
Message::builder()
.from(from)
.to(to)
.subject(subject)
.body(content.to_string())
.unwrap()
};
match mailer.send(&email_build) {
Ok(_) => {
log::info!("Mail was sent.");
}
Err(err) => {
log::info!("Could not send email: {:?}", err);
}
}
}
pub fn get_email_queue() -> &'static mut EmailSendingQueue {
static mut STATIC_ESQ: MaybeUninit<EmailSendingQueue> = MaybeUninit::uninit();
static ONCE: Once = Once::new();
ONCE.call_once(|| unsafe {
let conf = AppConfig::get().lock().unwrap().to_owned();
let emailconf = conf.email_conf.clone();
async_std::task::block_on(async {
let mut esq = EmailSendingQueue::new();
esq.config(
emailconf.smtp_server,
emailconf.password,
emailconf.port,
emailconf.mine_email,
emailconf.ssl,
);
STATIC_ESQ.as_mut_ptr().write(esq);
});
});
unsafe { &mut *STATIC_ESQ.as_mut_ptr() }
}
pub fn start_email_queue_thread() {
get_email_queue().start();
}
pub fn stop_email_queue_thread() {
get_email_queue().shutdown();
}