mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-25 20:40:24 +00:00
generate one autoconfig per email address
This commit is contained in:
parent
1246be8a5b
commit
7eba3a5186
6 changed files with 56 additions and 39 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -1462,7 +1462,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "email-lib"
|
name = "email-lib"
|
||||||
version = "0.20.0"
|
version = "0.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d79298206bcb8ada88ed67be329711502351ad55bf143025b310139701dd01a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"advisory-lock",
|
"advisory-lock",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -2957,7 +2959,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mml-lib"
|
name = "mml-lib"
|
||||||
version = "1.0.6"
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8ffcc68cd2f48395ee07fe85ec580154c6f8f22ff89972b4a1dd2452890d614"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"chumsky",
|
"chumsky",
|
||||||
|
@ -3709,7 +3713,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "process-lib"
|
name = "process-lib"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e83db4af201454004f9cdc5fb343031f6d84bddf8a0d41348bc9e82fab1f1ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -4245,7 +4251,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secret-lib"
|
name = "secret-lib"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af8fa450b77b5d8e0ac1cf7c741e912e54b568e8dcf888d80f5502c23899aeae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyring-lib",
|
"keyring-lib",
|
||||||
"process-lib",
|
"process-lib",
|
||||||
|
|
|
@ -5,6 +5,7 @@ use dialoguer::Input;
|
||||||
#[cfg(feature = "account-sync")]
|
#[cfg(feature = "account-sync")]
|
||||||
use email::account::sync::config::SyncConfig;
|
use email::account::sync::config::SyncConfig;
|
||||||
use email_address::EmailAddress;
|
use email_address::EmailAddress;
|
||||||
|
use log::{debug, trace, warn};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use crate::backend::{self, config::BackendConfig, BackendKind};
|
use crate::backend::{self, config::BackendConfig, BackendKind};
|
||||||
|
@ -35,6 +36,8 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
||||||
})
|
})
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
|
let email = &config.email;
|
||||||
|
|
||||||
config.display_name = Some(
|
config.display_name = Some(
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.with_prompt("Full display name")
|
.with_prompt("Full display name")
|
||||||
|
@ -49,7 +52,22 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
||||||
.into(),
|
.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")]
|
#[cfg(feature = "imap")]
|
||||||
Some(BackendConfig::Imap(imap_config)) => {
|
Some(BackendConfig::Imap(imap_config)) => {
|
||||||
config.imap = Some(imap_config);
|
config.imap = Some(imap_config);
|
||||||
|
@ -68,7 +86,7 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
match backend::wizard::configure_sender(&account_name, &config.email).await? {
|
match backend::wizard::configure_sender(&account_name, email, autoconfig).await? {
|
||||||
#[cfg(feature = "smtp")]
|
#[cfg(feature = "smtp")]
|
||||||
Some(BackendConfig::Smtp(smtp_config)) => {
|
Some(BackendConfig::Smtp(smtp_config)) => {
|
||||||
config.smtp = Some(smtp_config);
|
config.smtp = Some(smtp_config);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use autoconfig::config::Config as AutoConfig;
|
use autoconfig::config::Config as AutoConfig;
|
||||||
use dialoguer::Select;
|
use dialoguer::Select;
|
||||||
use log::{debug, warn};
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
#[cfg(feature = "imap")]
|
#[cfg(feature = "imap")]
|
||||||
use crate::imap;
|
use crate::imap;
|
||||||
|
@ -34,26 +32,10 @@ const SEND_MESSAGE_BACKEND_KINDS: &[BackendKind] = &[
|
||||||
BackendKind::Sendmail,
|
BackendKind::Sendmail,
|
||||||
];
|
];
|
||||||
|
|
||||||
static AUTOCONFIG: OnceLock<AutoConfig> = 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(
|
pub(crate) async fn configure(
|
||||||
#[allow(unused)] account_name: &str,
|
#[allow(unused)] account_name: &str,
|
||||||
#[allow(unused)] email: &str,
|
#[allow(unused)] email: &str,
|
||||||
|
autoconfig: Option<&AutoConfig>,
|
||||||
) -> Result<Option<BackendConfig>> {
|
) -> Result<Option<BackendConfig>> {
|
||||||
let kind = Select::with_theme(&*THEME)
|
let kind = Select::with_theme(&*THEME)
|
||||||
.with_prompt("Default email backend")
|
.with_prompt("Default email backend")
|
||||||
|
@ -65,7 +47,7 @@ pub(crate) async fn configure(
|
||||||
let config = match kind {
|
let config = match kind {
|
||||||
#[cfg(feature = "imap")]
|
#[cfg(feature = "imap")]
|
||||||
Some(kind) if kind == BackendKind::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")]
|
#[cfg(feature = "maildir")]
|
||||||
Some(kind) if kind == BackendKind::Maildir => Some(maildir::wizard::configure()?),
|
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(
|
pub(crate) async fn configure_sender(
|
||||||
#[allow(unused)] account_name: &str,
|
#[allow(unused)] account_name: &str,
|
||||||
#[allow(unused)] email: &str,
|
#[allow(unused)] email: &str,
|
||||||
|
autoconfig: Option<&AutoConfig>,
|
||||||
) -> Result<Option<BackendConfig>> {
|
) -> Result<Option<BackendConfig>> {
|
||||||
let kind = Select::with_theme(&*THEME)
|
let kind = Select::with_theme(&*THEME)
|
||||||
.with_prompt("Backend for sending messages")
|
.with_prompt("Backend for sending messages")
|
||||||
|
@ -91,7 +74,7 @@ pub(crate) async fn configure_sender(
|
||||||
let config = match kind {
|
let config = match kind {
|
||||||
#[cfg(feature = "smtp")]
|
#[cfg(feature = "smtp")]
|
||||||
Some(kind) if kind == BackendKind::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")]
|
#[cfg(feature = "sendmail")]
|
||||||
Some(kind) if kind == BackendKind::Sendmail => Some(sendmail::wizard::configure()?),
|
Some(kind) if kind == BackendKind::Sendmail => Some(sendmail::wizard::configure()?),
|
||||||
|
|
|
@ -191,13 +191,15 @@ mod test {
|
||||||
|
|
||||||
use crate::{account::config::TomlAccountConfig, config::TomlConfig};
|
use crate::{account::config::TomlAccountConfig, config::TomlConfig};
|
||||||
|
|
||||||
|
use super::pretty_serialize;
|
||||||
|
|
||||||
fn assert_eq(config: TomlAccountConfig, expected_toml: &str) {
|
fn assert_eq(config: TomlAccountConfig, expected_toml: &str) {
|
||||||
let config = TomlConfig {
|
let config = TomlConfig {
|
||||||
accounts: HashMap::from_iter([("test".into(), config)]),
|
accounts: HashMap::from_iter([("test".into(), config)]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let toml = super::pretty_serialize(&config).expect("serialize error");
|
let toml = pretty_serialize(&config).expect("serialize error");
|
||||||
assert_eq!(toml, expected_toml);
|
assert_eq!(toml, expected_toml);
|
||||||
|
|
||||||
let expected_config = toml::from_str(&toml).expect("deserialize error");
|
let expected_config = toml::from_str(&toml).expect("deserialize error");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Result;
|
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 dialoguer::{Confirm, Input, Password, Select};
|
||||||
use email::{
|
use email::{
|
||||||
account::config::{
|
account::config::{
|
||||||
|
@ -12,7 +12,7 @@ use oauth::v2_0::{AuthorizationCodeGrant, Client};
|
||||||
use secret::Secret;
|
use secret::Secret;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{config::BackendConfig, wizard::get_or_init_autoconfig},
|
backend::config::BackendConfig,
|
||||||
ui::{prompt, THEME},
|
ui::{prompt, THEME},
|
||||||
wizard_log, wizard_prompt,
|
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 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";
|
const CMD: &str = "Ask me a shell command that exposes my password";
|
||||||
|
|
||||||
pub(crate) async fn configure(account_name: &str, email: &str) -> Result<BackendConfig> {
|
pub(crate) async fn configure(
|
||||||
let autoconfig = get_or_init_autoconfig(email).await;
|
account_name: &str,
|
||||||
|
email: &str,
|
||||||
|
autoconfig: Option<&AutoConfig>,
|
||||||
|
) -> Result<BackendConfig> {
|
||||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||||
let autoconfig_server = autoconfig.and_then(|c| {
|
let autoconfig_server = autoconfig.and_then(|c| {
|
||||||
c.email_provider()
|
c.email_provider()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Result;
|
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 dialoguer::{Confirm, Input, Password, Select};
|
||||||
use email::{
|
use email::{
|
||||||
account::config::{
|
account::config::{
|
||||||
|
@ -12,7 +12,7 @@ use oauth::v2_0::{AuthorizationCodeGrant, Client};
|
||||||
use secret::Secret;
|
use secret::Secret;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{config::BackendConfig, wizard::get_or_init_autoconfig},
|
backend::config::BackendConfig,
|
||||||
ui::{prompt, THEME},
|
ui::{prompt, THEME},
|
||||||
wizard_log, wizard_prompt,
|
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 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";
|
const CMD: &str = "Ask me a shell command that exposes my password";
|
||||||
|
|
||||||
pub(crate) async fn configure(account_name: &str, email: &str) -> Result<BackendConfig> {
|
pub(crate) async fn configure(
|
||||||
let autoconfig = get_or_init_autoconfig(email).await;
|
account_name: &str,
|
||||||
|
email: &str,
|
||||||
|
autoconfig: Option<&AutoConfig>,
|
||||||
|
) -> Result<BackendConfig> {
|
||||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||||
let autoconfig_server = autoconfig.and_then(|c| {
|
let autoconfig_server = autoconfig.and_then(|c| {
|
||||||
c.email_provider()
|
c.email_provider()
|
||||||
|
@ -80,9 +83,9 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
.and_then(|s| s.port())
|
.and_then(|s| s.port())
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.unwrap_or_else(|| match &autoconfig_encryption {
|
.unwrap_or_else(|| match &autoconfig_encryption {
|
||||||
SmtpEncryptionKind::Tls => 993,
|
SmtpEncryptionKind::Tls => 465,
|
||||||
SmtpEncryptionKind::StartTls => 143,
|
SmtpEncryptionKind::StartTls => 587,
|
||||||
SmtpEncryptionKind::None => 143,
|
SmtpEncryptionKind::None => 25,
|
||||||
});
|
});
|
||||||
|
|
||||||
let (encryption, default_port) = match encryption_idx {
|
let (encryption, default_port) = match encryption_idx {
|
||||||
|
|
Loading…
Reference in a new issue