diff --git a/CHANGELOG.md b/CHANGELOG.md index 81c5e107e..332d32960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ## Changes +- refactorings #3375 ## Fixes - do not reset our database if imported backup cannot be decrypted #3397 diff --git a/src/config.rs b/src/config.rs index 0b1c094c8..44cb7e366 100644 --- a/src/config.rs +++ b/src/config.rs @@ -89,6 +89,11 @@ pub enum Config { #[strum(props(default = "1"))] FetchExistingMsgs, + /// If set to "1", then existing messages are considered to be already fetched. + /// This flag is reset after successful configuration. + #[strum(props(default = "1"))] + FetchedExistingMsgs, + #[strum(props(default = "0"))] KeyGenType, diff --git a/src/configure.rs b/src/configure.rs index b887a77c5..35cfdce60 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -8,7 +8,6 @@ mod server_params; use anyhow::{bail, ensure, Context as _, Result}; use async_std::prelude::*; use async_std::task; -use job::Action; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use crate::config::Config; @@ -20,8 +19,8 @@ use crate::job; use crate::login_param::{CertificateChecks, LoginParam, ServerLoginParam, Socks5Config}; use crate::message::{Message, Viewtype}; use crate::oauth2::dc_get_oauth2_addr; -use crate::param::Params; use crate::provider::{Protocol, Socket, UsernamePattern}; +use crate::scheduler::InterruptInfo; use crate::smtp::Smtp; use crate::stock_str; use crate::{chat, e2ee, provider}; @@ -469,11 +468,9 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { e2ee::ensure_secret_key_exists(ctx).await?; info!(ctx, "key generation completed"); - job::add( - ctx, - job::Job::new(Action::FetchExistingMsgs, 0, Params::new(), 0), - ) - .await?; + ctx.set_config_bool(Config::FetchedExistingMsgs, false) + .await?; + ctx.interrupt_inbox(InterruptInfo::new(false)).await; progress!(ctx, 940); update_device_chats_handle.await?; diff --git a/src/context.rs b/src/context.rs index a89231b68..3f6a46845 100644 --- a/src/context.rs +++ b/src/context.rs @@ -433,6 +433,12 @@ impl Context { .await? .to_string(), ); + res.insert( + "fetched_existing_msgs", + self.get_config_bool(Config::FetchedExistingMsgs) + .await? + .to_string(), + ); res.insert( "show_emails", self.get_config_int(Config::ShowEmails).await?.to_string(), diff --git a/src/imap.rs b/src/imap.rs index 9d9a0989a..2f0d7593a 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -24,7 +24,7 @@ use crate::constants::{ Blocked, Chattype, ShowEmails, DC_FETCH_EXISTING_MSGS_COUNT, DC_FOLDERS_CONFIGURED_VERSION, DC_LP_AUTH_OAUTH2, }; -use crate::contact::ContactId; +use crate::contact::{normalize_name, Contact, ContactId, Modifier, Origin}; use crate::context::Context; use crate::dc_receive_imf::{ dc_receive_imf_inner, from_field_to_contact_id, get_prefetch_parent_message, ReceivedMsg, @@ -905,6 +905,42 @@ impl Imap { Ok(read_cnt > 0) } + /// Read the recipients from old emails sent by the user and add them as contacts. + /// This way, we can already offer them some email addresses they can write to. + /// + /// Then, Fetch the last messages DC_FETCH_EXISTING_MSGS_COUNT emails from the server + /// and show them in the chat list. + pub(crate) async fn fetch_existing_msgs(&mut self, context: &Context) -> Result<()> { + if context.get_config_bool(Config::Bot).await? { + return Ok(()); // Bots don't want those messages + } + self.prepare(context).await.context("could not connect")?; + + add_all_recipients_as_contacts(context, self, Config::ConfiguredSentboxFolder).await; + add_all_recipients_as_contacts(context, self, Config::ConfiguredMvboxFolder).await; + add_all_recipients_as_contacts(context, self, Config::ConfiguredInboxFolder).await; + + if context.get_config_bool(Config::FetchExistingMsgs).await? { + for config in &[ + Config::ConfiguredMvboxFolder, + Config::ConfiguredInboxFolder, + Config::ConfiguredSentboxFolder, + ] { + if let Some(folder) = context.get_config(*config).await? { + self.fetch_new_messages(context, &folder, false, true) + .await + .context("could not fetch messages")?; + } + } + } + + info!(context, "Done fetching existing messages."); + context + .set_config_bool(Config::FetchedExistingMsgs, true) + .await?; + Ok(()) + } + /// Deletes batch of messages identified by their UID from the currently /// selected folder. async fn delete_message_batch( @@ -2287,6 +2323,50 @@ impl std::fmt::Display for UidRange { } } } +async fn add_all_recipients_as_contacts(context: &Context, imap: &mut Imap, folder: Config) { + let mailbox = if let Ok(Some(m)) = context.get_config(folder).await { + m + } else { + return; + }; + if let Err(e) = imap.select_with_uidvalidity(context, &mailbox).await { + // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: + warn!(context, "Could not select {}: {:#}", mailbox, e); + return; + } + match imap.get_all_recipients(context).await { + Ok(contacts) => { + let mut any_modified = false; + for contact in contacts { + let display_name_normalized = contact + .display_name + .as_ref() + .map(|s| normalize_name(s)) + .unwrap_or_default(); + + match Contact::add_or_lookup( + context, + &display_name_normalized, + &contact.addr, + Origin::OutgoingTo, + ) + .await + { + Ok((_, modified)) => { + if modified != Modifier::None { + any_modified = true; + } + } + Err(e) => warn!(context, "Could not add recipient: {}", e), + } + } + if any_modified { + context.emit_event(EventType::ContactsChanged(None)); + } + } + Err(e) => warn!(context, "Could not add recipients: {}", e), + }; +} #[cfg(test)] mod tests { diff --git a/src/job.rs b/src/job.rs index 721aa4702..96f93ef39 100644 --- a/src/job.rs +++ b/src/job.rs @@ -8,11 +8,8 @@ use anyhow::{Context as _, Result}; use deltachat_derive::{FromSql, ToSql}; use rand::{thread_rng, Rng}; -use crate::config::Config; -use crate::contact::{normalize_name, Contact, Modifier, Origin}; use crate::context::Context; use crate::dc_tools::time; -use crate::events::EventType; use crate::imap::Imap; use crate::param::Params; use crate::scheduler::InterruptInfo; @@ -58,8 +55,6 @@ macro_rules! job_try { )] #[repr(u32)] pub enum Action { - FetchExistingMsgs = 110, - // this is user initiated so it should have a fairly high priority UpdateRecentQuota = 140, @@ -156,45 +151,6 @@ impl Job { Ok(()) } - - /// Read the recipients from old emails sent by the user and add them as contacts. - /// This way, we can already offer them some email addresses they can write to. - /// - /// Then, Fetch the last messages DC_FETCH_EXISTING_MSGS_COUNT emails from the server - /// and show them in the chat list. - async fn fetch_existing_msgs(&mut self, context: &Context, imap: &mut Imap) -> Status { - if job_try!(context.get_config_bool(Config::Bot).await) { - return Status::Finished(Ok(())); // Bots don't want those messages - } - if let Err(err) = imap.prepare(context).await { - warn!(context, "could not connect: {:?}", err); - return Status::RetryLater; - } - - add_all_recipients_as_contacts(context, imap, Config::ConfiguredSentboxFolder).await; - add_all_recipients_as_contacts(context, imap, Config::ConfiguredMvboxFolder).await; - add_all_recipients_as_contacts(context, imap, Config::ConfiguredInboxFolder).await; - - if job_try!(context.get_config_bool(Config::FetchExistingMsgs).await) { - for config in &[ - Config::ConfiguredMvboxFolder, - Config::ConfiguredInboxFolder, - Config::ConfiguredSentboxFolder, - ] { - if let Some(folder) = job_try!(context.get_config(*config).await) { - if let Err(e) = imap.fetch_new_messages(context, &folder, false, true).await { - // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: - warn!(context, "Could not fetch messages, retrying: {:#}", e); - return Status::RetryLater; - }; - } - } - } - - info!(context, "Done fetching existing messages."); - Status::Finished(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 { @@ -250,51 +206,6 @@ pub async fn action_exists(context: &Context, action: Action) -> Result { Ok(exists) } -async fn add_all_recipients_as_contacts(context: &Context, imap: &mut Imap, folder: Config) { - let mailbox = if let Ok(Some(m)) = context.get_config(folder).await { - m - } else { - return; - }; - if let Err(e) = imap.select_with_uidvalidity(context, &mailbox).await { - // We are using Anyhow's .context() and to show the inner error, too, we need the {:#}: - warn!(context, "Could not select {}: {:#}", mailbox, e); - return; - } - match imap.get_all_recipients(context).await { - Ok(contacts) => { - let mut any_modified = false; - for contact in contacts { - let display_name_normalized = contact - .display_name - .as_ref() - .map(|s| normalize_name(s)) - .unwrap_or_default(); - - match Contact::add_or_lookup( - context, - &display_name_normalized, - &contact.addr, - Origin::OutgoingTo, - ) - .await - { - Ok((_, modified)) => { - if modified != Modifier::None { - any_modified = true; - } - } - Err(e) => warn!(context, "Could not add recipient: {}", e), - } - } - if any_modified { - context.emit_event(EventType::ContactsChanged(None)); - } - } - Err(e) => warn!(context, "Could not add recipients: {}", e), - }; -} - pub(crate) enum Connection<'a> { Inbox(&'a mut Imap), } @@ -371,7 +282,6 @@ async fn perform_job_action( let try_res = match job.action { Action::ResyncFolders => job.resync_folders(context, connection.inbox()).await, - Action::FetchExistingMsgs => job.fetch_existing_msgs(context, connection.inbox()).await, Action::UpdateRecentQuota => match context.update_recent_quota(connection.inbox()).await { Ok(status) => status, Err(err) => Status::Finished(Err(err)), @@ -422,10 +332,7 @@ pub async fn add(context: &Context, job: Job) -> Result<()> { if delay_seconds == 0 { match action { - Action::ResyncFolders - | Action::FetchExistingMsgs - | Action::UpdateRecentQuota - | Action::DownloadMsg => { + Action::ResyncFolders | Action::UpdateRecentQuota | Action::DownloadMsg => { info!(context, "interrupt: imap"); context.interrupt_inbox(InterruptInfo::new(false)).await; } diff --git a/src/scheduler.rs b/src/scheduler.rs index 655f867da..f9359e40a 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -130,6 +130,19 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne } }; + match ctx.get_config_bool(Config::FetchedExistingMsgs).await { + Ok(fetched_existing_msgs) => { + if !fetched_existing_msgs { + if let Err(err) = connection.fetch_existing_msgs(&ctx).await { + warn!(ctx, "Failed to fetch existing messages: {:#}", err); + } + } + } + Err(err) => { + warn!(ctx, "Can't get Config::FetchedExistingMsgs: {:#}", err); + } + } + info = fetch_idle(&ctx, &mut connection, Config::ConfiguredInboxFolder).await; } }