1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! # SMTP message sending

use async_smtp::{EmailAddress, Envelope, SendableEmail};

use super::Smtp;
use crate::config::Config;
use crate::context::Context;
use crate::events::EventType;
use crate::tools;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Envelope error: {}", _0)]
    Envelope(anyhow::Error),
    #[error("Send error: {}", _0)]
    SmtpSend(async_smtp::error::Error),
    #[error("SMTP has no transport")]
    NoTransport,
    #[error("{}", _0)]
    Other(#[from] anyhow::Error),
}

impl Smtp {
    /// Send a prepared mail to recipients.
    /// On successful send out Ok() is returned.
    pub async fn send(
        &mut self,
        context: &Context,
        recipients: &[EmailAddress],
        message: &[u8],
    ) -> Result<()> {
        if !context.get_config_bool(Config::Bot).await? {
            // Notify ratelimiter about sent message regardless of whether quota is exceeded or not.
            // Checking whether sending is allowed for low-priority messages should be done by the
            // caller.
            context.ratelimit.write().await.send();
        }

        let message_len_bytes = message.len();
        let recipients_display = recipients
            .iter()
            .map(|x| x.as_ref())
            .collect::<Vec<&str>>()
            .join(",");

        let envelope =
            Envelope::new(self.from.clone(), recipients.to_vec()).map_err(Error::Envelope)?;
        let mail = SendableEmail::new(envelope, message);

        if let Some(ref mut transport) = self.transport {
            transport.send(mail).await.map_err(Error::SmtpSend)?;

            let info_msg =
                format!("Message len={message_len_bytes} was SMTP-sent to {recipients_display}");
            info!(context, "{info_msg}.");
            context.emit_event(EventType::SmtpMessageSent(info_msg));
            self.last_success = Some(tools::Time::now());
        } else {
            warn!(
                context,
                "uh? SMTP has no transport, failed to send to {}", recipients_display
            );
            return Err(Error::NoTransport);
        }
        Ok(())
    }
}