deltachat/imap/
session.rs1use std::collections::BTreeMap;
2use std::ops::{Deref, DerefMut};
3
4use anyhow::{Context as _, Result};
5use async_imap::Session as ImapSession;
6use async_imap::types::Mailbox;
7use futures::TryStreamExt;
8use tokio::sync::Mutex;
9
10use crate::imap::capabilities::Capabilities;
11use crate::net::session::SessionStream;
12use crate::tools;
13
14const PREFETCH_FLAGS: &str = "(UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\
22 MESSAGE-ID \
23 DATE \
24 X-MICROSOFT-ORIGINAL-MESSAGE-ID \
25 FROM \
26 IN-REPLY-TO REFERENCES \
27 CHAT-VERSION \
28 CHAT-IS-POST-MESSAGE \
29 AUTO-SUBMITTED \
30 AUTOCRYPT-SETUP-MESSAGE\
31 )])";
32
33#[derive(Debug)]
34pub(crate) struct Session {
35 transport_id: u32,
36
37 pub(super) inner: ImapSession<Box<dyn SessionStream>>,
38
39 pub capabilities: Capabilities,
40
41 pub selected_folder: Option<String>,
43
44 pub selected_mailbox: Option<Mailbox>,
46
47 pub selected_folder_needs_expunge: bool,
48
49 pub(crate) last_full_folder_scan: Mutex<Option<tools::Time>>,
50
51 pub new_mail: bool,
55
56 pub resync_request_sender: async_channel::Sender<()>,
57}
58
59impl Deref for Session {
60 type Target = ImapSession<Box<dyn SessionStream>>;
61
62 fn deref(&self) -> &Self::Target {
63 &self.inner
64 }
65}
66
67impl DerefMut for Session {
68 fn deref_mut(&mut self) -> &mut Self::Target {
69 &mut self.inner
70 }
71}
72
73impl Session {
74 pub(crate) fn new(
75 inner: ImapSession<Box<dyn SessionStream>>,
76 capabilities: Capabilities,
77 resync_request_sender: async_channel::Sender<()>,
78 transport_id: u32,
79 ) -> Self {
80 Self {
81 transport_id,
82 inner,
83 capabilities,
84 selected_folder: None,
85 selected_mailbox: None,
86 selected_folder_needs_expunge: false,
87 last_full_folder_scan: Mutex::new(None),
88 new_mail: false,
89 resync_request_sender,
90 }
91 }
92
93 pub(crate) fn transport_id(&self) -> u32 {
95 self.transport_id
96 }
97
98 pub fn can_idle(&self) -> bool {
99 self.capabilities.can_idle
100 }
101
102 pub fn can_move(&self) -> bool {
103 self.capabilities.can_move
104 }
105
106 pub fn can_check_quota(&self) -> bool {
107 self.capabilities.can_check_quota
108 }
109
110 pub fn can_condstore(&self) -> bool {
111 self.capabilities.can_condstore
112 }
113
114 pub fn can_metadata(&self) -> bool {
115 self.capabilities.can_metadata
116 }
117
118 pub fn can_push(&self) -> bool {
119 self.capabilities.can_push
120 }
121
122 pub fn is_chatmail(&self) -> bool {
124 self.capabilities.is_chatmail
125 }
126
127 pub async fn list_folders(&mut self) -> Result<Vec<async_imap::types::Name>> {
129 let list = self.list(Some(""), Some("*")).await?.try_collect().await?;
130 Ok(list)
131 }
132
133 pub(crate) async fn prefetch(
136 &mut self,
137 uid_next: u32,
138 n_uids: u32,
139 ) -> Result<Vec<(u32, async_imap::types::Fetch)>> {
140 let uid_last = uid_next.saturating_add(n_uids - 1);
141 let set = format!("{uid_next}:{uid_last}");
143 let mut list = self
144 .uid_fetch(set, PREFETCH_FLAGS)
145 .await
146 .context("IMAP could not fetch")?;
147
148 let mut msgs = BTreeMap::new();
149 while let Some(msg) = list.try_next().await? {
150 if let Some(msg_uid) = msg.uid {
151 msgs.insert((msg.internal_date(), msg_uid), msg);
152 }
153 }
154
155 Ok(msgs.into_iter().map(|((_, uid), msg)| (uid, msg)).collect())
156 }
157}