use std::{
io,
net::{IpAddr, Ipv4Addr, SocketAddr},
sync::{Arc, Mutex},
};
use tokio::{net::TcpListener, sync::broadcast, task::JoinHandle};
#[derive(Clone, Debug)]
pub struct MailboxEvent {
pub user: String,
pub mailbox: String,
pub new_count: usize,
}
pub type MailboxNotifier = broadcast::Sender<MailboxEvent>;
pub use crate::auth::AuthConfig;
pub use crate::store::StorageBackend;
use crate::store::Store;
mod auth;
mod http;
mod imap;
mod smtp;
mod sqlite_store;
mod store;
mod tls;
#[derive(Clone, Debug)]
pub struct ServerConfig {
pub smtp_addr: SocketAddr,
pub imap_addr: SocketAddr,
pub http_addr: Option<SocketAddr>,
pub auth: Option<AuthConfig>,
pub storage: StorageBackend,
}
impl Default for ServerConfig {
fn default() -> Self {
let loopback = IpAddr::V4(Ipv4Addr::LOCALHOST);
Self {
smtp_addr: SocketAddr::new(loopback, 0),
imap_addr: SocketAddr::new(loopback, 0),
http_addr: None,
auth: None,
storage: StorageBackend::InMemory,
}
}
}
pub struct Server;
impl Server {
pub async fn start(config: ServerConfig) -> io::Result<RunningServer> {
let store = Arc::new(Mutex::new(Store::new(config.storage)?));
let auth = config.auth.unwrap_or_else(AuthConfig::from_env);
let smtp_listener = TcpListener::bind(config.smtp_addr).await?;
let imap_listener = TcpListener::bind(config.imap_addr).await?;
let smtp_addr = smtp_listener.local_addr()?;
let imap_addr = imap_listener.local_addr()?;
let (shutdown_tx, _) = broadcast::channel(4);
let (mailbox_notifier, _) = broadcast::channel::<MailboxEvent>(64);
let mut handles = Vec::new();
let smtp_handle = tokio::spawn(smtp::run_smtp(
smtp_listener,
store.clone(),
auth.clone(),
shutdown_tx.subscribe(),
mailbox_notifier.clone(),
));
handles.push(smtp_handle);
let imap_handle = tokio::spawn(imap::run_imap(
imap_listener,
store.clone(),
auth,
shutdown_tx.subscribe(),
mailbox_notifier,
));
handles.push(imap_handle);
let http_addr = if let Some(addr) = config.http_addr {
let http_listener = TcpListener::bind(addr).await?;
let bound_addr = http_listener.local_addr()?;
let http_handle = tokio::spawn(http::run_http(
http_listener,
store.clone(),
shutdown_tx.subscribe(),
));
handles.push(http_handle);
Some(bound_addr)
} else {
None
};
Ok(RunningServer {
smtp_addr,
imap_addr,
http_addr,
shutdown_tx,
handles,
})
}
}
pub struct RunningServer {
smtp_addr: SocketAddr,
imap_addr: SocketAddr,
http_addr: Option<SocketAddr>,
shutdown_tx: broadcast::Sender<()>,
handles: Vec<JoinHandle<()>>,
}
impl RunningServer {
pub fn smtp_addr(&self) -> SocketAddr {
self.smtp_addr
}
pub fn imap_addr(&self) -> SocketAddr {
self.imap_addr
}
pub fn http_addr(&self) -> Option<SocketAddr> {
self.http_addr
}
pub async fn stop(self) -> io::Result<()> {
let _ = self.shutdown_tx.send(());
for handle in self.handles {
let _ = handle.await;
}
Ok(())
}
}