diff --git a/src/connection.rs b/src/connection.rs new file mode 100644 index 0000000..aed11fa --- /dev/null +++ b/src/connection.rs @@ -0,0 +1,71 @@ +use std::sync::Arc; +use std::task::{Context, Poll}; + +use boitalettres::errors::Error as BalError; +use boitalettres::proto::{Request,Response}; +use futures::future::BoxFuture; +use tower::Service; + +use crate::mailstore::Mailstore; + +pub struct Connection { + pub mailstore: Arc, +} +impl Connection { + pub fn new(mailstore: Arc) -> Self { + Self { mailstore } + } +} +impl Service for Connection { + type Response = Response; + type Error = BalError; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + tracing::debug!("Got request: {:#?}", req); + let mailstore = self.mailstore.clone(); + Box::pin(async move { + use imap_codec::types::{ + command::CommandBody, + response::{Capability, Data}, + }; + + let r = match req.body { + CommandBody::Capability => { + let capabilities = vec![Capability::Imap4Rev1, Capability::Idle]; + let body = vec![Data::Capability(capabilities)]; + Response::ok( + "Pre-login capabilities listed, post-login capabilities have more.", + )? + .with_body(body) + } + CommandBody::Login { + username, + password, + } => { + let (u, p) = match (String::try_from(username), String::try_from(password)) { + (Ok(u), Ok(p)) => (u, p), + _ => { return Response::bad("Invalid characters") } + }; + + tracing::debug!(user = %u, "command.login"); + let creds = match mailstore.login_provider.login(&u, &p).await { + Err(_) => { return Response::no("[AUTHENTICATIONFAILED] Authentication failed.") } + Ok(c) => c, + }; + + Response::ok("Logged in")? + } + _ => Response::bad("Error in IMAP command received by server.")?, + }; + + Ok(r) + }) + } +} + + diff --git a/src/instance.rs b/src/instance.rs new file mode 100644 index 0000000..cb0468f --- /dev/null +++ b/src/instance.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; +use std::task::{Context, Poll}; + +use anyhow::Result; +use boitalettres::server::accept::addr::AddrStream; +use futures::future::BoxFuture; +use tower::Service; + +use crate::connection::Connection; +use crate::mailstore::Mailstore; + +pub struct Instance { + pub mailstore: Arc +} +impl Instance { + pub fn new(mailstore: Arc) -> Self { + Self { mailstore } + } +} +impl<'a> Service<&'a AddrStream> for Instance { + type Response = Connection; + type Error = anyhow::Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, addr: &'a AddrStream) -> Self::Future { + tracing::info!(remote_addr = %addr.remote_addr, local_addr = %addr.local_addr, "accept"); + let ms = self.mailstore.clone(); + Box::pin(async { + Ok(Connection::new(ms)) + }) + } +} + + diff --git a/src/mailstore.rs b/src/mailstore.rs new file mode 100644 index 0000000..e4387cb --- /dev/null +++ b/src/mailstore.rs @@ -0,0 +1,34 @@ +use std::sync::Arc; + +use anyhow::{bail, Result}; +use rusoto_signature::Region; + +use crate::config::*; +use crate::login::{ldap_provider::*, static_provider::*, *}; + +pub struct Mailstore { + pub login_provider: Box, +} +impl Mailstore { + pub fn new(config: Config) -> Result> { + let s3_region = Region::Custom { + name: config.aws_region.clone(), + endpoint: config.s3_endpoint, + }; + let k2v_region = Region::Custom { + name: config.aws_region, + endpoint: config.k2v_endpoint, + }; + let login_provider: Box = match (config.login_static, config.login_ldap) + { + (Some(st), None) => Box::new(StaticLoginProvider::new(st, k2v_region, s3_region)?), + (None, Some(ld)) => Box::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(Arc::new(Self { login_provider })) + } +} + + + diff --git a/src/main.rs b/src/main.rs index 8a8a255..abf3a82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ mod bayou; mod config; +mod connection; mod cryptoblob; +mod instance; mod login; mod mailbox; +mod mailstore; mod server; mod time; mod uidindex; diff --git a/src/server.rs b/src/server.rs index fe4f2ec..4fe5f12 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,147 +1,22 @@ -use anyhow::{bail, Result}; +use anyhow::Result; use std::sync::Arc; -use rusoto_signature::Region; - use crate::config::*; -use crate::login::{ldap_provider::*, static_provider::*, *}; -use crate::mailbox::Mailbox; +use crate::mailstore; +use crate::instance; -use boitalettres::proto::{Request, Response}; -use boitalettres::server::accept::addr::{AddrIncoming, AddrStream}; +use boitalettres::server::accept::addr::AddrIncoming; use boitalettres::server::Server as ImapServer; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tower::Service; -use futures::future::BoxFuture; - -pub struct Connection { - pub mailstore: Arc, -} -impl Connection { - pub fn new(mailstore: Arc) -> Self { - Self { mailstore } - } -} -impl Service for Connection { - type Response = Response; - type Error = boitalettres::errors::Error; - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Request) -> Self::Future { - tracing::debug!("Got request: {:#?}", req); - let mailstore = self.mailstore.clone(); - Box::pin(async move { - use imap_codec::types::{ - command::CommandBody, - response::{Capability, Data}, - }; - - let r = match req.body { - CommandBody::Capability => { - use tokio::time::{sleep, Duration}; - sleep(Duration::from_millis(100)).await; - let capabilities = vec![Capability::Imap4Rev1, Capability::Idle]; - let body = vec![Data::Capability(capabilities)]; - Response::ok( - "Pre-login capabilities listed, post-login capabilities have more.", - )? - .with_body(body) - } - CommandBody::Login { - username, - password, - } => { - let (u, p) = match (String::try_from(username), String::try_from(password)) { - (Ok(u), Ok(p)) => (u, p), - _ => { return Response::bad("Invalid characters") } - }; - - tracing::debug!(user = %u, "command.login"); - let creds = match mailstore.login_provider.login(&u, &p).await { - Err(_) => { return Response::no("[AUTHENTICATIONFAILED] Authentication failed.") } - Ok(c) => c, - }; - - Response::ok("Logged in")? - } - _ => Response::bad("Error in IMAP command received by server.")?, - }; - - Ok(r) - }) - } -} - -pub struct Instance { - pub mailstore: Arc -} -impl Instance { - pub fn new(mailstore: Arc) -> Self { - Self { mailstore } - } -} -impl<'a> Service<&'a AddrStream> for Instance { - type Response = Connection; - type Error = anyhow::Error; - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, addr: &'a AddrStream) -> Self::Future { - tracing::info!(remote_addr = %addr.remote_addr, local_addr = %addr.local_addr, "accept"); - let ms = self.mailstore.clone(); - Box::pin(async { - Ok(Connection::new(ms)) - }) - } -} - - - - -pub struct Mailstore { - pub login_provider: Box, -} -impl Mailstore { - pub fn new(config: Config) -> Result> { - let s3_region = Region::Custom { - name: config.aws_region.clone(), - endpoint: config.s3_endpoint, - }; - let k2v_region = Region::Custom { - name: config.aws_region, - endpoint: config.k2v_endpoint, - }; - let login_provider: Box = match (config.login_static, config.login_ldap) - { - (Some(st), None) => Box::new(StaticLoginProvider::new(st, k2v_region, s3_region)?), - (None, Some(ld)) => Box::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(Arc::new(Self { login_provider })) - } -} - - - pub struct Server { pub incoming: AddrIncoming, - pub mailstore: Arc, + pub mailstore: Arc, } impl Server { pub async fn new(config: Config) -> Result { Ok(Self { incoming: AddrIncoming::new("127.0.0.1:4567").await?, - mailstore: Mailstore::new(config)?, + mailstore: mailstore::Mailstore::new(config)?, }) } @@ -153,7 +28,7 @@ impl Server { //let mut mailbox = Mailbox::new(&creds, "TestMailbox".to_string()).await?; //mailbox.test().await?; - let server = ImapServer::new(self.incoming).serve(Instance::new(self.mailstore.clone())); + let server = ImapServer::new(self.incoming).serve(instance::Instance::new(self.mailstore.clone())); let _ = server.await?;