From 7ebc708acab9c91db41652cfbfe2814a3a27569d Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Wed, 3 Jan 2024 09:21:46 +0100 Subject: [PATCH] unselect implemented rfc3691 --- src/imap/command/anystate.rs | 7 +++++-- src/imap/command/examined.rs | 11 +++++++---- src/imap/command/selected.rs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/imap/command/anystate.rs b/src/imap/command/anystate.rs index 42fe645..f326852 100644 --- a/src/imap/command/anystate.rs +++ b/src/imap/command/anystate.rs @@ -6,8 +6,11 @@ use crate::imap::flow; use crate::imap::response::Response; pub(crate) fn capability(tag: Tag<'static>) -> Result<(Response<'static>, flow::Transition)> { - let capabilities: NonEmptyVec = - (vec![Capability::Imap4Rev1, Capability::Idle]).try_into()?; + let capabilities: NonEmptyVec = (vec![ + Capability::Imap4Rev1, + Capability::try_from("UNSELECT").unwrap(), + ]) + .try_into()?; let res = Response::build() .tag(tag) .message("Server capabilities") diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs index 7de94f4..8876297 100644 --- a/src/imap/command/examined.rs +++ b/src/imap/command/examined.rs @@ -28,7 +28,7 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl // Specific to the EXAMINE state (specialization of the SELECTED state) // ~3 commands -> close, fetch, search + NOOP - CommandBody::Close => ctx.close().await, + CommandBody::Close => ctx.close("CLOSE").await, CommandBody::Fetch { sequence_set, macro_or_item_names, @@ -44,10 +44,13 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl Response::build() .to_req(ctx.req) .message("Forbidden command: can't write in read-only mode (EXAMINE)") - .bad()?, + .no()?, flow::Transition::None, )), + // UNSELECT extension (rfc3691) + CommandBody::Unselect => ctx.close("UNSELECT").await, + // In examined mode, we fallback to authenticated when needed _ => { authenticated::dispatch(authenticated::AuthenticatedContext { @@ -64,11 +67,11 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl impl<'a> ExaminedContext<'a> { /// CLOSE in examined state is not the same as in selected state /// (in selected state it also does an EXPUNGE, here it doesn't) - async fn close(self) -> Result<(Response<'static>, flow::Transition)> { + async fn close(self, kind: &str) -> Result<(Response<'static>, flow::Transition)> { Ok(( Response::build() .to_req(self.req) - .message("CLOSE completed") + .message(format!("{} completed", kind)) .ok()?, flow::Transition::Unselect, )) diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index 220a952..0653226 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -59,6 +59,9 @@ pub async fn dispatch<'a>( uid, } => ctx.copy(sequence_set, mailbox, uid).await, + // UNSELECT extension (rfc3691) + CommandBody::Unselect => ctx.unselect().await, + // In selected mode, we fallback to authenticated when needed _ => { authenticated::dispatch(authenticated::AuthenticatedContext { @@ -84,6 +87,16 @@ impl<'a> SelectedContext<'a> { )) } + async fn unselect(self) -> Result<(Response<'static>, flow::Transition)> { + Ok(( + Response::build() + .to_req(self.req) + .message("UNSELECT completed") + .ok()?, + flow::Transition::Unselect, + )) + } + pub async fn fetch( self, sequence_set: &SequenceSet,