diff options
author | Alex Auvolat <alex@adnab.me> | 2022-06-29 17:58:31 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-06-29 17:58:31 +0200 |
commit | a8d0e4a994daca39f9619cddf2847c1a7820c040 (patch) | |
tree | 329dc1919fb0c7fc6c6531e9f53afbde7f83a542 /src/imap/mailbox_view.rs | |
parent | 733c59ed8060f8903d723635ed2e7c13ccca7974 (diff) | |
download | aerogramme-a8d0e4a994daca39f9619cddf2847c1a7820c040.tar.gz aerogramme-a8d0e4a994daca39f9619cddf2847c1a7820c040.zip |
Implement IDLE in selected state
Diffstat (limited to 'src/imap/mailbox_view.rs')
-rw-r--r-- | src/imap/mailbox_view.rs | 104 |
1 files changed, 91 insertions, 13 deletions
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index 76ca80c..6066528 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -1,10 +1,11 @@ +use std::num::NonZeroU32; use std::sync::Arc; use anyhow::{Error, Result}; use boitalettres::proto::res::body::Data as Body; use imap_codec::types::core::Atom; use imap_codec::types::flag::Flag; -use imap_codec::types::response::{Code, Data, Status}; +use imap_codec::types::response::{Code, Data, MessageAttribute, Status}; use crate::mail::mailbox::Mailbox; use crate::mail::uidindex::UidIndex; @@ -58,6 +59,79 @@ impl MailboxView { Ok((new_view, data)) } + /// Looks up state changes in the mailbox and produces a set of IMAP + /// responses describing the new state. + pub async fn update(&mut self) -> Result<Vec<Body>> { + self.mailbox.sync().await?; + // TODO THIS IS JUST A TEST REMOVE LATER + self.mailbox.test().await?; + + let new_view = MailboxView { + mailbox: self.mailbox.clone(), + known_state: self.mailbox.current_uid_index().await, + }; + + let mut data = Vec::<Body>::new(); + + if new_view.known_state.uidvalidity != self.known_state.uidvalidity { + // TODO: do we want to push less/more info than this? + data.push(new_view.uidvalidity()?); + data.push(new_view.exists()?); + data.push(new_view.uidnext()?); + } else { + // Calculate diff between two mailbox states + // See example in IMAP RFC in section on NOOP command: + // we want to produce something like this: + // C: a047 NOOP + // S: * 22 EXPUNGE + // S: * 23 EXISTS + // S: * 14 FETCH (UID 1305 FLAGS (\Seen \Deleted)) + // S: a047 OK Noop completed + // In other words: + // - notify client of expunged mails + // - if new mails arrived, notify client of number of existing mails + // - if flags changed for existing mails, tell client + + // - notify client of expunged mails + let mut n_expunge = 0; + for (i, (uid, uuid)) in self.known_state.idx_by_uid.iter().enumerate() { + if !new_view.known_state.table.contains_key(uuid) { + data.push(Body::Data(Data::Expunge( + NonZeroU32::try_from((i + 1 - n_expunge) as u32).unwrap(), + ))); + n_expunge += 1; + } + } + + // - if new mails arrived, notify client of number of existing mails + if new_view.known_state.table.len() != self.known_state.table.len() - n_expunge { + data.push(new_view.exists()?); + } + + // - if flags changed for existing mails, tell client + for (i, (uid, uuid)) in new_view.known_state.idx_by_uid.iter().enumerate() { + let old_mail = self.known_state.table.get(uuid); + let new_mail = new_view.known_state.table.get(uuid); + if old_mail.is_some() && old_mail != new_mail { + if let Some((uid, flags)) = new_mail { + data.push(Body::Data(Data::Fetch { + seq_or_uid: NonZeroU32::try_from((i + 1) as u32).unwrap(), + attributes: vec![ + MessageAttribute::Uid((*uid).try_into().unwrap()), + MessageAttribute::Flags( + flags.iter().filter_map(|f| string_to_flag(f)).collect(), + ), + ], + })); + } + } + } + } + + *self = new_view; + Ok(data) + } + // ---- /// Produce an OK [UIDVALIDITY _] message corresponding to `known_state` @@ -128,18 +202,7 @@ impl MailboxView { .known_state .idx_by_flag .flags() - .map(|f| match f.chars().next() { - Some('\\') => None, - Some('$') if f == "$unseen" => None, - Some(_) => match Atom::try_from(f.clone()) { - Err(_) => { - tracing::error!(flag=%f, "Unable to encode flag as IMAP atom"); - None - } - Ok(a) => Some(Flag::Keyword(a)), - }, - None => None, - }) + .map(|f| string_to_flag(f)) .flatten() .collect(); for f in DEFAULT_FLAGS.iter() { @@ -158,3 +221,18 @@ impl MailboxView { Ok(ret) } } + +fn string_to_flag(f: &str) -> Option<Flag> { + match f.chars().next() { + Some('\\') => None, + Some('$') if f == "$unseen" => None, + Some(_) => match Atom::try_from(f.clone()) { + Err(_) => { + tracing::error!(flag=%f, "Unable to encode flag as IMAP atom"); + None + } + Ok(a) => Some(Flag::Keyword(a)), + }, + None => None, + } +} |