diff --git a/src/accounts.rs b/src/accounts.rs index e0d39c952..0cad4ffee 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -5,6 +5,7 @@ use std::future::Future; use std::path::{Path, PathBuf}; use anyhow::{ensure, Context as _, Result}; +use futures::future::join_all; use serde::{Deserialize, Serialize}; use tokio::fs; use tokio::io::AsyncWriteExt; @@ -291,6 +292,33 @@ impl Accounts { } } + /// Performs a background fetch for all accounts in parallel. + /// + /// If you need a timeout, then use [Accounts::background_fetch_with_timeout] instead. + pub async fn background_fetch(&self) { + async fn background_fetch_and_log_error(account: Context) { + if let Err(error) = account.background_fetch().await { + warn!(account, "{error:#}"); + } + } + + join_all( + self.accounts + .values() + .cloned() + .map(background_fetch_and_log_error), + ) + .await; + } + + /// Performs a background fetch for all accounts in parallel with a timeout. + /// + /// If you want no timeout, then use [Accounts::background_fetch] instead. + pub async fn background_fetch_with_timeout(&self, timeout: Duration) -> Result<()> { + tokio::time::timeout(timeout, self.background_fetch()).await?; + Ok(()) + } + /// Emits a single event. pub fn emit_event(&self, event: EventType) { self.events.emit(Event { id: 0, typ: event }) diff --git a/src/context.rs b/src/context.rs index cfe7a9118..60f901d8d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -19,12 +19,12 @@ use crate::constants::DC_VERSION_STR; use crate::contact::Contact; use crate::debug_logging::DebugLogging; use crate::events::{Event, EventEmitter, EventType, Events}; -use crate::imap::ServerMetadata; +use crate::imap::{FolderMeaning, Imap, ServerMetadata}; use crate::key::{load_self_public_key, DcKey as _}; use crate::login_param::LoginParam; use crate::message::{self, MessageState, MsgId}; use crate::quota::QuotaInfo; -use crate::scheduler::SchedulerState; +use crate::scheduler::{convert_folder_meaning, SchedulerState}; use crate::sql::Sql; use crate::stock_str::StockStrings; use crate::timesmearing::SmearedTimestamp; @@ -441,6 +441,35 @@ impl Context { self.scheduler.maybe_network().await; } + /// Do a background fetch + /// pauses the scheduler and does one imap fetch, then unpauses and returns + pub async fn background_fetch(&self) -> Result<()> { + if !(self.is_configured().await?) { + return Ok(()); + } + + let _pause_guard = self.scheduler.pause(self.clone()).await?; + + // connection + let mut connection = Imap::new_configured(self, channel::bounded(1).1).await?; + connection.prepare(self).await?; + + // fetch imap folders + for folder_meaning in [FolderMeaning::Inbox, FolderMeaning::Mvbox] { + let (_, watch_folder) = convert_folder_meaning(self, folder_meaning).await?; + connection + .fetch_move_delete(self, &watch_folder, folder_meaning) + .await?; + } + + // update quota (to send warning if full) + if let Err(err) = self.update_recent_quota(&mut connection).await { + warn!(self, "Failed to update quota: {:#}.", err); + } + + Ok(()) + } + pub(crate) async fn schedule_resync(&self) -> Result<()> { self.resync_request.store(true, Ordering::Relaxed); self.scheduler.interrupt_inbox().await;