From f4c8ffca4c3f9b3afed4c6d2ed2a4ad80c161cfc Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Sun, 27 Dec 2020 03:49:11 +0300 Subject: [PATCH] Query MX records during provider autoconfiguration Previously MX records were queried only for OAuth 2 configuration and did not affect the list of servers tried. User was required to manually configure the servers for Google Workspace (former GSuite) domains. Now MX records are queried during configuration. If provider is found in offline database, its ID, corresponding to the filename, is saved as `configured_provider`. `configured_provider` is also set during database migration if email address uses the domain from the provider database, but no MX querying is done. --- deltachat-ffi/src/lib.rs | 2 +- examples/repl/cmdline.rs | 2 +- src/config.rs | 1 + src/configure/mod.rs | 148 ++++++++++++++++++++------------------- src/imap/mod.rs | 9 ++- src/login_param.rs | 14 ++++ src/oauth2.rs | 54 +++----------- src/provider/data.rs | 122 ++++++++++++++++++++++++++++++-- src/provider/mod.rs | 120 +++++++++++++++++++++++++------ src/provider/update.py | 29 +++++--- src/smtp/mod.rs | 7 +- src/smtp/send.rs | 6 +- src/sql.rs | 18 +++++ 13 files changed, 367 insertions(+), 165 deletions(-) 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)