diff --git a/src/config.rs b/src/config.rs index 76832d014..a39b54bca 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,6 @@ use crate::blob::BlobObject; use crate::constants::DC_VERSION_STR; use crate::context::Context; use crate::dc_tools::*; -use crate::job::*; use crate::mimefactory::RECOMMENDED_FILE_SIZE; use crate::stock::StockMessage; diff --git a/src/configure/mod.rs b/src/configure/mod.rs index 1fcf132b2..b7a896af9 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -10,10 +10,10 @@ use crate::config::Config; use crate::constants::*; use crate::context::Context; use crate::dc_tools::*; -use crate::job; +use crate::imap::Imap; use crate::login_param::{CertificateChecks, LoginParam}; use crate::oauth2::*; -use crate::param::Params; +use crate::smtp::Smtp; use crate::{chat, e2ee, provider}; use crate::message::Message; @@ -31,424 +31,417 @@ macro_rules! progress { } impl Context { + /// Checks if the context is already configured. + pub async fn is_configured(&self) -> bool { + self.sql.get_raw_config_bool(self, "configured").await + } + /// Starts a configuration job. pub async fn configure(&self) { if self.has_ongoing().await { warn!(self, "There is already another ongoing process running.",); return; } - // job::kill_action(self, job::Action::ConfigureImap).await; - // job::add(self, job::Action::ConfigureImap, 0, Params::new(), 0).await; + + if self.scheduler.read().await.is_running() { + warn!(self, "Can not configure, already running"); + return; + } + + if !self.sql.is_open().await { + error!(self, "Cannot configure, database not opened.",); + progress!(self, 0); + return; + } + if !self.alloc_ongoing().await { + error!(self, "Cannot allocate ongoing process"); + progress!(self, 0); + return; + } + let mut success = false; + let mut param_autoconfig: Option = None; + + info!(self, "Configure ...",); + + // Variables that are shared between steps: + let mut param = LoginParam::from_database(self, "").await; + // need all vars here to be mutable because rust thinks the same step could be called multiple times + // and also initialize, because otherwise rust thinks it's used while unitilized, even if thats not the case as the loop goes only forward + let mut param_domain = "undefined.undefined".to_owned(); + let mut param_addr_urlencoded: String = + "Internal Error: this value should never be used".to_owned(); + let mut keep_flags = 0; + + const STEP_12_USE_AUTOCONFIG: u8 = 12; + const STEP_13_AFTER_AUTOCONFIG: u8 = 13; + + let mut step_counter: u8 = 0; + let (_s, r) = async_std::sync::channel(1); + let mut imap = Imap::new(r); + let mut is_imap_connected = false; + + while !self.shall_stop_ongoing().await { + step_counter += 1; + + let success = match step_counter { + // Read login parameters from the database + 1 => { + progress!(self, 1); + if param.addr.is_empty() { + error!(self, "Please enter an email address.",); + } + !param.addr.is_empty() + } + // Step 1: Load the parameters and check email-address and password + 2 => { + if 0 != param.server_flags & DC_LP_AUTH_OAUTH2 { + // the used oauth2 addr may differ, check this. + // if dc_get_oauth2_addr() is not available in the oauth2 implementation, + // just use the given one. + progress!(self, 10); + if let Some(oauth2_addr) = + dc_get_oauth2_addr(self, ¶m.addr, ¶m.mail_pw) + .await + .and_then(|e| e.parse().ok()) + { + info!(self, "Authorized address is {}", oauth2_addr); + param.addr = oauth2_addr; + self.sql + .set_raw_config(self, "addr", Some(param.addr.as_str())) + .await + .ok(); + } + progress!(self, 20); + } + true // no oauth? - just continue it's no error + } + 3 => { + if let Ok(parsed) = param.addr.parse() { + let parsed: EmailAddress = parsed; + param_domain = parsed.domain; + param_addr_urlencoded = + utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string(); + true + } else { + error!(self, "Bad email-address."); + false + } + } + // Step 2: Autoconfig + 4 => { + progress!(self, 200); + + if param.mail_server.is_empty() + && param.mail_port == 0 + /*&¶m.mail_user.is_empty() -- the user can enter a loginname which is used by autoconfig then */ + && param.send_server.is_empty() + && param.send_port == 0 + && param.send_user.is_empty() + /*&¶m.send_pw.is_empty() -- the password cannot be auto-configured and is no criterion for autoconfig or not */ + && (param.server_flags & !DC_LP_AUTH_OAUTH2) == 0 + { + // no advanced parameters entered by the user: query provider-database or do Autoconfig + keep_flags = param.server_flags & DC_LP_AUTH_OAUTH2; + if let Some(new_param) = get_offline_autoconfig(self, ¶m) { + // got parameters from our provider-database, skip Autoconfig, preserve the OAuth2 setting + param_autoconfig = Some(new_param); + step_counter = STEP_12_USE_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop + } + } else { + // advanced parameters entered by the user: skip Autoconfig + step_counter = STEP_13_AFTER_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop + } + true + } + /* A. Search configurations from the domain used in the email-address, prefer encrypted */ + 5 => { + if param_autoconfig.is_none() { + let url = format!( + "https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", + param_domain, param_addr_urlencoded + ); + param_autoconfig = moz_autoconfigure(self, &url, ¶m).ok(); + } + true + } + 6 => { + progress!(self, 300); + if param_autoconfig.is_none() { + // the doc does not mention `emailaddress=`, however, Thunderbird adds it, see https://releases.mozilla.org/pub/thunderbird/ , which makes some sense + let url = format!( + "https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}", + param_domain, param_addr_urlencoded + ); + param_autoconfig = moz_autoconfigure(self, &url, ¶m).ok(); + } + true + } + /* Outlook section start ------------- */ + /* Outlook uses always SSL but different domains (this comment describes the next two steps) */ + 7 => { + progress!(self, 310); + if param_autoconfig.is_none() { + let url = format!("https://{}/autodiscover/autodiscover.xml", param_domain); + param_autoconfig = outlk_autodiscover(self, &url, ¶m).ok(); + } + true + } + 8 => { + progress!(self, 320); + if param_autoconfig.is_none() { + let url = format!( + "https://{}{}/autodiscover/autodiscover.xml", + "autodiscover.", param_domain + ); + param_autoconfig = outlk_autodiscover(self, &url, ¶m).ok(); + } + true + } + /* ----------- Outlook section end */ + 9 => { + progress!(self, 330); + if param_autoconfig.is_none() { + let url = format!( + "http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", + param_domain, param_addr_urlencoded + ); + param_autoconfig = moz_autoconfigure(self, &url, ¶m).ok(); + } + true + } + 10 => { + progress!(self, 340); + if param_autoconfig.is_none() { + // do not transfer the email-address unencrypted + let url = format!( + "http://{}/.well-known/autoconfig/mail/config-v1.1.xml", + param_domain + ); + param_autoconfig = moz_autoconfigure(self, &url, ¶m).ok(); + } + true + } + /* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */ + 11 => { + progress!(self, 350); + if param_autoconfig.is_none() { + /* always SSL for Thunderbird's database */ + let url = + format!("https://autoconfig.thunderbird.net/v1.1/{}", param_domain); + param_autoconfig = moz_autoconfigure(self, &url, ¶m).ok(); + } + true + } + /* C. Do we have any autoconfig result? + If you change the match-number here, also update STEP_12_COPY_AUTOCONFIG above + */ + STEP_12_USE_AUTOCONFIG => { + progress!(self, 500); + if let Some(ref cfg) = param_autoconfig { + info!(self, "Got autoconfig: {}", &cfg); + if !cfg.mail_user.is_empty() { + param.mail_user = cfg.mail_user.clone(); + } + param.mail_server = cfg.mail_server.clone(); /* all other values are always NULL when entering autoconfig */ + param.mail_port = cfg.mail_port; + param.send_server = cfg.send_server.clone(); + param.send_port = cfg.send_port; + param.send_user = cfg.send_user.clone(); + param.server_flags = cfg.server_flags; + /* although param_autoconfig's data are no longer needed from, + it is used to later to prevent trying variations of port/server/logins */ + } + param.server_flags |= keep_flags; + true + } + // Step 3: Fill missing fields with defaults + // If you change the match-number here, also update STEP_13_AFTER_AUTOCONFIG above + STEP_13_AFTER_AUTOCONFIG => { + if param.mail_server.is_empty() { + param.mail_server = format!("imap.{}", param_domain,) + } + if param.mail_port == 0 { + param.mail_port = if 0 != param.server_flags & (0x100 | 0x400) { + 143 + } else { + 993 + } + } + if param.mail_user.is_empty() { + param.mail_user = param.addr.clone(); + } + if param.send_server.is_empty() && !param.mail_server.is_empty() { + param.send_server = param.mail_server.clone(); + if param.send_server.starts_with("imap.") { + param.send_server = param.send_server.replacen("imap", "smtp", 1); + } + } + if param.send_port == 0 { + param.send_port = + if 0 != param.server_flags & DC_LP_SMTP_SOCKET_STARTTLS as i32 { + 587 + } else if 0 != param.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 { + 25 + } else { + 465 + } + } + if param.send_user.is_empty() && !param.mail_user.is_empty() { + param.send_user = param.mail_user.clone(); + } + if param.send_pw.is_empty() && !param.mail_pw.is_empty() { + param.send_pw = param.mail_pw.clone() + } + if !dc_exactly_one_bit_set(param.server_flags & DC_LP_AUTH_FLAGS as i32) { + param.server_flags &= !(DC_LP_AUTH_FLAGS as i32); + param.server_flags |= DC_LP_AUTH_NORMAL as i32 + } + if !dc_exactly_one_bit_set(param.server_flags & DC_LP_IMAP_SOCKET_FLAGS as i32) + { + param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS as i32); + param.server_flags |= if param.send_port == 143 { + DC_LP_IMAP_SOCKET_STARTTLS as i32 + } else { + DC_LP_IMAP_SOCKET_SSL as i32 + } + } + if !dc_exactly_one_bit_set( + param.server_flags & (DC_LP_SMTP_SOCKET_FLAGS as i32), + ) { + param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); + param.server_flags |= if param.send_port == 587 { + DC_LP_SMTP_SOCKET_STARTTLS as i32 + } else if param.send_port == 25 { + DC_LP_SMTP_SOCKET_PLAIN as i32 + } else { + DC_LP_SMTP_SOCKET_SSL as i32 + } + } + /* do we have a complete configuration? */ + if param.mail_server.is_empty() + || param.mail_port == 0 + || param.mail_user.is_empty() + || param.mail_pw.is_empty() + || param.send_server.is_empty() + || param.send_port == 0 + || param.send_user.is_empty() + || param.send_pw.is_empty() + || param.server_flags == 0 + { + error!(self, "Account settings incomplete."); + false + } else { + true + } + } + 14 => { + progress!(self, 600); + /* try to connect to IMAP - if we did not got an autoconfig, + do some further tries with different settings and username variations */ + is_imap_connected = try_imap_connections( + self, + &mut param, + param_autoconfig.is_some(), + &mut imap, + ) + .await; + is_imap_connected + } + 15 => { + progress!(self, 800); + try_smtp_connections(self, &mut param, param_autoconfig.is_some()).await + } + 16 => { + progress!(self, 900); + let create_mvbox = self.get_config_bool(Config::MvboxWatch).await + || self.get_config_bool(Config::MvboxMove).await; + if let Err(err) = imap.ensure_configured_folders(self, create_mvbox).await { + warn!(self, "configuring folders failed: {:?}", err); + false + } else { + let res = imap.select_with_uidvalidity(self, "INBOX").await; + if let Err(err) = res { + error!(self, "could not read INBOX status: {:?}", err); + false + } else { + true + } + } + } + 17 => { + progress!(self, 910); + /* configuration success - write back the configured parameters with the "configured_" prefix; also write the "configured"-flag */ + param + .save_to_database( + self, + "configured_", /*the trailing underscore is correct*/ + ) + .await + .ok(); + + self.sql + .set_raw_config_bool(self, "configured", true) + .await + .ok(); + true + } + 18 => { + progress!(self, 920); + // we generate the keypair just now - we could also postpone this until the first message is sent, however, + // this may result in a unexpected and annoying delay when the user sends his very first message + // (~30 seconds on a Moto G4 play) and might looks as if message sending is always that slow. + success = e2ee::ensure_secret_key_exists(self).await.is_ok(); + info!(self, "key generation completed"); + progress!(self, 940); + break; // We are done here + } + _ => { + error!(self, "Internal error: step counter out of bound",); + break; + } + }; + + if !success { + break; + } + } + if is_imap_connected { + imap.disconnect(self).await; + } + + // remember the entered parameters on success + // and restore to last-entered on failure. + // this way, the parameters visible to the ui are always in-sync with the current configuration. + if success { + LoginParam::from_database(self, "") + .await + .save_to_database(self, "configured_raw_") + .await + .ok(); + } else { + LoginParam::from_database(self, "configured_raw_") + .await + .save_to_database(self, "") + .await + .ok(); + } + + if let Some(provider) = provider::get_provider_info(¶m.addr) { + if !provider.after_login_hint.is_empty() { + let mut msg = Message::new(Viewtype::Text); + msg.text = Some(provider.after_login_hint.to_string()); + if chat::add_device_msg(self, Some("core-provider-info"), Some(&mut msg)) + .await + .is_err() + { + warn!(self, "cannot add after_login_hint as core-provider-info"); + } + } + } + + self.free_ongoing().await; + progress!(self, if success { 1000 } else { 0 }); } - - /// Checks if the context is already configured. - pub async fn is_configured(&self) -> bool { - self.sql.get_raw_config_bool(self, "configured").await - } -} - -/******************************************************************************* - * Configure JOB - ******************************************************************************/ -#[allow(clippy::cognitive_complexity)] -pub(crate) async fn job_configure_imap(context: &Context) -> job::Status { - unimplemented!() - // if !context.sql.is_open().await { - // error!(context, "Cannot configure, database not opened.",); - // progress!(context, 0); - // return job::Status::Finished(Err(format_err!("Database not opened"))); - // } - // if !context.alloc_ongoing().await { - // progress!(context, 0); - // return job::Status::Finished(Err(format_err!("Cannot allocated ongoing process"))); - // } - // let mut success = false; - // let mut imap_connected_here = false; - // let mut smtp_connected_here = false; - - // let mut param_autoconfig: Option = None; - - // // context.inbox_thread.imap.disconnect(context).await; - // // context.sentbox_thread.imap.disconnect(context).await; - // // context.mvbox_thread.imap.disconnect(context).await; - // // context.smtp.disconnect().await; - - // info!(context, "Configure ...",); - - // // Variables that are shared between steps: - // let mut param = LoginParam::from_database(context, "").await; - // // need all vars here to be mutable because rust thinks the same step could be called multiple times - // // and also initialize, because otherwise rust thinks it's used while unitilized, even if thats not the case as the loop goes only forward - // let mut param_domain = "undefined.undefined".to_owned(); - // let mut param_addr_urlencoded: String = - // "Internal Error: this value should never be used".to_owned(); - // let mut keep_flags = 0; - - // const STEP_12_USE_AUTOCONFIG: u8 = 12; - // const STEP_13_AFTER_AUTOCONFIG: u8 = 13; - - // let mut step_counter: u8 = 0; - // while !context.shall_stop_ongoing().await { - // step_counter += 1; - - // let success = match step_counter { - // // Read login parameters from the database - // 1 => { - // progress!(context, 1); - // if param.addr.is_empty() { - // error!(context, "Please enter an email address.",); - // } - // !param.addr.is_empty() - // } - // // Step 1: Load the parameters and check email-address and password - // 2 => { - // if 0 != param.server_flags & DC_LP_AUTH_OAUTH2 { - // // the used oauth2 addr may differ, check this. - // // if dc_get_oauth2_addr() is not available in the oauth2 implementation, - // // just use the given one. - // progress!(context, 10); - // if let Some(oauth2_addr) = - // dc_get_oauth2_addr(context, ¶m.addr, ¶m.mail_pw) - // .await - // .and_then(|e| e.parse().ok()) - // { - // info!(context, "Authorized address is {}", oauth2_addr); - // param.addr = oauth2_addr; - // context - // .sql - // .set_raw_config(context, "addr", Some(param.addr.as_str())) - // .await - // .ok(); - // } - // progress!(context, 20); - // } - // true // no oauth? - just continue it's no error - // } - // 3 => { - // if let Ok(parsed) = param.addr.parse() { - // let parsed: EmailAddress = parsed; - // param_domain = parsed.domain; - // param_addr_urlencoded = - // utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string(); - // true - // } else { - // error!(context, "Bad email-address."); - // false - // } - // } - // // Step 2: Autoconfig - // 4 => { - // progress!(context, 200); - - // if param.mail_server.is_empty() - // && param.mail_port == 0 - // /*&¶m.mail_user.is_empty() -- the user can enter a loginname which is used by autoconfig then */ - // && param.send_server.is_empty() - // && param.send_port == 0 - // && param.send_user.is_empty() - // /*&¶m.send_pw.is_empty() -- the password cannot be auto-configured and is no criterion for autoconfig or not */ - // && (param.server_flags & !DC_LP_AUTH_OAUTH2) == 0 - // { - // // no advanced parameters entered by the user: query provider-database or do Autoconfig - // keep_flags = param.server_flags & DC_LP_AUTH_OAUTH2; - // if let Some(new_param) = get_offline_autoconfig(context, ¶m) { - // // got parameters from our provider-database, skip Autoconfig, preserve the OAuth2 setting - // param_autoconfig = Some(new_param); - // step_counter = STEP_12_USE_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop - // } - // } else { - // // advanced parameters entered by the user: skip Autoconfig - // step_counter = STEP_13_AFTER_AUTOCONFIG - 1; // minus one as step_counter is increased on next loop - // } - // true - // } - // /* A. Search configurations from the domain used in the email-address, prefer encrypted */ - // 5 => { - // if param_autoconfig.is_none() { - // let url = format!( - // "https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", - // param_domain, param_addr_urlencoded - // ); - // param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok(); - // } - // true - // } - // 6 => { - // progress!(context, 300); - // if param_autoconfig.is_none() { - // // the doc does not mention `emailaddress=`, however, Thunderbird adds it, see https://releases.mozilla.org/pub/thunderbird/ , which makes some sense - // let url = format!( - // "https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}", - // param_domain, param_addr_urlencoded - // ); - // param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok(); - // } - // true - // } - // /* Outlook section start ------------- */ - // /* Outlook uses always SSL but different domains (this comment describes the next two steps) */ - // 7 => { - // progress!(context, 310); - // if param_autoconfig.is_none() { - // let url = format!("https://{}/autodiscover/autodiscover.xml", param_domain); - // param_autoconfig = outlk_autodiscover(context, &url, ¶m).ok(); - // } - // true - // } - // 8 => { - // progress!(context, 320); - // if param_autoconfig.is_none() { - // let url = format!( - // "https://{}{}/autodiscover/autodiscover.xml", - // "autodiscover.", param_domain - // ); - // param_autoconfig = outlk_autodiscover(context, &url, ¶m).ok(); - // } - // true - // } - // /* ----------- Outlook section end */ - // 9 => { - // progress!(context, 330); - // if param_autoconfig.is_none() { - // let url = format!( - // "http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}", - // param_domain, param_addr_urlencoded - // ); - // param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok(); - // } - // true - // } - // 10 => { - // progress!(context, 340); - // if param_autoconfig.is_none() { - // // do not transfer the email-address unencrypted - // let url = format!( - // "http://{}/.well-known/autoconfig/mail/config-v1.1.xml", - // param_domain - // ); - // param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok(); - // } - // true - // } - // /* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */ - // 11 => { - // progress!(context, 350); - // if param_autoconfig.is_none() { - // /* always SSL for Thunderbird's database */ - // let url = format!("https://autoconfig.thunderbird.net/v1.1/{}", param_domain); - // param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok(); - // } - // true - // } - // /* C. Do we have any autoconfig result? - // If you change the match-number here, also update STEP_12_COPY_AUTOCONFIG above - // */ - // STEP_12_USE_AUTOCONFIG => { - // progress!(context, 500); - // if let Some(ref cfg) = param_autoconfig { - // info!(context, "Got autoconfig: {}", &cfg); - // if !cfg.mail_user.is_empty() { - // param.mail_user = cfg.mail_user.clone(); - // } - // param.mail_server = cfg.mail_server.clone(); /* all other values are always NULL when entering autoconfig */ - // param.mail_port = cfg.mail_port; - // param.send_server = cfg.send_server.clone(); - // param.send_port = cfg.send_port; - // param.send_user = cfg.send_user.clone(); - // param.server_flags = cfg.server_flags; - // /* although param_autoconfig's data are no longer needed from, - // it is used to later to prevent trying variations of port/server/logins */ - // } - // param.server_flags |= keep_flags; - // true - // } - // // Step 3: Fill missing fields with defaults - // // If you change the match-number here, also update STEP_13_AFTER_AUTOCONFIG above - // STEP_13_AFTER_AUTOCONFIG => { - // if param.mail_server.is_empty() { - // param.mail_server = format!("imap.{}", param_domain,) - // } - // if param.mail_port == 0 { - // param.mail_port = if 0 != param.server_flags & (0x100 | 0x400) { - // 143 - // } else { - // 993 - // } - // } - // if param.mail_user.is_empty() { - // param.mail_user = param.addr.clone(); - // } - // if param.send_server.is_empty() && !param.mail_server.is_empty() { - // param.send_server = param.mail_server.clone(); - // if param.send_server.starts_with("imap.") { - // param.send_server = param.send_server.replacen("imap", "smtp", 1); - // } - // } - // if param.send_port == 0 { - // param.send_port = if 0 != param.server_flags & DC_LP_SMTP_SOCKET_STARTTLS as i32 - // { - // 587 - // } else if 0 != param.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 { - // 25 - // } else { - // 465 - // } - // } - // if param.send_user.is_empty() && !param.mail_user.is_empty() { - // param.send_user = param.mail_user.clone(); - // } - // if param.send_pw.is_empty() && !param.mail_pw.is_empty() { - // param.send_pw = param.mail_pw.clone() - // } - // if !dc_exactly_one_bit_set(param.server_flags & DC_LP_AUTH_FLAGS as i32) { - // param.server_flags &= !(DC_LP_AUTH_FLAGS as i32); - // param.server_flags |= DC_LP_AUTH_NORMAL as i32 - // } - // if !dc_exactly_one_bit_set(param.server_flags & DC_LP_IMAP_SOCKET_FLAGS as i32) { - // param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS as i32); - // param.server_flags |= if param.send_port == 143 { - // DC_LP_IMAP_SOCKET_STARTTLS as i32 - // } else { - // DC_LP_IMAP_SOCKET_SSL as i32 - // } - // } - // if !dc_exactly_one_bit_set(param.server_flags & (DC_LP_SMTP_SOCKET_FLAGS as i32)) { - // param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); - // param.server_flags |= if param.send_port == 587 { - // DC_LP_SMTP_SOCKET_STARTTLS as i32 - // } else if param.send_port == 25 { - // DC_LP_SMTP_SOCKET_PLAIN as i32 - // } else { - // DC_LP_SMTP_SOCKET_SSL as i32 - // } - // } - // /* do we have a complete configuration? */ - // if param.mail_server.is_empty() - // || param.mail_port == 0 - // || param.mail_user.is_empty() - // || param.mail_pw.is_empty() - // || param.send_server.is_empty() - // || param.send_port == 0 - // || param.send_user.is_empty() - // || param.send_pw.is_empty() - // || param.server_flags == 0 - // { - // error!(context, "Account settings incomplete."); - // false - // } else { - // true - // } - // } - // 14 => { - // progress!(context, 600); - // /* try to connect to IMAP - if we did not got an autoconfig, - // do some further tries with different settings and username variations */ - // imap_connected_here = - // try_imap_connections(context, &mut param, param_autoconfig.is_some()).await; - // imap_connected_here - // } - // 15 => { - // progress!(context, 800); - // smtp_connected_here = - // try_smtp_connections(context, &mut param, param_autoconfig.is_some()).await; - // smtp_connected_here - // } - // 16 => { - // progress!(context, 900); - // let create_mvbox = context.get_config_bool(Config::MvboxWatch).await - // || context.get_config_bool(Config::MvboxMove).await; - // let imap = &context.inbox_thread.imap; - // if let Err(err) = imap.ensure_configured_folders(context, create_mvbox).await { - // warn!(context, "configuring folders failed: {:?}", err); - // false - // } else { - // let res = imap.select_with_uidvalidity(context, "INBOX").await; - // if let Err(err) = res { - // error!(context, "could not read INBOX status: {:?}", err); - // false - // } else { - // true - // } - // } - // } - // 17 => { - // progress!(context, 910); - // /* configuration success - write back the configured parameters with the "configured_" prefix; also write the "configured"-flag */ - // param - // .save_to_database( - // context, - // "configured_", /*the trailing underscore is correct*/ - // ) - // .await - // .ok(); - - // context - // .sql - // .set_raw_config_bool(context, "configured", true) - // .await - // .ok(); - // true - // } - // 18 => { - // progress!(context, 920); - // // we generate the keypair just now - we could also postpone this until the first message is sent, however, - // // this may result in a unexpected and annoying delay when the user sends his very first message - // // (~30 seconds on a Moto G4 play) and might looks as if message sending is always that slow. - // success = e2ee::ensure_secret_key_exists(context).await.is_ok(); - // info!(context, "key generation completed"); - // progress!(context, 940); - // break; // We are done here - // } - // _ => { - // error!(context, "Internal error: step counter out of bound",); - // break; - // } - // }; - - // if !success { - // break; - // } - // } - // if imap_connected_here { - // context.inbox_thread.imap.disconnect(context).await; - // } - // if smtp_connected_here { - // context.smtp.disconnect().await; - // } - - // // remember the entered parameters on success - // // and restore to last-entered on failure. - // // this way, the parameters visible to the ui are always in-sync with the current configuration. - // if success { - // LoginParam::from_database(context, "") - // .await - // .save_to_database(context, "configured_raw_") - // .await - // .ok(); - // } else { - // LoginParam::from_database(context, "configured_raw_") - // .await - // .save_to_database(context, "") - // .await - // .ok(); - // } - - // if let Some(provider) = provider::get_provider_info(¶m.addr) { - // if !provider.after_login_hint.is_empty() { - // let mut msg = Message::new(Viewtype::Text); - // msg.text = Some(provider.after_login_hint.to_string()); - // if chat::add_device_msg(context, Some("core-provider-info"), Some(&mut msg)) - // .await - // .is_err() - // { - // warn!(context, "cannot add after_login_hint as core-provider-info"); - // } - // } - // } - - // context.free_ongoing().await; - // progress!(context, if success { 1000 } else { 0 }); - // job::Status::Finished(Ok(())) } #[allow(clippy::unnecessary_unwrap)] @@ -513,9 +506,10 @@ async fn try_imap_connections( context: &Context, mut param: &mut LoginParam, was_autoconfig: bool, + imap: &mut Imap, ) -> bool { // progress 650 and 660 - if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 0).await { + if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 0, imap).await { return res; } progress!(context, 670); @@ -530,7 +524,7 @@ async fn try_imap_connections( param.send_user = param.send_user.split_at(at).0.to_string(); } // progress 680 and 690 - if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 1).await { + if let Some(res) = try_imap_connection(context, &mut param, was_autoconfig, 1, imap).await { res } else { false @@ -542,8 +536,9 @@ async fn try_imap_connection( param: &mut LoginParam, was_autoconfig: bool, variation: usize, + imap: &mut Imap, ) -> Option { - if let Some(res) = try_imap_one_param(context, ¶m).await { + if let Some(res) = try_imap_one_param(context, ¶m, imap).await { return Some(res); } if was_autoconfig { @@ -552,36 +547,39 @@ async fn try_imap_connection( progress!(context, 650 + variation * 30); param.server_flags &= !(DC_LP_IMAP_SOCKET_FLAGS); param.server_flags |= DC_LP_IMAP_SOCKET_STARTTLS; - if let Some(res) = try_imap_one_param(context, ¶m).await { + if let Some(res) = try_imap_one_param(context, ¶m, imap).await { return Some(res); } progress!(context, 660 + variation * 30); param.mail_port = 143; - try_imap_one_param(context, ¶m).await + try_imap_one_param(context, ¶m, imap).await } -async fn try_imap_one_param(context: &Context, param: &LoginParam) -> Option { - unimplemented!(); - // let inf = format!( - // "imap: {}@{}:{} flags=0x{:x} certificate_checks={}", - // param.mail_user, - // param.mail_server, - // param.mail_port, - // param.server_flags, - // param.imap_certificate_checks - // ); - // info!(context, "Trying: {}", inf); - // if context.inbox_thread.imap.connect(context, ¶m).await { - // info!(context, "success: {}", inf); - // return Some(true); - // } - // if context.shall_stop_ongoing().await { - // return Some(false); - // } - // info!(context, "Could not connect: {}", inf); - // None +async fn try_imap_one_param( + context: &Context, + param: &LoginParam, + imap: &mut Imap, +) -> Option { + let inf = format!( + "imap: {}@{}:{} flags=0x{:x} certificate_checks={}", + param.mail_user, + param.mail_server, + param.mail_port, + param.server_flags, + param.imap_certificate_checks + ); + info!(context, "Trying: {}", inf); + if imap.connect(context, ¶m).await { + info!(context, "success: {}", inf); + return Some(true); + } + if context.shall_stop_ongoing().await { + return Some(false); + } + info!(context, "Could not connect: {}", inf); + None } async fn try_smtp_connections( @@ -589,8 +587,9 @@ async fn try_smtp_connections( mut param: &mut LoginParam, was_autoconfig: bool, ) -> bool { + let mut smtp = Smtp::new(); /* try to connect to SMTP - if we did not got an autoconfig, the first try was SSL-465 and we do a second try with STARTTLS-587 */ - if let Some(res) = try_smtp_one_param(context, ¶m).await { + if let Some(res) = try_smtp_one_param(context, ¶m, &mut smtp).await { return res; } if was_autoconfig { @@ -601,40 +600,49 @@ async fn try_smtp_connections( param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32; param.send_port = 587; - if let Some(res) = try_smtp_one_param(context, ¶m).await { + if let Some(res) = try_smtp_one_param(context, ¶m, &mut smtp).await { + if res { + smtp.disconnect().await; + } return res; } progress!(context, 860); param.server_flags &= !(DC_LP_SMTP_SOCKET_FLAGS as i32); param.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32; param.send_port = 25; - if let Some(res) = try_smtp_one_param(context, ¶m).await { + if let Some(res) = try_smtp_one_param(context, ¶m, &mut smtp).await { + if res { + smtp.disconnect().await; + } return res; } false } -async fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option { - unimplemented!() - // let inf = format!( - // "smtp: {}@{}:{} flags: 0x{:x}", - // param.send_user, param.send_server, param.send_port, param.server_flags - // ); - // info!(context, "Trying: {}", inf); - // match context.smtp.connect(context, ¶m).await { - // Ok(()) => { - // info!(context, "success: {}", inf); - // Some(true) - // } - // Err(err) => { - // if context.shall_stop_ongoing().await { - // Some(false) - // } else { - // warn!(context, "could not connect: {}", err); - // None - // } - // } - // } +async fn try_smtp_one_param( + context: &Context, + param: &LoginParam, + smtp: &mut Smtp, +) -> Option { + let inf = format!( + "smtp: {}@{}:{} flags: 0x{:x}", + param.send_user, param.send_server, param.send_port, param.server_flags + ); + info!(context, "Trying: {}", inf); + match smtp.connect(context, ¶m).await { + Ok(()) => { + info!(context, "success: {}", inf); + Some(true) + } + Err(err) => { + if context.shall_stop_ongoing().await { + Some(false) + } else { + warn!(context, "could not connect: {}", err); + None + } + } + } } #[cfg(test)] @@ -655,7 +663,7 @@ mod tests { .set_config(Config::MailPw, Some("123456")) .await .unwrap(); - job_configure_imap(&t.ctx).await; + t.ctx.configure().await; } #[async_std::test] diff --git a/src/context.rs b/src/context.rs index 6499c2a26..1127f5509 100644 --- a/src/context.rs +++ b/src/context.rs @@ -14,16 +14,13 @@ use crate::constants::*; use crate::contact::*; use crate::error::*; use crate::events::Event; -use crate::imap::*; use crate::job::{self, Action}; -use crate::job_thread::JobThread; use crate::key::Key; use crate::login_param::LoginParam; use crate::lot::Lot; use crate::message::{self, Message, MessengerMessage, MsgId}; use crate::param::Params; use crate::scheduler::Scheduler; -use crate::smtp::Smtp; use crate::sql::Sql; #[derive(Clone, Debug)] @@ -492,19 +489,6 @@ pub(crate) struct BobStatus { pub qr_scan: Option, } -#[derive(Debug, PartialEq, Clone, Copy)] -pub(crate) enum PerformJobsNeeded { - Not, - AtOnce, - AvoidDos, -} - -impl Default for PerformJobsNeeded { - fn default() -> Self { - Self::Not - } -} - pub fn get_version_str() -> &'static str { &DC_VERSION_STR } diff --git a/src/imap/idle.rs b/src/imap/idle.rs index 1959bc8f8..89a410879 100644 --- a/src/imap/idle.rs +++ b/src/imap/idle.rs @@ -4,7 +4,6 @@ use async_imap::extensions::idle::{Handle as ImapIdleHandle, IdleResponse}; use async_native_tls::TlsStream; use async_std::net::TcpStream; use async_std::prelude::*; -use std::sync::atomic::Ordering; use std::time::{Duration, SystemTime}; use crate::context::Context; diff --git a/src/imap/mod.rs b/src/imap/mod.rs index a5f3fcda4..307d12fbf 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -3,8 +3,6 @@ //! uses [async-email/async-imap](https://github.com/async-email/async-imap) //! to implement connect, fetch, delete functionality with standard IMAP servers. -use std::sync::atomic::{AtomicBool, Ordering}; - use num_traits::FromPrimitive; use async_imap::{ @@ -12,7 +10,7 @@ use async_imap::{ types::{Capability, Fetch, Flag, Mailbox, Name, NameAttribute}, }; use async_std::prelude::*; -use async_std::sync::{Mutex, Receiver, RwLock}; +use async_std::sync::Receiver; use crate::config::*; use crate::constants::*; diff --git a/src/imex.rs b/src/imex.rs index 209ea856b..528db3c46 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -17,7 +17,7 @@ use crate::dc_tools::*; use crate::e2ee; use crate::error::*; use crate::events::Event; -use crate::job::{self, Action, Job}; +use crate::job::Job; use crate::key::{self, Key}; use crate::message::{Message, MsgId}; use crate::mimeparser::SystemMessage; diff --git a/src/job.rs b/src/job.rs index bc0329776..b223c5979 100644 --- a/src/job.rs +++ b/src/job.rs @@ -6,8 +6,6 @@ use std::future::Future; use std::{fmt, time}; -use async_std::prelude::*; - use deltachat_derive::{FromSql, ToSql}; use itertools::Itertools; use rand::{thread_rng, Rng}; @@ -15,16 +13,13 @@ use rand::{thread_rng, Rng}; use crate::blob::BlobObject; use crate::chat::{self, ChatId}; use crate::config::Config; -use crate::configure::*; use crate::constants::*; use crate::contact::Contact; -use crate::context::{Context, PerformJobsNeeded}; +use crate::context::Context; use crate::dc_tools::*; use crate::error::{Error, Result}; use crate::events::Event; use crate::imap::*; -use crate::imex::*; -use crate::job; use crate::location; use crate::login_param::LoginParam; use crate::message::MsgId; diff --git a/src/job_thread.rs b/src/job_thread.rs deleted file mode 100644 index 12d86bc50..000000000 --- a/src/job_thread.rs +++ /dev/null @@ -1,218 +0,0 @@ -use async_std::sync::{channel, Arc, Mutex, Receiver, Sender}; - -use crate::context::Context; -use crate::error::{Error, Result}; -use crate::imap::Imap; - -#[derive(Debug)] -pub struct JobThread { - pub name: &'static str, - pub folder_config_name: &'static str, - pub imap: Imap, - pub state: Arc>, - notify_sender: Sender<()>, - notify_receiver: Receiver<()>, -} - -#[derive(Clone, Debug, Default)] -pub struct JobState { - jobs_needed: bool, - suspended: bool, - using_handle: bool, -} - -impl JobThread { - pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self { - let (notify_sender, notify_receiver) = channel(1); - - JobThread { - name, - folder_config_name, - imap, - state: Arc::new(Mutex::new(Default::default())), - notify_sender, - notify_receiver, - } - } - - pub async fn suspend(&mut self, context: &Context) { - info!(context, "Suspending {}-thread.", self.name,); - { - self.state.lock().await.suspended = true; - } - self.interrupt_idle(context).await; - loop { - let using_handle = self.state.lock().await.using_handle; - if !using_handle { - return; - } - async_std::task::sleep(std::time::Duration::from_micros(300 * 1000)).await; - } - } - - pub async fn unsuspend(&self, context: &Context) { - info!(context, "Unsuspending {}-thread.", self.name); - - { - let lock = &*self.state.clone(); - let mut state = lock.lock().await; - - state.suspended = false; - } - self.notify_sender.send(()).await; - } - - pub async fn try_interrupt_idle(&mut self, context: &Context) -> bool { - if self.state.lock().await.using_handle { - self.interrupt_idle(context).await; - return true; - } - - false - } - - pub async fn interrupt_idle(&mut self, context: &Context) { - { - self.state.lock().await.jobs_needed = true; - } - - info!(context, "Interrupting {}-IDLE...", self.name); - - self.imap.interrupt_idle(context).await; - - self.notify_sender.send(()).await; - - info!(context, "Interrupting {}-IDLE... finished", self.name); - } - - pub async fn fetch(&mut self, context: &Context, use_network: bool) { - { - let lock = &*self.state.clone(); - let mut state = lock.lock().await; - - if state.suspended { - return; - } - - state.using_handle = true; - } - - if use_network { - if let Err(err) = self.connect_and_fetch(context).await { - warn!(context, "connect+fetch failed: {}, reconnect & retry", err); - self.imap.trigger_reconnect(); - if let Err(err) = self.connect_and_fetch(context).await { - warn!(context, "connect+fetch failed: {}", err); - } - } - } - { - self.state.lock().await.using_handle = false; - } - } - - async fn connect_and_fetch(&mut self, context: &Context) -> Result<()> { - let prefix = format!("{}-fetch", self.name); - match self.imap.connect_configured(context).await { - Ok(()) => { - if let Some(watch_folder) = self.get_watch_folder(context).await { - let start = std::time::Instant::now(); - info!(context, "{} started...", prefix); - let res = self - .imap - .fetch(context, &watch_folder) - .await - .map_err(Into::into); - let elapsed = start.elapsed().as_millis(); - info!(context, "{} done in {:.3} ms.", prefix, elapsed); - - res - } else { - Err(Error::WatchFolderNotFound("not-set".to_string())) - } - } - Err(err) => Err(crate::error::Error::Message(err.to_string())), - } - } - - async fn get_watch_folder(&self, context: &Context) -> Option { - match context - .sql - .get_raw_config(context, self.folder_config_name) - .await - { - Some(name) => Some(name), - None => { - if self.folder_config_name == "configured_inbox_folder" { - // initialized with old version, so has not set configured_inbox_folder - Some("INBOX".to_string()) - } else { - None - } - } - } - } - - pub async fn idle(&mut self, context: &Context, use_network: bool) { - { - let lock = &*self.state.clone(); - let mut state = lock.lock().await; - - if state.jobs_needed { - info!( - context, - "{}-IDLE will not be started as it was interrupted while not idling.", - self.name, - ); - state.jobs_needed = false; - return; - } - - if state.suspended { - self.notify_receiver.recv().await; - return; - } - - state.using_handle = true; - - if !use_network { - state.using_handle = false; - self.notify_receiver.recv().await; - return; - } - } - - let prefix = format!("{}-IDLE", self.name); - let do_fake_idle = match self.imap.connect_configured(context).await { - Ok(()) => { - if !self.imap.can_idle() { - true // we have to do fake_idle - } else { - let watch_folder = self.get_watch_folder(context).await; - info!(context, "{} started...", prefix); - let res = self.imap.idle(context, watch_folder).await; - info!(context, "{} ended...", prefix); - if let Err(err) = res { - warn!(context, "{} failed: {} -> reconnecting", prefix, err); - // something is borked, let's start afresh on the next occassion - self.imap.disconnect(context).await; - } - false - } - } - Err(err) => { - info!(context, "{}-IDLE connection fail: {:?}", self.name, err); - // if the connection fails, use fake_idle to retry periodically - // fake_idle() will be woken up by interrupt_idle() as - // well so will act on maybe_network events - true - } - }; - if do_fake_idle { - let watch_folder = self.get_watch_folder(context).await; - self.imap.fake_idle(context, watch_folder).await; - } - - self.state.lock().await.using_handle = false; - } -} diff --git a/src/lib.rs b/src/lib.rs index 3d2dd376c..0012e2fdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,6 @@ pub mod imex; mod scheduler; #[macro_use] pub mod job; -mod job_thread; pub mod key; mod keyring; pub mod location; diff --git a/src/scheduler.rs b/src/scheduler.rs index 63ebedf7e..f1cf39974 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -217,14 +217,6 @@ impl Scheduler { _ => false, } } - - /// Check if the scheduler is stoppd. - pub fn is_stopped(&self) -> bool { - match self { - Scheduler::Stopped => true, - _ => false, - } - } } /// Connection state logic shared between imap and smtp connections. diff --git a/src/smtp/mod.rs b/src/smtp/mod.rs index 5491c763a..0bb516b97 100644 --- a/src/smtp/mod.rs +++ b/src/smtp/mod.rs @@ -2,14 +2,13 @@ pub mod send; -use async_std::sync::{channel, Receiver, RwLock, Sender}; use std::time::{Duration, Instant}; use async_smtp::smtp::client::net::*; use async_smtp::*; use crate::constants::*; -use crate::context::{Context, PerformJobsNeeded}; +use crate::context::Context; use crate::events::Event; use crate::login_param::{dc_build_tls, LoginParam}; use crate::oauth2::*;