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
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! A SMTP server that can be embedded into another program
//!
//! This library provides a simple embeddable SMTP server. The
//! server uses blocking IO and a threadpool.
//! # Examples
//! ```no_run
//! use mailin_embedded::{Server, SslConfig, Handler};
//!
//! #[derive(Clone)]
//! struct MyHandler {}
//! impl Handler for MyHandler{}
//!
//! let handler = MyHandler {};
//! let mut server = Server::new(handler);
//!
//! server.with_name("example.com")
//!    .with_ssl(SslConfig::None)
//!    .with_addr("127.0.0.1:25")
//!    .unwrap();
//! server.serve_forever();
//! ```

mod err;
mod running;
mod utils;

use crate::err::Error;
pub use crate::running::RunningServer;
pub use mailin::{
    AuthMechanism, AuthResult, DataResult, Handler, HeloResult, MailResult, RcptResult,
};
use std::net::{SocketAddr, TcpListener, ToSocketAddrs};

/// `SslConfig` is used to configure the STARTTLS configuration of the server
pub enum SslConfig {
    /// Do not support STARTTLS
    None,
    /// Use a self-signed certificate for STARTTLS
    SelfSigned {
        /// Certificate path
        cert_path: String,
        /// Path to key file
        key_path: String,
    },
    /// Use a certificate from an authority
    Trusted {
        /// Certificate path
        cert_path: String,
        /// Key file path
        key_path: String,
        /// Path to CA bundle
        chain_path: String,
    },
}

/// `Server` is used to configure and start the SMTP server
pub struct Server<H>
where
    H: Handler + Clone + Send + 'static,
{
    handler: H,
    name: String,
    ssl_config: SslConfig,
    num_threads: usize,
    auth: Vec<AuthMechanism>,
    tcp_listener: Option<TcpListener>,
    socket_address: Vec<SocketAddr>,
}

impl<H> Server<H>
where
    H: Handler + Clone + Send + 'static,
{
    /// Create a new server with the given Handler
    pub fn new(handler: H) -> Self {
        Self {
            handler,
            name: "localhost".to_owned(),
            ssl_config: SslConfig::None,
            num_threads: 4,
            auth: Vec::with_capacity(4),
            tcp_listener: None,
            socket_address: Vec::with_capacity(4),
        }
    }

    /// Give the server a name
    pub fn with_name<S>(&mut self, name: S) -> &mut Self
    where
        S: Into<String>,
    {
        self.name = name.into();
        self
    }

    /// Set the SSL configuration of the server
    pub fn with_ssl(&mut self, ssl_config: SslConfig) -> &mut Self {
        self.ssl_config = ssl_config;
        self
    }

    /// Set the size of the threadpool which is equal to the maximum number of
    /// concurrent SMTP sessions.
    pub fn with_num_threads(&mut self, num_threads: usize) -> &mut Self {
        self.num_threads = num_threads;
        self
    }

    /// Add an authentication mechanism that will supported by the server
    pub fn with_auth(&mut self, auth: AuthMechanism) -> &mut Self {
        self.auth.push(auth);
        self
    }

    /// Set a tcp listener from an already open socket
    pub fn with_tcp_listener(&mut self, listener: TcpListener) -> &mut Self {
        self.tcp_listener = Some(listener);
        self
    }

    /// Add ip addresses and ports to listen on.
    /// Returns an error if the given socket addresses are not valid.
    /// ```
    /// # use mailin_embedded::{Server, Handler};
    /// # #[derive(Clone)]
    /// # struct EmptyHandler {}
    /// # impl Handler for EmptyHandler {}
    /// # let mut server = Server::new(EmptyHandler {});
    /// server.with_addr("127.0.0.1:25").unwrap();
    /// ```
    pub fn with_addr<A: ToSocketAddrs>(&mut self, addr: A) -> Result<&mut Self, Error> {
        for addr in addr
            .to_socket_addrs()
            .map_err(|e| Error::with_source("Invalid socket address", e))?
        {
            self.socket_address.push(addr);
        }
        Ok(self)
    }

    /// Start the SMTP server in a background thread
    pub fn serve(self) -> Result<RunningServer, Error> {
        RunningServer::serve(self)
    }

    /// Start the SMTP server and run forever
    pub fn serve_forever(self) -> Result<(), Error> {
        let running = RunningServer::serve(self)?;
        running
            .join
            .join()
            .map_err(|_| Error::new("Error joining server"))
    }
}