asimov_imap_module/
reader.rs1use super::{
4 ImapCapabilities, ImapError, ImapIterator, ImapLocalCursor, ImapMessage, ImapOrderBy,
5 ImapRemoteCursor, ImapUrl,
6};
7use asimov_module::tracing;
8use core::error::Error;
9use imap::{ClientBuilder, ConnectionMode, ImapConnection, Session, TlsKind, types::Mailbox};
10use know::datatypes::EmailMessageId;
11use secrecy::ExposeSecret;
12
13#[allow(unused)]
14pub struct ImapReader {
15 session: Session<Box<dyn ImapConnection + 'static>>,
16 capabilities: ImapCapabilities,
17 mailbox: Mailbox,
18}
19
20impl ImapReader {
21 pub fn open(url: &ImapUrl) -> imap::Result<Self> {
22 let client = ClientBuilder::new(&url.host, url.port)
23 .mode(ConnectionMode::Tls)
24 .tls_kind(TlsKind::Rust)
25 .danger_skip_tls_verify(true)
26 .connect()?;
27
28 let mut session = client
29 .login(
30 url.user.clone().unwrap_or_else(|| "anonymous".to_string()),
31 url.password
32 .as_ref()
33 .map(|password| password.expose_secret())
34 .unwrap_or_default(),
35 )
36 .map_err(|e| e.0)?;
37
38 let capabilities: ImapCapabilities = session.capabilities()?.into();
39 tracing::trace!("{:?}", capabilities);
40
41 if capabilities.utf8_accept {
42 }
47
48 let mailbox = session.select(&url.mailbox)?;
49 Ok(Self {
50 session,
51 capabilities,
52 mailbox,
53 })
54 }
55
56 pub fn close(&mut self) -> imap::Result<()> {
57 self.session.logout()
58 }
59
60 pub fn iter(
61 &mut self,
62 order_by: ImapOrderBy,
63 limit: Option<usize>,
64 ) -> imap::Result<impl Iterator<Item = Result<ImapMessage, Box<dyn Error>>>> {
65 let fetch_query = "(UID FLAGS ENVELOPE)";
66 Ok(match (self.capabilities.sort, &order_by) {
67 (_, ImapOrderBy::None) => {
68 ImapIterator::new(self.session.fetch("1:*", fetch_query)?, None)
69 },
70 (true, _) => {
71 let cursor = ImapRemoteCursor::by(&mut self.session, order_by)?.limit(limit);
72 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
73 ImapIterator::new(fetches, Some(cursor.to_vec()))
74 },
75 (false, ImapOrderBy::Timestamp) => {
76 let cursor = ImapLocalCursor::<i64>::by_timestamp(&mut self.session)?.limit(limit);
77 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
78 ImapIterator::new(fetches, Some(cursor.to_vec()))
79 },
80 (false, ImapOrderBy::Date) => {
81 let cursor = ImapLocalCursor::<i64>::by_date(&mut self.session)?.limit(limit);
82 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
83 ImapIterator::new(fetches, Some(cursor.to_vec()))
84 },
85 (false, ImapOrderBy::From) => {
86 let cursor = ImapLocalCursor::<String>::by_from(&mut self.session)?.limit(limit);
87 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
88 ImapIterator::new(fetches, Some(cursor.to_vec()))
89 },
90 (false, ImapOrderBy::To) => {
91 let cursor = ImapLocalCursor::<String>::by_to(&mut self.session)?.limit(limit);
92 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
93 ImapIterator::new(fetches, Some(cursor.to_vec()))
94 },
95 (false, ImapOrderBy::Cc) => {
96 let cursor = ImapLocalCursor::<String>::by_cc(&mut self.session)?.limit(limit);
97 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
98 ImapIterator::new(fetches, Some(cursor.to_vec()))
99 },
100 (false, ImapOrderBy::Size) => {
101 let cursor = ImapLocalCursor::<usize>::by_size(&mut self.session)?.limit(limit);
102 let fetches = self.session.uid_fetch(cursor.to_string(), fetch_query)?;
103 ImapIterator::new(fetches, Some(cursor.to_vec()))
104 },
105 })
106 }
107
108 pub fn fetch(&mut self, message_id: &EmailMessageId) -> Result<Option<ImapMessage>, ImapError> {
109 let message_uid = self
110 .session
111 .uid_search(format!("HEADER Message-ID <{}>", message_id.as_str()))?
112 .into_iter()
113 .next();
114 let Some(message_uid) = message_uid else {
115 return Ok(None);
116 };
117 let fetch_results = self
118 .session
119 .uid_fetch(message_uid.to_string(), "(BODY[])")?;
120 let Some(fetch_result) = fetch_results.get(0) else {
121 return Ok(None);
122 };
123 Ok(Some(fetch_result.try_into()?))
124 }
125}