diff --git a/imap/src/client/inner.rs b/imap/src/client/inner.rs index 44fd01f..3dbe8f5 100644 --- a/imap/src/client/inner.rs +++ b/imap/src/client/inner.rs @@ -15,21 +15,23 @@ use tokio::{ sync::{mpsc, oneshot}, task::JoinHandle, }; -use tokio_rustls::{client::TlsStream, rustls::ClientConfig, webpki::DNSNameRef, TlsConnector}; +use tokio_rustls::{ + client::TlsStream, rustls::ClientConfig as RustlsConfig, webpki::DNSNameRef, TlsConnector, +}; use crate::command::Command; use crate::types::Response; -use super::ClientNotConnected; +use super::ClientConfig; pub type BoxedFunc = Box; pub type ResultMap = Arc, Option)>>>; -pub type GreetingRx = Arc)>>; +pub type GreetingState = Arc)>>; pub const TAG_PREFIX: &str = "panorama"; /// The lower-level Client struct, that is shared by all of the exported structs in the state machine. pub struct Client { - config: ClientNotConnected, + config: ClientConfig, conn: WriteHalf, symbols: StringStore, @@ -46,7 +48,7 @@ pub struct Client { exit_tx: mpsc::Sender<()>, /// used for receiving the greeting - greeting: GreetingRx, + greeting: GreetingState, } impl Client @@ -54,7 +56,7 @@ where C: AsyncRead + AsyncWrite + Unpin + Send + 'static, { /// Creates a new client that wraps a connection - pub fn new(conn: C, config: ClientNotConnected) -> Self { + pub fn new(conn: C, config: ClientConfig) -> Self { let (read_half, write_half) = io::split(conn); let results = Arc::new(RwLock::new(HashMap::new())); let (exit_tx, exit_rx) = mpsc::channel(1); @@ -79,9 +81,10 @@ where } } - pub fn wait_for_greeting(&self) -> GreetingHandler { + /// Returns a future that doesn't resolve until we receive a greeting from the server. + pub fn wait_for_greeting(&self) -> GreetingWaiter { debug!("waiting for greeting"); - GreetingHandler(self.greeting.clone()) + GreetingWaiter(self.greeting.clone()) } /// Sends a command to the server and returns a handle to retrieve the result @@ -100,7 +103,7 @@ where self.conn.flush().await?; debug!("[{}] written.", id); - ExecHandle(self, id).await; + ExecWaiter(self, id).await; let resp = { let mut handlers = self.results.write(); handlers.remove(&id).unwrap().0.unwrap() @@ -142,7 +145,7 @@ where let server_name = &self.config.hostname; - let mut tls_config = ClientConfig::new(); + let mut tls_config = RustlsConfig::new(); tls_config .root_store .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); @@ -154,9 +157,9 @@ where } } -pub struct GreetingHandler(GreetingRx); +pub struct GreetingWaiter(GreetingState); -impl Future for GreetingHandler { +impl Future for GreetingWaiter { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let (state, waker) = &mut *self.0.write(); @@ -171,9 +174,9 @@ impl Future for GreetingHandler { } } -pub struct ExecHandle<'a, C>(&'a Client, usize); +pub struct ExecWaiter<'a, C>(&'a Client, usize); -impl<'a, C> Future for ExecHandle<'a, C> { +impl<'a, C> Future for ExecWaiter<'a, C> { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut handlers = self.0.results.write(); @@ -198,7 +201,7 @@ async fn listen( conn: C, results: ResultMap, mut exit: mpsc::Receiver<()>, - greeting: GreetingRx, + greeting: GreetingState, ) -> Result where C: AsyncRead + Unpin, @@ -225,7 +228,7 @@ where if tag == "*" { debug!("UNTAGGED {:?}", rest); - + // TODO: verify that the greeting is actually an OK if let Some(greeting) = greeting.take() { let (greeting, waker) = &mut *greeting.write(); diff --git a/imap/src/client/mod.rs b/imap/src/client/mod.rs index c5aaac1..2809f4d 100644 --- a/imap/src/client/mod.rs +++ b/imap/src/client/mod.rs @@ -15,11 +15,10 @@ mod inner; use std::sync::Arc; use anyhow::Result; -use tokio::{ - io::{self, AsyncRead, AsyncWrite, ReadHalf, WriteHalf}, - net::TcpStream, +use tokio::net::TcpStream; +use tokio_rustls::{ + client::TlsStream, rustls::ClientConfig as RustlsConfig, webpki::DNSNameRef, TlsConnector, }; -use tokio_rustls::{client::TlsStream, rustls::ClientConfig, webpki::DNSNameRef, TlsConnector}; pub use self::inner::Client; @@ -28,13 +27,13 @@ pub use self::inner::Client; /// Call [`.build`][1] to _build_ the config, then run [`.connect`][2] to actually start opening /// the connection to the server. /// -/// [1]: self::ClientNotConnectedBuilder::build -/// [2]: self::ClientNotConnected::connect -pub type ClientBuilder = ClientNotConnectedBuilder; +/// [1]: self::ClientConfigBuilder::build +/// [2]: self::ClientConfig::connect +pub type ClientBuilder = ClientConfigBuilder; /// An IMAP client that hasn't been connected yet. #[derive(Builder, Clone, Debug)] -pub struct ClientNotConnected { +pub struct ClientConfig { /// The hostname of the IMAP server. If using TLS, must be an address hostname: String, @@ -47,14 +46,14 @@ pub struct ClientNotConnected { tls: bool, } -impl ClientNotConnected { +impl ClientConfig { pub async fn open(self) -> Result { let hostname = self.hostname.as_ref(); let port = self.port; let conn = TcpStream::connect((hostname, port)).await?; if self.tls { - let mut tls_config = ClientConfig::new(); + let mut tls_config = RustlsConfig::new(); tls_config .root_store .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); diff --git a/src/mail/mod.rs b/src/mail/mod.rs index 259f347..7bb1d49 100644 --- a/src/mail/mod.rs +++ b/src/mail/mod.rs @@ -6,7 +6,7 @@ mod imap2; use anyhow::Result; use futures::stream::StreamExt; use panorama_imap::{ - client::{ClientBuilder, ClientNotConnected}, + client::{ClientBuilder, ClientConfig}, command::Command as ImapCommand, }; use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle}; @@ -69,7 +69,7 @@ pub async fn run_mail( /// The main sequence of steps for the IMAP thread to follow async fn imap_main(acct: MailAccountConfig) -> Result<()> { - let builder: ClientNotConnected = ClientBuilder::default() + let builder: ClientConfig = ClientBuilder::default() .hostname(acct.imap.server.clone()) .port(acct.imap.port) .tls(matches!(acct.imap.tls, TlsMethod::On)) @@ -79,7 +79,7 @@ async fn imap_main(acct: MailAccountConfig) -> Result<()> { debug!("connecting to {}:{}", &acct.imap.server, acct.imap.port); let unauth = builder.open().await?; - let unauth = if matches!(acct.imap.tls, TlsMethod::Starttls) { + let mut unauth = if matches!(acct.imap.tls, TlsMethod::Starttls) { debug!("attempting to upgrade"); let client = unauth.upgrade().await?; debug!("upgrade successful"); @@ -90,6 +90,7 @@ async fn imap_main(acct: MailAccountConfig) -> Result<()> { debug!("preparing to auth"); // check if the authentication method is supported + unauth.capabilities().await?; // debug!("sending CAPABILITY"); // let result = unauth.capabilities().await?;