diff --git a/Cargo.lock b/Cargo.lock index a3c14fa..841a6c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1130,6 +1130,7 @@ dependencies = [ "maplit", "nom", "panorama-proto-common", + "serde", "stderrlog", "tokio", "tokio-rustls 0.23.1", diff --git a/daemon/src/config/mod.rs b/daemon/src/config/mod.rs index 14f333c..badf02a 100644 --- a/daemon/src/config/mod.rs +++ b/daemon/src/config/mod.rs @@ -9,6 +9,7 @@ use std::io::Read; use std::path::{Path, PathBuf}; use anyhow::Result; +use panorama_imap::client::auth::ImapAuth; #[cfg(feature = "config-watch")] pub use self::watcher::{spawn_config_watcher_system, ConfigWatcher}; @@ -62,21 +63,6 @@ pub struct ImapConfig { pub auth: ImapAuth, } -/// Method of authentication for the IMAP server -#[derive(Serialize, Deserialize, Clone, Derivative)] -#[derivative(Debug)] -#[serde(tag = "auth")] -pub enum ImapAuth { - /// Use plain username/password authentication - #[serde(rename = "plain")] - Plain { - username: String, - - #[derivative(Debug = "ignore")] - password: String, - }, -} - /// Describes when to perform the TLS handshake #[derive(Serialize, Deserialize, Clone, Debug)] pub enum TlsMethod { diff --git a/daemon/src/mail/mod.rs b/daemon/src/mail/mod.rs index 4fe0202..91aa4fb 100644 --- a/daemon/src/mail/mod.rs +++ b/daemon/src/mail/mod.rs @@ -49,9 +49,11 @@ pub async fn mail_main( .tls(matches!(acct.imap.tls, TlsMethod::On)) .build()?; let pool_config = PoolConfig { + auth_config: acct.imap.auth, + client_config, max_connections: 10, }; - let pool = ImapPool::new(client_config, pool_config); + let pool = ImapPool::new(pool_config); // grab one connection from that pool and start running a background // synchronization thread diff --git a/imap/Cargo.toml b/imap/Cargo.toml index 863f349..d6c0bcb 100644 --- a/imap/Cargo.toml +++ b/imap/Cargo.toml @@ -13,8 +13,9 @@ readme = "README.md" workspace = ".." [features] -default = ["pool", "rfc2177", "rfc6154"] +default = ["serialize", "pool", "rfc2177", "rfc6154"] low-level = [] +serialize = ["serde/derive"] pool = ["crossbeam"] rfc2087 = [] # quota rfc2177 = [] # idle @@ -41,10 +42,12 @@ tokio-util = { version = "0.6.9", features = ["codec"] } webpki-roots = "0.22.1" panorama-proto-common = { path = "../proto-common" } -crossbeam = { version = "0.8.1", optional = true } - # for fuzzing arbitrary = { version = "1.0.2", optional = true, features = ["derive"] } +crossbeam = { version = "0.8.1", optional = true } +serde = { version = "1.0.130", optional = true } + + [dev-dependencies] maplit = "1.0.2" diff --git a/imap/src/client/auth.rs b/imap/src/client/auth.rs index 0f4e6db..d79b896 100644 --- a/imap/src/client/auth.rs +++ b/imap/src/client/auth.rs @@ -4,8 +4,46 @@ use anyhow::Result; use panorama_proto_common::Bytes; use crate::client::inner::Inner; +use crate::interface::ImapClient; use crate::proto::command::{Command, CommandLogin}; +/// Method of authentication for the IMAP server +#[cfg_attr( + feature = "serialize", + derive(Serialize, Deserialize, Clone, Derivative) +)] +#[derivative(Debug)] +#[serde(tag = "auth")] +pub enum ImapAuth { + /// Use plain username/password authentication + #[serde(rename = "plain")] + Plain { + username: String, + + #[derivative(Debug = "ignore")] + password: String, + }, +} + +impl ImapAuth { + pub async fn perform_auth(&self, inner: &mut Inner) -> Result<()> + where + C: Client, + { + match self { + ImapAuth::Plain { username, password } => { + let command = Command::Login(CommandLogin { + userid: Bytes::from(username.clone()), + password: Bytes::from(password.clone()), + }); + + let result = inner.execute(command).await?; + todo!() + } + } + } +} + pub trait Client: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static { diff --git a/imap/src/client/client.rs b/imap/src/client/client.rs index be72ab1..0440eae 100644 --- a/imap/src/client/client.rs +++ b/imap/src/client/client.rs @@ -22,7 +22,7 @@ use crate::proto::{ }, }; -use super::auth::AuthMethod; +use super::auth::{AuthMethod, ImapAuth}; use super::inner::Inner; use super::response_stream::ResponseStream; use super::tls::wrap_tls; @@ -102,10 +102,7 @@ impl ClientUnauthenticated { } } - pub async fn auth( - self, - auth: impl AuthMethod, - ) -> Result { + pub async fn auth(self, auth: &ImapAuth) -> Result { match self { // this is a no-op, we don't need to upgrade ClientUnauthenticated::Encrypted(mut inner) => { diff --git a/imap/src/interface.rs b/imap/src/interface.rs index 66708c1..493aff1 100644 --- a/imap/src/interface.rs +++ b/imap/src/interface.rs @@ -1,6 +1,9 @@ use anyhow::Result; -use crate::proto::response::Envelope; +use crate::proto::{ + command::Command, + response::{Envelope, Response}, +}; #[async_trait] pub trait ImapClient { diff --git a/imap/src/lib.rs b/imap/src/lib.rs index 1730ba2..0f58450 100644 --- a/imap/src/lib.rs +++ b/imap/src/lib.rs @@ -15,6 +15,10 @@ extern crate log; #[macro_use] extern crate panorama_proto_common; +#[cfg(feature = "serialize")] +#[macro_use] +extern crate serde; + #[cfg(test)] #[macro_use] extern crate maplit; diff --git a/imap/src/pool/mod.rs b/imap/src/pool/mod.rs index f288cbb..77e9df8 100644 --- a/imap/src/pool/mod.rs +++ b/imap/src/pool/mod.rs @@ -4,7 +4,11 @@ use anyhow::Result; use crossbeam::queue::ArrayQueue; use tokio::sync::Semaphore; -use crate::{client::auth::{AuthMethod, Login}, interface::ImapClient, proto::response::Envelope}; +use crate::{ + client::auth::{ImapAuth, Login}, + interface::ImapClient, + proto::response::Envelope, +}; use super::client::{ClientAuthenticated, Config, ConfigBuilder}; @@ -12,6 +16,7 @@ use super::client::{ClientAuthenticated, Config, ConfigBuilder}; pub struct PoolConfig { pub max_connections: usize, pub client_config: Config, + pub auth_config: ImapAuth, } /// A pool of IMAP connections. @@ -49,10 +54,11 @@ pub struct InnerPool { impl InnerPool { pub fn init(config: PoolConfig) -> Self { + let max_connections = config.max_connections; InnerPool { config, - semaphore: Semaphore::new(config.max_connections), - connections: ArrayQueue::new(config.max_connections), + semaphore: Semaphore::new(max_connections), + connections: ArrayQueue::new(max_connections), } } @@ -75,8 +81,7 @@ impl InnerPool { debug!("Client connected to {}", self.config.client_config.hostname); // authenticate - let client_auth = client.auth(Login { - }); + let client_auth = client.auth(&self.config.auth_config); } };