use tokio::net::TcpStream;
use tokio_native_tls::native_tls;
pub struct ImapConnector {
pub server: String,
pub email: String,
pub port: u16,
}
pub struct ImapSession {
session: async_imap::Session<tokio_native_tls::TlsStream<TcpStream>>,
}
pub struct ImapMailbox {
pub name: String,
pub messages_total: u32,
pub messages_unread: u32,
pub messages_recent: u32,
}
impl ImapSession {
fn new(session: async_imap::Session<tokio_native_tls::TlsStream<TcpStream>>) -> Self {
Self { session }
}
pub async fn logout(mut self) -> Result<(), String> {
self.session
.logout()
.await
.map_err(|e| format!("IMAP logout failed: {}", e))
}
pub async fn open(&mut self, name: &str) -> Result<ImapMailbox, String> {
let mailbox = self
.session
.select(name)
.await
.map_err(|e| format!("IMAP open failed: {}", e))?;
Ok(ImapMailbox {
name: name.to_string(),
messages_total: mailbox.exists,
messages_unread: mailbox.unseen.unwrap_or(0),
messages_recent: mailbox.recent,
})
}
}
impl ImapConnector {
pub fn new(server: impl Into<String>, email: impl Into<String>) -> Self {
Self {
server: server.into(),
email: email.into(),
port: 993,
}
}
pub async fn connect(&self, pass: &str) -> Result<ImapSession, String> {
let tls_connector = native_tls::TlsConnector::new()
.map_err(|e| format!("Failed to initialize TLS: {}", e))?;
let tokio_connector = tokio_native_tls::TlsConnector::from(tls_connector);
let addr = format!("{}:{}", self.server, self.port);
let tcp_stream = TcpStream::connect(&addr)
.await
.map_err(|e| format!("TCP connection failed: {}", e))?;
let tls_stream = tokio_connector
.connect(&self.server, tcp_stream)
.await
.map_err(|e| format!("TLS handshake failed: {}", e))?;
println!("Logging in as {}...", self.email);
let client = async_imap::Client::new(tls_stream);
let session = client
.login(&self.email, pass)
.await
.map_err(|(e, _unauth_client)| format!("IMAP login failed: {}", e))?;
Ok(ImapSession::new(session))
}
}