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