aerogramme/src/imap/command/anonymous.rs

90 lines
2.9 KiB
Rust
Raw Normal View History

use anyhow::Result;
2024-01-01 08:34:13 +00:00
use imap_codec::imap_types::command::{Command, CommandBody};
use imap_codec::imap_types::core::{AString, NonEmptyVec};
use imap_codec::imap_types::response::{Capability, Data};
use imap_codec::imap_types::secret::Secret;
2022-06-20 16:09:20 +00:00
2024-01-01 18:25:28 +00:00
use crate::imap::command::anystate;
2022-06-20 16:09:20 +00:00
use crate::imap::flow;
use crate::imap::response::Response;
2022-06-29 10:50:44 +00:00
use crate::login::ArcLoginProvider;
2022-06-29 11:41:05 +00:00
use crate::mail::user::User;
2022-06-17 16:39:36 +00:00
//--- dispatching
2022-06-29 10:50:44 +00:00
pub struct AnonymousContext<'a> {
2024-01-01 08:34:13 +00:00
pub req: &'a Command<'static>,
pub login_provider: &'a ArcLoginProvider,
2022-06-29 10:50:44 +00:00
}
pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response, flow::Transition)> {
2024-01-01 08:34:13 +00:00
match &ctx.req.body {
2024-01-01 18:25:28 +00:00
// Any State
CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()),
CommandBody::Capability => anystate::capability(ctx.req.tag.clone()),
CommandBody::Logout => Ok((Response::bye()?, flow::Transition::Logout)),
// Specific to anonymous context (3 commands)
2022-06-29 10:50:44 +00:00
CommandBody::Login { username, password } => ctx.login(username, password).await,
2024-01-01 18:25:28 +00:00
CommandBody::Authenticate { .. } => {
anystate::not_implemented(ctx.req.tag.clone(), "authenticate")
2023-12-29 16:16:41 +00:00
}
2024-01-01 18:25:28 +00:00
//StartTLS is not implemented for now, we will probably go full TLS.
// Collect other commands
_ => anystate::wrong_state(ctx.req.tag.clone()),
2022-06-17 16:39:36 +00:00
}
}
2022-06-20 16:09:20 +00:00
//--- Command controllers, private
2022-06-17 16:39:36 +00:00
2022-06-29 10:50:44 +00:00
impl<'a> AnonymousContext<'a> {
async fn capability(self) -> Result<(Response, flow::Transition)> {
let capabilities: NonEmptyVec<Capability> =
(vec![Capability::Imap4Rev1, Capability::Idle]).try_into()?;
let res = Response::ok()
.to_req(self.req)
.message("Server capabilities")
.data(Data::Capability(capabilities))
.build()?;
2022-06-29 10:50:44 +00:00
Ok((res, flow::Transition::None))
}
2022-06-17 16:39:36 +00:00
2022-06-29 10:50:44 +00:00
async fn login(
self,
username: &AString<'a>,
password: &Secret<AString<'a>>,
) -> Result<(Response, flow::Transition)> {
2022-06-29 10:50:44 +00:00
let (u, p) = (
std::str::from_utf8(username.as_ref())?,
std::str::from_utf8(password.declassify().as_ref())?,
2022-06-29 10:50:44 +00:00
);
tracing::info!(user = %u, "command.login");
2022-06-17 16:39:36 +00:00
let creds = match self.login_provider.login(&u, &p).await {
2022-06-29 10:50:44 +00:00
Err(e) => {
tracing::debug!(error=%e, "authentication failed");
return Ok((
Response::no()
.to_req(self.req)
.message("Authentication failed")
.build()?,
2022-06-29 10:50:44 +00:00
flow::Transition::None,
));
}
Ok(c) => c,
};
2022-06-17 16:39:36 +00:00
let user = User::new(u.to_string(), creds).await?;
2022-06-29 10:50:44 +00:00
tracing::info!(username=%u, "connected");
Ok((
Response::ok()
.to_req(self.req)
.message("Completed")
.build()?,
2022-06-29 10:50:44 +00:00
flow::Transition::Authenticate(user),
))
}
2022-06-28 13:38:06 +00:00
}