mirror of
https://github.com/soywod/himalaya.git
synced 2025-04-22 17:23:27 +00:00
improve imap oauth2 api, add smtp oauth2 support
This commit is contained in:
commit
54ea9a3302
9 changed files with 351 additions and 92 deletions
47
CHANGELOG.md
47
CHANGELOG.md
|
@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added keyring support, which means Himalaya can now use your
|
||||
system's global keyring to get/set sensitive data like passwords or
|
||||
tokens.
|
||||
- Added required IMAP option `imap-auth` and SMTP option
|
||||
`smtp-auth`. Possible values: `passwd`, `oauth2`.
|
||||
- Added OAuth 2.0 support for IMAP and SMTP. To use it, set `imap-auth
|
||||
= "oauth2"`. You also need these options:
|
||||
|
||||
- `imap-oauth2-method`
|
||||
- `imap-oauth2-client-id`
|
||||
- `imap-oauth2-client-secret` or `imap-oauth2-client-secret-cmd` or
|
||||
`imap-oauth2-client-secret-keyring`
|
||||
- `imap-oauth2-auth-url`
|
||||
- `imap-oauth2-token-url`
|
||||
- `imap-oauth2-access-token` or `imap-oauth2-access-token-cmd` or
|
||||
`imap-oauth2-access-token-keyring`
|
||||
- `imap-oauth2-refresh-token` or `imap-oauth2-refresh-token-cmd` or
|
||||
`imap-oauth2-refresh-token-keyring`
|
||||
- `imap-oauth2-scope` or `imap-oauth2-scopes`
|
||||
- `imap-oauth2-pkce`
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed the way secrets are managed. A secret is a sensitive data
|
||||
like passwords or tokens. There is 3 possible ways to declare a
|
||||
secret in the config file:
|
||||
|
||||
- `<key> = "secret-value"` for the raw secret (unsafe, not
|
||||
recommanded),
|
||||
- `<key>-cmd = "echo 'secret-value'"` for command that retrieve the
|
||||
secret,
|
||||
- `<key>-keyring = "keyring-entry"` for entry in your system's
|
||||
global keyring that contains the secret.
|
||||
|
||||
This applies for:
|
||||
|
||||
- `imap-passwd`
|
||||
- `imap-oauth2-client-secret`
|
||||
- `imap-oauth2-access-token`
|
||||
- `imap-oauth2-refresh-token`
|
||||
- `smtp-passwd`
|
||||
- `smtp-oauth2-client-secret`
|
||||
- `smtp-oauth2-access-token`
|
||||
- `smtp-oauth2-refresh-token`
|
||||
|
||||
## [0.7.3] - 2023-05-01
|
||||
|
||||
### Fixed
|
||||
|
|
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -1147,6 +1147,9 @@ dependencies = [
|
|||
"log",
|
||||
"once_cell",
|
||||
"pimalaya-email",
|
||||
"pimalaya-keyring",
|
||||
"pimalaya-process",
|
||||
"pimalaya-secret",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
|
@ -2098,7 +2101,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pimalaya-email"
|
||||
version = "0.7.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#22db3ec886536897c54b72d4ab7d20beff0ffecc"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#524f8049566845369f55300a20843c48dfe7a620"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chrono",
|
||||
|
@ -2107,7 +2110,6 @@ dependencies = [
|
|||
"html-escape",
|
||||
"imap",
|
||||
"imap-proto",
|
||||
"keyring",
|
||||
"lettre",
|
||||
"log",
|
||||
"mail-parser",
|
||||
|
@ -2120,6 +2122,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"ouroboros",
|
||||
"pimalaya-oauth2",
|
||||
"pimalaya-secret",
|
||||
"proc-lock",
|
||||
"rayon",
|
||||
"regex",
|
||||
|
@ -2136,10 +2139,20 @@ dependencies = [
|
|||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-keyring"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#524f8049566845369f55300a20843c48dfe7a620"
|
||||
dependencies = [
|
||||
"keyring",
|
||||
"log",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-oauth2"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#22db3ec886536897c54b72d4ab7d20beff0ffecc"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#524f8049566845369f55300a20843c48dfe7a620"
|
||||
dependencies = [
|
||||
"log",
|
||||
"oauth2",
|
||||
|
@ -2148,6 +2161,27 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-process"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#524f8049566845369f55300a20843c48dfe7a620"
|
||||
dependencies = [
|
||||
"log",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pimalaya-secret"
|
||||
version = "0.0.1"
|
||||
source = "git+https://git.sr.ht/~soywod/pimalaya#524f8049566845369f55300a20843c48dfe7a620"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pimalaya-keyring",
|
||||
"pimalaya-oauth2",
|
||||
"pimalaya-process",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -43,15 +43,22 @@ clap = "4.0"
|
|||
clap_complete = "4.0"
|
||||
clap_mangen = "0.2"
|
||||
console = "0.15.2"
|
||||
dirs = "4.0.0"
|
||||
dialoguer = "0.10.2"
|
||||
dirs = "4.0.0"
|
||||
email_address = "0.2.4"
|
||||
env_logger = "0.8"
|
||||
erased-serde = "0.3"
|
||||
pimalaya-email = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
indicatif = "0.17"
|
||||
log = "0.4"
|
||||
once_cell = "1.16.0"
|
||||
pimalaya-email = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
pimalaya-keyring = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
pimalaya-process = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
pimalaya-secret = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
# pimalaya-email = { path = "/home/soywod/sourcehut/pimalaya/email" }
|
||||
# pimalaya-keyring = { path = "/home/soywod/sourcehut/pimalaya/keyring" }
|
||||
# pimalaya-process = { path = "/home/soywod/sourcehut/pimalaya/process" }
|
||||
# pimalaya-secret = { path = "/home/soywod/sourcehut/pimalaya/secret" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
shellexpand = "2.1"
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat,
|
||||
ImapAuthConfig, MaildirConfig, OAuth2ClientSecret, OAuth2Config, OAuth2Method, OAuth2Scopes,
|
||||
SendmailConfig, SmtpConfig,
|
||||
ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
|
||||
SendmailConfig, SmtpAuthConfig, SmtpConfig,
|
||||
};
|
||||
use pimalaya_keyring::Entry;
|
||||
use pimalaya_process::Cmd;
|
||||
use pimalaya_secret::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, path::PathBuf};
|
||||
|
||||
|
@ -12,27 +15,61 @@ use pimalaya_email::ImapConfig;
|
|||
#[cfg(feature = "notmuch-backend")]
|
||||
use pimalaya_email::NotmuchConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SmtpConfig")]
|
||||
struct SmtpConfigDef {
|
||||
#[serde(rename = "smtp-host")]
|
||||
pub host: String,
|
||||
#[serde(rename = "smtp-port")]
|
||||
pub port: u16,
|
||||
#[serde(rename = "smtp-ssl")]
|
||||
pub ssl: Option<bool>,
|
||||
#[serde(rename = "smtp-starttls")]
|
||||
pub starttls: Option<bool>,
|
||||
#[serde(rename = "smtp-insecure")]
|
||||
pub insecure: Option<bool>,
|
||||
#[serde(rename = "smtp-login")]
|
||||
pub login: String,
|
||||
#[serde(rename = "smtp-passwd-cmd")]
|
||||
pub passwd_cmd: String,
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Entry", from = "String")]
|
||||
pub struct EntryDef;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Cmd", from = "String")]
|
||||
pub struct SingleCmdDef;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Cmd", from = "Vec<String>")]
|
||||
pub struct PipelineDef;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Cmd", from = "SingleCmdOrPipeline")]
|
||||
pub struct SingleCmdOrPipelineDef;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SingleCmdOrPipeline {
|
||||
#[serde(with = "SingleCmdDef")]
|
||||
SingleCmd(Cmd),
|
||||
#[serde(with = "PipelineDef")]
|
||||
Pipeline(Cmd),
|
||||
}
|
||||
|
||||
impl From<SingleCmdOrPipeline> for Cmd {
|
||||
fn from(cmd: SingleCmdOrPipeline) -> Cmd {
|
||||
match cmd {
|
||||
SingleCmdOrPipeline::SingleCmd(cmd) => cmd,
|
||||
SingleCmdOrPipeline::Pipeline(cmd) => cmd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "Secret", rename_all = "kebab-case")]
|
||||
pub enum SecretDef {
|
||||
Raw(String),
|
||||
#[serde(with = "SingleCmdOrPipelineDef")]
|
||||
Cmd(Cmd),
|
||||
#[serde(with = "EntryDef")]
|
||||
Keyring(Entry),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Method")]
|
||||
pub enum OAuth2MethodDef {
|
||||
#[serde(rename = "xoauth2", alias = "XOAUTH2")]
|
||||
XOAuth2,
|
||||
#[serde(rename = "oauthbearer", alias = "OAUTHBEARER")]
|
||||
OAuthBearer,
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "ImapConfig")]
|
||||
pub struct ImapConfigDef {
|
||||
#[serde(rename = "imap-host")]
|
||||
|
@ -47,7 +84,7 @@ pub struct ImapConfigDef {
|
|||
pub insecure: Option<bool>,
|
||||
#[serde(rename = "imap-login")]
|
||||
pub login: String,
|
||||
#[serde(rename = "imap-auth", with = "ImapAuthConfigDef")]
|
||||
#[serde(flatten, with = "ImapAuthConfigDef")]
|
||||
pub auth: ImapAuthConfig,
|
||||
#[serde(rename = "imap-notify-cmd")]
|
||||
pub notify_cmd: Option<String>,
|
||||
|
@ -58,51 +95,50 @@ pub struct ImapConfigDef {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "ImapAuthConfig", rename_all = "kebab-case")]
|
||||
#[serde(remote = "ImapAuthConfig", tag = "imap-auth")]
|
||||
pub enum ImapAuthConfigDef {
|
||||
#[serde(skip)]
|
||||
None,
|
||||
RawPasswd(String),
|
||||
PasswdCmd(String),
|
||||
#[serde(with = "OAuth2ConfigDef", rename = "oauth2")]
|
||||
#[serde(rename = "passwd", alias = "password", with = "ImapPasswdConfigDef")]
|
||||
Passwd(#[serde(default)] PasswdConfig),
|
||||
#[serde(rename = "oauth2", with = "ImapOAuth2ConfigDef")]
|
||||
OAuth2(OAuth2Config),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Config", rename_all = "kebab-case")]
|
||||
pub struct OAuth2ConfigDef {
|
||||
#[serde(with = "OAuth2MethodDef")]
|
||||
#[serde(remote = "PasswdConfig")]
|
||||
pub struct ImapPasswdConfigDef {
|
||||
#[serde(rename = "imap-passwd", with = "SecretDef", default)]
|
||||
pub passwd: Secret,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Config")]
|
||||
pub struct ImapOAuth2ConfigDef {
|
||||
#[serde(rename = "imap-oauth2-method", with = "OAuth2MethodDef", default)]
|
||||
pub method: OAuth2Method,
|
||||
#[serde(rename = "imap-oauth2-client-id")]
|
||||
pub client_id: String,
|
||||
#[serde(with = "OAuth2ClientSecretDef")]
|
||||
pub client_secret: OAuth2ClientSecret,
|
||||
#[serde(rename = "imap-oauth2-client-secret", with = "SecretDef", default)]
|
||||
pub client_secret: Secret,
|
||||
#[serde(rename = "imap-oauth2-auth-url")]
|
||||
pub auth_url: String,
|
||||
#[serde(rename = "imap-oauth2-token-url")]
|
||||
pub token_url: String,
|
||||
#[serde(flatten, with = "OAuth2ScopesDef")]
|
||||
#[serde(rename = "imap-oauth2-access-token", with = "SecretDef", default)]
|
||||
pub access_token: Secret,
|
||||
#[serde(rename = "imap-oauth2-refresh-token", with = "SecretDef", default)]
|
||||
pub refresh_token: Secret,
|
||||
#[serde(flatten, with = "ImapOAuth2ScopesDef")]
|
||||
pub scopes: OAuth2Scopes,
|
||||
#[serde(default)]
|
||||
#[serde(rename = "imap-oauth2-pkce", default)]
|
||||
pub pkce: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2ClientSecret", rename_all = "kebab-case")]
|
||||
pub enum OAuth2ClientSecretDef {
|
||||
Raw(String),
|
||||
Cmd(String),
|
||||
Keyring,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Method", rename_all = "lowercase")]
|
||||
pub enum OAuth2MethodDef {
|
||||
XOAuth2,
|
||||
OAuthBearer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Scopes", rename_all = "kebab-case")]
|
||||
pub enum OAuth2ScopesDef {
|
||||
#[serde(remote = "OAuth2Scopes")]
|
||||
pub enum ImapOAuth2ScopesDef {
|
||||
#[serde(rename = "imap-oauth2-scope")]
|
||||
Scope(String),
|
||||
#[serde(rename = "imap-oauth2-scopes")]
|
||||
Scopes(Vec<String>),
|
||||
}
|
||||
|
||||
|
@ -146,6 +182,73 @@ pub enum EmailSenderDef {
|
|||
Sendmail(SendmailConfig),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SmtpConfig")]
|
||||
struct SmtpConfigDef {
|
||||
#[serde(rename = "smtp-host")]
|
||||
pub host: String,
|
||||
#[serde(rename = "smtp-port")]
|
||||
pub port: u16,
|
||||
#[serde(rename = "smtp-ssl")]
|
||||
pub ssl: Option<bool>,
|
||||
#[serde(rename = "smtp-starttls")]
|
||||
pub starttls: Option<bool>,
|
||||
#[serde(rename = "smtp-insecure")]
|
||||
pub insecure: Option<bool>,
|
||||
#[serde(rename = "smtp-login")]
|
||||
pub login: String,
|
||||
#[serde(flatten, with = "SmtpAuthConfigDef")]
|
||||
pub auth: SmtpAuthConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SmtpAuthConfig", tag = "smtp-auth")]
|
||||
pub enum SmtpAuthConfigDef {
|
||||
#[serde(rename = "passwd", alias = "password", with = "SmtpPasswdConfigDef")]
|
||||
Passwd(#[serde(default)] PasswdConfig),
|
||||
#[serde(rename = "oauth2", with = "SmtpOAuth2ConfigDef")]
|
||||
OAuth2(OAuth2Config),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "PasswdConfig", default)]
|
||||
pub struct SmtpPasswdConfigDef {
|
||||
#[serde(rename = "smtp-passwd", with = "SecretDef", default)]
|
||||
pub passwd: Secret,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Config")]
|
||||
pub struct SmtpOAuth2ConfigDef {
|
||||
#[serde(rename = "smtp-oauth2-method", with = "OAuth2MethodDef", default)]
|
||||
pub method: OAuth2Method,
|
||||
#[serde(rename = "smtp-oauth2-client-id")]
|
||||
pub client_id: String,
|
||||
#[serde(rename = "smtp-oauth2-client-secret", with = "SecretDef", default)]
|
||||
pub client_secret: Secret,
|
||||
#[serde(rename = "smtp-oauth2-auth-url")]
|
||||
pub auth_url: String,
|
||||
#[serde(rename = "smtp-oauth2-token-url")]
|
||||
pub token_url: String,
|
||||
#[serde(rename = "smtp-oauth2-access-token", with = "SecretDef", default)]
|
||||
pub access_token: Secret,
|
||||
#[serde(rename = "smtp-oauth2-refresh-token", with = "SecretDef", default)]
|
||||
pub refresh_token: Secret,
|
||||
#[serde(flatten, with = "SmtpOAuth2ScopesDef")]
|
||||
pub scopes: OAuth2Scopes,
|
||||
#[serde(rename = "smtp-oauth2-pkce", default)]
|
||||
pub pkce: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "OAuth2Scopes")]
|
||||
pub enum SmtpOAuth2ScopesDef {
|
||||
#[serde(rename = "smtp-oauth2-scope")]
|
||||
Scope(String),
|
||||
#[serde(rename = "smtp-oauth2-scopes")]
|
||||
Scopes(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
||||
pub struct SendmailConfigDef {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use pimalaya_email::ImapConfig;
|
||||
use std::io;
|
||||
|
||||
use crate::account::{
|
||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
|
||||
|
@ -59,11 +58,3 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
|||
DeserializedImapAccountConfig { base, backend },
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
pub(crate) fn configure_oauth2_client_secret() -> io::Result<String> {
|
||||
Input::with_theme(&*THEME)
|
||||
.with_prompt("Enter your OAuth 2.0 client secret:")
|
||||
.report(false)
|
||||
.interact()
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ use super::DeserializedConfig;
|
|||
use crate::account::{DeserializedAccountConfig, DeserializedBaseAccountConfig};
|
||||
use anyhow::{anyhow, Result};
|
||||
use console::style;
|
||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
|
||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select};
|
||||
use log::trace;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{fs, process};
|
||||
use std::{fs, io, process};
|
||||
|
||||
const BACKENDS: &[&str] = &[
|
||||
"Maildir",
|
||||
|
@ -163,3 +163,20 @@ fn configure_base() -> Result<DeserializedBaseAccountConfig> {
|
|||
|
||||
Ok(base_account_config)
|
||||
}
|
||||
|
||||
pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {
|
||||
Password::with_theme(&*THEME)
|
||||
.with_prompt(prompt)
|
||||
.with_confirmation(
|
||||
"Confirm password:",
|
||||
"Passwords do not match, please try again.",
|
||||
)
|
||||
.interact()
|
||||
}
|
||||
|
||||
pub(crate) fn prompt_secret(prompt: &str) -> io::Result<String> {
|
||||
Input::with_theme(&*THEME)
|
||||
.with_prompt(prompt)
|
||||
.report(false)
|
||||
.interact()
|
||||
}
|
||||
|
|
|
@ -42,10 +42,11 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
|
|||
.default(base.email.clone())
|
||||
.interact()?;
|
||||
|
||||
smtp_config.passwd_cmd = Input::with_theme(&*THEME)
|
||||
.with_prompt("What shell command should we run to get your password?")
|
||||
.default(format!("pass show {}", &base.email))
|
||||
.interact()?;
|
||||
// FIXME: add all variants: password, password command and oauth2
|
||||
// smtp_config.auth = Input::with_theme(&*THEME)
|
||||
// .with_prompt("What shell command should we run to get your password?")
|
||||
// .default(format!("pass show {}", &base.email))
|
||||
// .interact()?;
|
||||
|
||||
Ok(EmailSender::Smtp(smtp_config))
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
||||
EmailSender, EmailTextPlainFormat, MaildirConfig,
|
||||
EmailSender, EmailTextPlainFormat, ImapAuthConfig, MaildirConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
@ -46,10 +46,29 @@ impl DeserializedAccountConfig {
|
|||
BackendConfig::Maildir(config.backend.clone()),
|
||||
),
|
||||
#[cfg(feature = "imap-backend")]
|
||||
DeserializedAccountConfig::Imap(config) => (
|
||||
config.base.to_account_config(name, global_config),
|
||||
BackendConfig::Imap(config.backend.clone()),
|
||||
),
|
||||
DeserializedAccountConfig::Imap(config) => {
|
||||
let mut imap_config = config.backend.clone();
|
||||
|
||||
match &mut imap_config.auth {
|
||||
ImapAuthConfig::Passwd(secret) => {
|
||||
secret.replace_undefined_entry_with(format!("{name}-imap-passwd"));
|
||||
}
|
||||
ImapAuthConfig::OAuth2(config) => {
|
||||
config.client_secret.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-client-secret"
|
||||
));
|
||||
config.access_token.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-access-token"
|
||||
));
|
||||
config.refresh_token.replace_undefined_entry_with(format!(
|
||||
"{name}-imap-oauth2-refresh-token"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let account_config = config.base.to_account_config(name, global_config);
|
||||
(account_config, BackendConfig::Imap(imap_config))
|
||||
}
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
DeserializedAccountConfig::Notmuch(config) => (
|
||||
config.base.to_account_config(name, global_config),
|
||||
|
|
|
@ -4,14 +4,17 @@
|
|||
|
||||
use anyhow::Result;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use log::{info, trace};
|
||||
use log::{info, trace, warn};
|
||||
use pimalaya_email::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
|
||||
BackendSyncBuilder, BackendSyncProgressEvent,
|
||||
BackendSyncBuilder, BackendSyncProgressEvent, EmailSender, ImapAuthConfig, SmtpAuthConfig,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::{wizard::imap::configure_oauth2_client_secret, DeserializedConfig},
|
||||
config::{
|
||||
wizard::{prompt_passwd, prompt_secret},
|
||||
DeserializedConfig,
|
||||
},
|
||||
printer::{PrintTableOpts, Printer},
|
||||
Accounts,
|
||||
};
|
||||
|
@ -23,21 +26,58 @@ pub fn configure(
|
|||
reset: bool,
|
||||
) -> Result<()> {
|
||||
info!("entering the configure account handler");
|
||||
match backend_config {
|
||||
BackendConfig::None => (),
|
||||
BackendConfig::Maildir(_) => (),
|
||||
|
||||
if reset {
|
||||
#[cfg(feature = "imap-backend")]
|
||||
BackendConfig::Imap(imap_config) => {
|
||||
imap_config.auth.configure(
|
||||
&account_config.name,
|
||||
reset,
|
||||
configure_oauth2_client_secret,
|
||||
)?;
|
||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
||||
let reset = match &imap_config.auth {
|
||||
ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
warn!("error while resetting imap secrets, skipping it");
|
||||
warn!("{err}");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
BackendConfig::Notmuch(config) => (),
|
||||
};
|
||||
println!("Account {} configured!", account_config.name);
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
||||
let reset = match &smtp_config.auth {
|
||||
SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
warn!("error while resetting smtp secrets, skipping it");
|
||||
warn!("{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
||||
match &imap_config.auth {
|
||||
ImapAuthConfig::Passwd(passwd) => {
|
||||
passwd.configure(|| prompt_passwd("Enter your IMAP password:"))
|
||||
}
|
||||
ImapAuthConfig::OAuth2(oauth2) => {
|
||||
oauth2.configure(|| prompt_secret("Enter your IMAP OAuth 2.0 client secret:"))
|
||||
}
|
||||
}?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "smtp-sender")]
|
||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
||||
match &smtp_config.auth {
|
||||
SmtpAuthConfig::Passwd(passwd) => {
|
||||
passwd.configure(|| prompt_passwd("Enter your SMTP password:"))
|
||||
}
|
||||
SmtpAuthConfig::OAuth2(oauth2) => {
|
||||
oauth2.configure(|| prompt_secret("Enter your SMTP OAuth 2.0 client secret:"))
|
||||
}
|
||||
}?;
|
||||
}
|
||||
|
||||
println!("Account successfully configured!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue