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 let mut last_scan = context.last_full_folder_scan.lock().await;
21 if let Some(last_scan) = *last_scan {
22 let elapsed_secs = time_elapsed(&last_scan).as_secs();
23 let debounce_secs = context
24 .get_config_u64(Config::ScanAllFoldersDebounceSecs)
25 .await?;
26
27 if elapsed_secs < debounce_secs {
28 return Ok(false);
29 }
30 }
31 info!(context, "Starting full folder scan");
32
33 let folders = session.list_folders().await?;
34 let watched_folders = get_watched_folders(context).await?;
35
36 let mut folder_configs = BTreeMap::new();
37 let mut folder_names = Vec::new();
38
39 for folder in folders {
40 let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
41 if folder_meaning == FolderMeaning::Virtual {
42 continue;
47 }
48 folder_names.push(folder.name().to_string());
49 let folder_name_meaning = get_folder_meaning_by_name(folder.name());
50
51 if let Some(config) = folder_meaning.to_config() {
52 folder_configs.insert(config, folder.name().to_string());
54 } else if let Some(config) = folder_name_meaning.to_config() {
55 folder_configs
57 .entry(config)
58 .or_insert_with(|| folder.name().to_string());
59 }
60
61 let folder_meaning = match folder_meaning {
62 FolderMeaning::Unknown => folder_name_meaning,
63 _ => folder_meaning,
64 };
65
66 if !watched_folders.contains(&folder.name().to_string())
68 && folder_meaning != FolderMeaning::Drafts
69 && folder_meaning != FolderMeaning::Trash
70 {
71 self.fetch_move_delete(context, session, folder.name(), folder_meaning)
72 .await
73 .context("Can't fetch new msgs in scanned folder")
74 .log_err(context)
75 .ok();
76 }
77 }
78
79 for conf in [
81 Config::ConfiguredSentboxFolder,
82 Config::ConfiguredTrashFolder,
83 ] {
84 let val = folder_configs.get(&conf).map(|s| s.as_str());
85 let interrupt = conf == Config::ConfiguredTrashFolder
86 && val.is_some()
87 && context.get_config(conf).await?.is_none();
88 context.set_config_internal(conf, val).await?;
89 if interrupt {
90 context.scheduler.interrupt_oboxes().await;
93 }
94 }
95
96 info!(context, "Found folders: {folder_names:?}.");
97 last_scan.replace(tools::Time::now());
98 Ok(true)
99 }
100}
101
102pub(crate) async fn get_watched_folder_configs(context: &Context) -> Result<Vec<Config>> {
103 let mut res = vec![Config::ConfiguredInboxFolder];
104 if context.get_config_bool(Config::SentboxWatch).await? {
105 res.push(Config::ConfiguredSentboxFolder);
106 }
107 if context.should_watch_mvbox().await? {
108 res.push(Config::ConfiguredMvboxFolder);
109 }
110 Ok(res)
111}
112
113pub(crate) async fn get_watched_folders(context: &Context) -> Result<Vec<String>> {
114 let mut res = Vec::new();
115 for folder_config in get_watched_folder_configs(context).await? {
116 if let Some(folder) = context.get_config(folder_config).await? {
117 res.push(folder);
118 }
119 }
120 Ok(res)
121}