chimes_utils/utils/
mail.rs

1use 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    /**
93     * 启动邮件发送队列
94     */
95    fn start(&'static mut self) {
96        if !self.started {
97            self.started = true;
98            start_email_queue(self);
99        }
100    }
101
102    /**
103     * 关闭邮件发送队列
104     */
105    fn shutdown(&mut self) {
106        if self.started {
107            self.started = false;
108            self.notify.notify_waiters();
109            // waiting for shutdown
110        }
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            // Open connection to Gmail
128            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    // 使用MaybeUninit延迟初始化
210    static mut STATIC_ESQ: MaybeUninit<EmailSendingQueue> = MaybeUninit::uninit();
211    // Once带锁保证只进行一次初始化
212    static ONCE: Once = Once::new();
213
214    ONCE.call_once(|| unsafe {
215        // CONF = 1u64;
216        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}