1use std::collections::{HashMap, HashSet};
2use std::io;
3use std::sync::OnceLock;
4
5use time::{OffsetDateTime, format_description};
6
7use crate::sqlite_store::SqliteStore;
8
9#[derive(Clone, Debug, Default)]
11pub enum StorageBackend {
12 #[default]
14 InMemory,
15 Sqlite(String),
17}
18
19pub enum Store {
21 InMemory(InMemoryStore),
23 Sqlite(SqliteStore),
25}
26
27#[derive(Clone)]
28pub(crate) struct StoreSnapshot {
29 pub users: Vec<String>,
30 pub mailboxes: HashMap<String, Vec<String>>,
31 pub subscriptions: HashMap<String, Vec<String>>,
32 pub messages: HashMap<(String, String), Vec<Message>>,
33}
34
35impl Store {
36 pub fn new(backend: StorageBackend) -> io::Result<Self> {
38 match backend {
39 StorageBackend::InMemory => Ok(Self::InMemory(InMemoryStore::new())),
40 StorageBackend::Sqlite(path) => {
41 let store = SqliteStore::new(&path).map_err(io::Error::other)?;
42 Ok(Self::Sqlite(store))
43 }
44 }
45 }
46
47 #[allow(dead_code)]
48 pub fn ensure_mailbox(&mut self, user: &str, mailbox: &str) {
49 match self {
50 Self::InMemory(s) => s.ensure_mailbox(user, mailbox),
51 Self::Sqlite(s) => {
52 let _ = s.ensure_mailbox_for_user(user, mailbox);
53 }
54 }
55 }
56
57 pub fn list_mailboxes(&mut self, user: &str) -> Vec<String> {
58 match self {
59 Self::InMemory(s) => s.list_mailboxes(user),
60 Self::Sqlite(s) => s.list_mailboxes(user).unwrap_or_default(),
61 }
62 }
63
64 pub fn create_mailbox(&mut self, user: &str, mailbox: &str) -> bool {
65 match self {
66 Self::InMemory(s) => s.create_mailbox(user, mailbox),
67 Self::Sqlite(s) => s.create_mailbox(user, mailbox).unwrap_or(false),
68 }
69 }
70
71 pub fn rename_mailbox(&mut self, user: &str, old: &str, new: &str) -> bool {
72 match self {
73 Self::InMemory(s) => s.rename_mailbox(user, old, new),
74 Self::Sqlite(s) => s.rename_mailbox(user, old, new).unwrap_or(false),
75 }
76 }
77
78 pub fn delete_mailbox(&mut self, user: &str, mailbox: &str) -> bool {
79 match self {
80 Self::InMemory(s) => s.delete_mailbox(user, mailbox),
81 Self::Sqlite(s) => s.delete_mailbox(user, mailbox).unwrap_or(false),
82 }
83 }
84
85 pub fn subscribe(&mut self, user: &str, mailbox: &str) -> bool {
86 match self {
87 Self::InMemory(s) => s.subscribe(user, mailbox),
88 Self::Sqlite(s) => s.subscribe(user, mailbox).unwrap_or(false),
89 }
90 }
91
92 pub fn unsubscribe(&mut self, user: &str, mailbox: &str) -> bool {
93 match self {
94 Self::InMemory(s) => s.unsubscribe(user, mailbox),
95 Self::Sqlite(s) => s.unsubscribe(user, mailbox).unwrap_or(false),
96 }
97 }
98
99 pub fn list_subscriptions(&mut self, user: &str) -> Vec<String> {
100 match self {
101 Self::InMemory(s) => s.list_subscriptions(user),
102 Self::Sqlite(s) => s.list_subscriptions(user).unwrap_or_default(),
103 }
104 }
105
106 pub fn append(
107 &mut self,
108 user: &str,
109 mailbox: &str,
110 data: Vec<u8>,
111 internal_date: String,
112 ) -> u32 {
113 match self {
114 Self::InMemory(s) => s.append(user, mailbox, data, internal_date),
115 Self::Sqlite(s) => s.append(user, mailbox, data, internal_date).unwrap_or(0),
116 }
117 }
118
119 pub fn append_with_flags(
120 &mut self,
121 user: &str,
122 mailbox: &str,
123 data: Vec<u8>,
124 internal_date: String,
125 flags: &FlagSet,
126 ) -> u32 {
127 match self {
128 Self::InMemory(s) => s.append_with_flags(user, mailbox, data, internal_date, flags),
129 Self::Sqlite(s) => s
130 .append_with_flags(user, mailbox, data, internal_date, flags)
131 .unwrap_or(0),
132 }
133 }
134
135 pub fn list(&mut self, user: &str, mailbox: &str) -> Vec<Message> {
136 match self {
137 Self::InMemory(s) => s.list(user, mailbox),
138 Self::Sqlite(s) => s.list(user, mailbox).unwrap_or_default(),
139 }
140 }
141
142 pub fn apply_flags_by_seq(
143 &mut self,
144 user: &str,
145 mailbox: &str,
146 seq: u32,
147 op: FlagOp,
148 flags: &FlagSet,
149 ) {
150 match self {
151 Self::InMemory(s) => s.apply_flags_by_seq(user, mailbox, seq, op, flags),
152 Self::Sqlite(s) => {
153 let _ = s.apply_flags_by_seq(user, mailbox, seq, op, flags);
154 }
155 }
156 }
157
158 pub fn apply_flags_by_uid(
159 &mut self,
160 user: &str,
161 mailbox: &str,
162 uid: u32,
163 op: FlagOp,
164 flags: &FlagSet,
165 ) {
166 match self {
167 Self::InMemory(s) => s.apply_flags_by_uid(user, mailbox, uid, op, flags),
168 Self::Sqlite(s) => {
169 let _ = s.apply_flags_by_uid(user, mailbox, uid, op, flags);
170 }
171 }
172 }
173
174 pub fn expunge_deleted(&mut self, user: &str, mailbox: &str) -> Vec<u32> {
175 match self {
176 Self::InMemory(s) => s.expunge_deleted(user, mailbox),
177 Self::Sqlite(s) => s.expunge_deleted(user, mailbox).unwrap_or_default(),
178 }
179 }
180
181 pub fn expunge_deleted_by_uid(&mut self, user: &str, mailbox: &str, uids: &[u32]) -> Vec<u32> {
182 match self {
183 Self::InMemory(s) => s.expunge_deleted_by_uid(user, mailbox, uids),
184 Self::Sqlite(s) => s
185 .expunge_deleted_by_uid(user, mailbox, uids)
186 .unwrap_or_default(),
187 }
188 }
189
190 pub fn copy_by_seq_set(&mut self, user: &str, src: &str, seqs: &[u32], dest: &str) -> usize {
191 match self {
192 Self::InMemory(s) => s.copy_by_seq_set(user, src, seqs, dest),
193 Self::Sqlite(s) => s.copy_by_seq_set(user, src, seqs, dest).unwrap_or(0),
194 }
195 }
196
197 pub fn move_by_seq_set(&mut self, user: &str, src: &str, seqs: &[u32], dest: &str) -> Vec<u32> {
198 match self {
199 Self::InMemory(s) => s.move_by_seq_set(user, src, seqs, dest),
200 Self::Sqlite(s) => s.move_by_seq_set(user, src, seqs, dest).unwrap_or_default(),
201 }
202 }
203
204 pub fn seqs_from_uids(&self, user: &str, mailbox: &str, uids: &[u32]) -> Vec<u32> {
205 match self {
206 Self::InMemory(s) => s.seqs_from_uids(user, mailbox, uids),
207 Self::Sqlite(s) => s.seqs_from_uids(user, mailbox, uids).unwrap_or_default(),
208 }
209 }
210
211 pub fn reset(&mut self) {
213 match self {
214 Self::InMemory(s) => s.reset(),
215 Self::Sqlite(s) => {
216 let _ = s.reset();
217 }
218 }
219 }
220
221 pub(crate) fn snapshot(&mut self) -> StoreSnapshot {
222 let users = self.list_users();
223 let mut mailboxes = HashMap::new();
224 let mut subscriptions = HashMap::new();
225 let mut messages = HashMap::new();
226
227 for user in &users {
228 let boxes = self.list_mailboxes(user);
229 mailboxes.insert(user.clone(), boxes.clone());
230 let subs = self.list_subscriptions(user);
231 subscriptions.insert(user.clone(), subs);
232 for mailbox in boxes {
233 let list = self.list(user, &mailbox);
234 messages.insert((user.clone(), mailbox), list);
235 }
236 }
237
238 StoreSnapshot {
239 users,
240 mailboxes,
241 subscriptions,
242 messages,
243 }
244 }
245
246 pub(crate) fn restore_snapshot(&mut self, snapshot: &StoreSnapshot) {
247 self.reset();
248
249 for user in &snapshot.users {
250 let mailboxes = snapshot.mailboxes.get(user).cloned().unwrap_or_default();
251 for mailbox in mailboxes {
252 self.ensure_mailbox(user, &mailbox);
253 if let Some(items) = snapshot.messages.get(&(user.clone(), mailbox.clone())) {
254 for message in items {
255 let flags = FlagSet {
256 seen: message.seen,
257 flagged: message.flagged,
258 deleted: message.deleted,
259 answered: message.answered,
260 draft: message.draft,
261 };
262 self.append_with_flags(
263 user,
264 &mailbox,
265 message.data.clone(),
266 message.internal_date.clone(),
267 &flags,
268 );
269 }
270 }
271 }
272 let subs = snapshot
273 .subscriptions
274 .get(user)
275 .cloned()
276 .unwrap_or_default();
277 for mailbox in subs {
278 let _ = self.subscribe(user, &mailbox);
279 }
280 }
281 }
282
283 pub fn purge_messages(&mut self) {
285 match self {
286 Self::InMemory(s) => s.purge_messages(),
287 Self::Sqlite(s) => {
288 let _ = s.purge_messages();
289 }
290 }
291 }
292
293 pub fn list_users(&self) -> Vec<String> {
295 match self {
296 Self::InMemory(s) => s.list_users(),
297 Self::Sqlite(s) => s.list_users().unwrap_or_default(),
298 }
299 }
300
301 pub fn delete_by_uid(&mut self, user: &str, mailbox: &str, uid: u32) {
303 match self {
304 Self::InMemory(s) => s.delete_by_uid(user, mailbox, uid),
305 Self::Sqlite(s) => {
306 let _ = s.delete_by_uid(user, mailbox, uid);
307 }
308 }
309 }
310}
311
312#[derive(Clone, Debug)]
313pub struct Message {
314 pub uid: u32,
315 pub data: Vec<u8>,
316 pub internal_date: String,
317 pub seen: bool,
318 pub flagged: bool,
319 pub deleted: bool,
320 pub answered: bool,
321 pub draft: bool,
322}
323
324pub const DEFAULT_INTERNAL_DATE: &str = "01-Jan-2020 00:00:00 +0000";
325
326pub fn current_internal_date() -> String {
328 format_internal_date(OffsetDateTime::now_utc())
329}
330
331fn format_internal_date(date: OffsetDateTime) -> String {
332 static FORMAT: OnceLock<Vec<format_description::FormatItem<'static>>> = OnceLock::new();
333 let format = FORMAT.get_or_init(|| {
334 format_description::parse(
335 "[day]-[month repr:short]-[year] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]",
336 )
337 .expect("internal date format should be valid")
338 });
339 date.format(format)
340 .unwrap_or_else(|_| DEFAULT_INTERNAL_DATE.to_string())
341}
342
343#[derive(Clone, Copy, Debug)]
344pub enum FlagOp {
345 Add,
346 Remove,
347 Replace,
348}
349
350#[derive(Default, Clone, Debug)]
351pub struct FlagSet {
352 pub seen: bool,
353 pub flagged: bool,
354 pub deleted: bool,
355 pub answered: bool,
356 pub draft: bool,
357}
358
359#[derive(Default)]
360pub struct InMemoryStore {
361 pub users: HashMap<String, HashMap<String, Vec<Message>>>,
362 pub subscriptions: HashMap<String, HashSet<String>>,
363}
364
365impl InMemoryStore {
366 pub fn new() -> Self {
367 Self {
368 users: HashMap::new(),
369 subscriptions: HashMap::new(),
370 }
371 }
372
373 pub fn ensure_mailbox(&mut self, user: &str, mailbox: &str) {
374 let mailboxes = self.users.entry(user.to_string()).or_default();
375 mailboxes.entry(mailbox.to_string()).or_default();
376 }
377
378 pub fn list_mailboxes(&mut self, user: &str) -> Vec<String> {
379 self.ensure_mailbox(user, "INBOX");
380 let mailboxes = self.users.entry(user.to_string()).or_default();
381 let mut names: Vec<String> = mailboxes.keys().cloned().collect();
382 names.sort();
383 names
384 }
385
386 pub fn create_mailbox(&mut self, user: &str, mailbox: &str) -> bool {
387 if mailbox.eq_ignore_ascii_case("INBOX") {
388 return false;
389 }
390 let mailboxes = self.users.entry(user.to_string()).or_default();
391 if mailboxes.contains_key(mailbox) {
392 return false;
393 }
394 mailboxes.insert(mailbox.to_string(), Vec::new());
395 true
396 }
397
398 pub fn rename_mailbox(&mut self, user: &str, old: &str, new: &str) -> bool {
399 let mailboxes = self.users.entry(user.to_string()).or_default();
400 if !mailboxes.contains_key(old) || mailboxes.contains_key(new) {
401 return false;
402 }
403 if let Some(messages) = mailboxes.remove(old) {
404 mailboxes.insert(new.to_string(), messages);
405 if let Some(subs) = self.subscriptions.get_mut(user)
406 && subs.remove(old)
407 {
408 subs.insert(new.to_string());
409 }
410 return true;
411 }
412 false
413 }
414
415 pub fn delete_mailbox(&mut self, user: &str, mailbox: &str) -> bool {
416 if mailbox.eq_ignore_ascii_case("INBOX") {
417 return false;
418 }
419 let mailboxes = self.users.entry(user.to_string()).or_default();
420 let removed = mailboxes.remove(mailbox).is_some();
421 if removed && let Some(subs) = self.subscriptions.get_mut(user) {
422 subs.remove(mailbox);
423 }
424 removed
425 }
426
427 pub fn subscribe(&mut self, user: &str, mailbox: &str) -> bool {
428 let exists = self
429 .users
430 .get(user)
431 .and_then(|boxes| boxes.get(mailbox))
432 .is_some()
433 || mailbox.eq_ignore_ascii_case("INBOX");
434 if !exists && !mailbox.eq_ignore_ascii_case("INBOX") {
435 return false;
436 }
437 let subs = self.subscriptions.entry(user.to_string()).or_default();
438 subs.insert(mailbox.to_string());
439 true
440 }
441
442 pub fn unsubscribe(&mut self, user: &str, mailbox: &str) -> bool {
443 let subs = self.subscriptions.entry(user.to_string()).or_default();
444 subs.remove(mailbox)
445 }
446
447 pub fn list_subscriptions(&mut self, user: &str) -> Vec<String> {
448 let subs = self.subscriptions.entry(user.to_string()).or_default();
449 let mut names: Vec<String> = subs.iter().cloned().collect();
450 names.sort();
451 names
452 }
453
454 pub fn append(
455 &mut self,
456 user: &str,
457 mailbox: &str,
458 data: Vec<u8>,
459 internal_date: String,
460 ) -> u32 {
461 self.ensure_mailbox(user, mailbox);
462 let mailboxes = self.users.entry(user.to_string()).or_default();
463 let box_ref = mailboxes.entry(mailbox.to_string()).or_default();
464 let uid = u32::try_from(box_ref.len())
465 .unwrap_or(u32::MAX)
466 .saturating_add(1);
467 box_ref.push(Message {
468 uid,
469 data,
470 internal_date,
471 seen: false,
472 flagged: false,
473 deleted: false,
474 answered: false,
475 draft: false,
476 });
477 uid
478 }
479
480 pub fn append_with_flags(
481 &mut self,
482 user: &str,
483 mailbox: &str,
484 data: Vec<u8>,
485 internal_date: String,
486 flags: &FlagSet,
487 ) -> u32 {
488 self.ensure_mailbox(user, mailbox);
489 let mailboxes = self.users.entry(user.to_string()).or_default();
490 let box_ref = mailboxes.entry(mailbox.to_string()).or_default();
491 let uid = u32::try_from(box_ref.len())
492 .unwrap_or(u32::MAX)
493 .saturating_add(1);
494 box_ref.push(Message {
495 uid,
496 data,
497 internal_date,
498 seen: flags.seen,
499 flagged: flags.flagged,
500 deleted: flags.deleted,
501 answered: flags.answered,
502 draft: flags.draft,
503 });
504 uid
505 }
506
507 pub fn list(&mut self, user: &str, mailbox: &str) -> Vec<Message> {
508 self.ensure_mailbox(user, mailbox);
509 self.users
510 .get(user)
511 .and_then(|boxes| boxes.get(mailbox).cloned())
512 .unwrap_or_default()
513 }
514
515 pub fn apply_flags_by_seq(
516 &mut self,
517 user: &str,
518 mailbox: &str,
519 seq: u32,
520 op: FlagOp,
521 flags: &FlagSet,
522 ) {
523 let index = seq.saturating_sub(1) as usize;
524 if let Some(messages) = self
525 .users
526 .get_mut(user)
527 .and_then(|boxes| boxes.get_mut(mailbox))
528 && let Some(message) = messages.get_mut(index)
529 {
530 apply_flags(message, op, flags);
531 }
532 }
533
534 pub fn apply_flags_by_uid(
535 &mut self,
536 user: &str,
537 mailbox: &str,
538 uid: u32,
539 op: FlagOp,
540 flags: &FlagSet,
541 ) {
542 if let Some(messages) = self
543 .users
544 .get_mut(user)
545 .and_then(|boxes| boxes.get_mut(mailbox))
546 {
547 for message in messages {
548 if message.uid == uid {
549 apply_flags(message, op, flags);
550 }
551 }
552 }
553 }
554
555 pub fn expunge_deleted(&mut self, user: &str, mailbox: &str) -> Vec<u32> {
556 let mut expunged = Vec::new();
557 if let Some(messages) = self
558 .users
559 .get_mut(user)
560 .and_then(|boxes| boxes.get_mut(mailbox))
561 {
562 let mut i = 0;
563 while i < messages.len() {
564 if messages[i].deleted {
565 if let Ok(seq) = u32::try_from(i + 1) {
566 expunged.push(seq);
567 }
568 messages.remove(i);
569 } else {
570 i += 1;
571 }
572 }
573 }
574 expunged
575 }
576
577 pub fn expunge_deleted_by_uid(&mut self, user: &str, mailbox: &str, uids: &[u32]) -> Vec<u32> {
578 let mut expunged = Vec::new();
579 if let Some(messages) = self
580 .users
581 .get_mut(user)
582 .and_then(|boxes| boxes.get_mut(mailbox))
583 {
584 let mut i = 0;
585 while i < messages.len() {
586 if messages[i].deleted && uids.contains(&messages[i].uid) {
587 if let Ok(seq) = u32::try_from(i + 1) {
588 expunged.push(seq);
589 }
590 messages.remove(i);
591 } else {
592 i += 1;
593 }
594 }
595 }
596 expunged
597 }
598
599 pub fn copy_by_seq_set(&mut self, user: &str, src: &str, seqs: &[u32], dest: &str) -> usize {
600 self.ensure_mailbox(user, dest);
601 let source_messages = self
602 .users
603 .get(user)
604 .and_then(|boxes| boxes.get(src).cloned())
605 .unwrap_or_default();
606 let mailboxes = self.users.entry(user.to_string()).or_default();
607 let dest_box = mailboxes.entry(dest.to_string()).or_default();
608 let mut copied = 0;
609 for seq in seqs {
610 let index = seq.saturating_sub(1) as usize;
611 if let Some(message) = source_messages.get(index) {
612 let uid = u32::try_from(dest_box.len())
613 .unwrap_or(u32::MAX)
614 .saturating_add(1);
615 dest_box.push(Message {
616 uid,
617 data: message.data.clone(),
618 internal_date: message.internal_date.clone(),
619 seen: message.seen,
620 flagged: message.flagged,
621 deleted: false,
622 answered: message.answered,
623 draft: message.draft,
624 });
625 copied += 1;
626 }
627 }
628 copied
629 }
630
631 pub fn move_by_seq_set(&mut self, user: &str, src: &str, seqs: &[u32], dest: &str) -> Vec<u32> {
632 let mut expunged = Vec::new();
633 self.copy_by_seq_set(user, src, seqs, dest);
634 if let Some(messages) = self
635 .users
636 .get_mut(user)
637 .and_then(|boxes| boxes.get_mut(src))
638 {
639 let mut indices: Vec<usize> = seqs
640 .iter()
641 .filter_map(|seq| seq.checked_sub(1).map(|v| v as usize))
642 .collect();
643 indices.sort_by(|a, b| b.cmp(a));
644 for idx in indices {
645 if idx < messages.len() {
646 if let Ok(seq) = u32::try_from(idx + 1) {
647 expunged.push(seq);
648 }
649 messages.remove(idx);
650 }
651 }
652 }
653 expunged
654 }
655
656 pub fn seqs_from_uids(&self, user: &str, mailbox: &str, uids: &[u32]) -> Vec<u32> {
657 let mut seqs = Vec::new();
658 if let Some(messages) = self.users.get(user).and_then(|boxes| boxes.get(mailbox)) {
659 for uid in uids {
660 if let Some(pos) = messages.iter().position(|m| m.uid == *uid)
661 && let Ok(seq) = u32::try_from(pos + 1)
662 {
663 seqs.push(seq);
664 }
665 }
666 }
667 seqs
668 }
669
670 pub fn reset(&mut self) {
672 self.users.clear();
673 self.subscriptions.clear();
674 }
675
676 pub fn purge_messages(&mut self) {
678 for mailboxes in self.users.values_mut() {
679 for messages in mailboxes.values_mut() {
680 messages.clear();
681 }
682 }
683 }
684
685 pub fn list_users(&self) -> Vec<String> {
687 self.users.keys().cloned().collect()
688 }
689
690 pub fn delete_by_uid(&mut self, user: &str, mailbox: &str, uid: u32) {
692 if let Some(messages) = self
693 .users
694 .get_mut(user)
695 .and_then(|boxes| boxes.get_mut(mailbox))
696 {
697 messages.retain(|m| m.uid != uid);
698 }
699 }
700}
701
702pub fn uid_to_seq(store: &Store, user: &str, mailbox: &str, uid: u32) -> Option<u32> {
704 match store {
705 Store::InMemory(s) => s
706 .users
707 .get(user)
708 .and_then(|boxes| boxes.get(mailbox))
709 .and_then(|messages| {
710 messages
711 .iter()
712 .position(|m| m.uid == uid)
713 .and_then(|idx| u32::try_from(idx + 1).ok())
714 }),
715 Store::Sqlite(s) => s.list(user, mailbox).ok().and_then(|messages| {
716 messages
717 .iter()
718 .position(|m| m.uid == uid)
719 .and_then(|idx| u32::try_from(idx + 1).ok())
720 }),
721 }
722}
723
724fn apply_flags(message: &mut Message, op: FlagOp, flags: &FlagSet) {
725 match op {
726 FlagOp::Add => {
727 if flags.seen {
728 message.seen = true;
729 }
730 if flags.flagged {
731 message.flagged = true;
732 }
733 if flags.deleted {
734 message.deleted = true;
735 }
736 if flags.answered {
737 message.answered = true;
738 }
739 if flags.draft {
740 message.draft = true;
741 }
742 }
743 FlagOp::Remove => {
744 if flags.seen {
745 message.seen = false;
746 }
747 if flags.flagged {
748 message.flagged = false;
749 }
750 if flags.deleted {
751 message.deleted = false;
752 }
753 if flags.answered {
754 message.answered = false;
755 }
756 if flags.draft {
757 message.draft = false;
758 }
759 }
760 FlagOp::Replace => {
761 message.seen = flags.seen;
762 message.flagged = flags.flagged;
763 message.deleted = flags.deleted;
764 message.answered = flags.answered;
765 message.draft = flags.draft;
766 }
767 }
768}