panorama/imap/src/client/mod.rs

109 lines
3.3 KiB
Rust
Raw Normal View History

2021-02-20 05:03:33 +00:00
//! IMAP Client
//! ===
//!
//! The IMAP client in this module is implemented as a state machine in the type system: methods
//! that are not supported in a particular state (ex. fetch in an unauthenticated state) cannot be
//! expressed in the type system entirely.
2021-02-20 07:30:58 +00:00
//!
//! Because there's many client types for the different types of clients, you'll want to start
//! here:
//!
//! - [ClientBuilder][self::ClientBuilder] : Constructs the config for the IMAP client
2021-02-20 05:03:33 +00:00
mod inner;
use std::sync::Arc;
use anyhow::Result;
use tokio::{
io::{self, AsyncRead, AsyncWrite, ReadHalf, WriteHalf},
net::TcpStream,
};
use tokio_rustls::{client::TlsStream, rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
use self::inner::Client;
2021-02-20 07:30:58 +00:00
/// Struct used to start building the config for a 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
2021-02-20 05:24:46 +00:00
pub type ClientBuilder = ClientNotConnectedBuilder;
2021-02-20 05:03:33 +00:00
/// An IMAP client that hasn't been connected yet.
#[derive(Builder, Clone, Debug)]
pub struct ClientNotConnected {
/// The hostname of the IMAP server. If using TLS, must be an address
hostname: String,
/// The port of the IMAP server.
port: u16,
/// Whether or not the client is using an encrypted stream.
///
/// To upgrade the connection later, use the upgrade method.
tls: bool,
}
impl ClientNotConnected {
pub async fn connect(self) -> Result<ClientUnauthenticated> {
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();
tls_config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
let tls_config = TlsConnector::from(Arc::new(tls_config));
let dnsname = DNSNameRef::try_from_ascii_str(hostname).unwrap();
let conn = tls_config.connect(dnsname, conn).await?;
let inner = Client::new(conn);
return Ok(ClientUnauthenticated::Encrypted(
2021-02-20 07:30:58 +00:00
ClientUnauthenticatedEncrypted { inner },
2021-02-20 05:03:33 +00:00
));
}
let inner = Client::new(conn);
return Ok(ClientUnauthenticated::Unencrypted(
2021-02-20 07:30:58 +00:00
ClientUnauthenticatedUnencrypted { inner },
2021-02-20 05:03:33 +00:00
));
}
}
pub enum ClientUnauthenticated {
2021-02-20 07:30:58 +00:00
Encrypted(ClientUnauthenticatedEncrypted),
Unencrypted(ClientUnauthenticatedUnencrypted),
2021-02-20 05:03:33 +00:00
}
2021-02-20 07:30:58 +00:00
impl ClientUnauthenticated {
pub async fn supports(&mut self) -> Result<()> {
match self {
ClientUnauthenticated::Encrypted(e) => e.inner.supports().await?,
ClientUnauthenticated::Unencrypted(e) => e.inner.supports().await?,
}
Ok(())
}
}
2021-02-20 05:03:33 +00:00
2021-02-20 07:30:58 +00:00
pub struct ClientUnauthenticatedUnencrypted {
2021-02-20 05:03:33 +00:00
/// Connection to the remote server
inner: Client<TcpStream>,
}
2021-02-20 07:30:58 +00:00
impl ClientUnauthenticatedUnencrypted {
2021-02-20 05:03:33 +00:00
pub async fn upgrade(&self) {}
}
/// An IMAP client that isn't authenticated.
2021-02-20 07:30:58 +00:00
pub struct ClientUnauthenticatedEncrypted {
2021-02-20 05:03:33 +00:00
/// Connection to the remote server
inner: Client<TlsStream<TcpStream>>,
}
2021-02-20 07:30:58 +00:00
impl ClientUnauthenticatedEncrypted {}