Switching to upstream boitalettres

This commit is contained in:
Quentin 2022-06-28 09:34:24 +02:00
parent 390bad0ec4
commit 927b461f25
Signed by: quentin
GPG Key ID: E9602264D639FF68
7 changed files with 40 additions and 102 deletions

2
Cargo.lock generated
View File

@ -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",

View File

@ -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" }

View File

@ -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)
))
}

View File

@ -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::<Body>::new();
let mut res = Vec::<ImapRes>::new();
res.push(Body::Data(Data::Exists(sum.exists)));
res.push(ImapRes::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));
res.push(Body::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));
let tr = flow::Transition::Select(mb);
Ok((res, tr))
Ok((
Response::ok("Select completed")?.with_body(res),
flow::Transition::Select(mb),
))
}
}

View File

@ -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,
))
}

View File

@ -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)
}
}

View File

@ -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")
}
},
};