1use crate::command::ImapCommand;
13use crate::handler_auth::{handle_login, handle_logout};
14use crate::handler_mailbox::{
15 handle_create, handle_create_special_use, handle_delete, handle_idle, handle_list, handle_lsub,
16 handle_namespace, handle_rename, handle_select, handle_subscribe, handle_unsubscribe,
17};
18use crate::handler_message::{
19 handle_append, handle_close, handle_copy, handle_expunge, handle_fetch, handle_move,
20 handle_search, handle_store, handle_uid,
21};
22use crate::response::ImapResponse;
23use crate::session::ImapSession;
24use rusmes_auth::AuthBackend;
25use rusmes_storage::{MailboxStore, MessageStore, MetadataStore};
26use std::sync::Arc;
27
28pub struct HandlerContext {
30 pub mailbox_store: Arc<dyn MailboxStore>,
31 pub message_store: Arc<dyn MessageStore>,
32 pub metadata_store: Arc<dyn MetadataStore>,
33 pub auth_backend: Arc<dyn AuthBackend>,
34}
35
36impl HandlerContext {
37 pub fn new(
39 mailbox_store: Arc<dyn MailboxStore>,
40 message_store: Arc<dyn MessageStore>,
41 metadata_store: Arc<dyn MetadataStore>,
42 auth_backend: Arc<dyn AuthBackend>,
43 ) -> Self {
44 Self {
45 mailbox_store,
46 message_store,
47 metadata_store,
48 auth_backend,
49 }
50 }
51}
52
53#[allow(clippy::too_many_arguments)]
55pub async fn handle_command(
56 ctx: &HandlerContext,
57 session: &mut ImapSession,
58 tag: &str,
59 command: ImapCommand,
60) -> anyhow::Result<ImapResponse> {
61 match command {
62 ImapCommand::Capability => handle_capability(tag).await,
63 ImapCommand::Noop => handle_noop(tag).await,
64 ImapCommand::Login { user, password } => {
65 handle_login(ctx, session, tag, &user, &password).await
66 }
67 ImapCommand::Authenticate {
68 mechanism,
69 initial_response,
70 } => {
71 let _ = (mechanism, initial_response);
75 Ok(ImapResponse::bad(
76 tag,
77 "AUTHENTICATE must be handled by server loop",
78 ))
79 }
80 ImapCommand::Logout => handle_logout(session, tag).await,
81 ImapCommand::Select { mailbox } => handle_select(ctx, session, tag, &mailbox, false).await,
82 ImapCommand::Examine { mailbox } => handle_select(ctx, session, tag, &mailbox, true).await,
83 ImapCommand::Fetch { sequence, items } => {
84 handle_fetch(ctx, session, tag, &sequence, &items).await
85 }
86 ImapCommand::Store {
87 sequence,
88 mode,
89 flags,
90 } => handle_store(ctx, session, tag, &sequence, mode, &flags).await,
91 ImapCommand::Search { criteria } => handle_search(ctx, session, tag, &criteria).await,
92 ImapCommand::List { reference, mailbox } => {
93 handle_list(ctx, session, tag, &reference, &mailbox).await
94 }
95 ImapCommand::Lsub { reference, mailbox } => {
96 handle_lsub(ctx, session, tag, &reference, &mailbox).await
97 }
98 ImapCommand::Subscribe { mailbox } => handle_subscribe(ctx, session, tag, &mailbox).await,
99 ImapCommand::Unsubscribe { mailbox } => {
100 handle_unsubscribe(ctx, session, tag, &mailbox).await
101 }
102 ImapCommand::Create { mailbox } => handle_create(ctx, session, tag, &mailbox).await,
103 ImapCommand::CreateSpecialUse {
104 mailbox,
105 special_use,
106 } => handle_create_special_use(ctx, session, tag, &mailbox, &special_use).await,
107 ImapCommand::Delete { mailbox } => handle_delete(ctx, session, tag, &mailbox).await,
108 ImapCommand::Rename { old, new } => handle_rename(ctx, session, tag, &old, &new).await,
109 ImapCommand::Append {
110 mailbox,
111 flags,
112 date_time,
113 message_literal,
114 } => {
115 handle_append(
116 ctx,
117 session,
118 tag,
119 &mailbox,
120 &flags,
121 date_time.as_deref(),
122 &message_literal,
123 )
124 .await
125 }
126 ImapCommand::Copy { sequence, mailbox } => {
127 handle_copy(ctx, session, tag, &sequence, &mailbox).await
128 }
129 ImapCommand::Move { sequence, mailbox } => {
130 handle_move(ctx, session, tag, &sequence, &mailbox).await
131 }
132 ImapCommand::Expunge => handle_expunge(ctx, session, tag).await,
133 ImapCommand::Close => handle_close(ctx, session, tag).await,
134 ImapCommand::Idle => handle_idle(ctx, session, tag).await,
135 ImapCommand::Namespace => handle_namespace(tag, session).await,
136 ImapCommand::Uid { subcommand } => handle_uid(ctx, session, tag, subcommand.as_ref()).await,
137 }
138}
139
140async fn handle_capability(tag: &str) -> anyhow::Result<ImapResponse> {
142 let capabilities = vec![
144 "IMAP4rev1",
145 "LITERAL+",
146 "SASL-IR",
147 "LOGIN-REFERRALS",
148 "ID",
149 "ENABLE",
150 "IDLE",
151 "NAMESPACE",
152 "UIDPLUS",
153 "LIST-EXTENDED",
154 "UNSELECT",
155 "CHILDREN",
156 "SPECIAL-USE",
157 "MOVE",
158 "AUTH=PLAIN",
160 "AUTH=LOGIN",
161 "AUTH=CRAM-MD5",
162 "AUTH=SCRAM-SHA-256",
163 "AUTH=XOAUTH2",
164 ];
165
166 let cap_list = capabilities.join(" ");
167 Ok(ImapResponse::ok(
168 tag,
169 format!("[CAPABILITY {}] Capability completed", cap_list),
170 ))
171}
172
173async fn handle_noop(tag: &str) -> anyhow::Result<ImapResponse> {
175 Ok(ImapResponse::ok(tag, "NOOP completed"))
176}