diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs index f2d7c21..5924f53 100644 --- a/src/imap/command/authenticated.rs +++ b/src/imap/command/authenticated.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Error, Result}; use boitalettres::proto::Response; use imap_codec::types::command::CommandBody; -use imap_codec::types::core::Tag; +use imap_codec::types::core::{Atom, Tag}; +use imap_codec::types::flag::Flag; use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec}; use imap_codec::types::response::{Code, Data, Response as ImapRes, Status}; @@ -10,6 +11,14 @@ use crate::imap::flow; use crate::imap::session::InnerContext; use crate::mailbox::Mailbox; +const DEFAULT_FLAGS: [Flag; 5] = [ + Flag::Seen, + Flag::Answered, + Flag::Flagged, + Flag::Deleted, + Flag::Draft, +]; + pub async fn dispatch<'a>( inner: InnerContext<'a>, user: &'a flow::User, @@ -114,7 +123,6 @@ impl<'a> StateContext<'a> { "First unseen UID", ) .map_err(Error::msg)?; - //let r_permanentflags = Status::ok(None, Some(Code:: let mut res = Vec::::new(); @@ -122,7 +130,21 @@ impl<'a> StateContext<'a> { res.push(ImapRes::Data(Data::Recent(sum.recent))); - res.push(ImapRes::Data(Data::Flags(vec![]))); + let mut flags: Vec = sum.flags.map(|f| match f.chars().next() { + Some('\\') => None, + Some('$') if f == "$unseen" => None, + Some(_) => match Atom::try_from(f.clone()) { + Err(_) => { + tracing::error!(username=%self.user.name, mailbox=%name, flag=%f, "Unable to encode flag as IMAP atom"); + None + }, + Ok(a) => Some(Flag::Keyword(a)), + }, + None => None, + }).flatten().collect(); + flags.extend_from_slice(&DEFAULT_FLAGS); + + res.push(ImapRes::Data(Data::Flags(flags.clone()))); let uid_validity = Status::ok( None, @@ -149,6 +171,14 @@ impl<'a> StateContext<'a> { res.push(ImapRes::Status(status_unseen)); } + flags.push(Flag::Permanent); + let permanent_flags = Status::ok( + None, + Some(Code::PermanentFlags(flags)), + "Flags permitted", + ).map_err(Error::msg)?; + res.push(ImapRes::Status(permanent_flags)); + let last = Status::ok( Some(self.tag.clone()), Some(Code::ReadWrite), diff --git a/src/mailbox.rs b/src/mailbox.rs index b8921ec..7945cba 100644 --- a/src/mailbox.rs +++ b/src/mailbox.rs @@ -15,6 +15,7 @@ pub struct Summary<'a> { pub next: ImapUid, pub exists: u32, pub recent: u32, + pub flags: FlagIter<'a>, pub unseen: Option<&'a ImapUid>, } impl std::fmt::Display for Summary<'_> { @@ -69,6 +70,7 @@ impl Mailbox { next: state.uidnext, exists: u32::try_from(state.idx_by_uid.len())?, recent: u32::try_from(recent)?, + flags: state.idx_by_flag.flags(), unseen, }); } diff --git a/src/uidindex.rs b/src/uidindex.rs index 9b85238..d78d8e4 100644 --- a/src/uidindex.rs +++ b/src/uidindex.rs @@ -164,6 +164,7 @@ impl BayouState for UidIndex { // ---- FlagIndex implementation ---- #[derive(Clone)] pub struct FlagIndex(HashMap>); +pub type FlagIter<'a> = im::hashmap::Keys<'a, Flag, OrdSet>; impl FlagIndex { fn new() -> Self { @@ -182,9 +183,14 @@ impl FlagIndex { self.0.get_mut(flag).and_then(|set| set.remove(&uid)); }); } + pub fn get(&self, f: &Flag) -> Option<&OrdSet> { self.0.get(f) } + + pub fn flags(&self) -> FlagIter { + self.0.keys() + } } // ---- CUSTOM SERIALIZATION AND DESERIALIZATION ----