Switching to upstream boitalettres
This commit is contained in:
parent
390bad0ec4
commit
927b461f25
7 changed files with 40 additions and 102 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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" }
|
||||
|
||||
|
|
|
@ -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)
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue