From 07eea38765aecbd53e51be199094eba2871dc7ad Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 1 Jan 2024 19:25:28 +0100 Subject: ported commands --- src/imap/command/examined.rs | 132 +++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 68 deletions(-) (limited to 'src/imap/command/examined.rs') diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs index 8037d1d..cab3fdd 100644 --- a/src/imap/command/examined.rs +++ b/src/imap/command/examined.rs @@ -1,89 +1,111 @@ use std::sync::Arc; use anyhow::Result; -use boitalettres::proto::Request; -use boitalettres::proto::Response; -use imap_codec::imap_types::command::{CommandBody, SearchKey}; -use imap_codec::imap_types::core::{Charset, NonZeroBytes}; -use imap_codec::imap_types::datetime::MyDateTime; -use imap_codec::imap_types::fetch_attributes::MacroOrFetchAttributes; -use imap_codec::imap_types::flag::Flag; -use imap_codec::imap_types::mailbox::Mailbox as MailboxCodec; -use imap_codec::imap_types::response::Code; +use imap_codec::imap_types::command::{Command, CommandBody}; +use imap_codec::imap_types::core::Charset; +use imap_codec::imap_types::fetch::MacroOrMessageDataItemNames; +use imap_codec::imap_types::search::SearchKey; use imap_codec::imap_types::sequence::SequenceSet; -use crate::imap::command::authenticated; +use crate::imap::command::anystate; use crate::imap::flow; use crate::imap::mailbox_view::MailboxView; +use crate::imap::response::Response; use crate::mail::user::User; pub struct ExaminedContext<'a> { - pub req: &'a Request, + pub req: &'a Command<'a>, pub user: &'a Arc, pub mailbox: &'a mut MailboxView, } pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response, flow::Transition)> { - match &ctx.req.command.body { - // CLOSE in examined state is not the same as in selected state - // (in selected state it also does an EXPUNGE, here it doesn't) + match &ctx.req.body { + // Any State + // noop is specific to this state + CommandBody::Capability => anystate::capability(ctx.req.tag.clone()), + CommandBody::Logout => Ok((Response::bye()?, flow::Transition::Logout)), + + // Specific to the EXAMINE state (specialization of the SELECTED state) + // ~3 commands -> close, fetch, search + NOOP CommandBody::Close => ctx.close().await, CommandBody::Fetch { sequence_set, - attributes, + macro_or_item_names, uid, - } => ctx.fetch(sequence_set, attributes, uid).await, + } => ctx.fetch(sequence_set, macro_or_item_names, uid).await, CommandBody::Search { charset, criteria, uid, } => ctx.search(charset, criteria, uid).await, - CommandBody::Noop => ctx.noop().await, - CommandBody::Append { - mailbox, - flags, - date, - message, - } => ctx.append(mailbox, flags, date, message).await, - _ => { - let ctx = authenticated::AuthenticatedContext { - req: ctx.req, - user: ctx.user, - }; - authenticated::dispatch(ctx).await - } + CommandBody::Noop | CommandBody::Check => ctx.noop().await, + CommandBody::Expunge { .. } | CommandBody::Store { .. } => Ok(( + Response::bad() + .to_req(ctx.req) + .message("Forbidden command: can't write in read-only mode (EXAMINE)") + .build()?, + flow::Transition::None, + )), + + // The command does not belong to this state + _ => anystate::wrong_state(ctx.req.tag.clone()), } } // --- PRIVATE --- 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, flow::Transition)> { - Ok((Response::ok("CLOSE completed")?, flow::Transition::Unselect)) + Ok(( + Response::ok() + .to_req(self.req) + .message("CLOSE completed") + .build()?, + flow::Transition::Unselect, + )) } pub async fn fetch( self, sequence_set: &SequenceSet, - attributes: &MacroOrFetchAttributes, + attributes: &MacroOrMessageDataItemNames<'a>, uid: &bool, ) -> Result<(Response, flow::Transition)> { match self.mailbox.fetch(sequence_set, attributes, uid).await { Ok(resp) => Ok(( - Response::ok("FETCH completed")?.with_body(resp), + Response::ok() + .to_req(self.req) + .message("FETCH completed") + .set_data(resp) + .build()?, + flow::Transition::None, + )), + Err(e) => Ok(( + Response::no() + .to_req(self.req) + .message(e.to_string()) + .build()?, flow::Transition::None, )), - Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)), } } pub async fn search( self, - _charset: &Option, - _criteria: &SearchKey, + _charset: &Option>, + _criteria: &SearchKey<'a>, _uid: &bool, ) -> Result<(Response, flow::Transition)> { - Ok((Response::bad("Not implemented")?, flow::Transition::None)) + Ok(( + Response::bad() + .to_req(self.req) + .message("Not implemented") + .build()?, + flow::Transition::None, + )) } pub async fn noop(self) -> Result<(Response, flow::Transition)> { @@ -91,38 +113,12 @@ impl<'a> ExaminedContext<'a> { let updates = self.mailbox.update().await?; Ok(( - Response::ok("NOOP completed.")?.with_body(updates), + Response::ok() + .to_req(self.req) + .message("NOOP completed.") + .set_data(updates) + .build()?, flow::Transition::None, )) } - - async fn append( - self, - mailbox: &MailboxCodec, - flags: &[Flag], - date: &Option, - message: &NonZeroBytes, - ) -> Result<(Response, flow::Transition)> { - let ctx2 = authenticated::AuthenticatedContext { - req: self.req, - user: self.user, - }; - - match ctx2.append_internal(mailbox, flags, date, message).await { - Ok((mb, uidvalidity, uid)) => { - let resp = Response::ok("APPEND completed")?.with_extra_code(Code::Other( - "APPENDUID".try_into().unwrap(), - Some(format!("{} {}", uidvalidity, uid)), - )); - - if Arc::ptr_eq(&mb, &self.mailbox.mailbox) { - let data = self.mailbox.update().await?; - Ok((resp.with_body(data), flow::Transition::None)) - } else { - Ok((resp, flow::Transition::None)) - } - } - Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)), - } - } } -- cgit v1.2.3