From 7eba3a51861f53de07d3eeeb0a74714efd044504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Mon, 15 Jan 2024 15:27:14 +0100 Subject: [PATCH] generate one autoconfig per email address --- Cargo.lock | 16 ++++++++++++---- src/account/wizard.rs | 22 ++++++++++++++++++++-- src/backend/wizard.rs | 25 ++++--------------------- src/config/wizard.rs | 4 +++- src/imap/wizard.rs | 11 +++++++---- src/smtp/wizard.rs | 17 ++++++++++------- 6 files changed, 56 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8a8bec..1417624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1462,7 +1462,9 @@ dependencies = [ [[package]] name = "email-lib" -version = "0.20.0" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d79298206bcb8ada88ed67be329711502351ad55bf143025b310139701dd01a0" dependencies = [ "advisory-lock", "anyhow", @@ -2957,7 +2959,9 @@ dependencies = [ [[package]] name = "mml-lib" -version = "1.0.6" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ffcc68cd2f48395ee07fe85ec580154c6f8f22ff89972b4a1dd2452890d614" dependencies = [ "async-recursion", "chumsky", @@ -3709,7 +3713,9 @@ dependencies = [ [[package]] name = "process-lib" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e83db4af201454004f9cdc5fb343031f6d84bddf8a0d41348bc9e82fab1f1ee" dependencies = [ "log", "once_cell", @@ -4245,7 +4251,9 @@ dependencies = [ [[package]] name = "secret-lib" -version = "0.3.2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8fa450b77b5d8e0ac1cf7c741e912e54b568e8dcf888d80f5502c23899aeae" dependencies = [ "keyring-lib", "process-lib", diff --git a/src/account/wizard.rs b/src/account/wizard.rs index c153d41..28835ca 100644 --- a/src/account/wizard.rs +++ b/src/account/wizard.rs @@ -5,6 +5,7 @@ use dialoguer::Input; #[cfg(feature = "account-sync")] use email::account::sync::config::SyncConfig; use email_address::EmailAddress; +use log::{debug, trace, warn}; #[allow(unused)] use crate::backend::{self, config::BackendConfig, BackendKind}; @@ -35,6 +36,8 @@ pub(crate) async fn configure() -> Result> { }) .interact()?; + let email = &config.email; + config.display_name = Some( Input::with_theme(&*THEME) .with_prompt("Full display name") @@ -49,7 +52,22 @@ pub(crate) async fn configure() -> Result> { .into(), ); - match backend::wizard::configure(&account_name, &config.email).await? { + let autoconfig = match autoconfig::from_addr(email).await { + Ok(autoconfig) => { + println!("An automatic configuration has been found for {email},"); + println!("it will be used by default for the rest of the configuration.\n"); + trace!("{autoconfig:#?}"); + Some(autoconfig) + } + Err(err) => { + warn!("cannot discover configuration from {email}: {err}"); + debug!("{err:?}"); + None + } + }; + let autoconfig = autoconfig.as_ref(); + + match backend::wizard::configure(&account_name, email, autoconfig).await? { #[cfg(feature = "imap")] Some(BackendConfig::Imap(imap_config)) => { config.imap = Some(imap_config); @@ -68,7 +86,7 @@ pub(crate) async fn configure() -> Result> { _ => (), }; - match backend::wizard::configure_sender(&account_name, &config.email).await? { + match backend::wizard::configure_sender(&account_name, email, autoconfig).await? { #[cfg(feature = "smtp")] Some(BackendConfig::Smtp(smtp_config)) => { config.smtp = Some(smtp_config); diff --git a/src/backend/wizard.rs b/src/backend/wizard.rs index 4d2d5b8..9bb9648 100644 --- a/src/backend/wizard.rs +++ b/src/backend/wizard.rs @@ -1,8 +1,6 @@ use anyhow::Result; use autoconfig::config::Config as AutoConfig; use dialoguer::Select; -use log::{debug, warn}; -use std::sync::OnceLock; #[cfg(feature = "imap")] use crate::imap; @@ -34,26 +32,10 @@ const SEND_MESSAGE_BACKEND_KINDS: &[BackendKind] = &[ BackendKind::Sendmail, ]; -static AUTOCONFIG: OnceLock = OnceLock::new(); - -#[cfg(any(feature = "imap", feature = "smtp"))] -pub(crate) async fn get_or_init_autoconfig(email: &str) -> Option<&AutoConfig> { - match AUTOCONFIG.get() { - Some(autoconfig) => Some(autoconfig), - None => match autoconfig::from_addr(email).await { - Ok(autoconfig) => Some(AUTOCONFIG.get_or_init(|| autoconfig)), - Err(err) => { - warn!("cannot discover SMTP configuration from {email}: {err}"); - debug!("{err:?}"); - None - } - }, - } -} - pub(crate) async fn configure( #[allow(unused)] account_name: &str, #[allow(unused)] email: &str, + autoconfig: Option<&AutoConfig>, ) -> Result> { let kind = Select::with_theme(&*THEME) .with_prompt("Default email backend") @@ -65,7 +47,7 @@ pub(crate) async fn configure( let config = match kind { #[cfg(feature = "imap")] Some(kind) if kind == BackendKind::Imap => { - Some(imap::wizard::configure(account_name, email).await?) + Some(imap::wizard::configure(account_name, email, autoconfig).await?) } #[cfg(feature = "maildir")] Some(kind) if kind == BackendKind::Maildir => Some(maildir::wizard::configure()?), @@ -80,6 +62,7 @@ pub(crate) async fn configure( pub(crate) async fn configure_sender( #[allow(unused)] account_name: &str, #[allow(unused)] email: &str, + autoconfig: Option<&AutoConfig>, ) -> Result> { let kind = Select::with_theme(&*THEME) .with_prompt("Backend for sending messages") @@ -91,7 +74,7 @@ pub(crate) async fn configure_sender( let config = match kind { #[cfg(feature = "smtp")] Some(kind) if kind == BackendKind::Smtp => { - Some(smtp::wizard::configure(account_name, email).await?) + Some(smtp::wizard::configure(account_name, email, autoconfig).await?) } #[cfg(feature = "sendmail")] Some(kind) if kind == BackendKind::Sendmail => Some(sendmail::wizard::configure()?), diff --git a/src/config/wizard.rs b/src/config/wizard.rs index 4898ea3..ce849fb 100644 --- a/src/config/wizard.rs +++ b/src/config/wizard.rs @@ -191,13 +191,15 @@ mod test { use crate::{account::config::TomlAccountConfig, config::TomlConfig}; + use super::pretty_serialize; + fn assert_eq(config: TomlAccountConfig, expected_toml: &str) { let config = TomlConfig { accounts: HashMap::from_iter([("test".into(), config)]), ..Default::default() }; - let toml = super::pretty_serialize(&config).expect("serialize error"); + let toml = pretty_serialize(&config).expect("serialize error"); assert_eq!(toml, expected_toml); let expected_config = toml::from_str(&toml).expect("deserialize error"); diff --git a/src/imap/wizard.rs b/src/imap/wizard.rs index d2cf4a9..29c41b9 100644 --- a/src/imap/wizard.rs +++ b/src/imap/wizard.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use autoconfig::config::{AuthenticationType, SecurityType, ServerType}; +use autoconfig::config::{AuthenticationType, Config as AutoConfig, SecurityType, ServerType}; use dialoguer::{Confirm, Input, Password, Select}; use email::{ account::config::{ @@ -12,7 +12,7 @@ use oauth::v2_0::{AuthorizationCodeGrant, Client}; use secret::Secret; use crate::{ - backend::{config::BackendConfig, wizard::get_or_init_autoconfig}, + backend::config::BackendConfig, ui::{prompt, THEME}, wizard_log, wizard_prompt, }; @@ -32,8 +32,11 @@ const KEYRING: &str = "Ask my password, then save it in my system's global keyri const RAW: &str = "Ask my password, then save it in the configuration file (not safe)"; const CMD: &str = "Ask me a shell command that exposes my password"; -pub(crate) async fn configure(account_name: &str, email: &str) -> Result { - let autoconfig = get_or_init_autoconfig(email).await; +pub(crate) async fn configure( + account_name: &str, + email: &str, + autoconfig: Option<&AutoConfig>, +) -> Result { let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2()); let autoconfig_server = autoconfig.and_then(|c| { c.email_provider() diff --git a/src/smtp/wizard.rs b/src/smtp/wizard.rs index b313856..356b50d 100644 --- a/src/smtp/wizard.rs +++ b/src/smtp/wizard.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use autoconfig::config::{AuthenticationType, SecurityType, ServerType}; +use autoconfig::config::{AuthenticationType, Config as AutoConfig, SecurityType, ServerType}; use dialoguer::{Confirm, Input, Password, Select}; use email::{ account::config::{ @@ -12,7 +12,7 @@ use oauth::v2_0::{AuthorizationCodeGrant, Client}; use secret::Secret; use crate::{ - backend::{config::BackendConfig, wizard::get_or_init_autoconfig}, + backend::config::BackendConfig, ui::{prompt, THEME}, wizard_log, wizard_prompt, }; @@ -32,8 +32,11 @@ const KEYRING: &str = "Ask my password, then save it in my system's global keyri const RAW: &str = "Ask my password, then save it in the configuration file (not safe)"; const CMD: &str = "Ask me a shell command that exposes my password"; -pub(crate) async fn configure(account_name: &str, email: &str) -> Result { - let autoconfig = get_or_init_autoconfig(email).await; +pub(crate) async fn configure( + account_name: &str, + email: &str, + autoconfig: Option<&AutoConfig>, +) -> Result { let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2()); let autoconfig_server = autoconfig.and_then(|c| { c.email_provider() @@ -80,9 +83,9 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result 993, - SmtpEncryptionKind::StartTls => 143, - SmtpEncryptionKind::None => 143, + SmtpEncryptionKind::Tls => 465, + SmtpEncryptionKind::StartTls => 587, + SmtpEncryptionKind::None => 25, }); let (encryption, default_port) = match encryption_idx {