use crate::command::ImapCommand;
use crate::handler_auth::{handle_login, handle_logout};
use crate::handler_mailbox::{
handle_create, handle_create_special_use, handle_delete, handle_idle, handle_list, handle_lsub,
handle_namespace, handle_rename, handle_select, handle_subscribe, handle_unsubscribe,
};
use crate::handler_message::{
handle_append, handle_close, handle_copy, handle_expunge, handle_fetch, handle_move,
handle_search, handle_store, handle_uid,
};
use crate::mailbox_registry::MailboxRegistry;
use crate::response::ImapResponse;
use crate::session::ImapSession;
use rusmes_auth::AuthBackend;
use rusmes_storage::{MailboxStore, MessageStore, MetadataStore};
use std::sync::Arc;
pub struct HandlerContext {
pub mailbox_store: Arc<dyn MailboxStore>,
pub message_store: Arc<dyn MessageStore>,
pub metadata_store: Arc<dyn MetadataStore>,
pub auth_backend: Arc<dyn AuthBackend>,
pub mailbox_registry: Arc<MailboxRegistry>,
}
impl HandlerContext {
pub fn new(
mailbox_store: Arc<dyn MailboxStore>,
message_store: Arc<dyn MessageStore>,
metadata_store: Arc<dyn MetadataStore>,
auth_backend: Arc<dyn AuthBackend>,
) -> Self {
Self {
mailbox_store,
message_store,
metadata_store,
auth_backend,
mailbox_registry: Arc::new(MailboxRegistry::new()),
}
}
pub fn with_registry(
mailbox_store: Arc<dyn MailboxStore>,
message_store: Arc<dyn MessageStore>,
metadata_store: Arc<dyn MetadataStore>,
auth_backend: Arc<dyn AuthBackend>,
mailbox_registry: Arc<MailboxRegistry>,
) -> Self {
Self {
mailbox_store,
message_store,
metadata_store,
auth_backend,
mailbox_registry,
}
}
}
#[allow(clippy::too_many_arguments)]
pub async fn handle_command(
ctx: &HandlerContext,
session: &mut ImapSession,
tag: &str,
command: ImapCommand,
) -> anyhow::Result<ImapResponse> {
match command {
ImapCommand::Capability => handle_capability(tag).await,
ImapCommand::Noop => handle_noop(tag).await,
ImapCommand::Login { user, password } => {
handle_login(ctx, session, tag, &user, &password).await
}
ImapCommand::Authenticate {
mechanism,
initial_response,
} => {
let _ = (mechanism, initial_response);
Ok(ImapResponse::bad(
tag,
"AUTHENTICATE must be handled by server loop",
))
}
ImapCommand::Logout => handle_logout(session, tag).await,
ImapCommand::Select { mailbox } => handle_select(ctx, session, tag, &mailbox, false).await,
ImapCommand::Examine { mailbox } => handle_select(ctx, session, tag, &mailbox, true).await,
ImapCommand::Fetch { sequence, items } => {
handle_fetch(ctx, session, tag, &sequence, &items).await
}
ImapCommand::Store {
sequence,
mode,
flags,
} => handle_store(ctx, session, tag, &sequence, mode, &flags).await,
ImapCommand::Search { criteria } => handle_search(ctx, session, tag, &criteria).await,
ImapCommand::List { reference, mailbox } => {
handle_list(ctx, session, tag, &reference, &mailbox).await
}
ImapCommand::Lsub { reference, mailbox } => {
handle_lsub(ctx, session, tag, &reference, &mailbox).await
}
ImapCommand::Subscribe { mailbox } => handle_subscribe(ctx, session, tag, &mailbox).await,
ImapCommand::Unsubscribe { mailbox } => {
handle_unsubscribe(ctx, session, tag, &mailbox).await
}
ImapCommand::Create { mailbox } => handle_create(ctx, session, tag, &mailbox).await,
ImapCommand::CreateSpecialUse {
mailbox,
special_use,
} => handle_create_special_use(ctx, session, tag, &mailbox, &special_use).await,
ImapCommand::Delete { mailbox } => handle_delete(ctx, session, tag, &mailbox).await,
ImapCommand::Rename { old, new } => handle_rename(ctx, session, tag, &old, &new).await,
ImapCommand::Append {
mailbox,
flags,
date_time,
message_literal,
} => {
handle_append(
ctx,
session,
tag,
&mailbox,
&flags,
date_time.as_deref(),
&message_literal,
)
.await
}
ImapCommand::Copy { sequence, mailbox } => {
handle_copy(ctx, session, tag, &sequence, &mailbox).await
}
ImapCommand::Move { sequence, mailbox } => {
handle_move(ctx, session, tag, &sequence, &mailbox).await
}
ImapCommand::Expunge => handle_expunge(ctx, session, tag).await,
ImapCommand::Close => handle_close(ctx, session, tag).await,
ImapCommand::Idle => handle_idle(ctx, session, tag).await,
ImapCommand::Namespace => handle_namespace(tag, session).await,
ImapCommand::Uid { subcommand } => handle_uid(ctx, session, tag, subcommand.as_ref()).await,
ImapCommand::Compress { mechanism } => handle_compress(session, tag, &mechanism).await,
}
}
async fn handle_capability(tag: &str) -> anyhow::Result<ImapResponse> {
let capabilities = vec![
"IMAP4rev1",
"LITERAL+",
"SASL-IR",
"LOGIN-REFERRALS",
"ID",
"ENABLE",
"IDLE",
"NAMESPACE",
"UIDPLUS",
"LIST-EXTENDED",
"UNSELECT",
"CHILDREN",
"SPECIAL-USE",
"MOVE",
"COMPRESS=DEFLATE",
"AUTH=PLAIN",
"AUTH=LOGIN",
"AUTH=CRAM-MD5",
"AUTH=SCRAM-SHA-256",
"AUTH=XOAUTH2",
];
let cap_list = capabilities.join(" ");
Ok(ImapResponse::ok(
tag,
format!("[CAPABILITY {}] Capability completed", cap_list),
))
}
async fn handle_noop(tag: &str) -> anyhow::Result<ImapResponse> {
Ok(ImapResponse::ok(tag, "NOOP completed"))
}
async fn handle_compress(
session: &mut ImapSession,
tag: &str,
mechanism: &str,
) -> anyhow::Result<ImapResponse> {
if !mechanism.eq_ignore_ascii_case("DEFLATE") {
return Ok(ImapResponse::no(
tag,
format!("COMPRESS: unsupported mechanism {mechanism}"),
));
}
if session.compress_pending {
return Ok(ImapResponse::no(tag, "COMPRESS: already active"));
}
session.compress_pending = true;
Ok(ImapResponse::ok(
tag,
"[COMPRESSIONACTIVE] Begin DEFLATE compression",
))
}