mail_send/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7#![doc = include_str!("../README.md")]
8
9pub mod smtp;
10use std::net::IpAddr;
11use std::{fmt::Display, hash::Hash, time::Duration};
12use tokio::io::{AsyncRead, AsyncWrite};
13use tokio_rustls::TlsConnector;
14
15#[cfg(feature = "builder")]
16pub use mail_builder;
17
18#[cfg(feature = "dkim")]
19pub use mail_auth;
20
21#[derive(Debug)]
22pub enum Error {
23    /// I/O error
24    Io(std::io::Error),
25
26    /// TLS error
27    Tls(Box<rustls::Error>),
28
29    /// Base64 decode error
30    Base64(base64::DecodeError),
31
32    // SMTP authentication error.
33    Auth(smtp::auth::Error),
34
35    /// Failure parsing SMTP reply
36    UnparseableReply,
37
38    /// Unexpected SMTP reply.
39    UnexpectedReply(smtp_proto::Response<String>),
40
41    /// SMTP authentication failure.
42    AuthenticationFailed(smtp_proto::Response<String>),
43
44    /// Invalid TLS name provided.
45    InvalidTLSName,
46
47    /// Missing authentication credentials.
48    MissingCredentials,
49
50    /// Missing message sender.
51    MissingMailFrom,
52
53    /// Missing message recipients.
54    MissingRcptTo,
55
56    /// The server does no support any of the available authentication methods.
57    UnsupportedAuthMechanism,
58
59    /// Connection timeout.
60    Timeout,
61
62    /// STARTTLS not available
63    MissingStartTls,
64}
65
66impl std::error::Error for Error {
67    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
68        match self {
69            Error::Io(err) => err.source(),
70            Error::Tls(err) => err.source(),
71            Error::Base64(err) => err.source(),
72            _ => None,
73        }
74    }
75}
76
77pub type Result<T> = std::result::Result<T, Error>;
78
79/// SMTP client builder
80#[derive(Clone)]
81pub struct SmtpClientBuilder<T: AsRef<str> + PartialEq + Eq + Hash> {
82    pub timeout: Duration,
83    pub tls_connector: TlsConnector,
84    pub tls_hostname: T,
85    pub tls_implicit: bool,
86    pub credentials: Option<Credentials<T>>,
87    pub addr: String,
88    pub is_lmtp: bool,
89    pub say_ehlo: bool,
90    pub local_host: String,
91    pub local_ip: Option<IpAddr>,
92}
93
94/// SMTP client builder
95pub struct SmtpClient<T: AsyncRead + AsyncWrite> {
96    pub stream: T,
97    pub timeout: Duration,
98}
99
100#[derive(Clone, PartialEq, Eq, Hash)]
101pub enum Credentials<T: AsRef<str> + PartialEq + Eq + Hash> {
102    Plain { username: T, secret: T },
103    OAuthBearer { token: T },
104    XOauth2 { username: T, secret: T },
105}
106
107impl Default for Credentials<String> {
108    fn default() -> Self {
109        Credentials::Plain {
110            username: String::new(),
111            secret: String::new(),
112        }
113    }
114}
115
116impl Display for Error {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        match self {
119            Error::Io(e) => write!(f, "I/O error: {e}"),
120            Error::Tls(e) => write!(f, "TLS error: {e}"),
121            Error::Base64(e) => write!(f, "Base64 decode error: {e}"),
122            Error::Auth(e) => write!(f, "SMTP authentication error: {e}"),
123            Error::UnparseableReply => write!(f, "Unparseable SMTP reply"),
124            Error::UnexpectedReply(e) => write!(f, "Unexpected reply: {e}"),
125            Error::AuthenticationFailed(e) => write!(f, "Authentication failed: {e}"),
126            Error::InvalidTLSName => write!(f, "Invalid TLS name provided"),
127            Error::MissingCredentials => write!(f, "Missing authentication credentials"),
128            Error::MissingMailFrom => write!(f, "Missing message sender"),
129            Error::MissingRcptTo => write!(f, "Missing message recipients"),
130            Error::UnsupportedAuthMechanism => write!(
131                f,
132                "The server does no support any of the available authentication methods"
133            ),
134            Error::Timeout => write!(f, "Connection timeout"),
135            Error::MissingStartTls => write!(f, "STARTTLS extension unavailable"),
136        }
137    }
138}
139
140impl From<std::io::Error> for Error {
141    fn from(err: std::io::Error) -> Self {
142        Error::Io(err)
143    }
144}
145
146impl From<base64::DecodeError> for Error {
147    fn from(err: base64::DecodeError) -> Self {
148        Error::Base64(err)
149    }
150}