diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 4754f0266..4d3f3253c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -3358,7 +3358,7 @@ pub unsafe extern "C" fn dc_provider_new_from_email( return ptr::null(); } let addr = to_string_lossy(addr); - match provider::get_provider_info(addr.as_str()) { + match block_on(provider::get_provider_info(addr.as_str())) { Some(provider) => provider, None => ptr::null_mut(), } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index bfca417d6..cf1657bee 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1055,7 +1055,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu } "providerinfo" => { ensure!(!arg1.is_empty(), "Argument missing."); - match provider::get_provider_info(arg1) { + match provider::get_provider_info(arg1).await { Some(info) => { println!("Information for provider belonging to {}:", arg1); println!("status: {}", info.status as u32); diff --git a/src/config.rs b/src/config.rs index b8895bbf0..be94261ba 100644 --- a/src/config.rs +++ b/src/config.rs @@ -116,6 +116,7 @@ pub enum Config { ConfiguredInboxFolder, ConfiguredMvboxFolder, ConfiguredSentboxFolder, + ConfiguredProvider, Configured, #[strum(serialize = "sys.version")] diff --git a/src/configure/mod.rs b/src/configure/mod.rs index b9e29d9db..7f0965af3 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -85,7 +85,7 @@ impl Context { let success = configure(self, &mut param).await; self.set_config(Config::NotifyAboutWrongPw, None).await?; - if let Some(provider) = provider::get_provider_info(¶m.addr) { + if let Some(provider) = param.provider { if let Some(config_defaults) = &provider.config_defaults { for def in config_defaults.iter() { if !self.config_exists(def.key).await { @@ -205,9 +205,51 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { { // no advanced parameters entered by the user: query provider-database or do Autoconfig - if let Some(servers) = get_offline_autoconfig(ctx, ¶m.addr) { - param_autoconfig = Some(servers); + info!( + ctx, + "checking internal provider-info for offline autoconfig" + ); + + if let Some(provider) = provider::get_provider_info(¶m_domain).await { + param.provider = Some(provider); + match provider.status { + provider::Status::OK | provider::Status::PREPARATION => { + if provider.server.is_empty() { + info!(ctx, "offline autoconfig found, but no servers defined"); + param_autoconfig = None; + } else { + info!(ctx, "offline autoconfig found"); + let servers = provider + .server + .iter() + .map(|s| ServerParams { + protocol: s.protocol, + socket: s.socket, + hostname: s.hostname.to_string(), + port: s.port, + username: match s.username_pattern { + UsernamePattern::EMAIL => param.addr.to_string(), + UsernamePattern::EMAILLOCALPART => { + if let Some(at) = param.addr.find('@') { + param.addr.split_at(at).0.to_string() + } else { + param.addr.to_string() + } + } + }, + }) + .collect(); + + param_autoconfig = Some(servers) + } + } + provider::Status::BROKEN => { + info!(ctx, "offline autoconfig found, provider is broken"); + param_autoconfig = None; + } + } } else { + info!(ctx, "no offline autoconfig found"); param_autoconfig = get_autoconfig(ctx, param, ¶m_domain, ¶m_addr_urlencoded).await; } @@ -257,6 +299,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { .filter(|params| params.protocol == Protocol::SMTP) .cloned() .collect(); + let provider_strict_tls = param.provider.map_or(false, |provider| provider.strict_tls); let smtp_config_task = task::spawn(async move { let mut smtp_configured = false; @@ -267,8 +310,15 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { smtp_param.port = smtp_server.port; smtp_param.security = smtp_server.socket; - match try_smtp_one_param(&context_smtp, &smtp_param, &smtp_addr, oauth2, &mut smtp) - .await + match try_smtp_one_param( + &context_smtp, + &smtp_param, + &smtp_addr, + oauth2, + provider_strict_tls, + &mut smtp, + ) + .await { Ok(_) => { smtp_configured = true; @@ -304,7 +354,16 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { param.imap.port = imap_server.port; param.imap.security = imap_server.socket; - match try_imap_one_param(ctx, ¶m.imap, ¶m.addr, oauth2, &mut imap).await { + match try_imap_one_param( + ctx, + ¶m.imap, + ¶m.addr, + oauth2, + provider_strict_tls, + &mut imap, + ) + .await + { Ok(_) => { imap_configured = true; break; @@ -446,59 +505,12 @@ async fn get_autoconfig( None } -fn get_offline_autoconfig(context: &Context, addr: &str) -> Option> { - info!( - context, - "checking internal provider-info for offline autoconfig" - ); - - if let Some(provider) = provider::get_provider_info(&addr) { - match provider.status { - provider::Status::OK | provider::Status::PREPARATION => { - if provider.server.is_empty() { - info!(context, "offline autoconfig found, but no servers defined"); - None - } else { - info!(context, "offline autoconfig found"); - let servers = provider - .server - .iter() - .map(|s| ServerParams { - protocol: s.protocol, - socket: s.socket, - hostname: s.hostname.to_string(), - port: s.port, - username: match s.username_pattern { - UsernamePattern::EMAIL => addr.to_string(), - UsernamePattern::EMAILLOCALPART => { - if let Some(at) = addr.find('@') { - addr.split_at(at).0.to_string() - } else { - addr.to_string() - } - } - }, - }) - .collect(); - Some(servers) - } - } - provider::Status::BROKEN => { - info!(context, "offline autoconfig found, provider is broken"); - None - } - } - } else { - info!(context, "no offline autoconfig found"); - None - } -} - async fn try_imap_one_param( context: &Context, param: &ServerLoginParam, addr: &str, oauth2: bool, + provider_strict_tls: bool, imap: &mut Imap, ) -> Result<(), ConfigurationError> { let inf = format!( @@ -507,7 +519,10 @@ async fn try_imap_one_param( ); info!(context, "Trying: {}", inf); - if let Err(err) = imap.connect(context, param, addr, oauth2).await { + if let Err(err) = imap + .connect(context, param, addr, oauth2, provider_strict_tls) + .await + { info!(context, "failure: {}", err); Err(ConfigurationError { config: inf, @@ -524,6 +539,7 @@ async fn try_smtp_one_param( param: &ServerLoginParam, addr: &str, oauth2: bool, + provider_strict_tls: bool, smtp: &mut Smtp, ) -> Result<(), ConfigurationError> { let inf = format!( @@ -532,7 +548,10 @@ async fn try_smtp_one_param( ); info!(context, "Trying: {}", inf); - if let Err(err) = smtp.connect(context, param, addr, oauth2).await { + if let Err(err) = smtp + .connect(context, param, addr, oauth2, provider_strict_tls) + .await + { info!(context, "failure: {}", err); Err(ConfigurationError { config: inf, @@ -601,7 +620,6 @@ pub enum Error { mod tests { #![allow(clippy::indexing_slicing)] - use super::*; use crate::config::*; use crate::test_utils::*; @@ -614,20 +632,4 @@ mod tests { t.set_config(Config::MailPw, Some("123456")).await.unwrap(); assert!(t.configure().await.is_err()); } - - #[async_std::test] - async fn test_get_offline_autoconfig() { - let context = TestContext::new().await.ctx; - - let addr = "someone123@example.org"; - assert!(get_offline_autoconfig(&context, addr).is_none()); - - let addr = "someone123@nauta.cu"; - let found_params = get_offline_autoconfig(&context, addr).unwrap(); - assert_eq!(found_params.len(), 2); - assert_eq!(found_params[0].protocol, Protocol::IMAP); - assert_eq!(found_params[0].hostname, "imap.nauta.cu".to_string()); - assert_eq!(found_params[1].protocol, Protocol::SMTP); - assert_eq!(found_params[1].hostname, "smtp.nauta.cu".to_string()); - } } diff --git a/src/imap/mod.rs b/src/imap/mod.rs index a0ab1f9e0..47de2a0bd 100644 --- a/src/imap/mod.rs +++ b/src/imap/mod.rs @@ -26,7 +26,7 @@ use crate::message::{self, update_server_uid, MessageState}; use crate::mimeparser; use crate::oauth2::dc_get_oauth2_access_token; use crate::param::Params; -use crate::provider::{get_provider_info, Socket}; +use crate::provider::Socket; use crate::{ chat, dc_tools::dc_extract_grpid_from_rfc724_mid, scheduler::InterruptInfo, stock::StockMessage, }; @@ -339,6 +339,7 @@ impl Imap { ¶m.imap, ¶m.addr, param.server_flags & DC_LP_AUTH_OAUTH2 != 0, + param.provider.map_or(false, |provider| provider.strict_tls), ) .await { @@ -360,6 +361,7 @@ impl Imap { lp: &ServerLoginParam, addr: &str, oauth2: bool, + provider_strict_tls: bool, ) -> Result<()> { if lp.server.is_empty() || lp.user.is_empty() || lp.password.is_empty() { bail!("Incomplete IMAP connection parameters"); @@ -369,11 +371,8 @@ impl Imap { let mut config = &mut self.config; config.addr = addr.to_string(); config.lp = lp.clone(); - let provider = get_provider_info(&addr); config.strict_tls = match lp.certificate_checks { - CertificateChecks::Automatic => { - provider.map_or(false, |provider| provider.strict_tls) - } + CertificateChecks::Automatic => provider_strict_tls, CertificateChecks::Strict => true, CertificateChecks::AcceptInvalidCertificates | CertificateChecks::AcceptInvalidCertificates2 => false, diff --git a/src/login_param.rs b/src/login_param.rs index 3de612626..6c53f2cf6 100644 --- a/src/login_param.rs +++ b/src/login_param.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::fmt; +use crate::provider::{get_provider_by_id, Provider}; use crate::{context::Context, provider::Socket}; #[derive(Copy, Clone, Debug, Display, FromPrimitive, PartialEq, Eq)] @@ -48,6 +49,7 @@ pub struct LoginParam { pub imap: ServerLoginParam, pub smtp: ServerLoginParam, pub server_flags: i32, + pub provider: Option<&'static Provider>, } impl LoginParam { @@ -130,6 +132,12 @@ impl LoginParam { .await .unwrap_or_default(); + let key = format!("{}provider", prefix); + let provider = sql + .get_raw_config(context, key) + .await + .and_then(|provider_id| get_provider_by_id(&provider_id)); + LoginParam { addr, imap: ServerLoginParam { @@ -148,6 +156,7 @@ impl LoginParam { security: send_security, certificate_checks: smtp_certificate_checks, }, + provider, server_flags, } } @@ -216,6 +225,11 @@ impl LoginParam { sql.set_raw_config_int(context, key, self.server_flags) .await?; + if let Some(provider) = self.provider { + let key = format!("{}provider", prefix); + sql.set_raw_config(context, key, Some(provider.id)).await?; + } + Ok(()) } } diff --git a/src/oauth2.rs b/src/oauth2.rs index eab0da849..9a1b8902a 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -1,9 +1,7 @@ //! OAuth 2 module -use regex::Regex; use std::collections::HashMap; -use async_std_resolver::{config, resolver}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use serde::Deserialize; @@ -19,7 +17,6 @@ const OAUTH2_GMAIL: Oauth2 = Oauth2 { init_token: "https://accounts.google.com/o/oauth2/token?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&code=$CODE&grant_type=authorization_code", refresh_token: "https://accounts.google.com/o/oauth2/token?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&refresh_token=$REFRESH_TOKEN&grant_type=refresh_token", get_userinfo: Some("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=$ACCESS_TOKEN"), - mx_pattern: Some(r"^aspmx\.l\.google\.com\.$"), }; const OAUTH2_YANDEX: Oauth2 = Oauth2 { @@ -29,11 +26,8 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 { init_token: "https://oauth.yandex.com/token?grant_type=authorization_code&code=$CODE&client_id=$CLIENT_ID&client_secret=58b8c6e94cf44fbe952da8511955dacf", refresh_token: "https://oauth.yandex.com/token?grant_type=refresh_token&refresh_token=$REFRESH_TOKEN&client_id=$CLIENT_ID&client_secret=58b8c6e94cf44fbe952da8511955dacf", get_userinfo: None, - mx_pattern: None, }; -const OAUTH2_PROVIDERS: [Oauth2; 1] = [OAUTH2_GMAIL]; - #[derive(Debug, Clone, PartialEq, Eq)] struct Oauth2 { client_id: &'static str, @@ -41,7 +35,6 @@ struct Oauth2 { init_token: &'static str, refresh_token: &'static str, get_userinfo: Option<&'static str>, - mx_pattern: Option<&'static str>, } /// OAuth 2 Access Token Response @@ -276,47 +269,16 @@ impl Oauth2 { .find('@') .map(|index| addr_normalized.split_at(index + 1).1) { - if let Some(provider) = provider::get_provider_info(&addr_normalized) { - match &provider.oauth2_authorizer { - Some(Oauth2Authorizer::Gmail) => Some(OAUTH2_GMAIL), - Some(Oauth2Authorizer::Yandex) => Some(OAUTH2_YANDEX), - None => None, // provider known to not support oauth2, no mx-lookup required - } - } else { - Oauth2::lookup_mx(domain).await - } - } else { - None - } - } - - async fn lookup_mx(domain: impl AsRef) -> Option { - if let Ok(resolver) = resolver( - config::ResolverConfig::default(), - config::ResolverOpts::default(), - ) - .await - { - let mut fqdn: String = String::from(domain.as_ref()); - if !fqdn.ends_with('.') { - fqdn.push('.'); - } - - if let Ok(res) = resolver.mx_lookup(fqdn).await { - for provider in OAUTH2_PROVIDERS.iter() { - if let Some(pattern) = provider.mx_pattern { - let re = Regex::new(pattern).unwrap(); - - for rr in res.iter() { - if re.is_match(&rr.exchange().to_lowercase().to_utf8()) { - return Some(provider.clone()); - } - } - } - } + if let Some(oauth2_authorizer) = provider::get_provider_info(&domain) + .await + .and_then(|provider| provider.oauth2_authorizer.as_ref()) + { + return Some(match oauth2_authorizer { + Oauth2Authorizer::Gmail => OAUTH2_GMAIL, + Oauth2Authorizer::Yandex => OAUTH2_YANDEX, + }); } } - None } diff --git a/src/provider/data.rs b/src/provider/data.rs index cf695adff..50f91fe02 100644 --- a/src/provider/data.rs +++ b/src/provider/data.rs @@ -10,6 +10,7 @@ use once_cell::sync::Lazy; // aktivix.org.md: aktivix.org static P_AKTIVIX_ORG: Lazy = Lazy::new(|| Provider { + id: "aktivix.org", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -39,6 +40,7 @@ static P_AKTIVIX_ORG: Lazy = Lazy::new(|| Provider { // aol.md: aol.com static P_AOL: Lazy = Lazy::new(|| { Provider { + id: "aol", status: Status::PREPARATION, before_login_hint: "To log in to AOL with Delta Chat, you need to set up an app password in the AOL web interface.", after_login_hint: "", @@ -56,6 +58,7 @@ static P_AOL: Lazy = Lazy::new(|| { // arcor.de.md: arcor.de static P_ARCOR_DE: Lazy = Lazy::new(|| Provider { + id: "arcor.de", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -84,6 +87,7 @@ static P_ARCOR_DE: Lazy = Lazy::new(|| Provider { // autistici.org.md: autistici.org static P_AUTISTICI_ORG: Lazy = Lazy::new(|| Provider { + id: "autistici.org", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -112,6 +116,7 @@ static P_AUTISTICI_ORG: Lazy = Lazy::new(|| Provider { // bluewin.ch.md: bluewin.ch static P_BLUEWIN_CH: Lazy = Lazy::new(|| Provider { + id: "bluewin.ch", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -140,6 +145,7 @@ static P_BLUEWIN_CH: Lazy = Lazy::new(|| Provider { // buzon.uy.md: buzon.uy static P_BUZON_UY: Lazy = Lazy::new(|| Provider { + id: "buzon.uy", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -168,6 +174,7 @@ static P_BUZON_UY: Lazy = Lazy::new(|| Provider { // chello.at.md: chello.at static P_CHELLO_AT: Lazy = Lazy::new(|| Provider { + id: "chello.at", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -196,6 +203,7 @@ static P_CHELLO_AT: Lazy = Lazy::new(|| Provider { // comcast.md: xfinity.com, comcast.net static P_COMCAST: Lazy = Lazy::new(|| Provider { + id: "comcast", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -209,6 +217,7 @@ static P_COMCAST: Lazy = Lazy::new(|| Provider { // dismail.de.md: dismail.de static P_DISMAIL_DE: Lazy = Lazy::new(|| Provider { + id: "dismail.de", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -222,6 +231,7 @@ static P_DISMAIL_DE: Lazy = Lazy::new(|| Provider { // disroot.md: disroot.org static P_DISROOT: Lazy = Lazy::new(|| Provider { + id: "disroot", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -235,6 +245,7 @@ static P_DISROOT: Lazy = Lazy::new(|| Provider { // dubby.org.md: dubby.org static P_DUBBY_ORG: Lazy = Lazy::new(|| Provider { + id: "dubby.org", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -285,9 +296,24 @@ static P_DUBBY_ORG: Lazy = Lazy::new(|| Provider { oauth2_authorizer: None, }); -// example.com.md: example.com, example.org +// espiv.net.md: espiv.net +static P_ESPIV_NET: Lazy = Lazy::new(|| Provider { + id: "espiv.net", + status: Status::OK, + before_login_hint: "", + after_login_hint: "", + overview_page: "https://providers.delta.chat/espiv-net", + server: vec![], + config_defaults: None, + strict_tls: true, + max_smtp_rcpt_to: None, + oauth2_authorizer: None, +}); + +// example.com.md: example.com, example.org, example.net static P_EXAMPLE_COM: Lazy = Lazy::new(|| { Provider { + id: "example.com", status: Status::BROKEN, before_login_hint: "Hush this provider doesn't exist!", after_login_hint: "This provider doesn't really exist, so you can't use it :/ If you need an email provider for Delta Chat, take a look at providers.delta.chat!", @@ -305,6 +331,7 @@ static P_EXAMPLE_COM: Lazy = Lazy::new(|| { // fastmail.md: fastmail.com static P_FASTMAIL: Lazy = Lazy::new(|| Provider { + id: "fastmail", status: Status::PREPARATION, before_login_hint: "You must create an app-specific password for Delta Chat before you can log in.", @@ -320,6 +347,7 @@ static P_FASTMAIL: Lazy = Lazy::new(|| Provider { // firemail.de.md: firemail.at, firemail.de static P_FIREMAIL_DE: Lazy = Lazy::new(|| { Provider { + id: "firemail.de", status: Status::PREPARATION, before_login_hint: "Firemail erlaubt nur bei bezahlten Accounts den vollen Zugriff auf das E-Mail-Protokoll. Wenn Sie nicht für Firemail bezahlen, verwenden Sie bitte einen anderen E-Mail-Anbieter.", after_login_hint: "Leider schränkt Firemail die maximale Gruppengröße ein. Je nach Bezahlmodell sind nur 5 bis 30 Gruppenmitglieder erlaubt.", @@ -335,6 +363,7 @@ static P_FIREMAIL_DE: Lazy = Lazy::new(|| { // five.chat.md: five.chat static P_FIVE_CHAT: Lazy = Lazy::new(|| Provider { + id: "five.chat", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -365,6 +394,7 @@ static P_FIVE_CHAT: Lazy = Lazy::new(|| Provider { // freenet.de.md: freenet.de static P_FREENET_DE: Lazy = Lazy::new(|| Provider { + id: "freenet.de", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -391,9 +421,10 @@ static P_FREENET_DE: Lazy = Lazy::new(|| Provider { oauth2_authorizer: None, }); -// gmail.md: gmail.com, googlemail.com +// gmail.md: gmail.com, googlemail.com, google.com static P_GMAIL: Lazy = Lazy::new(|| { Provider { + id: "gmail", status: Status::PREPARATION, before_login_hint: "For Gmail accounts, you need to create an app-password if you have \"2-Step Verification\" enabled. If this setting is not available, you need to enable \"less secure apps\".", after_login_hint: "", @@ -411,6 +442,7 @@ static P_GMAIL: Lazy = Lazy::new(|| { // gmx.net.md: gmx.net, gmx.de, gmx.at, gmx.ch, gmx.org, gmx.eu, gmx.info, gmx.biz, gmx.com static P_GMX_NET: Lazy = Lazy::new(|| Provider { + id: "gmx.net", status: Status::PREPARATION, before_login_hint: "You must allow IMAP access to your account before you can login.", after_login_hint: "", @@ -446,6 +478,7 @@ static P_GMX_NET: Lazy = Lazy::new(|| Provider { // hermes.radio.md: hermes.radio static P_HERMES_RADIO: Lazy = Lazy::new(|| Provider { + id: "hermes.radio", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -477,6 +510,7 @@ static P_HERMES_RADIO: Lazy = Lazy::new(|| Provider { // hey.com.md: hey.com static P_HEY_COM: Lazy = Lazy::new(|| { Provider { + id: "hey.com", status: Status::BROKEN, before_login_hint: "hey.com does not offer the standard IMAP e-mail protocol, so you cannot log in with Delta Chat to hey.com.", after_login_hint: "", @@ -492,6 +526,7 @@ static P_HEY_COM: Lazy = Lazy::new(|| { // i.ua.md: i.ua static P_I_UA: Lazy = Lazy::new(|| Provider { + id: "i.ua", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -505,6 +540,7 @@ static P_I_UA: Lazy = Lazy::new(|| Provider { // icloud.md: icloud.com, me.com, mac.com static P_ICLOUD: Lazy = Lazy::new(|| Provider { + id: "icloud", status: Status::PREPARATION, before_login_hint: "You must create an app-specific password for Delta Chat before you can login.", @@ -534,6 +570,7 @@ static P_ICLOUD: Lazy = Lazy::new(|| Provider { // kolst.com.md: kolst.com static P_KOLST_COM: Lazy = Lazy::new(|| Provider { + id: "kolst.com", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -547,6 +584,7 @@ static P_KOLST_COM: Lazy = Lazy::new(|| Provider { // kontent.com.md: kontent.com static P_KONTENT_COM: Lazy = Lazy::new(|| Provider { + id: "kontent.com", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -560,6 +598,7 @@ static P_KONTENT_COM: Lazy = Lazy::new(|| Provider { // mail.ru.md: mail.ru, inbox.ru, bk.ru, list.ru static P_MAIL_RU: Lazy = Lazy::new(|| Provider { + id: "mail.ru", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -573,6 +612,7 @@ static P_MAIL_RU: Lazy = Lazy::new(|| Provider { // mailbox.org.md: mailbox.org, secure.mailbox.org static P_MAILBOX_ORG: Lazy = Lazy::new(|| Provider { + id: "mailbox.org", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -587,6 +627,7 @@ static P_MAILBOX_ORG: Lazy = Lazy::new(|| Provider { // nauta.cu.md: nauta.cu static P_NAUTA_CU: Lazy = Lazy::new(|| { Provider { + id: "nauta.cu", status: Status::OK, before_login_hint: "", after_login_hint: "Atención - con nauta.cu, puede enviar mensajes sólo a un máximo de 20 personas a la vez. En grupos más grandes, no puede enviar mensajes o abandonar el grupo.", @@ -614,6 +655,7 @@ static P_NAUTA_CU: Lazy = Lazy::new(|| { // outlook.com.md: hotmail.com, outlook.com, office365.com, outlook.com.tr, live.com static P_OUTLOOK_COM: Lazy = Lazy::new(|| { Provider { + id: "outlook.com", status: Status::BROKEN, before_login_hint: "Outlook.com email addresses will not work as expected as these servers remove some important transport information. Hopefully sooner or later there will be a fix, for now we suggest to use another email address.", after_login_hint: "Outlook.com email addresses will not work as expected as these servers remove some important transport information. Unencrypted 1-on-1 chats kind of work, but groups and encryption don't. Hopefully sooner or later there will be a fix, for now we suggest to use another email address.", @@ -631,6 +673,7 @@ static P_OUTLOOK_COM: Lazy = Lazy::new(|| { // posteo.md: posteo.de, posteo.af, posteo.at, posteo.be, posteo.ch, posteo.cl, posteo.co, posteo.co.uk, posteo.com.br, posteo.cr, posteo.cz, posteo.dk, posteo.ee, posteo.es, posteo.eu, posteo.fi, posteo.gl, posteo.gr, posteo.hn, posteo.hr, posteo.hu, posteo.ie, posteo.in, posteo.is, posteo.jp, posteo.la, posteo.li, posteo.lt, posteo.lu, posteo.me, posteo.mx, posteo.my, posteo.net, posteo.nl, posteo.no, posteo.nz, posteo.org, posteo.pe, posteo.pl, posteo.pm, posteo.pt, posteo.ro, posteo.ru, posteo.se, posteo.sg, posteo.si, posteo.tn, posteo.uk, posteo.us static P_POSTEO: Lazy = Lazy::new(|| Provider { + id: "posteo", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -660,6 +703,7 @@ static P_POSTEO: Lazy = Lazy::new(|| Provider { // protonmail.md: protonmail.com, protonmail.ch static P_PROTONMAIL: Lazy = Lazy::new(|| { Provider { + id: "protonmail", status: Status::BROKEN, before_login_hint: "Protonmail does not offer the standard IMAP e-mail protocol, so you cannot log in with Delta Chat to Protonmail.", after_login_hint: "To use Delta Chat with Protonmail, the IMAP bridge must be running in the background. If you have connectivity issues, double check whether it works as expected.", @@ -675,6 +719,7 @@ static P_PROTONMAIL: Lazy = Lazy::new(|| { // riseup.net.md: riseup.net static P_RISEUP_NET: Lazy = Lazy::new(|| Provider { + id: "riseup.net", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -688,6 +733,7 @@ static P_RISEUP_NET: Lazy = Lazy::new(|| Provider { // rogers.com.md: rogers.com static P_ROGERS_COM: Lazy = Lazy::new(|| Provider { + id: "rogers.com", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -701,6 +747,7 @@ static P_ROGERS_COM: Lazy = Lazy::new(|| Provider { // systemli.org.md: systemli.org static P_SYSTEMLI_ORG: Lazy = Lazy::new(|| Provider { + id: "systemli.org", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -715,6 +762,7 @@ static P_SYSTEMLI_ORG: Lazy = Lazy::new(|| Provider { // t-online.md: t-online.de, magenta.de static P_T_ONLINE: Lazy = Lazy::new(|| { Provider { + id: "t-online", status: Status::PREPARATION, before_login_hint: "To use Delta Chat with a T-Online email address, you need to create an app password in the web interface.", after_login_hint: "", @@ -732,6 +780,7 @@ static P_T_ONLINE: Lazy = Lazy::new(|| { // testrun.md: testrun.org static P_TESTRUN: Lazy = Lazy::new(|| Provider { + id: "testrun", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -784,6 +833,7 @@ static P_TESTRUN: Lazy = Lazy::new(|| Provider { // tiscali.it.md: tiscali.it static P_TISCALI_IT: Lazy = Lazy::new(|| Provider { + id: "tiscali.it", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -812,6 +862,7 @@ static P_TISCALI_IT: Lazy = Lazy::new(|| Provider { // ukr.net.md: ukr.net static P_UKR_NET: Lazy = Lazy::new(|| Provider { + id: "ukr.net", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -825,6 +876,7 @@ static P_UKR_NET: Lazy = Lazy::new(|| Provider { // undernet.uy.md: undernet.uy static P_UNDERNET_UY: Lazy = Lazy::new(|| Provider { + id: "undernet.uy", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -853,6 +905,7 @@ static P_UNDERNET_UY: Lazy = Lazy::new(|| Provider { // vfemail.md: vfemail.net static P_VFEMAIL: Lazy = Lazy::new(|| Provider { + id: "vfemail", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -866,6 +919,7 @@ static P_VFEMAIL: Lazy = Lazy::new(|| Provider { // vodafone.de.md: vodafone.de, vodafonemail.de static P_VODAFONE_DE: Lazy = Lazy::new(|| Provider { + id: "vodafone.de", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -895,6 +949,7 @@ static P_VODAFONE_DE: Lazy = Lazy::new(|| Provider { // web.de.md: web.de, email.de, flirt.ms, hallo.ms, kuss.ms, love.ms, magic.ms, singles.ms, cool.ms, kanzler.ms, okay.ms, party.ms, pop.ms, stars.ms, techno.ms, clever.ms, deutschland.ms, genial.ms, ich.ms, online.ms, smart.ms, wichtig.ms, action.ms, fussball.ms, joker.ms, planet.ms, power.ms static P_WEB_DE: Lazy = Lazy::new(|| { Provider { + id: "web.de", status: Status::PREPARATION, before_login_hint: "You must allow IMAP access to your account before you can login.", after_login_hint: "Note: if you have your web.de spam settings too strict, you won't receive contact requests from new people. If you want to receive contact requests, you should disable the \"3-Wege-Spamschutz\" in the web.de settings. Read how: https://hilfe.web.de/email/spam-und-viren/spamschutz-einstellungen.html", @@ -914,6 +969,7 @@ static P_WEB_DE: Lazy = Lazy::new(|| { // yahoo.md: yahoo.com, yahoo.de, yahoo.it, yahoo.fr, yahoo.es, yahoo.se, yahoo.co.uk, yahoo.co.nz, yahoo.com.au, yahoo.com.ar, yahoo.com.br, yahoo.com.mx, ymail.com, rocketmail.com, yahoodns.net static P_YAHOO: Lazy = Lazy::new(|| { Provider { + id: "yahoo", status: Status::PREPARATION, before_login_hint: "To use Delta Chat with your Yahoo email address you have to create an \"App-Password\" in the account security screen.", after_login_hint: "", @@ -931,6 +987,7 @@ static P_YAHOO: Lazy = Lazy::new(|| { // yandex.ru.md: yandex.com, yandex.by, yandex.kz, yandex.ru, yandex.ua, ya.ru, narod.ru static P_YANDEX_RU: Lazy = Lazy::new(|| Provider { + id: "yandex.ru", status: Status::PREPARATION, before_login_hint: "For Yandex accounts, you have to set IMAP protocol option turned on.", after_login_hint: "", @@ -959,6 +1016,7 @@ static P_YANDEX_RU: Lazy = Lazy::new(|| Provider { // ziggo.nl.md: ziggo.nl static P_ZIGGO_NL: Lazy = Lazy::new(|| Provider { + id: "ziggo.nl", status: Status::OK, before_login_hint: "", after_login_hint: "", @@ -985,7 +1043,7 @@ static P_ZIGGO_NL: Lazy = Lazy::new(|| Provider { oauth2_authorizer: None, }); -pub static PROVIDER_DATA: Lazy> = Lazy::new(|| { +pub(crate) static PROVIDER_DATA: Lazy> = Lazy::new(|| { [ ("aktivix.org", &*P_AKTIVIX_ORG), ("aol.com", &*P_AOL), @@ -999,8 +1057,10 @@ pub static PROVIDER_DATA: Lazy> = Lazy: ("dismail.de", &*P_DISMAIL_DE), ("disroot.org", &*P_DISROOT), ("dubby.org", &*P_DUBBY_ORG), + ("espiv.net", &*P_ESPIV_NET), ("example.com", &*P_EXAMPLE_COM), ("example.org", &*P_EXAMPLE_COM), + ("example.net", &*P_EXAMPLE_COM), ("fastmail.com", &*P_FASTMAIL), ("firemail.at", &*P_FIREMAIL_DE), ("firemail.de", &*P_FIREMAIL_DE), @@ -1008,6 +1068,7 @@ pub static PROVIDER_DATA: Lazy> = Lazy: ("freenet.de", &*P_FREENET_DE), ("gmail.com", &*P_GMAIL), ("googlemail.com", &*P_GMAIL), + ("google.com", &*P_GMAIL), ("gmx.net", &*P_GMX_NET), ("gmx.de", &*P_GMX_NET), ("gmx.at", &*P_GMX_NET), @@ -1156,5 +1217,58 @@ pub static PROVIDER_DATA: Lazy> = Lazy: .collect() }); +pub(crate) static PROVIDER_IDS: Lazy> = Lazy::new(|| { + [ + ("aktivix.org", &*P_AKTIVIX_ORG), + ("aol", &*P_AOL), + ("arcor.de", &*P_ARCOR_DE), + ("autistici.org", &*P_AUTISTICI_ORG), + ("bluewin.ch", &*P_BLUEWIN_CH), + ("buzon.uy", &*P_BUZON_UY), + ("chello.at", &*P_CHELLO_AT), + ("comcast", &*P_COMCAST), + ("dismail.de", &*P_DISMAIL_DE), + ("disroot", &*P_DISROOT), + ("dubby.org", &*P_DUBBY_ORG), + ("espiv.net", &*P_ESPIV_NET), + ("example.com", &*P_EXAMPLE_COM), + ("fastmail", &*P_FASTMAIL), + ("firemail.de", &*P_FIREMAIL_DE), + ("five.chat", &*P_FIVE_CHAT), + ("freenet.de", &*P_FREENET_DE), + ("gmail", &*P_GMAIL), + ("gmx.net", &*P_GMX_NET), + ("hermes.radio", &*P_HERMES_RADIO), + ("hey.com", &*P_HEY_COM), + ("i.ua", &*P_I_UA), + ("icloud", &*P_ICLOUD), + ("kolst.com", &*P_KOLST_COM), + ("kontent.com", &*P_KONTENT_COM), + ("mail.ru", &*P_MAIL_RU), + ("mailbox.org", &*P_MAILBOX_ORG), + ("nauta.cu", &*P_NAUTA_CU), + ("outlook.com", &*P_OUTLOOK_COM), + ("posteo", &*P_POSTEO), + ("protonmail", &*P_PROTONMAIL), + ("riseup.net", &*P_RISEUP_NET), + ("rogers.com", &*P_ROGERS_COM), + ("systemli.org", &*P_SYSTEMLI_ORG), + ("t-online", &*P_T_ONLINE), + ("testrun", &*P_TESTRUN), + ("tiscali.it", &*P_TISCALI_IT), + ("ukr.net", &*P_UKR_NET), + ("undernet.uy", &*P_UNDERNET_UY), + ("vfemail", &*P_VFEMAIL), + ("vodafone.de", &*P_VODAFONE_DE), + ("web.de", &*P_WEB_DE), + ("yahoo", &*P_YAHOO), + ("yandex.ru", &*P_YANDEX_RU), + ("ziggo.nl", &*P_ZIGGO_NL), + ] + .iter() + .copied() + .collect() +}); + pub static PROVIDER_UPDATED: Lazy = - Lazy::new(|| chrono::NaiveDate::from_ymd(2020, 12, 26)); + Lazy::new(|| chrono::NaiveDate::from_ymd(2021, 1, 8)); diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 57133f56e..c29de27ba 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -3,8 +3,8 @@ mod data; use crate::config::Config; -use crate::dc_tools::EmailAddress; -use crate::provider::data::{PROVIDER_DATA, PROVIDER_UPDATED}; +use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED}; +use async_std_resolver::{config, resolver}; use chrono::{NaiveDateTime, NaiveTime}; #[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)] @@ -68,6 +68,8 @@ pub struct ConfigDefault { #[derive(Debug)] pub struct Provider { + /// Unique ID, corresponding to provider database filename. + pub id: &'static str, pub status: Status, pub before_login_hint: &'static str, pub after_login_hint: &'static str, @@ -79,20 +81,84 @@ pub struct Provider { pub oauth2_authorizer: Option, } -pub fn get_provider_info(addr: &str) -> Option<&Provider> { - let domain = match addr.parse::() { - Ok(addr) => addr.domain, - Err(_err) => return None, - } - .to_lowercase(); +/// Returns provider for the given domain. +/// +/// This function looks up domain in offline database first. If not +/// found, it queries MX record for the domain and looks up offline +/// database for MX domains. +/// +/// For compatibility, email address can be passed to this function +/// instead of the domain. +pub async fn get_provider_info(domain: &str) -> Option<&'static Provider> { + let domain = domain.rsplitn(2, '@').next()?; - if let Some(provider) = PROVIDER_DATA.get(domain.as_str()) { + if let Some(provider) = get_provider_by_domain(domain) { + return Some(provider); + } + + if let Some(provider) = get_provider_by_mx(domain).await { + return Some(provider); + } + + None +} + +/// Finds a provider in offline database based on domain. +pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> { + if let Some(provider) = PROVIDER_DATA.get(domain.to_lowercase().as_str()) { return Some(*provider); } None } +/// Finds a provider based on MX record for the given domain. +/// +/// For security reasons, only Gmail can be configured this way. +pub async fn get_provider_by_mx(domain: impl AsRef) -> Option<&'static Provider> { + if let Ok(resolver) = resolver( + config::ResolverConfig::default(), + config::ResolverOpts::default(), + ) + .await + { + let mut fqdn: String = String::from(domain.as_ref()); + if !fqdn.ends_with('.') { + fqdn.push('.'); + } + + if let Ok(mx_domains) = resolver.mx_lookup(fqdn).await { + for (provider_domain, provider) in PROVIDER_DATA.iter() { + if provider.id != "gmail" { + // MX lookup is limited to Gmail for security reasons + continue; + } + + let provider_fqdn = provider_domain.to_string() + "."; + let provider_fqdn_dot = ".".to_string() + &provider_fqdn; + + for mx_domain in mx_domains.iter() { + let mx_domain = mx_domain.exchange().to_lowercase().to_utf8(); + + if mx_domain == provider_fqdn || mx_domain.ends_with(&provider_fqdn_dot) { + return Some(provider); + } + } + } + } + } + + None +} + +pub fn get_provider_by_id(id: &str) -> Option<&'static Provider> { + if let Some(provider) = PROVIDER_IDS.get(id) { + Some(&provider) + } else { + None + } +} + // returns update timestamp in seconds, UTC, compatible for comparison with time() and database times pub fn get_provider_update_timestamp() -> i64 { NaiveDateTime::new(*PROVIDER_UPDATED, NaiveTime::from_hms(0, 0, 0)).timestamp_millis() / 1_000 @@ -107,24 +173,21 @@ mod tests { use chrono::NaiveDate; #[test] - fn test_get_provider_info_unexistant() { - let provider = get_provider_info("user@unexistant.org"); + fn test_get_provider_by_domain_unexistant() { + let provider = get_provider_by_domain("unexistant.org"); assert!(provider.is_none()); } #[test] - fn test_get_provider_info_mixed_case() { - let provider = get_provider_info("uSer@nAUta.Cu").unwrap(); + fn test_get_provider_by_domain_mixed_case() { + let provider = get_provider_by_domain("nAUta.Cu").unwrap(); assert!(provider.status == Status::OK); } #[test] - fn test_get_provider_info() { - let provider = get_provider_info("nauta.cu"); // this is no email address - assert!(provider.is_none()); - - let addr = "user@nauta.cu"; - let provider = get_provider_info(addr).unwrap(); + fn test_get_provider_by_domain() { + let addr = "nauta.cu"; + let provider = get_provider_by_domain(addr).unwrap(); assert!(provider.status == Status::OK); let server = &provider.server[0]; assert_eq!(server.protocol, Protocol::IMAP); @@ -139,15 +202,30 @@ mod tests { assert_eq!(server.port, 25); assert_eq!(server.username_pattern, UsernamePattern::EMAIL); - let provider = get_provider_info("user@gmail.com").unwrap(); + let provider = get_provider_by_domain("gmail.com").unwrap(); assert!(provider.status == Status::PREPARATION); assert!(!provider.before_login_hint.is_empty()); assert!(!provider.overview_page.is_empty()); - let provider = get_provider_info("user@googlemail.com").unwrap(); + let provider = get_provider_by_domain("googlemail.com").unwrap(); assert!(provider.status == Status::PREPARATION); } + #[test] + fn test_get_provider_by_id() { + let provider = get_provider_by_id("gmail").unwrap(); + assert!(provider.id == "gmail"); + } + + #[async_std::test] + async fn test_get_provider_info() { + assert!(get_provider_info("").await.is_none()); + assert!(get_provider_info("google.com").await.unwrap().id == "gmail"); + + // get_provider_info() accepts email addresses for backwards compatibility + assert!(get_provider_info("example@google.com").await.unwrap().id == "gmail"); + } + #[test] fn test_get_provider_update_timestamp() { let timestamp_past = NaiveDateTime::new( diff --git a/src/provider/update.py b/src/provider/update.py index 7b1686620..a121fa5d4 100755 --- a/src/provider/update.py +++ b/src/provider/update.py @@ -8,7 +8,8 @@ import datetime out_all = "" out_domains = "" -domains_dict = {} +out_ids = "" +domains_set = set() def camel(name): words = name.split("_") @@ -22,15 +23,19 @@ def cleanstr(s): return s +def file2id(f): + return os.path.basename(f).replace(".md", "") + + def file2varname(f): - f = f[f.rindex("/")+1:].replace(".md", "") + f = file2id(f) f = f.replace(".", "_") f = f.replace("-", "_") return "P_" + f.upper() def file2url(f): - f = f[f.rindex("/")+1:].replace(".md", "") + f = file2id(f) f = f.replace(".", "-") return "https://providers.delta.chat/" + f @@ -61,14 +66,16 @@ def process_data(data, file): if domain == "" or domain.count(".") < 1 or domain.lower() != domain: raise TypeError("bad domain: " + domain) - global domains_dict - if domains_dict.get(domain, False): + global domains_set + if domain in domains_set: raise TypeError("domain used twice: " + domain) - domains_dict[domain] = True + domains_set.add(domain) domains += " (\"" + domain + "\", &*" + file2varname(file) + "),\n" comment += domain + ", " + ids = "" + ids += " (\"" + file2id(file) + "\", &*" + file2varname(file) + "),\n" server = "" has_imap = False @@ -115,6 +122,7 @@ def process_data(data, file): after_login_hint = cleanstr(data.get("after_login_hint", "")) if (not has_imap and not has_smtp) or (has_imap and has_smtp): provider += "static " + file2varname(file) + ": Lazy = Lazy::new(|| Provider {\n" + provider += " id: \"" + file2id(file) + "\",\n" provider += " status: Status::" + status + ",\n" provider += " before_login_hint: \"" + before_login_hint + "\",\n" provider += " after_login_hint: \"" + after_login_hint + "\",\n" @@ -132,13 +140,14 @@ def process_data(data, file): raise TypeError("status PREPARATION or BROKEN requires before_login_hint: " + file) # finally, add the provider - global out_all, out_domains + global out_all, out_domains, out_ids out_all += "// " + file[file.rindex("/")+1:] + ": " + comment.strip(", ") + "\n" # also add provider with no special things to do - # eg. _not_ supporting oauth2 is also an information and we can skip the mx-lookup in this case out_all += provider out_domains += domains + out_ids += ids def process_file(file): @@ -172,10 +181,14 @@ if __name__ == "__main__": process_dir(sys.argv[1]) - out_all += "pub static PROVIDER_DATA: Lazy> = Lazy::new(|| [\n" + out_all += "pub(crate) static PROVIDER_DATA: Lazy> = Lazy::new(|| [\n" out_all += out_domains; out_all += "].iter().copied().collect());\n\n" + out_all += "pub(crate) static PROVIDER_IDS: Lazy> = Lazy::new(|| [\n" + out_all += out_ids; + out_all += "].iter().copied().collect());\n\n" + now = datetime.datetime.utcnow() out_all += "pub static PROVIDER_UPDATED: Lazy = "\ "Lazy::new(|| chrono::NaiveDate::from_ymd("+str(now.year)+", "+str(now.month)+", "+str(now.day)+"));\n" diff --git a/src/smtp/mod.rs b/src/smtp/mod.rs index 14cfa2889..5af59fd0b 100644 --- a/src/smtp/mod.rs +++ b/src/smtp/mod.rs @@ -12,7 +12,7 @@ use crate::context::Context; use crate::events::EventType; use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam, ServerLoginParam}; use crate::oauth2::*; -use crate::provider::{get_provider_info, Socket}; +use crate::provider::Socket; use crate::stock::StockMessage; /// SMTP write and read timeout in seconds. @@ -107,6 +107,7 @@ impl Smtp { &lp.smtp, &lp.addr, lp.server_flags & DC_LP_AUTH_OAUTH2 != 0, + lp.provider.map_or(false, |provider| provider.strict_tls), ) .await; if let Err(ref err) = res { @@ -130,6 +131,7 @@ impl Smtp { lp: &ServerLoginParam, addr: &str, oauth2: bool, + provider_strict_tls: bool, ) -> Result<()> { if self.is_connected().await { warn!(context, "SMTP already connected."); @@ -151,9 +153,8 @@ impl Smtp { let domain = &lp.server; let port = lp.port; - let provider = get_provider_info(addr); let strict_tls = match lp.certificate_checks { - CertificateChecks::Automatic => provider.map_or(false, |provider| provider.strict_tls), + CertificateChecks::Automatic => provider_strict_tls, CertificateChecks::Strict => true, CertificateChecks::AcceptInvalidCertificates | CertificateChecks::AcceptInvalidCertificates2 => false, diff --git a/src/smtp/send.rs b/src/smtp/send.rs index 561820010..25ed97ea1 100644 --- a/src/smtp/send.rs +++ b/src/smtp/send.rs @@ -7,7 +7,7 @@ use crate::config::Config; use crate::constants::DEFAULT_MAX_SMTP_RCPT_TO; use crate::context::Context; use crate::events::EventType; -use crate::provider::get_provider_info; +use crate::provider::get_provider_by_id; use itertools::Itertools; use std::time::Duration; @@ -38,9 +38,9 @@ impl Smtp { let message_len_bytes = message.len(); let mut chunk_size = DEFAULT_MAX_SMTP_RCPT_TO; - if let Some(provider) = get_provider_info( + if let Some(provider) = get_provider_by_id( &context - .get_config(Config::ConfiguredAddr) + .get_config(Config::ConfiguredProvider) .await .unwrap_or_default(), ) { diff --git a/src/sql.rs b/src/sql.rs index 246b7eb97..0640ac0d9 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -16,6 +16,7 @@ use crate::ephemeral::start_ephemeral_timers; use crate::error::format_err; use crate::param::*; use crate::peerstate::*; +use crate::provider::get_provider_by_domain; use crate::{ chat::{update_device_icon, update_saved_messages_icon}, config::Config, @@ -1395,6 +1396,23 @@ CREATE INDEX devmsglabels_index1 ON devmsglabels (label); .await?; sql.set_raw_config_int(context, "dbversion", 69).await?; } + if dbversion < 70 { + info!(context, "[migration] v70"); + if let Some(addr) = context.get_config(Config::ConfiguredAddr).await { + if let Ok(domain) = addr.parse::().map(|email| email.domain) { + context + .set_config( + Config::ConfiguredProvider, + get_provider_by_domain(&domain).map(|provider| provider.id), + ) + .await?; + } else { + warn!(context, "Can't parse configured address: {:?}", addr); + } + } + + sql.set_raw_config_int(context, "dbversion", 70).await?; + } // (2) updates that require high-level objects // (the structure is complete now and all objects are usable)