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