diff --git a/src/message_reader.rs b/src/message_reader.rs index 20990b7..9c72123 100644 --- a/src/message_reader.rs +++ b/src/message_reader.rs @@ -1,165 +1,13 @@ -use std::{ - collections::HashMap, - error::Error, - fs::{read_dir, DirEntry}, - net::TcpStream, - path::PathBuf, - vec::IntoIter, -}; +use std::vec::IntoIter; -use imap::Session; -use rustls_connector::RustlsConnector; +pub(crate) mod data_directory; +pub(crate) mod imap; + +pub(crate) use data_directory::DataDirectoryMessageReader; +pub(crate) use crate::message_reader::imap::ImapReader; 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) - } -} diff --git a/src/message_reader/data_directory.rs b/src/message_reader/data_directory.rs new file mode 100644 index 0000000..8af9be9 --- /dev/null +++ b/src/message_reader/data_directory.rs @@ -0,0 +1,62 @@ +use std::{ + fs::{read_dir, DirEntry}, + path::PathBuf, + vec::IntoIter, +}; +use crate::message::Message; +use crate::message_reader::EmailReader; + +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) + } +} \ No newline at end of file diff --git a/src/message_reader/imap.rs b/src/message_reader/imap.rs new file mode 100644 index 0000000..11b940c --- /dev/null +++ b/src/message_reader/imap.rs @@ -0,0 +1,104 @@ +use std::{ + collections::HashMap, + error::Error, + net::TcpStream, + vec::IntoIter, +}; + +use imap::Session; +use rustls_connector::RustlsConnector; +use crate::message::Message; +use crate::message_reader::EmailReader; + +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) + } +}