deltachat/imap/
scan_folders.rs1use std::collections::BTreeMap;
2
3use anyhow::{Context as _, Result};
4
5use super::{get_folder_meaning_by_attrs, get_folder_meaning_by_name};
6use crate::config::Config;
7use crate::imap::{session::Session, Imap};
8use crate::log::LogExt;
9use crate::tools::{self, time_elapsed};
10use crate::{context::Context, imap::FolderMeaning};
11
12impl Imap {
13 pub(crate) async fn scan_folders(
15 &mut self,
16 context: &Context,
17 session: &mut Session,
18 ) -> Result<bool> {
19 {
21 let mut last_scan = context.last_full_folder_scan.lock().await;
22 if let Some(last_scan) = *last_scan {
23 let elapsed_secs = time_elapsed(&last_scan).as_secs();
24 let debounce_secs = context
25 .get_config_u64(Config::ScanAllFoldersDebounceSecs)
26 .await?;
27
28 if elapsed_secs < debounce_secs {
29 return Ok(false);
30 }
31 }
32
33 last_scan.replace(tools::Time::now());
38 }
39 info!(context, "Starting full folder scan");
40
41 let folders = session.list_folders().await?;
42 let watched_folders = get_watched_folders(context).await?;
43
44 let mut folder_configs = BTreeMap::new();
45 let mut folder_names = Vec::new();
46
47 for folder in folders {
48 let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
49 if folder_meaning == FolderMeaning::Virtual {
50 continue;
55 }
56 folder_names.push(folder.name().to_string());
57 let folder_name_meaning = get_folder_meaning_by_name(folder.name());
58
59 if let Some(config) = folder_meaning.to_config() {
60 folder_configs.insert(config, folder.name().to_string());
62 } else if let Some(config) = folder_name_meaning.to_config() {
63 folder_configs
65 .entry(config)
66 .or_insert_with(|| folder.name().to_string());
67 }
68
69 let folder_meaning = match folder_meaning {
70 FolderMeaning::Unknown => folder_name_meaning,
71 _ => folder_meaning,
72 };
73
74 if !watched_folders.contains(&folder.name().to_string())
76 && folder_meaning != FolderMeaning::Drafts
77 && folder_meaning != FolderMeaning::Trash
78 {
79 self.fetch_move_delete(context, session, folder.name(), folder_meaning)
80 .await
81 .context("Can't fetch new msgs in scanned folder")
82 .log_err(context)
83 .ok();
84 }
85 }
86
87 for conf in [
89 Config::ConfiguredSentboxFolder,
90 Config::ConfiguredTrashFolder,
91 ] {
92 let val = folder_configs.get(&conf).map(|s| s.as_str());
93 let interrupt = conf == Config::ConfiguredTrashFolder
94 && val.is_some()
95 && context.get_config(conf).await?.is_none();
96 context.set_config_internal(conf, val).await?;
97 if interrupt {
98 context.scheduler.interrupt_oboxes().await;
101 }
102 }
103
104 info!(context, "Found folders: {folder_names:?}.");
105 Ok(true)
106 }
107}
108
109pub(crate) async fn get_watched_folder_configs(context: &Context) -> Result<Vec<Config>> {
110 let mut res = vec![Config::ConfiguredInboxFolder];
111 if context.get_config_bool(Config::SentboxWatch).await? {
112 res.push(Config::ConfiguredSentboxFolder);
113 }
114 if context.should_watch_mvbox().await? {
115 res.push(Config::ConfiguredMvboxFolder);
116 }
117 Ok(res)
118}
119
120pub(crate) async fn get_watched_folders(context: &Context) -> Result<Vec<String>> {
121 let mut res = Vec::new();
122 for folder_config in get_watched_folder_configs(context).await? {
123 if let Some(folder) = context.get_config(folder_config).await? {
124 res.push(folder);
125 }
126 }
127 Ok(res)
128}