use std::sync::Arc; use anyhow::{bail, Result}; use futures::try_join; use log::*; use tokio::sync::watch; use crate::config::*; use crate::imap; use crate::lmtp::*; use crate::login::ArcLoginProvider; use crate::login::{ldap_provider::*, static_provider::*, Region}; pub struct Server { lmtp_server: Option>, imap_server: Option, } impl Server { pub async fn new(config: Config) -> Result { let (login, lmtp_conf, imap_conf) = build(config)?; let lmtp_server = lmtp_conf.map(|cfg| LmtpServer::new(cfg, login.clone())); let imap_server = match imap_conf { Some(cfg) => Some(imap::new(cfg, login.clone()).await?), None => None, }; Ok(Self { lmtp_server, imap_server, }) } pub async fn run(self) -> Result<()> { tracing::info!("Starting Aerogramme..."); let (exit_signal, provoke_exit) = watch_ctrl_c(); let _exit_on_err = move |err: anyhow::Error| { error!("Error: {}", err); let _ = provoke_exit.send(true); }; try_join!( async { match self.lmtp_server.as_ref() { None => Ok(()), Some(s) => s.run(exit_signal.clone()).await, } }, async { match self.imap_server { None => Ok(()), Some(s) => s.run(exit_signal.clone()).await, } } )?; Ok(()) } } fn build(config: Config) -> Result<(ArcLoginProvider, Option, Option)> { let s3_region = Region { name: config.aws_region.clone(), endpoint: config.s3_endpoint, }; let k2v_region = Region { name: config.aws_region, endpoint: config.k2v_endpoint, }; let lp: ArcLoginProvider = match (config.login_static, config.login_ldap) { (Some(st), None) => Arc::new(StaticLoginProvider::new(st, k2v_region, s3_region)?), (None, Some(ld)) => Arc::new(LdapLoginProvider::new(ld, k2v_region, s3_region)?), (Some(_), Some(_)) => { bail!("A single login provider must be set up in config file") } (None, None) => bail!("No login provider is set up in config file"), }; Ok((lp, config.lmtp, config.imap)) } pub fn watch_ctrl_c() -> (watch::Receiver, Arc>) { let (send_cancel, watch_cancel) = watch::channel(false); let send_cancel = Arc::new(send_cancel); let send_cancel_2 = send_cancel.clone(); tokio::spawn(async move { tokio::signal::ctrl_c() .await .expect("failed to install CTRL+C signal handler"); info!("Received CTRL+C, shutting down."); send_cancel.send(true).unwrap(); }); (watch_cancel, send_cancel_2) }