aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/imap/command/anonymous.rs39
-rw-r--r--src/imap/command/authenticated.rs59
-rw-r--r--src/imap/command/selected.rs8
-rw-r--r--src/imap/mod.rs2
-rw-r--r--src/imap/session.rs30
7 files changed, 40 insertions, 102 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a5364b8..44c9cb1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -362,7 +362,7 @@ dependencies = [
[[package]]
name = "boitalettres"
version = "0.0.1"
-source = "git+https://git.deuxfleurs.fr/quentin/boitalettres.git?branch=expose_req_res#2c43b7686a7cd06f733719350bd61f792d20338e"
+source = "git+https://git.deuxfleurs.fr/KokaKiwi/boitalettres.git?branch=main#01ee8c872b15e2aecb4c0ef5c2dd3aa6cc3c4d3c"
dependencies = [
"async-compat",
"bytes",
diff --git a/Cargo.toml b/Cargo.toml
index 2cbf5f9..4393d1c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,7 +41,7 @@ tower = "0.4"
imap-codec = "0.5"
k2v-client = { git = "https://git.deuxfleurs.fr/Deuxfleurs/garage.git", branch = "main" }
-boitalettres = { git = "https://git.deuxfleurs.fr/quentin/boitalettres.git", branch = "expose_req_res" }
+boitalettres = { git = "https://git.deuxfleurs.fr/KokaKiwi/boitalettres.git", branch = "main" }
smtp-message = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
smtp-server = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs
index c1b800e..a2f2260 100644
--- a/src/imap/command/anonymous.rs
+++ b/src/imap/command/anonymous.rs
@@ -10,16 +10,13 @@ use crate::imap::session::InnerContext;
//--- dispatching
pub async fn dispatch<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Transition)> {
- match &ctx.req.body {
+ match &ctx.req.command.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),
+ _ => Ok((
+ Response::no("This command is not available in the ANONYMOUS state.")?,
+ flow::Transition::No
+ )),
}
}
@@ -27,13 +24,7 @@ pub async fn dispatch<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Tran
async fn capability<'a>(ctx: InnerContext<'a>) -> Result<(Response, flow::Transition)> {
let capabilities = vec![Capability::Imap4Rev1, Capability::Idle];
- let res = vec![
- ImapRes::Data(Data::Capability(capabilities)),
- ImapRes::Status(
- Status::ok(Some(ctx.req.tag.clone()), None, "Server capabilities")
- .map_err(Error::msg)?,
- ),
- ];
+ let res = Response::ok("Server capabilities")?.with_body(Data::Capability(capabilities));
Ok((res, flow::Transition::No))
}
@@ -51,13 +42,7 @@ async fn login<'a>(
let creds = match ctx.login.login(&u, &p).await {
Err(e) => {
tracing::debug!(error=%e, "authentication failed");
- return Ok((
- vec![ImapRes::Status(
- Status::no(Some(ctx.req.tag.clone()), None, "Authentication failed")
- .map_err(Error::msg)?,
- )],
- flow::Transition::No,
- ));
+ return Ok((Response::no("Authentication failed")?, flow::Transition::No));
}
Ok(c) => c,
};
@@ -66,16 +51,10 @@ async fn login<'a>(
creds,
name: u.clone(),
};
- 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,
+ Response::ok("Completed")?,
+ flow::Transition::Authenticate(user)
))
}
diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs
index 0b1f01e..85c1c82 100644
--- a/src/imap/command/authenticated.rs
+++ b/src/imap/command/authenticated.rs
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Error, Result};
-use boitalettres::proto::Response;
+use boitalettres::proto::{Response, res::body::Data as Body};
use imap_codec::types::command::CommandBody;
-use imap_codec::types::core::{Atom, Tag};
+use imap_codec::types::core::Atom;
use imap_codec::types::flag::Flag;
use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
use imap_codec::types::response::{Code, Data, Response as ImapRes, Status};
@@ -23,13 +23,9 @@ 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,
- };
+ let ctx = StateContext { user, inner };
- match &ctx.inner.req.body {
+ match &ctx.inner.req.command.body {
CommandBody::Lsub {
reference,
mailbox_wildcard,
@@ -48,7 +44,6 @@ pub async fn dispatch<'a>(
struct StateContext<'a> {
inner: InnerContext<'a>,
user: &'a flow::User,
- tag: &'a Tag,
}
impl<'a> StateContext<'a> {
@@ -58,9 +53,7 @@ impl<'a> StateContext<'a> {
mailbox_wildcard: &ListMailbox,
) -> Result<(Response, flow::Transition)> {
Ok((
- vec![ImapRes::Status(
- Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?,
- )],
+ Response::bad("Not implemented")?,
flow::Transition::No,
))
}
@@ -71,9 +64,7 @@ impl<'a> StateContext<'a> {
mailbox_wildcard: &ListMailbox,
) -> Result<(Response, flow::Transition)> {
Ok((
- vec![ImapRes::Status(
- Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?,
- )],
+ Response::bad("Not implemented")?,
flow::Transition::No,
))
}
@@ -115,20 +106,11 @@ impl<'a> StateContext<'a> {
let sum = mb.summary().await?;
tracing::trace!(summary=%sum, "mailbox.summary");
- 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 mut res = Vec::<ImapRes>::new();
+ let mut res = Vec::<Body>::new();
- res.push(ImapRes::Data(Data::Exists(sum.exists)));
+ res.push(Body::Data(Data::Exists(sum.exists)));
- res.push(ImapRes::Data(Data::Recent(sum.recent)));
+ res.push(Body::Data(Data::Recent(sum.recent)));
let mut flags: Vec<Flag> = sum.flags.map(|f| match f.chars().next() {
Some('\\') => None,
@@ -144,7 +126,7 @@ impl<'a> StateContext<'a> {
}).flatten().collect();
flags.extend_from_slice(&DEFAULT_FLAGS);
- res.push(ImapRes::Data(Data::Flags(flags.clone())));
+ res.push(Body::Data(Data::Flags(flags.clone())));
let uid_validity = Status::ok(
None,
@@ -152,14 +134,14 @@ impl<'a> StateContext<'a> {
"UIDs valid"
)
.map_err(Error::msg)?;
- res.push(ImapRes::Status(uid_validity));
+ res.push(Body::Status(uid_validity));
let next_uid = Status::ok(
None,
Some(Code::UidNext(sum.next)),
"Predict next UID"
).map_err(Error::msg)?;
- res.push(ImapRes::Status(next_uid));
+ res.push(Body::Status(next_uid));
if let Some(unseen) = sum.unseen {
let status_unseen = Status::ok(
@@ -168,7 +150,7 @@ impl<'a> StateContext<'a> {
"First unseen UID",
)
.map_err(Error::msg)?;
- res.push(ImapRes::Status(status_unseen));
+ res.push(Body::Status(status_unseen));
}
flags.push(Flag::Permanent);
@@ -177,16 +159,11 @@ impl<'a> StateContext<'a> {
Some(Code::PermanentFlags(flags)),
"Flags permitted",
).map_err(Error::msg)?;
- res.push(ImapRes::Status(permanent_flags));
-
- let last = Status::ok(
- Some(self.tag.clone()),
- Some(Code::ReadWrite),
- "Select completed",
- ).map_err(Error::msg)?;
- res.push(ImapRes::Status(last));
+ res.push(Body::Status(permanent_flags));
- let tr = flow::Transition::Select(mb);
- Ok((res, tr))
+ Ok((
+ Response::ok("Select completed")?.with_body(res),
+ flow::Transition::Select(mb),
+ ))
}
}
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index e44bf36..e013eaa 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -17,13 +17,12 @@ pub async fn dispatch<'a>(
mailbox: &'a Mailbox,
) -> Result<(Response, flow::Transition)> {
let ctx = StateContext {
- tag: &inner.req.tag,
inner,
user,
mailbox,
};
- match &ctx.inner.req.body {
+ match &ctx.inner.req.command.body {
CommandBody::Fetch {
sequence_set,
attributes,
@@ -39,7 +38,6 @@ struct StateContext<'a> {
inner: InnerContext<'a>,
user: &'a flow::User,
mailbox: &'a Mailbox,
- tag: &'a Tag,
}
impl<'a> StateContext<'a> {
@@ -50,9 +48,7 @@ impl<'a> StateContext<'a> {
uid: &bool,
) -> Result<(Response, flow::Transition)> {
Ok((
- vec![ImapRes::Status(
- Status::bad(Some(self.tag.clone()), None, "Not implemented").map_err(Error::msg)?,
- )],
+ Response::bad("Not implemented")?,
flow::Transition::No,
))
}
diff --git a/src/imap/mod.rs b/src/imap/mod.rs
index 4b44140..63f0220 100644
--- a/src/imap/mod.rs
+++ b/src/imap/mod.rs
@@ -92,7 +92,7 @@ impl Service<Request> for Connection {
}
fn call(&mut self, req: Request) -> Self::Future {
- tracing::debug!("Got request: {:#?}", req);
+ tracing::debug!("Got request: {:#?}", req.command);
self.session.process(req)
}
}
diff --git a/src/imap/session.rs b/src/imap/session.rs
index 2ff4117..dfef0f4 100644
--- a/src/imap/session.rs
+++ b/src/imap/session.rs
@@ -40,7 +40,6 @@ impl Manager {
pub fn process(&self, req: Request) -> BoxFuture<'static, Result<Response, BalError>> {
let (tx, rx) = oneshot::channel();
- let tag = req.tag.clone();
let msg = Message { req, tx };
// We use try_send on a bounded channel to protect the daemons from DoS.
@@ -51,17 +50,13 @@ impl Manager {
Ok(()) => (),
Err(TrySendError::Full(_)) => {
return async {
- Status::bad(Some(tag), None, "Too fast! Send less pipelined requests!")
- .map(|s| vec![ImapRes::Status(s)])
- .map_err(|e| BalError::Text(e.to_string()))
+ Response::bad("Too fast! Send less pipelined requests.")
}
.boxed()
}
Err(TrySendError::Closed(_)) => {
return async {
- Status::bad(Some(tag), None, "The session task has exited")
- .map(|s| vec![ImapRes::Status(s)])
- .map_err(|e| BalError::Text(e.to_string()))
+ Response::bad("Session task has existed.")
}
.boxed()
}
@@ -73,9 +68,7 @@ impl Manager {
Ok(r) => r,
Err(e) => {
tracing::warn!("Got error {:#?}", e);
- Status::bad(Some(tag), None, "No response from the session handler")
- .map(|s| vec![ImapRes::Status(s)])
- .map_err(|e| BalError::Text(e.to_string()))
+ Response::bad("No response from the session handler")
}
}
}
@@ -122,21 +115,16 @@ impl Instance {
};
// Command behavior is modulated by the state.
- // To prevent state error, we handle the same command in separate code path depending
- // on the State.
+ // To prevent state error, we handle the same command in separate code paths.
let ctrl = match &self.state {
flow::State::NotAuthenticated => anonymous::dispatch(ctx).await,
flow::State::Authenticated(user) => authenticated::dispatch(ctx, user).await,
flow::State::Selected(user, mailbox) => {
selected::dispatch(ctx, user, mailbox).await
}
- _ => Status::bad(
- Some(ctx.req.tag.clone()),
- None,
- "No commands are allowed in the LOGOUT state.",
- )
- .map(|s| (vec![ImapRes::Status(s)], flow::Transition::No))
- .map_err(Error::msg),
+ _ => Response::bad("No commands are allowed in the LOGOUT state.")
+ .map(|r| (r, flow::Transition::No))
+ .map_err(Error::msg),
};
// Process result
@@ -152,9 +140,7 @@ impl Instance {
Ok(be) => Err(be),
Err(e) => {
tracing::warn!(error=%e, "internal.error");
- Status::bad(Some(msg.req.tag.clone()), None, "Internal error")
- .map(|s| vec![ImapRes::Status(s)])
- .map_err(|e| BalError::Text(e.to_string()))
+ Response::bad("Internal error")
}
},
};