diff --git a/src/context.rs b/src/context.rs index 1d8dbe847..0f6d721ea 100644 --- a/src/context.rs +++ b/src/context.rs @@ -211,6 +211,9 @@ pub struct InnerContext { /// Set to true if quota update is requested. pub(crate) quota_update_request: AtomicBool, + /// IMAP UID resync request. + pub(crate) resync_request: AtomicBool, + /// Server ID response if ID capability is supported /// and the server returned non-NIL on the inbox connection. /// @@ -371,6 +374,7 @@ impl Context { ratelimit: RwLock::new(Ratelimit::new(Duration::new(60, 0), 6.0)), // Allow to send 6 messages immediately, no more than once every 10 seconds. quota: RwLock::new(None), quota_update_request: AtomicBool::new(false), + resync_request: AtomicBool::new(false), server_id: RwLock::new(None), creation_time: std::time::SystemTime::now(), last_full_folder_scan: Mutex::new(None), diff --git a/src/imap.rs b/src/imap.rs index 9889df69b..f6d36182d 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -904,6 +904,24 @@ impl Imap { info!(context, "Done fetching existing messages."); Ok(()) } + + /// Synchronizes UIDs for all folders. + pub(crate) async fn resync_folders(&mut self, context: &Context) -> Result<()> { + self.prepare(context).await?; + + let all_folders = self + .list_folders(context) + .await + .context("listing folders for resync")?; + for folder in all_folders { + let folder_meaning = get_folder_meaning(&folder); + if folder_meaning != FolderMeaning::Virtual { + self.resync_folder_uids(context, folder.name(), folder_meaning) + .await?; + } + } + Ok(()) + } } impl Session { diff --git a/src/job.rs b/src/job.rs index ff37ea543..25f3814a2 100644 --- a/src/job.rs +++ b/src/job.rs @@ -6,13 +6,14 @@ #![allow(missing_docs)] use std::fmt; +use std::sync::atomic::Ordering; use anyhow::{Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use rand::{thread_rng, Rng}; use crate::context::Context; -use crate::imap::{get_folder_meaning, FolderMeaning, Imap}; +use crate::imap::Imap; use crate::scheduler::InterruptInfo; use crate::tools::time; @@ -24,7 +25,6 @@ const JOB_RETRIES: u32 = 17; pub enum Status { Finished(Result<()>), RetryNow, - RetryLater, } #[macro_export] @@ -62,10 +62,6 @@ pub enum Action { // Most messages are downloaded automatically on fetch // and do not go through this job. DownloadMsg = 250, - - // UID synchronization is high-priority to make sure correct UIDs - // are used by message moving/deletion. - ResyncFolders = 300, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -142,52 +138,6 @@ impl Job { Ok(()) } - /// Synchronizes UIDs for all folders. - async fn resync_folders(&mut self, context: &Context, imap: &mut Imap) -> Status { - if let Err(err) = imap.prepare(context).await { - warn!(context, "could not connect: {:#}", err); - return Status::RetryLater; - } - - let all_folders = match imap.list_folders(context).await { - Ok(v) => v, - Err(e) => { - warn!(context, "Listing folders for resync failed: {:#}", e); - return Status::RetryLater; - } - }; - - let mut any_failed = false; - - for folder in all_folders { - let folder_meaning = get_folder_meaning(&folder); - if folder_meaning == FolderMeaning::Virtual { - continue; - } - if let Err(e) = imap - .resync_folder_uids(context, folder.name(), folder_meaning) - .await - { - warn!(context, "{:#}", e); - any_failed = true; - } - } - - if any_failed { - Status::RetryLater - } else { - Status::Finished(Ok(())) - } - } -} - -/// Delete all pending jobs with the given action. -pub async fn kill_action(context: &Context, action: Action) -> Result<()> { - context - .sql - .execute("DELETE FROM jobs WHERE action=?;", paramsv![action]) - .await?; - Ok(()) } pub(crate) enum Connection<'a> { @@ -211,7 +161,7 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_ }; match try_res { - Status::RetryNow | Status::RetryLater => { + Status::RetryNow => { let tries = job.tries + 1; if tries < JOB_RETRIES { @@ -265,7 +215,6 @@ async fn perform_job_action( info!(context, "begin immediate try {} of job {}", tries, job); let try_res = match job.action { - Action::ResyncFolders => job.resync_folders(context, connection.inbox()).await, Action::DownloadMsg => job.download_msg(context, connection.inbox()).await, }; @@ -287,8 +236,12 @@ fn get_backoff_time_offset(tries: u32) -> i64 { } pub(crate) async fn schedule_resync(context: &Context) -> Result<()> { - kill_action(context, Action::ResyncFolders).await?; - add(context, Job::new(Action::ResyncFolders, 0)).await?; + context.resync_request.store(true, Ordering::Relaxed); + context + .interrupt_inbox(InterruptInfo { + probe_network: false, + }) + .await; Ok(()) } diff --git a/src/scheduler.rs b/src/scheduler.rs index 4ce26c45d..13582d2ea 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -129,13 +129,21 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne info = Default::default(); } None => { - let requested = ctx.quota_update_request.swap(false, Ordering::Relaxed); - if requested { + let quota_requested = ctx.quota_update_request.swap(false, Ordering::Relaxed); + if quota_requested { if let Err(err) = ctx.update_recent_quota(&mut connection).await { warn!(ctx, "Failed to update quota: {:#}.", err); } } + let resync_requested = ctx.resync_request.swap(false, Ordering::Relaxed); + if resync_requested { + if let Err(err) = connection.resync_folders(&ctx).await { + warn!(ctx, "Failed to resync folders: {:#}.", err); + ctx.resync_request.store(true, Ordering::Relaxed); + } + } + maybe_add_time_based_warnings(&ctx).await; match ctx.get_config_i64(Config::LastHousekeeping).await {