Skip to main content

elektromail/
store.rs

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/// Storage backend configuration.
10#[derive(Clone, Debug, Default)]
11pub enum StorageBackend {
12    /// In-memory storage (default, non-persistent).
13    #[default]
14    InMemory,
15    /// SQLite storage with persistence to the given file path.
16    Sqlite(String),
17}
18
19/// Unified store that can be either in-memory or SQLite-backed.
20pub enum Store {
21    /// In-memory storage (non-persistent).
22    InMemory(InMemoryStore),
23    /// SQLite-backed storage (persistent).
24    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    /// Create a new store from the given backend configuration.
37    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    /// Reset all state (users, mailboxes, messages).
212    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    /// Purge all messages but keep users and mailboxes.
284    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    /// List all users.
294    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    /// Delete a message by UID.
302    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
326/// Return the current timestamp formatted for IMAP INTERNALDATE.
327pub 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    /// Reset all state.
671    pub fn reset(&mut self) {
672        self.users.clear();
673        self.subscriptions.clear();
674    }
675
676    /// Purge all messages but keep users and mailboxes.
677    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    /// List all users.
686    pub fn list_users(&self) -> Vec<String> {
687        self.users.keys().cloned().collect()
688    }
689
690    /// Delete a message by UID.
691    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
702/// Convert a UID to a sequence number for the given mailbox.
703pub 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}