diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs index a2f2260..8de27cb 100644 --- a/src/imap/command/anonymous.rs +++ b/src/imap/command/anonymous.rs @@ -11,11 +11,12 @@ use crate::imap::session::InnerContext; pub async fn dispatch<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Transition)> { match &ctx.req.command.body { + CommandBody::Noop => Ok((Response::ok("Noop completed.")?, flow::Transition::No)), CommandBody::Capability => capability(ctx).await, CommandBody::Login { username, password } => login(ctx, username, password).await, _ => Ok(( Response::no("This command is not available in the ANONYMOUS state.")?, - flow::Transition::No + flow::Transition::No, )), } } @@ -55,6 +56,6 @@ async fn login<'a>( tracing::info!(username=%u, "connected"); Ok(( Response::ok("Completed")?, - flow::Transition::Authenticate(user) + flow::Transition::Authenticate(user), )) } diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs index 85c1c82..4ba4968 100644 --- a/src/imap/command/authenticated.rs +++ b/src/imap/command/authenticated.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Error, Result}; -use boitalettres::proto::{Response, res::body::Data as Body}; +use boitalettres::proto::{res::body::Data as Body, Response}; use imap_codec::types::command::CommandBody; use imap_codec::types::core::Atom; use imap_codec::types::flag::Flag; @@ -12,11 +12,11 @@ use crate::imap::session::InnerContext; use crate::mail::Mailbox; const DEFAULT_FLAGS: [Flag; 5] = [ - Flag::Seen, - Flag::Answered, - Flag::Flagged, - Flag::Deleted, - Flag::Draft, + Flag::Seen, + Flag::Answered, + Flag::Flagged, + Flag::Deleted, + Flag::Draft, ]; pub async fn dispatch<'a>( @@ -52,10 +52,7 @@ impl<'a> StateContext<'a> { reference: &MailboxCodec, mailbox_wildcard: &ListMailbox, ) -> Result<(Response, flow::Transition)> { - Ok(( - Response::bad("Not implemented")?, - flow::Transition::No, - )) + Ok((Response::bad("Not implemented")?, flow::Transition::No)) } async fn list( @@ -63,10 +60,7 @@ impl<'a> StateContext<'a> { reference: &MailboxCodec, mailbox_wildcard: &ListMailbox, ) -> Result<(Response, flow::Transition)> { - Ok(( - Response::bad("Not implemented")?, - flow::Transition::No, - )) + Ok((Response::bad("Not implemented")?, flow::Transition::No)) } /* @@ -128,37 +122,25 @@ impl<'a> StateContext<'a> { res.push(Body::Data(Data::Flags(flags.clone()))); - let uid_validity = Status::ok( - None, - Some(Code::UidValidity(sum.validity)), - "UIDs valid" - ) + let uid_validity = Status::ok(None, Some(Code::UidValidity(sum.validity)), "UIDs valid") .map_err(Error::msg)?; res.push(Body::Status(uid_validity)); - let next_uid = Status::ok( - None, - Some(Code::UidNext(sum.next)), - "Predict next UID" - ).map_err(Error::msg)?; + let next_uid = Status::ok(None, Some(Code::UidNext(sum.next)), "Predict next UID") + .map_err(Error::msg)?; res.push(Body::Status(next_uid)); if let Some(unseen) = sum.unseen { - let status_unseen = Status::ok( - None, - Some(Code::Unseen(unseen.clone())), - "First unseen UID", - ) - .map_err(Error::msg)?; + let status_unseen = + Status::ok(None, Some(Code::Unseen(unseen.clone())), "First unseen UID") + .map_err(Error::msg)?; res.push(Body::Status(status_unseen)); } flags.push(Flag::Permanent); - let permanent_flags = Status::ok( - None, - Some(Code::PermanentFlags(flags)), - "Flags permitted", - ).map_err(Error::msg)?; + let permanent_flags = + Status::ok(None, Some(Code::PermanentFlags(flags)), "Flags permitted") + .map_err(Error::msg)?; res.push(Body::Status(permanent_flags)); Ok(( diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index e013eaa..cf0b71b 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -47,9 +47,6 @@ impl<'a> StateContext<'a> { attributes: &MacroOrFetchAttributes, uid: &bool, ) -> Result<(Response, flow::Transition)> { - Ok(( - Response::bad("Not implemented")?, - flow::Transition::No, - )) + Ok((Response::bad("Not implemented")?, flow::Transition::No)) } } diff --git a/src/imap/session.rs b/src/imap/session.rs index dfef0f4..d45a989 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -49,16 +49,10 @@ impl Manager { match self.tx.try_send(msg) { Ok(()) => (), Err(TrySendError::Full(_)) => { - return async { - Response::bad("Too fast! Send less pipelined requests.") - } - .boxed() + return async { Response::bad("Too fast! Send less pipelined requests.") }.boxed() } Err(TrySendError::Closed(_)) => { - return async { - Response::bad("Session task has existed.") - } - .boxed() + return async { Response::bad("Session task has existed.") }.boxed() } }; @@ -130,8 +124,11 @@ impl Instance { // Process result let res = match ctrl { Ok((res, tr)) => { - //@FIXME unwrap + //@FIXME remove unwrap self.state = self.state.apply(tr).unwrap(); + + //@FIXME enrich here the command with some status + Ok(res) } // Cast from anyhow::Error to Bal::Error diff --git a/src/mail/mod.rs b/src/mail/mod.rs index 2edcaa7..7c62c59 100644 --- a/src/mail/mod.rs +++ b/src/mail/mod.rs @@ -13,6 +13,10 @@ use crate::login::Credentials; use crate::mail::mail_ident::*; use crate::mail::uidindex::*; +// Internet Message Format +// aka RFC 822 - RFC 2822 - RFC 5322 +pub struct IMF(Vec); + pub struct Summary<'a> { pub validity: ImapUidvalidity, pub next: ImapUid, @@ -31,7 +35,6 @@ impl std::fmt::Display for Summary<'_> { } } - // Non standard but common flags: // https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml pub struct Mailbox { @@ -45,8 +48,6 @@ pub struct Mailbox { uid_index: Bayou, } -// IDEA: We store a specific flag named $unseen. -// If it is not present, we add the virtual flag \Seen impl Mailbox { pub fn new(creds: &Credentials, name: String) -> Result { let uid_index = Bayou::::new(creds, name.clone())?; @@ -61,12 +62,20 @@ impl Mailbox { }) } + // Get a summary of the mailbox, useful for the SELECT command for example pub async fn summary(&mut self) -> Result { self.uid_index.sync().await?; let state = self.uid_index.state(); - let unseen = state.idx_by_flag.get(&"$unseen".to_string()).and_then(|os| os.get_min()); - let recent = state.idx_by_flag.get(&"\\Recent".to_string()).map(|os| os.len()).unwrap_or(0); + let unseen = state + .idx_by_flag + .get(&"$unseen".to_string()) + .and_then(|os| os.get_min()); + let recent = state + .idx_by_flag + .get(&"\\Recent".to_string()) + .map(|os| os.len()) + .unwrap_or(0); return Ok(Summary { validity: state.uidvalidity, @@ -78,6 +87,34 @@ impl Mailbox { }); } + // Insert an email in the mailbox + pub async fn append(&mut self, msg: IMF) -> Result<()> { + Ok(()) + } + + // Copy an email from an external to this mailbox + // @FIXME is it needed or could we implement it with append? + pub async fn copy(&mut self, mailbox: String, uid: ImapUid) -> Result<()> { + Ok(()) + } + + // Delete all emails with the \Delete flag in the mailbox + // Can be called by CLOSE and EXPUNGE + // @FIXME do we want to implement this feature or a simpler "delete" command + // The controller could then "fetch \Delete" and call delete on each email? + pub async fn expunge(&mut self) -> Result<()> { + Ok(()) + } + + // Update flags of a range of emails + pub async fn store(&mut self) -> Result<()> { + Ok(()) + } + + pub async fn fetch(&mut self) -> Result<()> { + Ok(()) + } + pub async fn test(&mut self) -> Result<()> { self.uid_index.sync().await?; diff --git a/src/mail/uidindex.rs b/src/mail/uidindex.rs index 49dbba5..ad27c1b 100644 --- a/src/mail/uidindex.rs +++ b/src/mail/uidindex.rs @@ -186,7 +186,7 @@ impl FlagIndex { pub fn get(&self, f: &Flag) -> Option<&OrdSet> { self.0.get(f) - } + } pub fn flags(&self) -> FlagIter { self.0.keys()