tiny_web/sys/
mail.rs

1use std::{path::Path, sync::Arc};
2
3use lettre::{
4    message::{header::ContentType, Attachment, Mailbox, MultiPart, SinglePart},
5    transport::smtp::{
6        authentication::{Credentials, Mechanism},
7        client::{Tls, TlsParametersBuilder},
8    },
9    AsyncFileTransport, AsyncSendmailTransport, AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
10};
11use serde::Serialize;
12use serde_json::Value;
13use tiny_web_macro::fnv1a_64;
14use tokio::fs::create_dir_all;
15
16use super::{data::Data, dbs::adapter::DB, log::Log};
17
18/// Add file to the message struct.
19///
20/// # Values
21///
22/// * `name: String` - Name of file;
23/// * `mime: Option<String>` - Mime type of file;
24/// * `data: Vec<u8>` - Data.
25#[derive(Debug, Clone, Serialize)]
26pub struct MailBodyFile {
27    /// Name of file
28    pub name: String,
29    /// Mime type of file
30    pub mime: Option<String>,
31    /// Data
32    pub data: Vec<u8>,
33}
34
35/// Add html page to the message struct.
36///
37/// # Values
38///
39/// * `text: Option<String>` - Text part;
40/// * `html: String` - Html part;
41/// * `file: Vec<MailBodyFile>` - List of inline files.
42#[derive(Debug, Clone, Serialize)]
43pub struct MailBodyHtml {
44    /// Text part
45    pub text: Option<String>,
46    /// Html part
47    pub html: String,
48    /// List of inline files
49    pub file: Vec<MailBodyFile>,
50}
51
52/// Types of email messages
53#[derive(Debug, Clone, Serialize)]
54pub enum MailBody {
55    /// Text part
56    Text(String),
57    /// Html part
58    Html(MailBodyHtml),
59    /// File part
60    File(MailBodyFile),
61}
62
63/// Email message
64#[derive(Debug, Clone, Serialize)]
65pub struct MailMessage {
66    /// To
67    pub to: Vec<String>,
68    /// CC
69    pub cc: Option<Vec<String>>,
70    /// BCC
71    pub bcc: Option<Vec<String>>,
72    /// FROM
73    pub from: String,
74    /// REPLY-TO
75    pub reply_to: Option<String>,
76    /// SUBJECT
77    pub subject: String,
78    /// List of attachments
79    pub body: Vec<MailBody>,
80}
81
82/// Email config
83#[derive(Debug, Clone)]
84pub struct SmtpInfo {
85    /// Server we are connecting to
86    server: String,
87    /// Port to connect to
88    port: u16,
89    /// TLS security configuration
90    tls: Tls,
91    /// Optional enforced authentication mechanism
92    authentication: Vec<Mechanism>,
93    /// Credentials
94    credentials: Option<Credentials>,
95}
96
97/// Email provider
98#[derive(Debug, Clone)]
99pub enum MailProvider {
100    /// Don't send emails
101    None,
102    /// Use sendmail
103    Sendmail(String),
104    /// Use SMTP protocol
105    SMTP(SmtpInfo),
106    /// Save to folder
107    File(String),
108}
109
110/// Send email struct
111#[derive(Debug)]
112pub(crate) struct Mail {
113    /// Provider
114    pub provider: MailProvider,
115}
116
117impl Mail {
118    /// Create new provider
119    pub async fn new(db: Arc<DB>) -> Mail {
120        Mail { provider: Mail::get_provider(db).await }
121    }
122
123    /// Get provider from database
124    async fn get_provider(db: Arc<DB>) -> MailProvider {
125        if !db.in_use() {
126            return MailProvider::None;
127        }
128        match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:provider")], false).await {
129            Some(res) => {
130                if !res.is_empty() {
131                    let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
132                        row
133                    } else {
134                        Log::warning(3011, Some("query:lib_get_setting:mail:provider".to_owned()));
135                        return MailProvider::None;
136                    };
137                    if row.is_empty() {
138                        Log::warning(3011, Some("query:lib_get_setting:mail:provider:empty".to_owned()));
139                        return MailProvider::None;
140                    }
141                    let provider = if let Data::String(provider) = unsafe { row.get_unchecked(0) } {
142                        provider.clone()
143                    } else {
144                        Log::warning(3011, Some("query:lib_get_setting:mail:provider:type".to_owned()));
145                        return MailProvider::None;
146                    };
147
148                    match provider.as_ref() {
149                        "Sendmail" => match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:sendmail")], false).await {
150                            Some(res) => {
151                                if !res.is_empty() {
152                                    let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
153                                        row
154                                    } else {
155                                        Log::warning(3011, Some("query:lib_get_setting:mail:sendmail".to_owned()));
156                                        return MailProvider::None;
157                                    };
158                                    if row.is_empty() {
159                                        Log::warning(3011, Some("query:lib_get_setting:mail:sendmail:empty".to_owned()));
160                                        return MailProvider::None;
161                                    }
162                                    let path = if let Data::String(path) = unsafe { row.get_unchecked(0) } {
163                                        path.clone()
164                                    } else {
165                                        Log::warning(3011, Some("query:lib_get_setting:mail:sendmail:type".to_owned()));
166                                        return MailProvider::None;
167                                    };
168                                    if !path.is_empty() {
169                                        MailProvider::File(path)
170                                    } else {
171                                        Log::warning(3011, Some("mail:sendmail".to_owned()));
172                                        MailProvider::None
173                                    }
174                                } else {
175                                    Log::warning(3011, Some("mail:sendmail".to_owned()));
176                                    MailProvider::None
177                                }
178                            }
179                            None => {
180                                Log::warning(3011, Some("mail:sendmail".to_owned()));
181                                MailProvider::None
182                            }
183                        },
184                        "SMTP" => {
185                            let server =
186                                match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:server")], false).await {
187                                    Some(res) => {
188                                        if !res.is_empty() {
189                                            let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
190                                                row
191                                            } else {
192                                                Log::warning(3011, Some("query:lib_get_setting:mail:smtp:server".to_owned()));
193                                                return MailProvider::None;
194                                            };
195                                            if row.is_empty() {
196                                                Log::warning(3011, Some("query:lib_get_setting:mail:smtp:server:empty".to_owned()));
197                                                return MailProvider::None;
198                                            }
199                                            let server = if let Data::String(server) = unsafe { row.get_unchecked(0) } {
200                                                server.clone()
201                                            } else {
202                                                Log::warning(3011, Some("query:lib_get_setting:mail:smtp:server:type".to_owned()));
203                                                return MailProvider::None;
204                                            };
205                                            if !server.is_empty() {
206                                                server
207                                            } else {
208                                                Log::warning(3011, Some("mail:smtp:server".to_owned()));
209                                                return MailProvider::None;
210                                            }
211                                        } else {
212                                            Log::warning(3011, Some("mail:smtp:server".to_owned()));
213                                            return MailProvider::None;
214                                        }
215                                    }
216                                    None => {
217                                        Log::warning(3011, Some("mail:smtp:server".to_owned()));
218                                        return MailProvider::None;
219                                    }
220                                };
221                            let port = match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:port")], false).await {
222                                Some(res) => {
223                                    if !res.is_empty() {
224                                        let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
225                                            row
226                                        } else {
227                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:port".to_owned()));
228                                            return MailProvider::None;
229                                        };
230                                        if row.is_empty() {
231                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:port:empty".to_owned()));
232                                            return MailProvider::None;
233                                        }
234                                        let port = if let Data::String(port) = unsafe { row.get_unchecked(0) } {
235                                            port.clone()
236                                        } else {
237                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:port:type".to_owned()));
238                                            return MailProvider::None;
239                                        };
240                                        match port.parse::<u16>() {
241                                            Ok(port) => port,
242                                            Err(_) => {
243                                                Log::warning(3011, Some("mail:smtp:port".to_owned()));
244                                                return MailProvider::None;
245                                            }
246                                        }
247                                    } else {
248                                        Log::warning(3011, Some("mail:smtp:port".to_owned()));
249                                        return MailProvider::None;
250                                    }
251                                }
252                                None => {
253                                    Log::warning(3011, Some("mail:smtp:port".to_owned()));
254                                    return MailProvider::None;
255                                }
256                            };
257                            let tls = match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:tls")], false).await {
258                                Some(res) => {
259                                    if !res.is_empty() {
260                                        let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
261                                            row
262                                        } else {
263                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:tls".to_owned()));
264                                            return MailProvider::None;
265                                        };
266                                        if row.is_empty() {
267                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:tls:empty".to_owned()));
268                                            return MailProvider::None;
269                                        }
270                                        let tls = if let Data::String(tls) = unsafe { row.get_unchecked(0) } {
271                                            tls.clone()
272                                        } else {
273                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:tls:type".to_owned()));
274                                            return MailProvider::None;
275                                        };
276                                        if !tls.is_empty() {
277                                            match tls.as_ref() {
278                                                "None" => Tls::None,
279                                                "STARTTLS" => {
280                                                    let param = match TlsParametersBuilder::new(server.clone())
281                                                        .dangerous_accept_invalid_certs(true)
282                                                        .build()
283                                                    {
284                                                        Ok(param) => param,
285                                                        Err(_) => {
286                                                            Log::warning(3011, Some("mail:smtp:tls".to_owned()));
287                                                            return MailProvider::None;
288                                                        }
289                                                    };
290                                                    Tls::Required(param)
291                                                }
292                                                "SSL/TLS" => {
293                                                    let param = match TlsParametersBuilder::new(server.clone())
294                                                        .dangerous_accept_invalid_certs(true)
295                                                        .build()
296                                                    {
297                                                        Ok(param) => param,
298                                                        Err(_) => {
299                                                            Log::warning(3011, Some("mail:smtp:tls".to_owned()));
300                                                            return MailProvider::None;
301                                                        }
302                                                    };
303                                                    Tls::Wrapper(param)
304                                                }
305                                                _ => {
306                                                    Log::warning(3011, Some("mail:smtp:tls".to_owned()));
307                                                    return MailProvider::None;
308                                                }
309                                            }
310                                        } else {
311                                            Log::warning(3011, Some("mail:smtp:tls".to_owned()));
312                                            return MailProvider::None;
313                                        }
314                                    } else {
315                                        Log::warning(3011, Some("mail:smtp:tls".to_owned()));
316                                        return MailProvider::None;
317                                    }
318                                }
319                                None => {
320                                    Log::warning(3011, Some("mail:smtp:tls".to_owned()));
321                                    return MailProvider::None;
322                                }
323                            };
324                            let auth = match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:auth")], false).await {
325                                Some(res) => {
326                                    if !res.is_empty() {
327                                        let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
328                                            row
329                                        } else {
330                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:auth".to_owned()));
331                                            return MailProvider::None;
332                                        };
333                                        if row.is_empty() {
334                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:auth:empty".to_owned()));
335                                            return MailProvider::None;
336                                        }
337                                        let auth = if let Data::String(auth) = unsafe { row.get_unchecked(0) } {
338                                            auth.clone()
339                                        } else {
340                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:auth:type".to_owned()));
341                                            return MailProvider::None;
342                                        };
343                                        if !auth.is_empty() {
344                                            match auth.as_ref() {
345                                                "None" => Vec::new(),
346                                                "PLAIN" => vec![Mechanism::Plain],
347                                                "LOGIN" => vec![Mechanism::Login],
348                                                "XOAUTH2" => vec![Mechanism::Xoauth2],
349                                                _ => {
350                                                    Log::warning(3011, Some("mail:smtp:auth".to_owned()));
351                                                    return MailProvider::None;
352                                                }
353                                            }
354                                        } else {
355                                            Log::warning(3011, Some("mail:smtp:auth".to_owned()));
356                                            return MailProvider::None;
357                                        }
358                                    } else {
359                                        Log::warning(3011, Some("mail:smtp:auth".to_owned()));
360                                        return MailProvider::None;
361                                    }
362                                }
363                                None => {
364                                    Log::warning(3011, Some("mail:smtp:auth".to_owned()));
365                                    return MailProvider::None;
366                                }
367                            };
368                            let user = match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:user")], false).await {
369                                Some(res) => {
370                                    if !res.is_empty() {
371                                        let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
372                                            row
373                                        } else {
374                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:user".to_owned()));
375                                            return MailProvider::None;
376                                        };
377                                        if row.is_empty() {
378                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:user:empty".to_owned()));
379                                            return MailProvider::None;
380                                        }
381                                        let user = if let Data::String(user) = unsafe { row.get_unchecked(0) } {
382                                            user.clone()
383                                        } else {
384                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:user:type".to_owned()));
385                                            return MailProvider::None;
386                                        };
387                                        user
388                                    } else {
389                                        String::new()
390                                    }
391                                }
392                                None => {
393                                    Log::warning(3011, Some("mail:smtp:user".to_owned()));
394                                    return MailProvider::None;
395                                }
396                            };
397                            let pwd = match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:smtp:pwd")], false).await {
398                                Some(res) => {
399                                    if !res.is_empty() {
400                                        let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
401                                            row
402                                        } else {
403                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:pwd".to_owned()));
404                                            return MailProvider::None;
405                                        };
406                                        if row.is_empty() {
407                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:pwd:empty".to_owned()));
408                                            return MailProvider::None;
409                                        }
410                                        let pwd = if let Data::String(pwd) = unsafe { row.get_unchecked(0) } {
411                                            pwd.clone()
412                                        } else {
413                                            Log::warning(3011, Some("query:lib_get_setting:mail:smtp:pwd:type".to_owned()));
414                                            return MailProvider::None;
415                                        };
416                                        pwd
417                                    } else {
418                                        String::new()
419                                    }
420                                }
421                                None => {
422                                    Log::warning(3011, Some("mail:smtp:pwd".to_owned()));
423                                    return MailProvider::None;
424                                }
425                            };
426                            let cred = if !auth.is_empty() { Some(Credentials::new(user, pwd)) } else { None };
427                            MailProvider::SMTP(SmtpInfo {
428                                server,
429                                port,
430                                tls,
431                                authentication: auth,
432                                credentials: cred,
433                            })
434                        }
435                        "File" => match db.query_prepare(fnv1a_64!("lib_get_setting"), &[&fnv1a_64!("mail:file")], false).await {
436                            Some(res) => {
437                                if !res.is_empty() {
438                                    let row = if let Data::Vec(row) = unsafe { res.get_unchecked(0) } {
439                                        row
440                                    } else {
441                                        Log::warning(3011, Some("query:lib_get_setting:mail:file".to_owned()));
442                                        return MailProvider::None;
443                                    };
444                                    if row.is_empty() {
445                                        Log::warning(3011, Some("query:lib_get_setting:mail:smtp:file".to_owned()));
446                                        return MailProvider::None;
447                                    }
448                                    let path = if let Data::String(path) = unsafe { row.get_unchecked(0) } {
449                                        path.clone()
450                                    } else {
451                                        Log::warning(3011, Some("query:lib_get_setting:mail:smtp:file".to_owned()));
452                                        return MailProvider::None;
453                                    };
454                                    if !path.is_empty() {
455                                        if !Path::new(&path).is_dir() {
456                                            if let Err(e) = create_dir_all(&path).await {
457                                                Log::warning(3015, Some(e.to_string()));
458                                                return MailProvider::None;
459                                            }
460                                        }
461                                        MailProvider::File(path)
462                                    } else {
463                                        Log::warning(3011, Some("mail:file".to_owned()));
464                                        MailProvider::None
465                                    }
466                                } else {
467                                    Log::warning(3011, Some("mail:file".to_owned()));
468                                    MailProvider::None
469                                }
470                            }
471                            None => {
472                                Log::warning(3011, Some("mail:file".to_owned()));
473                                MailProvider::None
474                            }
475                        },
476                        "None" => MailProvider::None,
477                        _ => {
478                            Log::warning(3011, Some("mail:provider".to_owned()));
479                            MailProvider::None
480                        }
481                    }
482                } else {
483                    Log::warning(3011, Some("mail:provider".to_owned()));
484                    MailProvider::None
485                }
486            }
487            None => {
488                Log::warning(3011, Some("mail:provider".to_owned()));
489                MailProvider::None
490            }
491        }
492    }
493
494    /// Send email
495    pub async fn send(provider: MailProvider, db: Arc<DB>, message: MailMessage, user_id: i64, host: String) -> bool {
496        if !db.in_use() {
497            return true;
498        }
499        let json = match serde_json::to_value(&message) {
500            Ok(json) => json,
501            Err(e) => {
502                Log::warning(3002, Some(format!("Error: {}\nMessage: {:?}. ", e, message)));
503                return false;
504            }
505        };
506        let mut id: i64 = 0;
507        match provider {
508            MailProvider::Sendmail(path) => {
509                match Mail::create_message(Arc::clone(&db), message, &json, user_id, host, &mut id, "Sendmail").await {
510                    Ok(mes) => {
511                        let sender = AsyncSendmailTransport::<Tokio1Executor>::new_with_command(path);
512                        match sender.send(mes).await {
513                            Ok(_) => {
514                                db.execute_prepare(fnv1a_64!("lib_mail_ok"), &[&id]).await;
515                                true
516                            }
517                            Err(e) => {
518                                let e = Log::warning(3012, Some(format!("Error: {}", e)));
519                                db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
520                                false
521                            }
522                        }
523                    }
524                    Err(e) => {
525                        if id > 0 {
526                            db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
527                        }
528                        false
529                    }
530                }
531            }
532            MailProvider::SMTP(smtp) => match Mail::create_message(Arc::clone(&db), message, &json, user_id, host, &mut id, "SMTP").await {
533                Ok(mes) => {
534                    let mut sender = match &smtp.tls {
535                        Tls::None => match AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.server) {
536                            Ok(s) => s.port(smtp.port),
537                            Err(e) => {
538                                let e = Log::warning(3014, Some(format!("Error: {}", e)));
539                                db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
540                                return false;
541                            }
542                        },
543                        Tls::Required(_) => match AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&smtp.server) {
544                            Ok(s) => s.tls(smtp.tls).port(smtp.port),
545                            Err(e) => {
546                                let e = Log::warning(3014, Some(format!("Error: {}", e)));
547                                db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
548                                return false;
549                            }
550                        },
551                        Tls::Wrapper(_) => match AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.server) {
552                            Ok(s) => s.tls(smtp.tls).port(smtp.port),
553                            Err(e) => {
554                                let e = Log::warning(3014, Some(format!("Error: {}", e)));
555                                db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
556                                return false;
557                            }
558                        },
559                        Tls::Opportunistic(_) => match AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.server) {
560                            Ok(s) => s.port(smtp.port),
561                            Err(e) => {
562                                let e = Log::warning(3014, Some(format!("Error: {}", e)));
563                                db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
564                                return false;
565                            }
566                        },
567                    };
568                    if !smtp.authentication.is_empty() {
569                        sender = sender.authentication(smtp.authentication);
570                    }
571                    if let Some(credentials) = smtp.credentials {
572                        sender = sender.credentials(credentials);
573                    }
574
575                    match sender.build().send(mes).await {
576                        Ok(_) => {
577                            db.execute_prepare(fnv1a_64!("lib_mail_ok"), &[&id]).await;
578                            true
579                        }
580                        Err(e) => {
581                            let e = Log::warning(3014, Some(format!("Error: {}", e)));
582                            db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
583                            false
584                        }
585                    }
586                }
587                Err(e) => {
588                    if id > 0 {
589                        db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
590                    }
591                    false
592                }
593            },
594            MailProvider::File(path) => match Mail::create_message(Arc::clone(&db), message, &json, user_id, host, &mut id, "File").await {
595                Ok(mes) => {
596                    let sender = AsyncFileTransport::<Tokio1Executor>::new(path);
597                    match sender.send(mes).await {
598                        Ok(_) => {
599                            db.execute_prepare(fnv1a_64!("lib_mail_ok"), &[&id]).await;
600                            true
601                        }
602                        Err(e) => {
603                            let e = Log::warning(3013, Some(format!("Error: {}", e)));
604                            db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
605                            false
606                        }
607                    }
608                }
609                Err(e) => {
610                    if id > 0 {
611                        db.execute_prepare(fnv1a_64!("lib_mail_err"), &[&e, &id]).await;
612                    }
613                    false
614                }
615            },
616            MailProvider::None => db.execute_prepare(fnv1a_64!("lib_mail_add"), &[&user_id, &json.to_string()]).await.is_some(),
617        }
618    }
619
620    /// Create text email message from struct MailMessage
621    async fn create_message(
622        db: Arc<DB>,
623        message: MailMessage,
624        json: &Value,
625        user_id: i64,
626        host: String,
627        id: &mut i64,
628        transport: &str,
629    ) -> Result<Message, String> {
630        if !db.in_use() {
631            return Err(String::new());
632        }
633        let message_id = match db.query_prepare(fnv1a_64!("lib_mail_new"), &[&user_id, &json.to_string(), &transport], false).await {
634            Some(r) => {
635                if r.len() != 1 {
636                    Log::warning(3003, Some(format!("Message: {:?}.", &json)));
637                    return Err(String::new());
638                }
639                let row = if let Data::Vec(row) = unsafe { r.get_unchecked(0) } {
640                    row
641                } else {
642                    return Err(String::new());
643                };
644                if row.is_empty() {
645                    return Err(String::new());
646                }
647                let new_id = if let Data::I64(new_id) = unsafe { row.get_unchecked(0) } {
648                    *new_id
649                } else {
650                    return Err(String::new());
651                };
652                *id = new_id;
653                format!("{}@{}", id, host)
654            }
655            None => return Err(String::new()),
656        };
657
658        let from = match message.from.parse::<Mailbox>() {
659            Ok(f) => f,
660            Err(e) => {
661                let res = Log::warning(3004, Some(format!("Message: {:?}. Error: {}.", &json, e)));
662                return Err(res);
663            }
664        };
665        let mut mes = Message::builder().message_id(Some(message_id)).from(from);
666        if let Some(rto) = message.reply_to {
667            match rto.parse::<Mailbox>() {
668                Ok(r) => mes = mes.reply_to(r),
669                Err(e) => {
670                    let res = Log::warning(3005, Some(format!("Message: {:?}. Error: {}.", &json, e)));
671                    return Err(res);
672                }
673            }
674        }
675        for to in message.to {
676            match to.parse::<Mailbox>() {
677                Ok(t) => mes = mes.to(t),
678                Err(e) => {
679                    let res = Log::warning(3006, Some(format!("Message: {:?}. Error: {}.", &json, e)));
680                    return Err(res);
681                }
682            }
683        }
684        if let Some(mail_cc) = message.cc {
685            for cc in mail_cc {
686                match cc.parse::<Mailbox>() {
687                    Ok(c) => mes = mes.cc(c),
688                    Err(e) => {
689                        let res = Log::warning(3007, Some(format!("Message: {:?}. Error: {}.", &json, e)));
690                        return Err(res);
691                    }
692                }
693            }
694        }
695        if let Some(mail_cc) = message.bcc {
696            for cc in mail_cc {
697                match cc.parse::<Mailbox>() {
698                    Ok(c) => mes = mes.bcc(c),
699                    Err(e) => {
700                        let res = Log::warning(3008, Some(format!("Message: {:?}. Error: {}.", &json, e)));
701                        return Err(res);
702                    }
703                }
704            }
705        }
706        mes = mes.subject(message.subject);
707        let mes = if !message.body.is_empty() {
708            let mut part = MultiPart::mixed().build();
709            for body in message.body {
710                match body {
711                    MailBody::Text(s) => part = part.singlepart(SinglePart::plain(s)),
712                    MailBody::Html(html) => {
713                        if html.text.is_none() && html.file.is_empty() {
714                            part = part.singlepart(SinglePart::html(html.html));
715                        } else {
716                            let mut mp = MultiPart::alternative().build();
717                            if let Some(s) = html.text {
718                                mp = mp.singlepart(SinglePart::plain(s));
719                            }
720                            if html.file.is_empty() {
721                                mp = mp.singlepart(SinglePart::html(html.html));
722                            } else {
723                                let mut m = MultiPart::related().build();
724                                m = m.singlepart(SinglePart::html(html.html));
725                                for f in html.file {
726                                    let mime = match &f.mime {
727                                        Some(m) => m,
728                                        None => {
729                                            let (_, ext) = f.name.rsplit_once('.').unwrap_or(("", ""));
730                                            Mail::get_mime(ext)
731                                        }
732                                    };
733                                    let ct = match ContentType::parse(mime) {
734                                        Ok(ct) => ct,
735                                        Err(e) => {
736                                            let res = Log::warning(3010, Some(format!("Message: {:?}. Error: {}.", &json, e)));
737                                            return Err(res);
738                                        }
739                                    };
740                                    let a = Attachment::new_inline(f.name).body(f.data, ct);
741                                    m = m.singlepart(a);
742                                }
743                                mp = mp.multipart(m);
744                            }
745                            part = part.multipart(mp);
746                        }
747                    }
748                    MailBody::File(file) => {
749                        let mime = match &file.mime {
750                            Some(m) => m,
751                            None => {
752                                let (_, ext) = file.name.rsplit_once('.').unwrap_or(("", ""));
753                                Mail::get_mime(ext)
754                            }
755                        };
756                        let ct = match ContentType::parse(mime) {
757                            Ok(ct) => ct,
758                            Err(e) => {
759                                let res = Log::warning(3010, Some(format!("Message: {:?}. Error: {}.", &json, e)));
760                                return Err(res);
761                            }
762                        };
763                        let a = Attachment::new(file.name).body(file.data, ct);
764                        part = part.singlepart(a);
765                    }
766                }
767            }
768            match mes.multipart(part) {
769                Ok(mes) => mes,
770                Err(e) => {
771                    let res = Log::warning(3009, Some(format!("Message: {:?}. Error: {}.", &json, e)));
772                    return Err(res);
773                }
774            }
775        } else {
776            match mes.body("".to_string()) {
777                Ok(mes) => mes,
778                Err(e) => {
779                    let res = Log::warning(3009, Some(format!("Message: {:?}. Error: {}.", &json, e)));
780                    return Err(res);
781                }
782            }
783        };
784        Ok(mes)
785    }
786
787    /// Get mime from file extension
788    fn get_mime(ext: &str) -> &'static str {
789        match ext {
790            "7z" => "application/x-7z-compressed",
791            "aac" => "audio/aac",
792            "abw" => "application/x-abiword",
793            "arc" => "application/x-freearc",
794            "avi" => "video/x-msvideo",
795            "avif" => "image/avif",
796            "azw" => "application/vnd.amazon.ebook",
797            "bin" => "application/octet-stream",
798            "bmp" => "image/bmp",
799            "bz" => "application/x-bzip",
800            "bz2" => "application/x-bzip2",
801            "cda" => "application/x-cdf",
802            "csh" => "application/x-csh",
803            "css" => "text/css",
804            "csv" => "text/csv",
805            "doc" => "application/msword",
806            "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
807            "eot" => "application/vnd.ms-fontobject",
808            "epub" => "application/epub+zip",
809            "gif" => "image/gif",
810            "gz" => "application/gzip",
811            "htm" => "text/html",
812            "html" => "text/html",
813            "ico" => "image/vnd.microsoft.icon",
814            "ics" => "text/calendar",
815            "jar" => "application/java-archive",
816            "jpeg" => "image/jpeg",
817            "jpg" => "image/jpeg",
818            "js" => "text/javascript",
819            "json" => "application/json",
820            "jsonld" => "application/ld+json",
821            "mjs" => "text/javascript",
822            "mp3" => "audio/mpeg",
823            "mp4" => "video/mp4",
824            "mpeg" => "video/mpeg",
825            "mpkg" => "application/vnd.apple.installer+xml",
826            "odp" => "application/vnd.oasis.opendocument.presentation",
827            "ods" => "application/vnd.oasis.opendocument.spreadsheet",
828            "odt" => "application/vnd.oasis.opendocument.text",
829            "oga" => "audio/ogg",
830            "ogv" => "video/ogg",
831            "ogx" => "application/ogg",
832            "opus" => "audio/opus",
833            "otf" => "font/otf",
834            "pdf" => "application/pdf",
835            "php" => "application/x-httpd-php",
836            "png" => "image/png",
837            "ppt" => "application/vnd.ms-powerpoint",
838            "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
839            "rar" => "application/vnd.rar",
840            "rs" => "text/plain",
841            "rtf" => "application/rtf",
842            "sh" => "application/x-sh",
843            "svg" => "image/svg+xml",
844            "tar" => "application/x-tar",
845            "tif" => "image/tiff",
846            "tiff" => "image/tiff",
847            "ts" => "video/mp2t",
848            "ttf" => "font/ttf",
849            "txt" => "text/plain",
850            "vsd" => "application/vnd.visio",
851            "wav" => "audio/wav",
852            "weba" => "audio/webm",
853            "webm" => "video/webm",
854            "webp" => "image/webp",
855            "woff" => "font/woff",
856            "woff2" => "font/woff2",
857            "xhtml" => "application/xhtml+xml",
858            "xls" => "application/vnd.ms-excel",
859            "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
860            "xml" => "application/xml",
861            "xul" => "application/vnd.mozilla.xul+xml",
862            "zip" => "application/zip",
863            _ => "application/octet-stream",
864        }
865    }
866}