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::mailbox_registry::MailboxRegistry;
23use crate::response::ImapResponse;
24use crate::session::ImapSession;
25use rusmes_auth::AuthBackend;
26use rusmes_storage::{MailboxStore, MessageStore, MetadataStore};
27use std::sync::Arc;
28
29pub struct HandlerContext {
31 pub mailbox_store: Arc<dyn MailboxStore>,
32 pub message_store: Arc<dyn MessageStore>,
33 pub metadata_store: Arc<dyn MetadataStore>,
34 pub auth_backend: Arc<dyn AuthBackend>,
35 pub mailbox_registry: Arc<MailboxRegistry>,
37}
38
39impl HandlerContext {
40 pub fn new(
42 mailbox_store: Arc<dyn MailboxStore>,
43 message_store: Arc<dyn MessageStore>,
44 metadata_store: Arc<dyn MetadataStore>,
45 auth_backend: Arc<dyn AuthBackend>,
46 ) -> Self {
47 Self {
48 mailbox_store,
49 message_store,
50 metadata_store,
51 auth_backend,
52 mailbox_registry: Arc::new(MailboxRegistry::new()),
53 }
54 }
55
56 pub fn with_registry(
58 mailbox_store: Arc<dyn MailboxStore>,
59 message_store: Arc<dyn MessageStore>,
60 metadata_store: Arc<dyn MetadataStore>,
61 auth_backend: Arc<dyn AuthBackend>,
62 mailbox_registry: Arc<MailboxRegistry>,
63 ) -> Self {
64 Self {
65 mailbox_store,
66 message_store,
67 metadata_store,
68 auth_backend,
69 mailbox_registry,
70 }
71 }
72}
73
74#[allow(clippy::too_many_arguments)]
76pub async fn handle_command(
77 ctx: &HandlerContext,
78 session: &mut ImapSession,
79 tag: &str,
80 command: ImapCommand,
81) -> anyhow::Result<ImapResponse> {
82 match command {
83 ImapCommand::Capability => handle_capability(tag).await,
84 ImapCommand::Noop => handle_noop(tag).await,
85 ImapCommand::Login { user, password } => {
86 handle_login(ctx, session, tag, &user, &password).await
87 }
88 ImapCommand::Authenticate {
89 mechanism,
90 initial_response,
91 } => {
92 let _ = (mechanism, initial_response);
96 Ok(ImapResponse::bad(
97 tag,
98 "AUTHENTICATE must be handled by server loop",
99 ))
100 }
101 ImapCommand::Logout => handle_logout(session, tag).await,
102 ImapCommand::Select { mailbox } => handle_select(ctx, session, tag, &mailbox, false).await,
103 ImapCommand::Examine { mailbox } => handle_select(ctx, session, tag, &mailbox, true).await,
104 ImapCommand::Fetch { sequence, items } => {
105 handle_fetch(ctx, session, tag, &sequence, &items).await
106 }
107 ImapCommand::Store {
108 sequence,
109 mode,
110 flags,
111 } => handle_store(ctx, session, tag, &sequence, mode, &flags).await,
112 ImapCommand::Search { criteria } => handle_search(ctx, session, tag, &criteria).await,
113 ImapCommand::List { reference, mailbox } => {
114 handle_list(ctx, session, tag, &reference, &mailbox).await
115 }
116 ImapCommand::Lsub { reference, mailbox } => {
117 handle_lsub(ctx, session, tag, &reference, &mailbox).await
118 }
119 ImapCommand::Subscribe { mailbox } => handle_subscribe(ctx, session, tag, &mailbox).await,
120 ImapCommand::Unsubscribe { mailbox } => {
121 handle_unsubscribe(ctx, session, tag, &mailbox).await
122 }
123 ImapCommand::Create { mailbox } => handle_create(ctx, session, tag, &mailbox).await,
124 ImapCommand::CreateSpecialUse {
125 mailbox,
126 special_use,
127 } => handle_create_special_use(ctx, session, tag, &mailbox, &special_use).await,
128 ImapCommand::Delete { mailbox } => handle_delete(ctx, session, tag, &mailbox).await,
129 ImapCommand::Rename { old, new } => handle_rename(ctx, session, tag, &old, &new).await,
130 ImapCommand::Append {
131 mailbox,
132 flags,
133 date_time,
134 message_literal,
135 } => {
136 handle_append(
137 ctx,
138 session,
139 tag,
140 &mailbox,
141 &flags,
142 date_time.as_deref(),
143 &message_literal,
144 )
145 .await
146 }
147 ImapCommand::Copy { sequence, mailbox } => {
148 handle_copy(ctx, session, tag, &sequence, &mailbox).await
149 }
150 ImapCommand::Move { sequence, mailbox } => {
151 handle_move(ctx, session, tag, &sequence, &mailbox).await
152 }
153 ImapCommand::Expunge => handle_expunge(ctx, session, tag).await,
154 ImapCommand::Close => handle_close(ctx, session, tag).await,
155 ImapCommand::Idle => handle_idle(ctx, session, tag).await,
156 ImapCommand::Namespace => handle_namespace(tag, session).await,
157 ImapCommand::Uid { subcommand } => handle_uid(ctx, session, tag, subcommand.as_ref()).await,
158 ImapCommand::Compress { mechanism } => handle_compress(session, tag, &mechanism).await,
159 }
160}
161
162async fn handle_capability(tag: &str) -> anyhow::Result<ImapResponse> {
164 let capabilities = vec![
166 "IMAP4rev1",
167 "LITERAL+",
168 "SASL-IR",
169 "LOGIN-REFERRALS",
170 "ID",
171 "ENABLE",
172 "IDLE",
173 "NAMESPACE",
174 "UIDPLUS",
175 "LIST-EXTENDED",
176 "UNSELECT",
177 "CHILDREN",
178 "SPECIAL-USE",
179 "MOVE",
180 "COMPRESS=DEFLATE",
182 "AUTH=PLAIN",
184 "AUTH=LOGIN",
185 "AUTH=CRAM-MD5",
186 "AUTH=SCRAM-SHA-256",
187 "AUTH=XOAUTH2",
188 ];
189
190 let cap_list = capabilities.join(" ");
191 Ok(ImapResponse::ok(
192 tag,
193 format!("[CAPABILITY {}] Capability completed", cap_list),
194 ))
195}
196
197async fn handle_noop(tag: &str) -> anyhow::Result<ImapResponse> {
199 Ok(ImapResponse::ok(tag, "NOOP completed"))
200}
201
202async fn handle_compress(
209 session: &mut ImapSession,
210 tag: &str,
211 mechanism: &str,
212) -> anyhow::Result<ImapResponse> {
213 if !mechanism.eq_ignore_ascii_case("DEFLATE") {
214 return Ok(ImapResponse::no(
215 tag,
216 format!("COMPRESS: unsupported mechanism {mechanism}"),
217 ));
218 }
219 if session.compress_pending {
221 return Ok(ImapResponse::no(tag, "COMPRESS: already active"));
222 }
223 session.compress_pending = true;
225 Ok(ImapResponse::ok(
226 tag,
227 "[COMPRESSIONACTIVE] Begin DEFLATE compression",
228 ))
229}