mirror of
https://github.com/chatmail/core.git
synced 2026-04-20 06:56:29 +03:00
Connection establishment now happens only in one place in each IMAP loop. Now all connection establishment happens in one place and is limited by the ratelimit. Backoff was removed from fake_idle as it does not establish connections anymore. If connection fails, fake_idle will return an error. We then drop the connection and get back to the beginning of IMAP loop. Backoff may be still nice to have to delay retries in case of constant connection failures so we don't immediately hit ratelimit if the network is unusable and returns immediate error on each connection attempt (e.g. ICMP network unreachable error), but adding backoff for connection failures is out of scope for this change.
122 lines
4.5 KiB
Rust
122 lines
4.5 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use anyhow::{Context as _, Result};
|
|
|
|
use super::{get_folder_meaning_by_attrs, get_folder_meaning_by_name};
|
|
use crate::config::Config;
|
|
use crate::imap::{session::Session, Imap};
|
|
use crate::log::LogExt;
|
|
use crate::tools::{self, time_elapsed};
|
|
use crate::{context::Context, imap::FolderMeaning};
|
|
|
|
impl Imap {
|
|
/// Returns true if folders were scanned, false if scanning was postponed.
|
|
pub(crate) async fn scan_folders(
|
|
&mut self,
|
|
context: &Context,
|
|
session: &mut Session,
|
|
) -> Result<bool> {
|
|
// First of all, debounce to once per minute:
|
|
let mut last_scan = context.last_full_folder_scan.lock().await;
|
|
if let Some(last_scan) = *last_scan {
|
|
let elapsed_secs = time_elapsed(&last_scan).as_secs();
|
|
let debounce_secs = context
|
|
.get_config_u64(Config::ScanAllFoldersDebounceSecs)
|
|
.await?;
|
|
|
|
if elapsed_secs < debounce_secs {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
info!(context, "Starting full folder scan");
|
|
|
|
let folders = session.list_folders().await?;
|
|
let watched_folders = get_watched_folders(context).await?;
|
|
|
|
let mut folder_configs = BTreeMap::new();
|
|
|
|
for folder in folders {
|
|
let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
|
|
if folder_meaning == FolderMeaning::Virtual {
|
|
// Gmail has virtual folders that should be skipped. For example,
|
|
// emails appear in the inbox and under "All Mail" as soon as it is
|
|
// received. The code used to wrongly conclude that the email had
|
|
// already been moved and left it in the inbox.
|
|
continue;
|
|
}
|
|
let folder_name_meaning = get_folder_meaning_by_name(folder.name());
|
|
|
|
if let Some(config) = folder_meaning.to_config() {
|
|
// Always takes precedence
|
|
folder_configs.insert(config, folder.name().to_string());
|
|
} else if let Some(config) = folder_name_meaning.to_config() {
|
|
// only set if none has been already set
|
|
folder_configs
|
|
.entry(config)
|
|
.or_insert_with(|| folder.name().to_string());
|
|
}
|
|
|
|
let folder_meaning = match folder_meaning {
|
|
FolderMeaning::Unknown => folder_name_meaning,
|
|
_ => folder_meaning,
|
|
};
|
|
|
|
// Don't scan folders that are watched anyway
|
|
if !watched_folders.contains(&folder.name().to_string())
|
|
&& folder_meaning != FolderMeaning::Drafts
|
|
&& folder_meaning != FolderMeaning::Trash
|
|
{
|
|
// Drain leftover unsolicited EXISTS messages
|
|
session.server_sent_unsolicited_exists(context)?;
|
|
|
|
loop {
|
|
self.fetch_move_delete(context, session, folder.name(), folder_meaning)
|
|
.await
|
|
.context("Can't fetch new msgs in scanned folder")
|
|
.log_err(context)
|
|
.ok();
|
|
|
|
// If the server sent an unsocicited EXISTS during the fetch, we need to fetch again
|
|
if !session.server_sent_unsolicited_exists(context)? {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set configs for necessary folders. Or reset if the folder was deleted.
|
|
for conf in [
|
|
Config::ConfiguredSentboxFolder,
|
|
Config::ConfiguredTrashFolder,
|
|
] {
|
|
context
|
|
.set_config_internal(conf, folder_configs.get(&conf).map(|s| s.as_str()))
|
|
.await?;
|
|
}
|
|
|
|
last_scan.replace(tools::Time::now());
|
|
Ok(true)
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn get_watched_folder_configs(context: &Context) -> Result<Vec<Config>> {
|
|
let mut res = vec![Config::ConfiguredInboxFolder];
|
|
if context.get_config_bool(Config::SentboxWatch).await? {
|
|
res.push(Config::ConfiguredSentboxFolder);
|
|
}
|
|
if context.should_watch_mvbox().await? {
|
|
res.push(Config::ConfiguredMvboxFolder);
|
|
}
|
|
Ok(res)
|
|
}
|
|
|
|
pub(crate) async fn get_watched_folders(context: &Context) -> Result<Vec<String>> {
|
|
let mut res = Vec::new();
|
|
for folder_config in get_watched_folder_configs(context).await? {
|
|
if let Some(folder) = context.get_config(folder_config).await? {
|
|
res.push(folder);
|
|
}
|
|
}
|
|
Ok(res)
|
|
}
|