1use crate::session::{SmtpConfig, SmtpSessionHandler};
4use rusmes_auth::AuthBackend;
5use rusmes_core::{MailProcessorRouter, RateLimiter};
6use rusmes_storage::StorageBackend;
7use std::sync::Arc;
8use tokio::net::TcpListener;
9
10pub struct SmtpServer {
12 config: SmtpConfig,
13 bind_addr: String,
14 listener: Option<TcpListener>,
15 tls_config: Option<Arc<rustls::ServerConfig>>,
16 processor_router: Arc<MailProcessorRouter>,
17 auth_backend: Arc<dyn AuthBackend>,
18 rate_limiter: Arc<RateLimiter>,
19 storage_backend: Arc<dyn StorageBackend>,
20}
21
22impl SmtpServer {
23 #[allow(clippy::too_many_arguments)]
25 pub fn new(
26 config: SmtpConfig,
27 bind_addr: impl Into<String>,
28 processor_router: Arc<MailProcessorRouter>,
29 auth_backend: Arc<dyn AuthBackend>,
30 rate_limiter: Arc<RateLimiter>,
31 storage_backend: Arc<dyn StorageBackend>,
32 ) -> Self {
33 Self {
34 config,
35 bind_addr: bind_addr.into(),
36 listener: None,
37 tls_config: None,
38 processor_router,
39 auth_backend,
40 rate_limiter,
41 storage_backend,
42 }
43 }
44
45 pub fn with_tls(mut self, tls_config: Arc<rustls::ServerConfig>) -> Self {
47 self.tls_config = Some(tls_config);
48 self
49 }
50
51 pub async fn bind(&mut self) -> anyhow::Result<()> {
53 let listener = TcpListener::bind(&self.bind_addr).await?;
54 tracing::info!("SMTP server listening on {}", self.bind_addr);
55 self.listener = Some(listener);
56 Ok(())
57 }
58
59 pub async fn serve(&self) -> anyhow::Result<()> {
61 let listener = self
62 .listener
63 .as_ref()
64 .ok_or_else(|| anyhow::anyhow!("Server not bound - call bind() first"))?;
65
66 loop {
67 let (stream, remote_addr) = listener.accept().await?;
68 tracing::info!("New SMTP connection from {}", remote_addr);
69
70 let ip = remote_addr.ip();
72 if !self.rate_limiter.allow_connection(ip).await {
73 tracing::warn!("Connection rate limit exceeded for {}", ip);
74 drop(stream);
76 continue;
77 }
78
79 let session = SmtpSessionHandler::new(
80 stream,
81 remote_addr,
82 self.config.clone(),
83 self.processor_router.clone(),
84 self.auth_backend.clone(),
85 self.rate_limiter.clone(),
86 self.storage_backend.clone(),
87 );
88
89 let rate_limiter = self.rate_limiter.clone();
90
91 tokio::spawn(async move {
93 if let Err(e) = session.handle().await {
94 tracing::error!("SMTP session error from {}: {}", remote_addr, e);
95 }
96 rate_limiter.release_connection(ip).await;
98 });
99 }
100 }
101
102 pub async fn run(mut self) -> anyhow::Result<()> {
104 self.bind().await?;
105 self.serve().await
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use rusmes_metrics::MetricsCollector;
113 use rusmes_proto::Username;
114 use rusmes_storage::{MailboxStore, MessageStore, MetadataStore};
115
116 #[allow(dead_code)]
117 struct DummyAuthBackend;
118
119 #[async_trait::async_trait]
120 impl AuthBackend for DummyAuthBackend {
121 async fn authenticate(
122 &self,
123 _username: &rusmes_proto::Username,
124 _password: &str,
125 ) -> anyhow::Result<bool> {
126 Ok(true)
127 }
128
129 async fn verify_identity(
130 &self,
131 _username: &rusmes_proto::Username,
132 ) -> anyhow::Result<bool> {
133 Ok(true)
134 }
135
136 async fn list_users(&self) -> anyhow::Result<Vec<rusmes_proto::Username>> {
137 Ok(Vec::new())
138 }
139
140 async fn create_user(
141 &self,
142 _username: &rusmes_proto::Username,
143 _password: &str,
144 ) -> anyhow::Result<()> {
145 Ok(())
146 }
147
148 async fn delete_user(&self, _username: &rusmes_proto::Username) -> anyhow::Result<()> {
149 Ok(())
150 }
151
152 async fn change_password(
153 &self,
154 _username: &rusmes_proto::Username,
155 _new_password: &str,
156 ) -> anyhow::Result<()> {
157 Ok(())
158 }
159 }
160
161 #[allow(dead_code)]
162 struct DummyMailboxStore;
163
164 #[async_trait::async_trait]
165 impl MailboxStore for DummyMailboxStore {
166 async fn create_mailbox(
167 &self,
168 _path: &rusmes_storage::MailboxPath,
169 ) -> anyhow::Result<rusmes_storage::MailboxId> {
170 Ok(rusmes_storage::MailboxId::new())
171 }
172
173 async fn delete_mailbox(&self, _id: &rusmes_storage::MailboxId) -> anyhow::Result<()> {
174 Ok(())
175 }
176
177 async fn rename_mailbox(
178 &self,
179 _id: &rusmes_storage::MailboxId,
180 _new_path: &rusmes_storage::MailboxPath,
181 ) -> anyhow::Result<()> {
182 Ok(())
183 }
184
185 async fn get_mailbox(
186 &self,
187 _id: &rusmes_storage::MailboxId,
188 ) -> anyhow::Result<Option<rusmes_storage::Mailbox>> {
189 Ok(None)
190 }
191
192 async fn list_mailboxes(
193 &self,
194 _user: &Username,
195 ) -> anyhow::Result<Vec<rusmes_storage::Mailbox>> {
196 Ok(Vec::new())
197 }
198
199 async fn get_user_inbox(
200 &self,
201 _user: &Username,
202 ) -> anyhow::Result<Option<rusmes_storage::MailboxId>> {
203 Ok(None)
204 }
205
206 async fn subscribe_mailbox(
207 &self,
208 _user: &Username,
209 _mailbox_name: String,
210 ) -> anyhow::Result<()> {
211 Ok(())
212 }
213
214 async fn unsubscribe_mailbox(
215 &self,
216 _user: &Username,
217 _mailbox_name: &str,
218 ) -> anyhow::Result<()> {
219 Ok(())
220 }
221
222 async fn list_subscriptions(&self, _user: &Username) -> anyhow::Result<Vec<String>> {
223 Ok(Vec::new())
224 }
225 }
226
227 #[allow(dead_code)]
228 struct DummyMessageStore;
229
230 #[async_trait::async_trait]
231 impl MessageStore for DummyMessageStore {
232 async fn append_message(
233 &self,
234 _mailbox_id: &rusmes_storage::MailboxId,
235 _message: rusmes_proto::Mail,
236 ) -> anyhow::Result<rusmes_storage::MessageMetadata> {
237 Ok(rusmes_storage::MessageMetadata::new(
238 rusmes_proto::MessageId::new(),
239 rusmes_storage::MailboxId::new(),
240 1,
241 rusmes_storage::MessageFlags::new(),
242 0,
243 ))
244 }
245
246 async fn get_message(
247 &self,
248 _message_id: &rusmes_proto::MessageId,
249 ) -> anyhow::Result<Option<rusmes_proto::Mail>> {
250 Ok(None)
251 }
252
253 async fn delete_messages(
254 &self,
255 _message_ids: &[rusmes_proto::MessageId],
256 ) -> anyhow::Result<()> {
257 Ok(())
258 }
259
260 async fn set_flags(
261 &self,
262 _message_ids: &[rusmes_proto::MessageId],
263 _flags: rusmes_storage::MessageFlags,
264 ) -> anyhow::Result<()> {
265 Ok(())
266 }
267
268 async fn search(
269 &self,
270 _mailbox_id: &rusmes_storage::MailboxId,
271 _criteria: rusmes_storage::SearchCriteria,
272 ) -> anyhow::Result<Vec<rusmes_proto::MessageId>> {
273 Ok(Vec::new())
274 }
275
276 async fn copy_messages(
277 &self,
278 _message_ids: &[rusmes_proto::MessageId],
279 _dest_mailbox_id: &rusmes_storage::MailboxId,
280 ) -> anyhow::Result<Vec<rusmes_storage::MessageMetadata>> {
281 Ok(Vec::new())
282 }
283
284 async fn get_mailbox_messages(
285 &self,
286 _mailbox_id: &rusmes_storage::MailboxId,
287 ) -> anyhow::Result<Vec<rusmes_storage::MessageMetadata>> {
288 Ok(Vec::new())
289 }
290 }
291
292 #[allow(dead_code)]
293 struct DummyMetadataStore;
294
295 #[async_trait::async_trait]
296 impl MetadataStore for DummyMetadataStore {
297 async fn get_user_quota(&self, _user: &Username) -> anyhow::Result<rusmes_storage::Quota> {
298 Ok(rusmes_storage::Quota::new(0, 1024 * 1024 * 1024))
299 }
300
301 async fn set_user_quota(
302 &self,
303 _user: &Username,
304 _quota: rusmes_storage::Quota,
305 ) -> anyhow::Result<()> {
306 Ok(())
307 }
308
309 async fn get_mailbox_counters(
310 &self,
311 _mailbox_id: &rusmes_storage::MailboxId,
312 ) -> anyhow::Result<rusmes_storage::MailboxCounters> {
313 Ok(rusmes_storage::MailboxCounters::default())
314 }
315 }
316
317 #[allow(dead_code)]
318 struct DummyStorageBackend {
319 mailbox_store: Arc<dyn MailboxStore>,
320 message_store: Arc<dyn MessageStore>,
321 metadata_store: Arc<dyn MetadataStore>,
322 }
323
324 impl StorageBackend for DummyStorageBackend {
325 fn mailbox_store(&self) -> Arc<dyn MailboxStore> {
326 self.mailbox_store.clone()
327 }
328
329 fn message_store(&self) -> Arc<dyn MessageStore> {
330 self.message_store.clone()
331 }
332
333 fn metadata_store(&self) -> Arc<dyn MetadataStore> {
334 self.metadata_store.clone()
335 }
336 }
337
338 #[test]
339 fn test_server_creation() {
340 let config = SmtpConfig::default();
341 let metrics = Arc::new(MetricsCollector::new());
342 let router = Arc::new(MailProcessorRouter::new(metrics));
343 let auth = Arc::new(DummyAuthBackend);
344 let rate_limiter = Arc::new(rusmes_core::RateLimiter::new(
345 rusmes_core::RateLimitConfig::default(),
346 ));
347 let storage: Arc<dyn StorageBackend> = Arc::new(DummyStorageBackend {
348 mailbox_store: Arc::new(DummyMailboxStore),
349 message_store: Arc::new(DummyMessageStore),
350 metadata_store: Arc::new(DummyMetadataStore),
351 });
352
353 let server = SmtpServer::new(
354 config.clone(),
355 "127.0.0.1:2525",
356 router,
357 auth,
358 rate_limiter,
359 storage,
360 );
361
362 assert_eq!(server.bind_addr, "127.0.0.1:2525");
363 assert_eq!(server.config.hostname, config.hostname);
364 }
365}