diff options
Diffstat (limited to 'src/imap/command')
-rw-r--r-- | src/imap/command/anonymous.rs | 52 | ||||
-rw-r--r-- | src/imap/command/authenticated.rs | 113 | ||||
-rw-r--r-- | src/imap/command/selected.rs | 38 |
3 files changed, 130 insertions, 73 deletions
diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs index 7a5e514..c1b800e 100644 --- a/src/imap/command/anonymous.rs +++ b/src/imap/command/anonymous.rs @@ -1,5 +1,4 @@ - -use anyhow::{Result, Error}; +use anyhow::{Error, Result}; use boitalettres::proto::Response; use imap_codec::types::command::CommandBody; use imap_codec::types::core::AString; @@ -14,9 +13,13 @@ pub async fn dispatch<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Tran match &ctx.req.body { CommandBody::Capability => capability(ctx).await, CommandBody::Login { username, password } => login(ctx, username, password).await, - _ => Status::no(Some(ctx.req.tag.clone()), None, "This command is not available in the ANONYMOUS state.") - .map(|s| (vec![ImapRes::Status(s)], flow::Transition::No)) - .map_err(Error::msg), + _ => Status::no( + Some(ctx.req.tag.clone()), + None, + "This command is not available in the ANONYMOUS state.", + ) + .map(|s| (vec![ImapRes::Status(s)], flow::Transition::No)) + .map_err(Error::msg), } } @@ -28,23 +31,33 @@ async fn capability<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Transi ImapRes::Data(Data::Capability(capabilities)), ImapRes::Status( Status::ok(Some(ctx.req.tag.clone()), None, "Server capabilities") - .map_err(Error::msg)?, - ), + .map_err(Error::msg)?, + ), ]; Ok((res, flow::Transition::No)) } -async fn login<'a>(ctx: InnerContext<'a>, username: &AString, password: &AString) -> Result<(Response, flow::Transition)> { - let (u, p) = (String::try_from(username.clone())?, String::try_from(password.clone())?); +async fn login<'a>( + ctx: InnerContext<'a>, + username: &AString, + password: &AString, +) -> Result<(Response, flow::Transition)> { + let (u, p) = ( + String::try_from(username.clone())?, + String::try_from(password.clone())?, + ); tracing::info!(user = %u, "command.login"); let creds = match ctx.login.login(&u, &p).await { Err(e) => { tracing::debug!(error=%e, "authentication failed"); - return Ok((vec![ImapRes::Status( + return Ok(( + vec![ImapRes::Status( Status::no(Some(ctx.req.tag.clone()), None, "Authentication failed") - .map_err(Error::msg)?, - )], flow::Transition::No)); + .map_err(Error::msg)?, + )], + flow::Transition::No, + )); } Ok(c) => c, }; @@ -56,10 +69,13 @@ async fn login<'a>(ctx: InnerContext<'a>, username: &AString, password: &AString let tr = flow::Transition::Authenticate(user); tracing::info!(username=%u, "connected"); - Ok((vec![ - //@FIXME we could send a capability status here too - ImapRes::Status( - Status::ok(Some(ctx.req.tag.clone()), None, "completed").map_err(Error::msg)?, - ), - ], tr)) + Ok(( + vec![ + //@FIXME we could send a capability status here too + ImapRes::Status( + Status::ok(Some(ctx.req.tag.clone()), None, "completed").map_err(Error::msg)?, + ), + ], + tr, + )) } diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs index b4c4432..fca15fc 100644 --- a/src/imap/command/authenticated.rs +++ b/src/imap/command/authenticated.rs @@ -1,5 +1,4 @@ - -use anyhow::{Result, Error, anyhow}; +use anyhow::{anyhow, Error, Result}; use boitalettres::proto::Response; use imap_codec::types::command::CommandBody; use imap_codec::types::core::Tag; @@ -7,16 +6,29 @@ use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec}; use imap_codec::types::response::{Code, Data, Response as ImapRes, Status}; use crate::imap::command::anonymous; -use crate::imap::session::InnerContext; use crate::imap::flow; +use crate::imap::session::InnerContext; use crate::mailbox::Mailbox; -pub async fn dispatch<'a>(inner: InnerContext<'a>, user: &'a flow::User) -> Result<(Response, flow::Transition)> { - let ctx = StateContext { user, tag: &inner.req.tag, inner }; +pub async fn dispatch<'a>( + inner: InnerContext<'a>, + user: &'a flow::User, +) -> Result<(Response, flow::Transition)> { + let ctx = StateContext { + user, + tag: &inner.req.tag, + inner, + }; match &ctx.inner.req.body { - CommandBody::Lsub { reference, mailbox_wildcard, } => ctx.lsub(reference, mailbox_wildcard).await, - CommandBody::List { reference, mailbox_wildcard, } => ctx.list(reference, mailbox_wildcard).await, + CommandBody::Lsub { + reference, + mailbox_wildcard, + } => ctx.lsub(reference, mailbox_wildcard).await, + CommandBody::List { + reference, + mailbox_wildcard, + } => ctx.list(reference, mailbox_wildcard).await, CommandBody::Select { mailbox } => ctx.select(mailbox).await, _ => anonymous::dispatch(ctx.inner).await, } @@ -24,7 +36,6 @@ pub async fn dispatch<'a>(inner: InnerContext<'a>, user: &'a flow::User) -> Resu // --- PRIVATE --- - struct StateContext<'a> { inner: InnerContext<'a>, user: &'a flow::User, @@ -36,45 +47,50 @@ impl<'a> StateContext<'a> { &self, reference: &MailboxCodec, mailbox_wildcard: &ListMailbox, - ) -> Result<(Response, flow::Transition)> { - Ok((vec![ImapRes::Status( + ) -> Result<(Response, flow::Transition)> { + Ok(( + vec![ImapRes::Status( Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?, - )], flow::Transition::No)) + )], + flow::Transition::No, + )) } async fn list( &self, reference: &MailboxCodec, mailbox_wildcard: &ListMailbox, - ) -> Result<(Response, flow::Transition)> { - Ok((vec![ - ImapRes::Status(Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?), - ], flow::Transition::No)) + ) -> Result<(Response, flow::Transition)> { + Ok(( + vec![ImapRes::Status( + Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?, + )], + flow::Transition::No, + )) } /* - * TRACE BEGIN --- + * TRACE BEGIN --- - Example: C: A142 SELECT INBOX - S: * 172 EXISTS - S: * 1 RECENT - S: * OK [UNSEEN 12] Message 12 is first unseen - S: * OK [UIDVALIDITY 3857529045] UIDs valid - S: * OK [UIDNEXT 4392] Predicted next UID - S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) - S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited - S: A142 OK [READ-WRITE] SELECT completed + Example: C: A142 SELECT INBOX + S: * 172 EXISTS + S: * 1 RECENT + S: * OK [UNSEEN 12] Message 12 is first unseen + S: * OK [UIDVALIDITY 3857529045] UIDs valid + S: * OK [UIDNEXT 4392] Predicted next UID + S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited + S: A142 OK [READ-WRITE] SELECT completed - * TRACE END --- - */ + * TRACE END --- + */ async fn select(&self, mailbox: &MailboxCodec) -> Result<(Response, flow::Transition)> { let name = String::try_from(mailbox.clone())?; let mut mb = Mailbox::new(&self.user.creds, name.clone())?; tracing::info!(username=%self.user.name, mailbox=%name, "mailbox.selected"); - let sum = mb.summary().await?; tracing::trace!(summary=%sum, "mailbox.summary"); @@ -82,21 +98,34 @@ impl<'a> StateContext<'a> { let tr = flow::Transition::Select(mb); - let r_unseen = Status::ok(None, Some(Code::Unseen(std::num::NonZeroU32::new(1).ok_or(anyhow!("Invalid message identifier"))?)), "First unseen UID").map_err(Error::msg)?; + let r_unseen = Status::ok( + None, + Some(Code::Unseen( + std::num::NonZeroU32::new(1).ok_or(anyhow!("Invalid message identifier"))?, + )), + "First unseen UID", + ) + .map_err(Error::msg)?; //let r_permanentflags = Status::ok(None, Some(Code:: - Ok((vec![ - ImapRes::Data(Data::Exists(0)), - ImapRes::Data(Data::Recent(0)), - ImapRes::Data(Data::Flags(vec![])), - /*ImapRes::Status(), - ImapRes::Status(), - ImapRes::Status(),*/ - ImapRes::Status(Status::ok( - Some(self.tag.clone()), - Some(Code::ReadWrite), - "Select completed", - ).map_err(Error::msg)?), - ], tr)) + Ok(( + vec![ + ImapRes::Data(Data::Exists(0)), + ImapRes::Data(Data::Recent(0)), + ImapRes::Data(Data::Flags(vec![])), + /*ImapRes::Status(), + ImapRes::Status(), + ImapRes::Status(),*/ + ImapRes::Status( + Status::ok( + Some(self.tag.clone()), + Some(Code::ReadWrite), + "Select completed", + ) + .map_err(Error::msg)?, + ), + ], + tr, + )) } } diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index a068fab..fb6a75d 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -1,5 +1,4 @@ - -use anyhow::{Result, Error}; +use anyhow::{Error, Result}; use boitalettres::proto::Response; use imap_codec::types::command::CommandBody; use imap_codec::types::core::Tag; @@ -8,21 +7,32 @@ use imap_codec::types::response::{Response as ImapRes, Status}; use imap_codec::types::sequence::SequenceSet; use crate::imap::command::authenticated; -use crate::imap::session::InnerContext; use crate::imap::flow; +use crate::imap::session::InnerContext; use crate::mailbox::Mailbox; - -pub async fn dispatch<'a>(inner: InnerContext<'a>, user: &'a flow::User, mailbox: &'a Mailbox) -> Result<(Response, flow::Transition)> { - let ctx = StateContext { tag: &inner.req.tag, inner, user, mailbox }; +pub async fn dispatch<'a>( + inner: InnerContext<'a>, + user: &'a flow::User, + mailbox: &'a Mailbox, +) -> Result<(Response, flow::Transition)> { + let ctx = StateContext { + tag: &inner.req.tag, + inner, + user, + mailbox, + }; match &ctx.inner.req.body { - CommandBody::Fetch { sequence_set, attributes, uid, } => ctx.fetch(sequence_set, attributes, uid).await, + CommandBody::Fetch { + sequence_set, + attributes, + uid, + } => ctx.fetch(sequence_set, attributes, uid).await, _ => authenticated::dispatch(ctx.inner, user).await, } } - // --- PRIVATE --- struct StateContext<'a> { @@ -32,16 +42,18 @@ struct StateContext<'a> { tag: &'a Tag, } - impl<'a> StateContext<'a> { pub async fn fetch( &self, sequence_set: &SequenceSet, attributes: &MacroOrFetchAttributes, uid: &bool, - ) -> Result<(Response, flow::Transition)> { - Ok((vec![ - ImapRes::Status(Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?), - ], flow::Transition::No)) + ) -> Result<(Response, flow::Transition)> { + Ok(( + vec![ImapRes::Status( + Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?, + )], + flow::Transition::No, + )) } } |