unselect implemented rfc3691

This commit is contained in:
Quentin 2024-01-03 09:21:46 +01:00
parent b9a0c1e6ec
commit 7ebc708aca
Signed by: quentin
GPG key ID: E9602264D639FF68
3 changed files with 25 additions and 6 deletions

View file

@ -6,8 +6,11 @@ use crate::imap::flow;
use crate::imap::response::Response; use crate::imap::response::Response;
pub(crate) fn capability(tag: Tag<'static>) -> Result<(Response<'static>, flow::Transition)> { pub(crate) fn capability(tag: Tag<'static>) -> Result<(Response<'static>, flow::Transition)> {
let capabilities: NonEmptyVec<Capability> = let capabilities: NonEmptyVec<Capability> = (vec![
(vec![Capability::Imap4Rev1, Capability::Idle]).try_into()?; Capability::Imap4Rev1,
Capability::try_from("UNSELECT").unwrap(),
])
.try_into()?;
let res = Response::build() let res = Response::build()
.tag(tag) .tag(tag)
.message("Server capabilities") .message("Server capabilities")

View file

@ -28,7 +28,7 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl
// Specific to the EXAMINE state (specialization of the SELECTED state) // Specific to the EXAMINE state (specialization of the SELECTED state)
// ~3 commands -> close, fetch, search + NOOP // ~3 commands -> close, fetch, search + NOOP
CommandBody::Close => ctx.close().await, CommandBody::Close => ctx.close("CLOSE").await,
CommandBody::Fetch { CommandBody::Fetch {
sequence_set, sequence_set,
macro_or_item_names, macro_or_item_names,
@ -44,10 +44,13 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl
Response::build() Response::build()
.to_req(ctx.req) .to_req(ctx.req)
.message("Forbidden command: can't write in read-only mode (EXAMINE)") .message("Forbidden command: can't write in read-only mode (EXAMINE)")
.bad()?, .no()?,
flow::Transition::None, flow::Transition::None,
)), )),
// UNSELECT extension (rfc3691)
CommandBody::Unselect => ctx.close("UNSELECT").await,
// In examined mode, we fallback to authenticated when needed // In examined mode, we fallback to authenticated when needed
_ => { _ => {
authenticated::dispatch(authenticated::AuthenticatedContext { authenticated::dispatch(authenticated::AuthenticatedContext {
@ -64,11 +67,11 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl
impl<'a> ExaminedContext<'a> { impl<'a> ExaminedContext<'a> {
/// CLOSE in examined state is not the same as in selected state /// CLOSE in examined state is not the same as in selected state
/// (in selected state it also does an EXPUNGE, here it doesn't) /// (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(( Ok((
Response::build() Response::build()
.to_req(self.req) .to_req(self.req)
.message("CLOSE completed") .message(format!("{} completed", kind))
.ok()?, .ok()?,
flow::Transition::Unselect, flow::Transition::Unselect,
)) ))

View file

@ -59,6 +59,9 @@ pub async fn dispatch<'a>(
uid, uid,
} => ctx.copy(sequence_set, mailbox, uid).await, } => ctx.copy(sequence_set, mailbox, uid).await,
// UNSELECT extension (rfc3691)
CommandBody::Unselect => ctx.unselect().await,
// In selected mode, we fallback to authenticated when needed // In selected mode, we fallback to authenticated when needed
_ => { _ => {
authenticated::dispatch(authenticated::AuthenticatedContext { 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( pub async fn fetch(
self, self,
sequence_set: &SequenceSet, sequence_set: &SequenceSet,