use std::{ collections::HashMap, error::Error, fs::{read_dir, DirEntry}, net::TcpStream, path::PathBuf, vec::IntoIter, }; use imap::Session; use rustls_connector::RustlsConnector; use crate::Message; pub(crate) trait EmailReader { fn read_rfc822_messages(&mut self) -> Box>; } pub(crate) struct DataDirectoryMessageReader { path: PathBuf, } impl DataDirectoryMessageReader { pub fn new(path: PathBuf) -> Self { DataDirectoryMessageReader { path } } } impl EmailReader for DataDirectoryMessageReader { fn read_rfc822_messages(&mut self) -> Box> { println!("Reading files in {}", &self.path.display()); let reader = match read_dir(&self.path) { Ok(r) => r, Err(e) => { dbg!(e); return Box::new(Vec::new().into_iter()); } }; let items = reader .filter(|i| i.is_ok()) .map(|i| i.unwrap() as DirEntry) .filter(|d| match d.path().extension() { Some(ext) => ext == "eml", None => false, }) .filter_map(|i| { let uid = i .path() .file_stem() .unwrap() .to_owned() .into_string() .expect("Could not convert filename to string.") .split('_') .collect::>()[0] .trim_start_matches('0') .to_string(); if let Ok(data) = std::fs::read(i.path()) { Some((uid, data)) } else { None } }) .map(|i| Message::new(i.0, i.1)); let iter = items.collect::>().into_iter(); Box::new(iter) } } pub struct ImapReader { host: String, port: u16, username: String, password: String, } impl ImapReader { pub fn new(host: String, port: u16, username: String, password: String) -> Self { ImapReader { host, port, username, password, } } fn connect(&self) -> Result>, Box> { let mut session = self.open_session()?; session.examine("INBOX")?; let items = match session.uid_search("ALL") { Ok(i) => i, Err(e) => return Err(Box::new(e)), }; let mut msgs = HashMap::>::with_capacity(items.len()); //println!("# of messages: {}", &items.len()); for item in items { let msg = session.uid_fetch(&item.to_string(), "(BODY.PEEK[] UID)")?; let message = if let Some(m) = msg.iter().next() { m } else { continue; }; let body = message.body().expect("Message did not have a body."); msgs.insert(item.to_string(), body.to_owned()); } session.logout().expect("Could not log out"); Ok(msgs) } fn open_session( &self, ) -> Result< Session< rustls_connector::rustls::StreamOwned< rustls_connector::rustls::ClientConnection, TcpStream, >, >, Box, > { let stream = TcpStream::connect((self.host.as_ref(), self.port))?; let tls = RustlsConnector::new_with_webpki_roots_certs(); let tls_stream = tls.connect(&self.host, stream)?; let client = imap::Client::new(tls_stream); Ok(client .login(&self.username, &self.password) .map_err(|e| e.0)?) } } impl EmailReader for ImapReader { fn read_rfc822_messages(&mut self) -> Box> { let msgs = match self.connect() { Ok(m) => m, Err(e) => { dbg!(e); return Box::new(Vec::new().into_iter()); } }; let items = msgs .iter() .map(|i| Message::new(i.0.to_owned(), i.1.to_owned())); let iter = items.collect::>().into_iter(); Box::new(iter) } }