aboutsummaryrefslogtreecommitdiff
path: root/aero-proto/src/imap/command/anonymous.rs
diff options
context:
space:
mode:
Diffstat (limited to 'aero-proto/src/imap/command/anonymous.rs')
-rw-r--r--aero-proto/src/imap/command/anonymous.rs84
1 files changed, 84 insertions, 0 deletions
diff --git a/aero-proto/src/imap/command/anonymous.rs b/aero-proto/src/imap/command/anonymous.rs
new file mode 100644
index 0000000..f23ec17
--- /dev/null
+++ b/aero-proto/src/imap/command/anonymous.rs
@@ -0,0 +1,84 @@
+use anyhow::Result;
+use imap_codec::imap_types::command::{Command, CommandBody};
+use imap_codec::imap_types::core::AString;
+use imap_codec::imap_types::response::Code;
+use imap_codec::imap_types::secret::Secret;
+
+use aero_collections::user::User;
+use aero_user::login::ArcLoginProvider;
+
+use crate::imap::capability::ServerCapability;
+use crate::imap::command::anystate;
+use crate::imap::flow;
+use crate::imap::response::Response;
+
+//--- dispatching
+
+pub struct AnonymousContext<'a> {
+ pub req: &'a Command<'static>,
+ pub server_capabilities: &'a ServerCapability,
+ pub login_provider: &'a ArcLoginProvider,
+}
+
+pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response<'static>, flow::Transition)> {
+ match &ctx.req.body {
+ // Any State
+ CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()),
+ CommandBody::Capability => {
+ anystate::capability(ctx.req.tag.clone(), ctx.server_capabilities)
+ }
+ CommandBody::Logout => anystate::logout(),
+
+ // Specific to anonymous context (3 commands)
+ CommandBody::Login { username, password } => ctx.login(username, password).await,
+ CommandBody::Authenticate { .. } => {
+ anystate::not_implemented(ctx.req.tag.clone(), "authenticate")
+ }
+ //StartTLS is not implemented for now, we will probably go full TLS.
+
+ // Collect other commands
+ _ => anystate::wrong_state(ctx.req.tag.clone()),
+ }
+}
+
+//--- Command controllers, private
+
+impl<'a> AnonymousContext<'a> {
+ async fn login(
+ self,
+ username: &AString<'a>,
+ password: &Secret<AString<'a>>,
+ ) -> Result<(Response<'static>, flow::Transition)> {
+ let (u, p) = (
+ std::str::from_utf8(username.as_ref())?,
+ std::str::from_utf8(password.declassify().as_ref())?,
+ );
+ tracing::info!(user = %u, "command.login");
+
+ let creds = match self.login_provider.login(&u, &p).await {
+ Err(e) => {
+ tracing::debug!(error=%e, "authentication failed");
+ return Ok((
+ Response::build()
+ .to_req(self.req)
+ .message("Authentication failed")
+ .no()?,
+ flow::Transition::None,
+ ));
+ }
+ Ok(c) => c,
+ };
+
+ let user = User::new(u.to_string(), creds).await?;
+
+ tracing::info!(username=%u, "connected");
+ Ok((
+ Response::build()
+ .to_req(self.req)
+ .code(Code::Capability(self.server_capabilities.to_vec()))
+ .message("Completed")
+ .ok()?,
+ flow::Transition::Authenticate(user),
+ ))
+ }
+}