mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-25 04:20:22 +00:00
drastically simplified configs
Also started to refactor wizard (WIP).
This commit is contained in:
parent
0ff77b5179
commit
d814ae904a
16 changed files with 458 additions and 416 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -1630,23 +1630,6 @@ version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mime-msg-builder"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a3981dce6db3e7f9faa1124409a6b94436902ecb2670f374d361789d61eb34ac"
|
|
||||||
dependencies = [
|
|
||||||
"ammonia",
|
|
||||||
"chumsky 0.9.0",
|
|
||||||
"html-escape",
|
|
||||||
"lettre",
|
|
||||||
"log",
|
|
||||||
"regex",
|
|
||||||
"shellexpand",
|
|
||||||
"thiserror",
|
|
||||||
"tree_magic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -2068,7 +2051,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pimalaya-email"
|
name = "pimalaya-email"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"advisory-lock",
|
"advisory-lock",
|
||||||
"ammonia",
|
"ammonia",
|
||||||
|
@ -2084,11 +2067,11 @@ dependencies = [
|
||||||
"maildir",
|
"maildir",
|
||||||
"mailparse",
|
"mailparse",
|
||||||
"md5",
|
"md5",
|
||||||
"mime-msg-builder",
|
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"notmuch",
|
"notmuch",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ouroboros",
|
"ouroboros",
|
||||||
|
"pimalaya-email-tpl",
|
||||||
"pimalaya-oauth2",
|
"pimalaya-oauth2",
|
||||||
"pimalaya-process",
|
"pimalaya-process",
|
||||||
"pimalaya-secret",
|
"pimalaya-secret",
|
||||||
|
@ -2107,10 +2090,27 @@ dependencies = [
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pimalaya-email-tpl"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
|
dependencies = [
|
||||||
|
"ammonia",
|
||||||
|
"chumsky 0.9.0",
|
||||||
|
"html-escape",
|
||||||
|
"lettre",
|
||||||
|
"log",
|
||||||
|
"pimalaya-process",
|
||||||
|
"regex",
|
||||||
|
"shellexpand",
|
||||||
|
"thiserror",
|
||||||
|
"tree_magic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pimalaya-keyring"
|
name = "pimalaya-keyring"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyring",
|
"keyring",
|
||||||
"log",
|
"log",
|
||||||
|
@ -2120,7 +2120,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pimalaya-oauth2"
|
name = "pimalaya-oauth2"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"oauth2",
|
"oauth2",
|
||||||
|
@ -2132,7 +2132,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pimalaya-process"
|
name = "pimalaya-process"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -2141,7 +2141,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pimalaya-secret"
|
name = "pimalaya-secret"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://git.sr.ht/~soywod/pimalaya#715546fabf7246a7f4bff8371aff77f116ae9b9a"
|
source = "git+https://git.sr.ht/~soywod/pimalaya#05818504b399a911b88255b7d15592ee834bfbb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"pimalaya-keyring",
|
"pimalaya-keyring",
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use dirs::{config_dir, home_dir};
|
use dirs::{config_dir, home_dir};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use pimalaya_email::{AccountConfig, BackendConfig, EmailHooks, EmailTextPlainFormat};
|
use pimalaya_email::{AccountConfig, EmailHooks, EmailTextPlainFormat};
|
||||||
|
use pimalaya_process::Cmd;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, fs, path::PathBuf};
|
use std::{collections::HashMap, fs, path::PathBuf};
|
||||||
use toml;
|
use toml;
|
||||||
|
@ -33,11 +34,32 @@ pub struct DeserializedConfig {
|
||||||
pub email_reading_headers: Option<Vec<String>>,
|
pub email_reading_headers: Option<Vec<String>>,
|
||||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||||
pub email_reading_format: EmailTextPlainFormat,
|
pub email_reading_format: EmailTextPlainFormat,
|
||||||
pub email_reading_verify_cmd: Option<String>,
|
#[serde(
|
||||||
pub email_reading_decrypt_cmd: Option<String>,
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_reading_verify_cmd: Option<Cmd>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||||
pub email_writing_headers: Option<Vec<String>>,
|
pub email_writing_headers: Option<Vec<String>>,
|
||||||
pub email_writing_sign_cmd: Option<String>,
|
#[serde(
|
||||||
pub email_writing_encrypt_cmd: Option<String>,
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_writing_sign_cmd: Option<Cmd>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||||
|
pub email_sending_save_copy: Option<bool>,
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
with = "EmailHooksDef",
|
with = "EmailHooksDef",
|
||||||
|
@ -54,13 +76,15 @@ impl DeserializedConfig {
|
||||||
pub fn from_opt_path(path: Option<&str>) -> Result<Self> {
|
pub fn from_opt_path(path: Option<&str>) -> Result<Self> {
|
||||||
debug!("path: {:?}", path);
|
debug!("path: {:?}", path);
|
||||||
|
|
||||||
let config: Self = match path.map(|s| s.into()).or_else(Self::path) {
|
// let config: Self = match path.map(|s| s.into()).or_else(Self::path) {
|
||||||
Some(path) => {
|
// Some(path) => {
|
||||||
let content = fs::read_to_string(path).context("cannot read config file")?;
|
// let content = fs::read_to_string(path).context("cannot read config file")?;
|
||||||
toml::from_str(&content).context("cannot parse config file")?
|
// toml::from_str(&content).context("cannot parse config file")?
|
||||||
}
|
// }
|
||||||
None => wizard()?,
|
// None => wizard()?,
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
let config = wizard()?;
|
||||||
|
|
||||||
if config.accounts.is_empty() {
|
if config.accounts.is_empty() {
|
||||||
return Err(anyhow!("config file must contain at least one account"));
|
return Err(anyhow!("config file must contain at least one account"));
|
||||||
|
@ -90,17 +114,16 @@ impl DeserializedConfig {
|
||||||
.filter(|p| p.exists())
|
.filter(|p| p.exists())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_configs(&self, account_name: Option<&str>) -> Result<(AccountConfig, BackendConfig)> {
|
pub fn to_account_config(&self, account_name: Option<&str>) -> Result<AccountConfig> {
|
||||||
let (account_name, deserialized_account_config) = match account_name {
|
let (account_name, deserialized_account_config) = match account_name {
|
||||||
Some("default") | Some("") | None => self
|
Some("default") | Some("") | None => self
|
||||||
.accounts
|
.accounts
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|(name, account)| {
|
.find_map(|(name, account)| {
|
||||||
if account.is_default() {
|
account
|
||||||
Some((name.clone(), account))
|
.default
|
||||||
} else {
|
.filter(|default| *default == true)
|
||||||
None
|
.map(|_| (name.clone(), account))
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||||
Some(name) => self
|
Some(name) => self
|
||||||
|
@ -110,16 +133,15 @@ impl DeserializedConfig {
|
||||||
.ok_or_else(|| anyhow!(format!("cannot find account {}", name))),
|
.ok_or_else(|| anyhow!(format!("cannot find account {}", name))),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let (account_config, backend_config) =
|
Ok(deserialized_account_config.to_account_config(account_name, self))
|
||||||
deserialized_account_config.to_configs(account_name, self);
|
|
||||||
|
|
||||||
Ok((account_config, backend_config))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pimalaya_email::{EmailSender, MaildirConfig, PasswdConfig, SendmailConfig};
|
use pimalaya_email::{
|
||||||
|
BackendConfig, MaildirConfig, PasswdConfig, SenderConfig, SendmailConfig,
|
||||||
|
};
|
||||||
use pimalaya_secret::Secret;
|
use pimalaya_secret::Secret;
|
||||||
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
#[cfg(feature = "notmuch-backend")]
|
||||||
|
@ -132,14 +154,6 @@ mod tests {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
use crate::account::DeserializedBaseAccountConfig;
|
|
||||||
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
use crate::account::DeserializedImapAccountConfig;
|
|
||||||
use crate::account::DeserializedMaildirAccountConfig;
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
use crate::account::DeserializedNotmuchAccountConfig;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn make_config(config: &str) -> Result<DeserializedConfig> {
|
fn make_config(config: &str) -> Result<DeserializedConfig> {
|
||||||
|
@ -159,9 +173,23 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn account_missing_backend_field() {
|
fn account_missing_email_field() {
|
||||||
let config = make_config("[account]");
|
let config = make_config("[account]");
|
||||||
|
|
||||||
|
assert!(config
|
||||||
|
.unwrap_err()
|
||||||
|
.root_cause()
|
||||||
|
.to_string()
|
||||||
|
.contains("missing field `email`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn account_missing_backend_field() {
|
||||||
|
let config = make_config(
|
||||||
|
"[account]
|
||||||
|
email = \"test@localhost\"",
|
||||||
|
);
|
||||||
|
|
||||||
assert!(config
|
assert!(config
|
||||||
.unwrap_err()
|
.unwrap_err()
|
||||||
.root_cause()
|
.root_cause()
|
||||||
|
@ -173,6 +201,7 @@ mod tests {
|
||||||
fn account_invalid_backend_field() {
|
fn account_invalid_backend_field() {
|
||||||
let config = make_config(
|
let config = make_config(
|
||||||
"[account]
|
"[account]
|
||||||
|
email = \"test@localhost\"
|
||||||
backend = \"bad\"",
|
backend = \"bad\"",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -183,20 +212,6 @@ mod tests {
|
||||||
.contains("unknown variant `bad`"));
|
.contains("unknown variant `bad`"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn account_missing_email_field() {
|
|
||||||
let config = make_config(
|
|
||||||
"[account]
|
|
||||||
backend = \"none\"",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(config
|
|
||||||
.unwrap_err()
|
|
||||||
.root_cause()
|
|
||||||
.to_string()
|
|
||||||
.contains("missing field `email`"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn imap_account_missing_host_field() {
|
fn imap_account_missing_host_field() {
|
||||||
let config = make_config(
|
let config = make_config(
|
||||||
|
@ -420,6 +435,8 @@ mod tests {
|
||||||
#[cfg(feature = "smtp-sender")]
|
#[cfg(feature = "smtp-sender")]
|
||||||
#[test]
|
#[test]
|
||||||
fn account_smtp_sender_minimum_config() {
|
fn account_smtp_sender_minimum_config() {
|
||||||
|
use pimalaya_email::SenderConfig;
|
||||||
|
|
||||||
let config = make_config(
|
let config = make_config(
|
||||||
"[account]
|
"[account]
|
||||||
email = \"test@localhost\"
|
email = \"test@localhost\"
|
||||||
|
@ -437,9 +454,9 @@ mod tests {
|
||||||
DeserializedConfig {
|
DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account".into(),
|
"account".into(),
|
||||||
DeserializedAccountConfig::None(DeserializedBaseAccountConfig {
|
DeserializedAccountConfig {
|
||||||
email: "test@localhost".into(),
|
email: "test@localhost".into(),
|
||||||
email_sender: EmailSender::Smtp(SmtpConfig {
|
sender: SenderConfig::Smtp(SmtpConfig {
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 25,
|
port: 25,
|
||||||
login: "login".into(),
|
login: "login".into(),
|
||||||
|
@ -448,8 +465,8 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
..SmtpConfig::default()
|
..SmtpConfig::default()
|
||||||
}),
|
}),
|
||||||
..DeserializedBaseAccountConfig::default()
|
..DeserializedAccountConfig::default()
|
||||||
})
|
}
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
}
|
}
|
||||||
|
@ -471,13 +488,13 @@ mod tests {
|
||||||
DeserializedConfig {
|
DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account".into(),
|
"account".into(),
|
||||||
DeserializedAccountConfig::None(DeserializedBaseAccountConfig {
|
DeserializedAccountConfig {
|
||||||
email: "test@localhost".into(),
|
email: "test@localhost".into(),
|
||||||
email_sender: EmailSender::Sendmail(SendmailConfig {
|
sender: SenderConfig::Sendmail(SendmailConfig {
|
||||||
cmd: "echo send".into(),
|
cmd: Cmd::from("echo send")
|
||||||
}),
|
}),
|
||||||
..DeserializedBaseAccountConfig::default()
|
..DeserializedAccountConfig::default()
|
||||||
})
|
}
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
}
|
}
|
||||||
|
@ -503,12 +520,9 @@ mod tests {
|
||||||
DeserializedConfig {
|
DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account".into(),
|
"account".into(),
|
||||||
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig {
|
DeserializedAccountConfig {
|
||||||
base: DeserializedBaseAccountConfig {
|
email: "test@localhost".into(),
|
||||||
email: "test@localhost".into(),
|
backend: BackendConfig::Imap(ImapConfig {
|
||||||
..DeserializedBaseAccountConfig::default()
|
|
||||||
},
|
|
||||||
backend: ImapConfig {
|
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 993,
|
port: 993,
|
||||||
login: "login".into(),
|
login: "login".into(),
|
||||||
|
@ -516,8 +530,9 @@ mod tests {
|
||||||
passwd: Secret::new_cmd(String::from("echo password"))
|
passwd: Secret::new_cmd(String::from("echo password"))
|
||||||
}),
|
}),
|
||||||
..ImapConfig::default()
|
..ImapConfig::default()
|
||||||
}
|
}),
|
||||||
})
|
..DeserializedAccountConfig::default()
|
||||||
|
}
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
}
|
}
|
||||||
|
@ -538,15 +553,13 @@ mod tests {
|
||||||
DeserializedConfig {
|
DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account".into(),
|
"account".into(),
|
||||||
DeserializedAccountConfig::Maildir(DeserializedMaildirAccountConfig {
|
DeserializedAccountConfig {
|
||||||
base: DeserializedBaseAccountConfig {
|
email: "test@localhost".into(),
|
||||||
email: "test@localhost".into(),
|
backend: BackendConfig::Maildir(MaildirConfig {
|
||||||
..DeserializedBaseAccountConfig::default()
|
|
||||||
},
|
|
||||||
backend: MaildirConfig {
|
|
||||||
root_dir: "/tmp/maildir".into(),
|
root_dir: "/tmp/maildir".into(),
|
||||||
}
|
}),
|
||||||
})
|
..DeserializedAccountConfig::default()
|
||||||
|
}
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
}
|
}
|
||||||
|
@ -569,15 +582,13 @@ mod tests {
|
||||||
DeserializedConfig {
|
DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account".into(),
|
"account".into(),
|
||||||
DeserializedAccountConfig::Notmuch(DeserializedNotmuchAccountConfig {
|
DeserializedAccountConfig {
|
||||||
base: DeserializedBaseAccountConfig {
|
email: "test@localhost".into(),
|
||||||
email: "test@localhost".into(),
|
backend: BackendConfig::Notmuch(NotmuchConfig {
|
||||||
..DeserializedBaseAccountConfig::default()
|
|
||||||
},
|
|
||||||
backend: NotmuchConfig {
|
|
||||||
db_path: "/tmp/notmuch.db".into(),
|
db_path: "/tmp/notmuch.db".into(),
|
||||||
}
|
}),
|
||||||
})
|
..DeserializedAccountConfig::default()
|
||||||
|
}
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use pimalaya_email::{
|
use pimalaya_email::{
|
||||||
folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat,
|
folder::sync::Strategy as SyncFoldersStrategy, BackendConfig, EmailHooks, EmailTextPlainFormat,
|
||||||
ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
|
ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig,
|
||||||
SendmailConfig, SmtpAuthConfig, SmtpConfig,
|
SenderConfig, SendmailConfig, SmtpAuthConfig, SmtpConfig,
|
||||||
};
|
};
|
||||||
use pimalaya_keyring::Entry;
|
use pimalaya_keyring::Entry;
|
||||||
use pimalaya_process::Cmd;
|
use pimalaya_process::Cmd;
|
||||||
|
@ -29,7 +29,7 @@ pub struct PipelineDef;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "Cmd", from = "SingleCmdOrPipeline")]
|
#[serde(remote = "Cmd", from = "SingleCmdOrPipeline")]
|
||||||
pub struct SingleCmdOrPipelineDef;
|
pub struct CmdDef;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
@ -49,11 +49,36 @@ impl From<SingleCmdOrPipeline> for Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(remote = "Option<Cmd>", from = "OptionSingleCmdOrPipeline")]
|
||||||
|
pub struct OptionCmdDef;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum OptionSingleCmdOrPipeline {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
#[serde(with = "SingleCmdDef")]
|
||||||
|
SingleCmd(Cmd),
|
||||||
|
#[serde(with = "PipelineDef")]
|
||||||
|
Pipeline(Cmd),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OptionSingleCmdOrPipeline> for Option<Cmd> {
|
||||||
|
fn from(cmd: OptionSingleCmdOrPipeline) -> Option<Cmd> {
|
||||||
|
match cmd {
|
||||||
|
OptionSingleCmdOrPipeline::None => None,
|
||||||
|
OptionSingleCmdOrPipeline::SingleCmd(cmd) => Some(cmd),
|
||||||
|
OptionSingleCmdOrPipeline::Pipeline(cmd) => Some(cmd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "Secret", rename_all = "kebab-case")]
|
#[serde(remote = "Secret", rename_all = "kebab-case")]
|
||||||
pub enum SecretDef {
|
pub enum SecretDef {
|
||||||
Raw(String),
|
Raw(String),
|
||||||
#[serde(with = "SingleCmdOrPipelineDef")]
|
#[serde(with = "CmdDef")]
|
||||||
Cmd(Cmd),
|
Cmd(Cmd),
|
||||||
#[serde(with = "EntryDef")]
|
#[serde(with = "EntryDef")]
|
||||||
Keyring(Entry),
|
Keyring(Entry),
|
||||||
|
@ -68,6 +93,21 @@ pub enum OAuth2MethodDef {
|
||||||
OAuthBearer,
|
OAuthBearer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(remote = "BackendConfig", tag = "backend", rename_all = "kebab-case")]
|
||||||
|
pub enum BackendConfigDef {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
#[cfg(feature = "imap-backend")]
|
||||||
|
#[serde(with = "ImapConfigDef")]
|
||||||
|
Imap(ImapConfig),
|
||||||
|
#[serde(with = "MaildirConfigDef")]
|
||||||
|
Maildir(MaildirConfig),
|
||||||
|
#[cfg(feature = "notmuch-backend")]
|
||||||
|
#[serde(with = "NotmuchConfigDef")]
|
||||||
|
Notmuch(NotmuchConfig),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "ImapConfig")]
|
#[serde(remote = "ImapConfig")]
|
||||||
|
@ -106,7 +146,12 @@ pub enum ImapAuthConfigDef {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "PasswdConfig")]
|
#[serde(remote = "PasswdConfig")]
|
||||||
pub struct ImapPasswdConfigDef {
|
pub struct ImapPasswdConfigDef {
|
||||||
#[serde(rename = "imap-passwd", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "imap-passwd",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub passwd: Secret,
|
pub passwd: Secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,15 +162,30 @@ pub struct ImapOAuth2ConfigDef {
|
||||||
pub method: OAuth2Method,
|
pub method: OAuth2Method,
|
||||||
#[serde(rename = "imap-oauth2-client-id")]
|
#[serde(rename = "imap-oauth2-client-id")]
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
#[serde(rename = "imap-oauth2-client-secret", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "imap-oauth2-client-secret",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub client_secret: Secret,
|
pub client_secret: Secret,
|
||||||
#[serde(rename = "imap-oauth2-auth-url")]
|
#[serde(rename = "imap-oauth2-auth-url")]
|
||||||
pub auth_url: String,
|
pub auth_url: String,
|
||||||
#[serde(rename = "imap-oauth2-token-url")]
|
#[serde(rename = "imap-oauth2-token-url")]
|
||||||
pub token_url: String,
|
pub token_url: String,
|
||||||
#[serde(rename = "imap-oauth2-access-token", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "imap-oauth2-access-token",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub access_token: Secret,
|
pub access_token: Secret,
|
||||||
#[serde(rename = "imap-oauth2-refresh-token", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "imap-oauth2-refresh-token",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub refresh_token: Secret,
|
pub refresh_token: Secret,
|
||||||
#[serde(flatten, with = "ImapOAuth2ScopesDef")]
|
#[serde(flatten, with = "ImapOAuth2ScopesDef")]
|
||||||
pub scopes: OAuth2Scopes,
|
pub scopes: OAuth2Scopes,
|
||||||
|
@ -172,8 +232,8 @@ pub enum EmailTextPlainFormatDef {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "EmailSender", tag = "sender", rename_all = "kebab-case")]
|
#[serde(remote = "SenderConfig", tag = "sender", rename_all = "kebab-case")]
|
||||||
pub enum EmailSenderDef {
|
pub enum SenderConfigDef {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
#[serde(with = "SmtpConfigDef")]
|
#[serde(with = "SmtpConfigDef")]
|
||||||
|
@ -213,7 +273,12 @@ pub enum SmtpAuthConfigDef {
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "PasswdConfig", default)]
|
#[serde(remote = "PasswdConfig", default)]
|
||||||
pub struct SmtpPasswdConfigDef {
|
pub struct SmtpPasswdConfigDef {
|
||||||
#[serde(rename = "smtp-passwd", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "smtp-passwd",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub passwd: Secret,
|
pub passwd: Secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,15 +289,30 @@ pub struct SmtpOAuth2ConfigDef {
|
||||||
pub method: OAuth2Method,
|
pub method: OAuth2Method,
|
||||||
#[serde(rename = "smtp-oauth2-client-id")]
|
#[serde(rename = "smtp-oauth2-client-id")]
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
#[serde(rename = "smtp-oauth2-client-secret", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "smtp-oauth2-client-secret",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub client_secret: Secret,
|
pub client_secret: Secret,
|
||||||
#[serde(rename = "smtp-oauth2-auth-url")]
|
#[serde(rename = "smtp-oauth2-auth-url")]
|
||||||
pub auth_url: String,
|
pub auth_url: String,
|
||||||
#[serde(rename = "smtp-oauth2-token-url")]
|
#[serde(rename = "smtp-oauth2-token-url")]
|
||||||
pub token_url: String,
|
pub token_url: String,
|
||||||
#[serde(rename = "smtp-oauth2-access-token", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "smtp-oauth2-access-token",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub access_token: Secret,
|
pub access_token: Secret,
|
||||||
#[serde(rename = "smtp-oauth2-refresh-token", with = "SecretDef", default)]
|
#[serde(
|
||||||
|
rename = "smtp-oauth2-refresh-token",
|
||||||
|
with = "SecretDef",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Secret::is_undefined_entry"
|
||||||
|
)]
|
||||||
pub refresh_token: Secret,
|
pub refresh_token: Secret,
|
||||||
#[serde(flatten, with = "SmtpOAuth2ScopesDef")]
|
#[serde(flatten, with = "SmtpOAuth2ScopesDef")]
|
||||||
pub scopes: OAuth2Scopes,
|
pub scopes: OAuth2Scopes,
|
||||||
|
@ -249,11 +329,11 @@ pub enum SmtpOAuth2ScopesDef {
|
||||||
Scopes(Vec<String>),
|
Scopes(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
|
||||||
pub struct SendmailConfigDef {
|
pub struct SendmailConfigDef {
|
||||||
#[serde(rename = "sendmail-cmd")]
|
#[serde(rename = "sendmail-cmd", with = "CmdDef")]
|
||||||
cmd: String,
|
cmd: Cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the email hooks. Useful for doing extra email
|
/// Represents the email hooks. Useful for doing extra email
|
||||||
|
@ -262,7 +342,12 @@ pub struct SendmailConfigDef {
|
||||||
#[serde(remote = "EmailHooks", rename_all = "kebab-case")]
|
#[serde(remote = "EmailHooks", rename_all = "kebab-case")]
|
||||||
pub struct EmailHooksDef {
|
pub struct EmailHooksDef {
|
||||||
/// Represents the hook called just before sending an email.
|
/// Represents the hook called just before sending an email.
|
||||||
pub pre_send: Option<String>,
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub pre_send: Option<Cmd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::{Input, Select};
|
use dialoguer::{Input, Select};
|
||||||
use pimalaya_email::ImapConfig;
|
use pimalaya_email::{BackendConfig, ImapConfig};
|
||||||
|
|
||||||
use crate::account::{
|
use crate::account::DeserializedAccountConfig;
|
||||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{SECURITY_PROTOCOLS, THEME};
|
use super::{AUTH_MECHANISMS, CMD, KEYRING, RAW, SECRET, SECURITY_PROTOCOLS, THEME};
|
||||||
|
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
pub(crate) fn configure(base: &DeserializedAccountConfig) -> Result<BackendConfig> {
|
||||||
// TODO: Validate by checking as valid URI
|
// TODO: Validate by checking as valid URI
|
||||||
let mut backend = ImapConfig {
|
|
||||||
host: Input::with_theme(&*THEME)
|
use dialoguer::Password;
|
||||||
.with_prompt("Enter the IMAP host:")
|
use pimalaya_email::{ImapAuthConfig, PasswdConfig};
|
||||||
.default(format!("imap.{}", base.email.rsplit_once('@').unwrap().1))
|
use pimalaya_secret::Secret;
|
||||||
.interact()?,
|
|
||||||
..Default::default()
|
use super::PASSWD;
|
||||||
};
|
|
||||||
|
let mut imap_config = ImapConfig::default();
|
||||||
|
|
||||||
|
imap_config.host = Input::with_theme(&*THEME)
|
||||||
|
.with_prompt("What is your IMAP host:")
|
||||||
|
.default(format!("imap.{}", base.email.rsplit_once('@').unwrap().1))
|
||||||
|
.interact()?;
|
||||||
|
|
||||||
let default_port = match Select::with_theme(&*THEME)
|
let default_port = match Select::with_theme(&*THEME)
|
||||||
.with_prompt("Which security protocol do you want to use?")
|
.with_prompt("Which security protocol do you want to use?")
|
||||||
|
@ -26,35 +30,60 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
||||||
.interact_opt()?
|
.interact_opt()?
|
||||||
{
|
{
|
||||||
Some(idx) if SECURITY_PROTOCOLS[idx] == "SSL/TLS" => {
|
Some(idx) if SECURITY_PROTOCOLS[idx] == "SSL/TLS" => {
|
||||||
backend.ssl = Some(true);
|
imap_config.ssl = Some(true);
|
||||||
993
|
993
|
||||||
}
|
}
|
||||||
Some(idx) if SECURITY_PROTOCOLS[idx] == "STARTTLS" => {
|
Some(idx) if SECURITY_PROTOCOLS[idx] == "STARTTLS" => {
|
||||||
backend.starttls = Some(true);
|
imap_config.starttls = Some(true);
|
||||||
143
|
143
|
||||||
}
|
}
|
||||||
_ => 143,
|
_ => 143,
|
||||||
};
|
};
|
||||||
|
|
||||||
backend.port = Input::with_theme(&*THEME)
|
imap_config.port = Input::with_theme(&*THEME)
|
||||||
.with_prompt("Enter the IMAP port:")
|
.with_prompt("Which IMAP port would you like to use?")
|
||||||
.validate_with(|input: &String| input.parse::<u16>().map(|_| ()))
|
.validate_with(|input: &String| input.parse::<u16>().map(|_| ()))
|
||||||
.default(default_port.to_string())
|
.default(default_port.to_string())
|
||||||
.interact()
|
.interact()
|
||||||
.map(|input| input.parse::<u16>().unwrap())?;
|
.map(|input| input.parse::<u16>().unwrap())?;
|
||||||
|
|
||||||
backend.login = Input::with_theme(&*THEME)
|
imap_config.login = Input::with_theme(&*THEME)
|
||||||
.with_prompt("Enter your IMAP login:")
|
.with_prompt("What is your IMAP login?")
|
||||||
.default(base.email.clone())
|
.default(base.email.clone())
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
|
let auth = Select::with_theme(&*THEME)
|
||||||
|
.with_prompt("Which IMAP authentication mechanism would you like to use?")
|
||||||
|
.items(AUTH_MECHANISMS)
|
||||||
|
.default(0)
|
||||||
|
.interact_opt()?;
|
||||||
|
|
||||||
|
imap_config.auth = match auth {
|
||||||
|
Some(idx) if AUTH_MECHANISMS[idx] == PASSWD => {
|
||||||
|
let secret = Select::with_theme(&*THEME)
|
||||||
|
.with_prompt("How would you like to store your password?")
|
||||||
|
.items(SECRET)
|
||||||
|
.default(0)
|
||||||
|
.interact_opt()?;
|
||||||
|
match secret {
|
||||||
|
Some(idx) if SECRET[idx] == RAW => ImapAuthConfig::Passwd(PasswdConfig {
|
||||||
|
passwd: Secret::new_raw(
|
||||||
|
Password::with_theme(&*THEME)
|
||||||
|
.with_prompt("What is your IMAP password?")
|
||||||
|
.interact()?,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
_ => ImapAuthConfig::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ImapAuthConfig::default(),
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: add all variants: password, password command and oauth2
|
// FIXME: add all variants: password, password command and oauth2
|
||||||
// backend.passwd_cmd = Input::with_theme(&*THEME)
|
// backend.passwd_cmd = Input::with_theme(&*THEME)
|
||||||
// .with_prompt("What shell command should we run to get your password?")
|
// .with_prompt("What shell command should we run to get your password?")
|
||||||
// .default(format!("pass show {}", &base.email))
|
// .default(format!("pass show {}", &base.email))
|
||||||
// .interact()?;
|
// .interact()?;
|
||||||
|
|
||||||
Ok(DeserializedAccountConfig::Imap(
|
Ok(BackendConfig::Imap(imap_config))
|
||||||
DeserializedImapAccountConfig { base, backend },
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use super::THEME;
|
|
||||||
use crate::account::{
|
|
||||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedMaildirAccountConfig,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::Input;
|
use dialoguer::Input;
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use pimalaya_email::MaildirConfig;
|
use pimalaya_email::{BackendConfig, MaildirConfig};
|
||||||
|
|
||||||
|
use super::THEME;
|
||||||
|
|
||||||
|
pub(crate) fn configure() -> Result<BackendConfig> {
|
||||||
|
let mut maildir_config = MaildirConfig::default();
|
||||||
|
|
||||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
|
||||||
let input = if let Some(home) = home_dir() {
|
let input = if let Some(home) = home_dir() {
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.default(home.join("Mail").display().to_string())
|
.default(home.join("Mail").display().to_string())
|
||||||
|
@ -19,12 +19,7 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
||||||
.interact_text()?
|
.interact_text()?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(DeserializedAccountConfig::Maildir(
|
maildir_config.root_dir = input.into();
|
||||||
DeserializedMaildirAccountConfig {
|
|
||||||
base,
|
Ok(BackendConfig::Maildir(maildir_config))
|
||||||
backend: MaildirConfig {
|
|
||||||
root_dir: input.into(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,26 +8,37 @@ mod smtp;
|
||||||
mod validators;
|
mod validators;
|
||||||
|
|
||||||
use super::DeserializedConfig;
|
use super::DeserializedConfig;
|
||||||
use crate::account::{DeserializedAccountConfig, DeserializedBaseAccountConfig};
|
use crate::account::DeserializedAccountConfig;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use console::style;
|
use console::style;
|
||||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select};
|
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use pimalaya_email::{BackendConfig, SenderConfig};
|
||||||
use std::{fs, io, process};
|
use std::{fs, io, process};
|
||||||
|
|
||||||
const BACKENDS: &[&str] = &[
|
const BACKENDS: &[&str] = &[
|
||||||
"Maildir",
|
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
"IMAP",
|
"IMAP",
|
||||||
|
"Maildir",
|
||||||
#[cfg(feature = "notmuch-backend")]
|
#[cfg(feature = "notmuch-backend")]
|
||||||
"Notmuch",
|
"Notmuch",
|
||||||
|
"None",
|
||||||
];
|
];
|
||||||
|
|
||||||
const SENDERS: &[&str] = &["SMTP", "Sendmail"];
|
const SENDERS: &[&str] = &["SMTP", "Sendmail"];
|
||||||
|
|
||||||
const SECURITY_PROTOCOLS: &[&str] = &["SSL/TLS", "STARTTLS", "None"];
|
const SECURITY_PROTOCOLS: &[&str] = &["SSL/TLS", "STARTTLS", "None"];
|
||||||
|
|
||||||
|
const AUTH_MECHANISMS: &[&str] = &[PASSWD, OAUTH2];
|
||||||
|
const PASSWD: &str = "Password";
|
||||||
|
const OAUTH2: &str = "OAuth 2.0";
|
||||||
|
|
||||||
|
const SECRET: &[&str] = &[RAW, CMD, KEYRING];
|
||||||
|
const RAW: &str = "In clear, in your configuration (not recommanded)";
|
||||||
|
const CMD: &str = "From a shell command";
|
||||||
|
const KEYRING: &str = "From your system's global keyring";
|
||||||
|
|
||||||
// A wizard should have pretty colors 💅
|
// A wizard should have pretty colors 💅
|
||||||
static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
|
static THEME: Lazy<ColorfulTheme> = Lazy::new(ColorfulTheme::default);
|
||||||
|
|
||||||
|
@ -45,9 +56,10 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine path to save to
|
// Determine path to save to
|
||||||
let path = dirs::config_dir()
|
// let path = dirs::config_dir()
|
||||||
.map(|p| p.join("himalaya").join("config.toml"))
|
// .map(|p| p.join("himalaya").join("config.toml"))
|
||||||
.ok_or_else(|| anyhow!("The wizard could not determine the config directory. Aborting"))?;
|
// .ok_or_else(|| anyhow!("The wizard could not determine the config directory. Aborting"))?;
|
||||||
|
let path = std::path::PathBuf::from("/home/soywod/config.wizard.toml");
|
||||||
|
|
||||||
let mut config = DeserializedConfig::default();
|
let mut config = DeserializedConfig::default();
|
||||||
|
|
||||||
|
@ -74,7 +86,7 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
||||||
|
|
||||||
// If one acounts is setup, make it the default. If multiple accounts are setup, decide which
|
// If one acounts is setup, make it the default. If multiple accounts are setup, decide which
|
||||||
// will be the default. If no accounts are setup, exit the process
|
// will be the default. If no accounts are setup, exit the process
|
||||||
let default = match config.accounts.len() {
|
let default_account = match config.accounts.len() {
|
||||||
1 => Some(config.accounts.values_mut().next().unwrap()),
|
1 => Some(config.accounts.values_mut().next().unwrap()),
|
||||||
i if i > 1 => {
|
i if i > 1 => {
|
||||||
let accounts = config.accounts.clone();
|
let accounts = config.accounts.clone();
|
||||||
|
@ -97,14 +109,8 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
||||||
_ => process::exit(0),
|
_ => process::exit(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
match default {
|
if let Some(account) = default_account {
|
||||||
Some(DeserializedAccountConfig::None(default)) => default.default = Some(true),
|
account.default = Some(true);
|
||||||
Some(DeserializedAccountConfig::Maildir(default)) => default.base.default = Some(true),
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
Some(DeserializedAccountConfig::Imap(default)) => default.base.default = Some(true),
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
Some(DeserializedAccountConfig::Notmuch(default)) => default.base.default = Some(true),
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize config to file
|
// Serialize config to file
|
||||||
|
@ -117,18 +123,18 @@ pub(crate) fn wizard() -> Result<DeserializedConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
|
fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
|
||||||
let mut base = configure_base()?;
|
let mut config = DeserializedAccountConfig::default();
|
||||||
let sender = Select::with_theme(&*THEME)
|
|
||||||
.with_prompt("Which sender would you like use with your account?")
|
|
||||||
.items(SENDERS)
|
|
||||||
.default(0)
|
|
||||||
.interact_opt()?;
|
|
||||||
|
|
||||||
base.email_sender = match sender {
|
config.email = Input::with_theme(&*THEME)
|
||||||
Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&base),
|
.with_prompt("What is your email address?")
|
||||||
Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(),
|
.validate_with(validators::EmailValidator)
|
||||||
_ => return Ok(None),
|
.interact()?;
|
||||||
}?;
|
|
||||||
|
config.display_name = Some(
|
||||||
|
Input::with_theme(&*THEME)
|
||||||
|
.with_prompt("Which name would you like to display with your email?")
|
||||||
|
.interact()?,
|
||||||
|
);
|
||||||
|
|
||||||
let backend = Select::with_theme(&*THEME)
|
let backend = Select::with_theme(&*THEME)
|
||||||
.with_prompt("Which backend would you like to configure your account for?")
|
.with_prompt("Which backend would you like to configure your account for?")
|
||||||
|
@ -136,32 +142,28 @@ fn configure_account() -> Result<Option<DeserializedAccountConfig>> {
|
||||||
.default(0)
|
.default(0)
|
||||||
.interact_opt()?;
|
.interact_opt()?;
|
||||||
|
|
||||||
match backend {
|
config.backend = match backend {
|
||||||
Some(idx) if BACKENDS[idx] == "Maildir" => Ok(Some(maildir::configure(base)?)),
|
Some(idx) if BACKENDS[idx] == "IMAP" => imap::configure(&config),
|
||||||
#[cfg(feature = "imap-backend")]
|
Some(idx) if BACKENDS[idx] == "Maildir" => maildir::configure(),
|
||||||
Some(idx) if BACKENDS[idx] == "IMAP" => Ok(Some(imap::configure(base)?)),
|
Some(idx) if BACKENDS[idx] == "Notmuch" => notmuch::configure(),
|
||||||
#[cfg(feature = "notmuch-backend")]
|
Some(idx) if BACKENDS[idx] == "None" => Ok(BackendConfig::None),
|
||||||
Some(idx) if BACKENDS[idx] == "Notmuch" => Ok(Some(notmuch::configure(base)?)),
|
_ => return Ok(None),
|
||||||
_ => Ok(None),
|
}?;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_base() -> Result<DeserializedBaseAccountConfig> {
|
let sender = Select::with_theme(&*THEME)
|
||||||
let mut base_account_config = DeserializedBaseAccountConfig {
|
.with_prompt("Which sender would you like use with your account?")
|
||||||
email: Input::with_theme(&*THEME)
|
.items(SENDERS)
|
||||||
.with_prompt("Enter your email:")
|
.default(0)
|
||||||
.validate_with(validators::EmailValidator)
|
.interact_opt()?;
|
||||||
.interact()?,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
base_account_config.display_name = Some(
|
config.sender = match sender {
|
||||||
Input::with_theme(&*THEME)
|
Some(idx) if SENDERS[idx] == "SMTP" => smtp::configure(&config),
|
||||||
.with_prompt("Enter display name:")
|
Some(idx) if SENDERS[idx] == "Sendmail" => sendmail::configure(),
|
||||||
.interact()?,
|
Some(idx) if SENDERS[idx] == "None" => Ok(SenderConfig::None),
|
||||||
);
|
_ => return Ok(None),
|
||||||
|
}?;
|
||||||
|
|
||||||
Ok(base_account_config)
|
Ok(Some(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {
|
pub(crate) fn prompt_passwd(prompt: &str) -> io::Result<String> {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use super::THEME;
|
|
||||||
use crate::account::{
|
|
||||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedNotmuchAccountConfig,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::Input;
|
use dialoguer::Input;
|
||||||
use pimalaya_email::{NotmuchBackend, NotmuchConfig};
|
use pimalaya_email::{BackendConfig, NotmuchBackend, NotmuchConfig};
|
||||||
|
|
||||||
pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<DeserializedAccountConfig> {
|
use super::THEME;
|
||||||
let db_path = match NotmuchBackend::get_default_db_path() {
|
|
||||||
|
pub(crate) fn configure() -> Result<BackendConfig> {
|
||||||
|
let mut notmuch_config = NotmuchConfig::default();
|
||||||
|
|
||||||
|
notmuch_config.db_path = match NotmuchBackend::get_default_db_path() {
|
||||||
Ok(db) => db,
|
Ok(db) => db,
|
||||||
_ => {
|
_ => {
|
||||||
let input: String = Input::with_theme(&*THEME)
|
let input: String = Input::with_theme(&*THEME)
|
||||||
|
@ -17,9 +17,5 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result<Deseriali
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let backend = NotmuchConfig { db_path };
|
Ok(BackendConfig::Notmuch(notmuch_config))
|
||||||
|
|
||||||
Ok(DeserializedAccountConfig::Notmuch(
|
|
||||||
DeserializedNotmuchAccountConfig { base, backend },
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
use super::THEME;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::Input;
|
use dialoguer::Input;
|
||||||
use pimalaya_email::{EmailSender, SendmailConfig};
|
use pimalaya_email::{SenderConfig, SendmailConfig};
|
||||||
|
|
||||||
pub(crate) fn configure() -> Result<EmailSender> {
|
use super::THEME;
|
||||||
Ok(EmailSender::Sendmail(SendmailConfig {
|
|
||||||
cmd: Input::with_theme(&*THEME)
|
pub(crate) fn configure() -> Result<SenderConfig> {
|
||||||
.with_prompt("Enter an external command to send a mail: ")
|
let mut sendmail_config = SendmailConfig::default();
|
||||||
.default("/usr/bin/msmtp".to_owned())
|
|
||||||
.interact()?,
|
sendmail_config.cmd = Input::with_theme(&*THEME)
|
||||||
}))
|
.with_prompt("Enter an external command to send an email: ")
|
||||||
|
.default("/usr/bin/msmtp".to_owned())
|
||||||
|
.interact()?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
Ok(SenderConfig::Sendmail(sendmail_config))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
use super::{SECURITY_PROTOCOLS, THEME};
|
|
||||||
use crate::account::DeserializedBaseAccountConfig;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use dialoguer::{Input, Select};
|
use dialoguer::{Input, Select};
|
||||||
use pimalaya_email::{EmailSender, SmtpConfig};
|
use pimalaya_email::{SenderConfig, SmtpConfig};
|
||||||
|
|
||||||
pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSender> {
|
use crate::account::DeserializedAccountConfig;
|
||||||
|
|
||||||
|
use super::{SECURITY_PROTOCOLS, THEME};
|
||||||
|
|
||||||
|
pub(crate) fn configure(config: &DeserializedAccountConfig) -> Result<SenderConfig> {
|
||||||
let mut smtp_config = SmtpConfig {
|
let mut smtp_config = SmtpConfig {
|
||||||
host: Input::with_theme(&*THEME)
|
host: Input::with_theme(&*THEME)
|
||||||
.with_prompt("Enter the SMTP host: ")
|
.with_prompt("Enter the SMTP host: ")
|
||||||
.default(format!("smtp.{}", base.email.rsplit_once('@').unwrap().1))
|
.default(format!("smtp.{}", config.email.rsplit_once('@').unwrap().1))
|
||||||
.interact()?,
|
.interact()?,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -39,7 +41,7 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
|
||||||
|
|
||||||
smtp_config.login = Input::with_theme(&*THEME)
|
smtp_config.login = Input::with_theme(&*THEME)
|
||||||
.with_prompt("Enter your SMTP login:")
|
.with_prompt("Enter your SMTP login:")
|
||||||
.default(base.email.clone())
|
.default(config.email.clone())
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
// FIXME: add all variants: password, password command and oauth2
|
// FIXME: add all variants: password, password command and oauth2
|
||||||
|
@ -48,5 +50,5 @@ pub(crate) fn configure(base: &DeserializedBaseAccountConfig) -> Result<EmailSen
|
||||||
// .default(format!("pass show {}", &base.email))
|
// .default(format!("pass show {}", &base.email))
|
||||||
// .interact()?;
|
// .interact()?;
|
||||||
|
|
||||||
Ok(EmailSender::Smtp(smtp_config))
|
Ok(SenderConfig::Smtp(smtp_config))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
//! accounts from the config file.
|
//! accounts from the config file.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use pimalaya_email::BackendConfig;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{collections::hash_map::Iter, ops::Deref};
|
use std::{collections::hash_map::Iter, ops::Deref};
|
||||||
|
|
||||||
|
@ -39,19 +40,19 @@ impl PrintTable for Accounts {
|
||||||
impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
|
impl From<Iter<'_, String, DeserializedAccountConfig>> for Accounts {
|
||||||
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
|
fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self {
|
||||||
let mut accounts: Vec<_> = map
|
let mut accounts: Vec<_> = map
|
||||||
.map(|(name, account)| match account {
|
.map(|(name, account)| match &account.backend {
|
||||||
DeserializedAccountConfig::Maildir(config) => {
|
BackendConfig::None => Account::new(name, "none", false),
|
||||||
Account::new(name, "maildir", config.base.default.unwrap_or_default())
|
BackendConfig::Maildir(_) => {
|
||||||
|
Account::new(name, "maildir", account.default.unwrap_or_default())
|
||||||
}
|
}
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
DeserializedAccountConfig::Imap(config) => {
|
BackendConfig::Imap(_) => {
|
||||||
Account::new(name, "imap", config.base.default.unwrap_or_default())
|
Account::new(name, "imap", account.default.unwrap_or_default())
|
||||||
}
|
}
|
||||||
#[cfg(feature = "notmuch-backend")]
|
#[cfg(feature = "notmuch-backend")]
|
||||||
DeserializedAccountConfig::Notmuch(config) => {
|
BackendConfig::Notmuch(_) => {
|
||||||
Account::new(name, "notmuch", config.base.default.unwrap_or_default())
|
Account::new(name, "notmuch", account.default.unwrap_or_default())
|
||||||
}
|
}
|
||||||
DeserializedAccountConfig::None(..) => Account::new(name, "none", false),
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());
|
accounts.sort_by(|a, b| b.name.partial_cmp(&a.name).unwrap());
|
||||||
|
|
|
@ -5,93 +5,18 @@
|
||||||
|
|
||||||
use pimalaya_email::{
|
use pimalaya_email::{
|
||||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
||||||
EmailSender, EmailTextPlainFormat, ImapAuthConfig, MaildirConfig,
|
EmailTextPlainFormat, SenderConfig,
|
||||||
};
|
};
|
||||||
|
use pimalaya_process::Cmd;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
use pimalaya_email::ImapConfig;
|
|
||||||
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
use pimalaya_email::NotmuchConfig;
|
|
||||||
|
|
||||||
use crate::config::{prelude::*, DeserializedConfig};
|
use crate::config::{prelude::*, DeserializedConfig};
|
||||||
|
|
||||||
/// Represents all existing kind of account config.
|
/// Represents all existing kind of account config.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[serde(tag = "backend", rename_all = "kebab-case")]
|
|
||||||
pub enum DeserializedAccountConfig {
|
|
||||||
None(DeserializedBaseAccountConfig),
|
|
||||||
Maildir(DeserializedMaildirAccountConfig),
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
Imap(DeserializedImapAccountConfig),
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
Notmuch(DeserializedNotmuchAccountConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeserializedAccountConfig {
|
|
||||||
pub fn to_configs(
|
|
||||||
&self,
|
|
||||||
name: String,
|
|
||||||
global_config: &DeserializedConfig,
|
|
||||||
) -> (AccountConfig, BackendConfig) {
|
|
||||||
match self {
|
|
||||||
DeserializedAccountConfig::None(config) => (
|
|
||||||
config.to_account_config(name, global_config),
|
|
||||||
BackendConfig::None,
|
|
||||||
),
|
|
||||||
DeserializedAccountConfig::Maildir(config) => (
|
|
||||||
config.base.to_account_config(name, global_config),
|
|
||||||
BackendConfig::Maildir(config.backend.clone()),
|
|
||||||
),
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
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),
|
|
||||||
BackendConfig::Notmuch(config.backend.clone()),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_default(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
DeserializedAccountConfig::None(config) => config.default.unwrap_or_default(),
|
|
||||||
DeserializedAccountConfig::Maildir(config) => config.base.default.unwrap_or_default(),
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
DeserializedAccountConfig::Imap(config) => config.base.default.unwrap_or_default(),
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
DeserializedAccountConfig::Notmuch(config) => config.base.default.unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(tag = "backend", rename_all = "kebab-case")]
|
||||||
pub struct DeserializedBaseAccountConfig {
|
pub struct DeserializedAccountConfig {
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub default: Option<bool>,
|
pub default: Option<bool>,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
|
@ -106,13 +31,32 @@ pub struct DeserializedBaseAccountConfig {
|
||||||
pub email_reading_headers: Option<Vec<String>>,
|
pub email_reading_headers: Option<Vec<String>>,
|
||||||
#[serde(default, with = "EmailTextPlainFormatDef")]
|
#[serde(default, with = "EmailTextPlainFormatDef")]
|
||||||
pub email_reading_format: EmailTextPlainFormat,
|
pub email_reading_format: EmailTextPlainFormat,
|
||||||
pub email_reading_verify_cmd: Option<String>,
|
#[serde(
|
||||||
pub email_reading_decrypt_cmd: Option<String>,
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_reading_verify_cmd: Option<Cmd>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_reading_decrypt_cmd: Option<Cmd>,
|
||||||
pub email_writing_headers: Option<Vec<String>>,
|
pub email_writing_headers: Option<Vec<String>>,
|
||||||
pub email_writing_sign_cmd: Option<String>,
|
#[serde(
|
||||||
pub email_writing_encrypt_cmd: Option<String>,
|
default,
|
||||||
#[serde(flatten, with = "EmailSenderDef")]
|
with = "OptionCmdDef",
|
||||||
pub email_sender: EmailSender,
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_writing_sign_cmd: Option<Cmd>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "OptionCmdDef",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub email_writing_encrypt_cmd: Option<Cmd>,
|
||||||
|
pub email_sending_save_copy: Option<bool>,
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
with = "EmailHooksDef",
|
with = "EmailHooksDef",
|
||||||
|
@ -125,9 +69,14 @@ pub struct DeserializedBaseAccountConfig {
|
||||||
pub sync_dir: Option<PathBuf>,
|
pub sync_dir: Option<PathBuf>,
|
||||||
#[serde(default, with = "SyncFoldersStrategyDef")]
|
#[serde(default, with = "SyncFoldersStrategyDef")]
|
||||||
pub sync_folders_strategy: SyncFoldersStrategy,
|
pub sync_folders_strategy: SyncFoldersStrategy,
|
||||||
|
|
||||||
|
#[serde(flatten, with = "BackendConfigDef")]
|
||||||
|
pub backend: BackendConfig,
|
||||||
|
#[serde(flatten, with = "SenderConfigDef")]
|
||||||
|
pub sender: SenderConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeserializedBaseAccountConfig {
|
impl DeserializedAccountConfig {
|
||||||
pub fn to_account_config(&self, name: String, config: &DeserializedConfig) -> AccountConfig {
|
pub fn to_account_config(&self, name: String, config: &DeserializedConfig) -> AccountConfig {
|
||||||
let mut folder_aliases = config
|
let mut folder_aliases = config
|
||||||
.folder_aliases
|
.folder_aliases
|
||||||
|
@ -222,39 +171,16 @@ impl DeserializedBaseAccountConfig {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.or_else(|| config.email_writing_headers.as_ref().map(ToOwned::to_owned)),
|
.or_else(|| config.email_writing_headers.as_ref().map(ToOwned::to_owned)),
|
||||||
email_sender: self.email_sender.to_owned(),
|
email_sending_save_copy: self.email_sending_save_copy.unwrap_or(true),
|
||||||
email_hooks: EmailHooks {
|
email_hooks: EmailHooks {
|
||||||
pre_send: self.email_hooks.pre_send.clone(),
|
pre_send: self.email_hooks.pre_send.clone(),
|
||||||
},
|
},
|
||||||
sync: self.sync,
|
sync: self.sync,
|
||||||
sync_dir: self.sync_dir.clone(),
|
sync_dir: self.sync_dir.clone(),
|
||||||
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
||||||
|
|
||||||
|
backend: self.backend.clone(),
|
||||||
|
sender: self.sender.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[cfg(feature = "imap-backend")]
|
|
||||||
pub struct DeserializedImapAccountConfig {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: DeserializedBaseAccountConfig,
|
|
||||||
#[serde(flatten, with = "ImapConfigDef")]
|
|
||||||
pub backend: ImapConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
|
||||||
pub struct DeserializedMaildirAccountConfig {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: DeserializedBaseAccountConfig,
|
|
||||||
#[serde(flatten, with = "MaildirConfigDef")]
|
|
||||||
pub backend: MaildirConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[cfg(feature = "notmuch-backend")]
|
|
||||||
pub struct DeserializedNotmuchAccountConfig {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: DeserializedBaseAccountConfig,
|
|
||||||
#[serde(flatten, with = "NotmuchConfigDef")]
|
|
||||||
pub backend: NotmuchConfig,
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use pimalaya_email::{
|
use pimalaya_email::{
|
||||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
|
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendConfig,
|
||||||
BackendSyncBuilder, BackendSyncProgressEvent, EmailSender, ImapAuthConfig, SmtpAuthConfig,
|
BackendSyncBuilder, BackendSyncProgressEvent, ImapAuthConfig, SenderConfig, SmtpAuthConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -20,16 +20,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Configure the current selected account
|
/// Configure the current selected account
|
||||||
pub fn configure(
|
pub fn configure(config: &AccountConfig, reset: bool) -> Result<()> {
|
||||||
account_config: &AccountConfig,
|
|
||||||
backend_config: &BackendConfig,
|
|
||||||
reset: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
info!("entering the configure account handler");
|
info!("entering the configure account handler");
|
||||||
|
|
||||||
if reset {
|
if reset {
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||||
let reset = match &imap_config.auth {
|
let reset = match &imap_config.auth {
|
||||||
ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
ImapAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||||
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
ImapAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||||
|
@ -41,7 +37,7 @@ pub fn configure(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "smtp-sender")]
|
#[cfg(feature = "smtp-sender")]
|
||||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||||
let reset = match &smtp_config.auth {
|
let reset = match &smtp_config.auth {
|
||||||
SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
SmtpAuthConfig::Passwd(passwd) => passwd.reset(),
|
||||||
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
SmtpAuthConfig::OAuth2(oauth2) => oauth2.reset(),
|
||||||
|
@ -54,7 +50,7 @@ pub fn configure(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
if let BackendConfig::Imap(imap_config) = backend_config {
|
if let BackendConfig::Imap(imap_config) = &config.backend {
|
||||||
match &imap_config.auth {
|
match &imap_config.auth {
|
||||||
ImapAuthConfig::Passwd(passwd) => {
|
ImapAuthConfig::Passwd(passwd) => {
|
||||||
passwd.configure(|| prompt_passwd("Enter your IMAP password:"))
|
passwd.configure(|| prompt_passwd("Enter your IMAP password:"))
|
||||||
|
@ -66,7 +62,7 @@ pub fn configure(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "smtp-sender")]
|
#[cfg(feature = "smtp-sender")]
|
||||||
if let EmailSender::Smtp(smtp_config) = &account_config.email_sender {
|
if let SenderConfig::Smtp(smtp_config) = &config.sender {
|
||||||
match &smtp_config.auth {
|
match &smtp_config.auth {
|
||||||
SmtpAuthConfig::Passwd(passwd) => {
|
SmtpAuthConfig::Passwd(passwd) => {
|
||||||
passwd.configure(|| prompt_passwd("Enter your SMTP password:"))
|
passwd.configure(|| prompt_passwd("Enter your SMTP password:"))
|
||||||
|
@ -296,9 +292,7 @@ mod tests {
|
||||||
use termcolor::ColorSpec;
|
use termcolor::ColorSpec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account::{
|
account::DeserializedAccountConfig,
|
||||||
DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig,
|
|
||||||
},
|
|
||||||
printer::{Print, PrintTable, WriteColor},
|
printer::{Print, PrintTable, WriteColor},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -370,13 +364,11 @@ mod tests {
|
||||||
let deserialized_config = DeserializedConfig {
|
let deserialized_config = DeserializedConfig {
|
||||||
accounts: HashMap::from_iter([(
|
accounts: HashMap::from_iter([(
|
||||||
"account-1".into(),
|
"account-1".into(),
|
||||||
DeserializedAccountConfig::Imap(DeserializedImapAccountConfig {
|
DeserializedAccountConfig {
|
||||||
base: DeserializedBaseAccountConfig {
|
default: Some(true),
|
||||||
default: Some(true),
|
backend: BackendConfig::Imap(ImapConfig::default()),
|
||||||
..DeserializedBaseAccountConfig::default()
|
..DeserializedAccountConfig::default()
|
||||||
},
|
},
|
||||||
backend: ImapConfig::default(),
|
|
||||||
}),
|
|
||||||
)]),
|
)]),
|
||||||
..DeserializedConfig::default()
|
..DeserializedConfig::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -165,9 +165,9 @@ pub fn list<P: Printer>(
|
||||||
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
/// [mailto]: https://en.wikipedia.org/wiki/Mailto
|
||||||
pub fn mailto<P: Printer>(
|
pub fn mailto<P: Printer>(
|
||||||
config: &AccountConfig,
|
config: &AccountConfig,
|
||||||
printer: &mut P,
|
|
||||||
backend: &mut dyn Backend,
|
backend: &mut dyn Backend,
|
||||||
sender: &mut dyn Sender,
|
sender: &mut dyn Sender,
|
||||||
|
printer: &mut P,
|
||||||
url: &Url,
|
url: &Url,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut tpl = TplBuilder::default().to(url.path());
|
let mut tpl = TplBuilder::default().to(url.path());
|
||||||
|
|
|
@ -74,8 +74,8 @@ pub fn save<P: Printer>(
|
||||||
})
|
})
|
||||||
.compile(
|
.compile(
|
||||||
CompilerBuilder::default()
|
CompilerBuilder::default()
|
||||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let id = backend.add_email(folder, &email, &Flags::default())?;
|
let id = backend.add_email(folder, &email, &Flags::default())?;
|
||||||
|
@ -104,8 +104,8 @@ pub fn send<P: Printer>(
|
||||||
})
|
})
|
||||||
.compile(
|
.compile(
|
||||||
CompilerBuilder::default()
|
CompilerBuilder::default()
|
||||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||||
)?;
|
)?;
|
||||||
sender.send(&email)?;
|
sender.send(&email)?;
|
||||||
backend.add_email(folder, &email, &Flags::default())?;
|
backend.add_email(folder, &email, &Flags::default())?;
|
||||||
|
|
67
src/main.rs
67
src/main.rs
|
@ -51,17 +51,16 @@ fn main() -> Result<()> {
|
||||||
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||||
let url = Url::parse(&raw_args[1])?;
|
let url = Url::parse(&raw_args[1])?;
|
||||||
let config = DeserializedConfig::from_opt_path(None)?;
|
let config = DeserializedConfig::from_opt_path(None)?;
|
||||||
let (account_config, backend_config) = config.to_configs(None)?;
|
let account_config = config.to_account_config(None)?;
|
||||||
let mut backend =
|
let mut backend = BackendBuilder::new().build(&account_config)?;
|
||||||
BackendBuilder::new().build(account_config.clone(), backend_config.clone())?;
|
let mut sender = SenderBuilder::new().build(&account_config)?;
|
||||||
let mut sender = SenderBuilder::build(&account_config)?;
|
|
||||||
let mut printer = StdoutPrinter::default();
|
let mut printer = StdoutPrinter::default();
|
||||||
|
|
||||||
return email::handlers::mailto(
|
return email::handlers::mailto(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
|
||||||
backend.as_mut(),
|
backend.as_mut(),
|
||||||
sender.as_mut(),
|
sender.as_mut(),
|
||||||
|
&mut printer,
|
||||||
&url,
|
&url,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -88,12 +87,12 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
// inits config
|
// inits config
|
||||||
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
|
let config = DeserializedConfig::from_opt_path(config::args::parse_arg(&m))?;
|
||||||
let (account_config, backend_config) = config.to_configs(account::args::parse_arg(&m))?;
|
let account_config = config.to_account_config(account::args::parse_arg(&m))?;
|
||||||
let folder = folder::args::parse_source_arg(&m);
|
let folder = folder::args::parse_source_arg(&m);
|
||||||
|
|
||||||
// checks IMAP commands
|
// checks IMAP commands
|
||||||
#[cfg(feature = "imap-backend")]
|
#[cfg(feature = "imap-backend")]
|
||||||
if let BackendConfig::Imap(imap_config) = &backend_config {
|
if let BackendConfig::Imap(imap_config) = &account_config.backend {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
|
|
||||||
// FIXME: find a way to downcast `backend` instead of
|
// FIXME: find a way to downcast `backend` instead of
|
||||||
|
@ -112,7 +111,7 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// inits services
|
// inits services
|
||||||
let mut sender = SenderBuilder::build(&account_config)?;
|
let mut sender = SenderBuilder::new().build(&account_config)?;
|
||||||
let mut printer = StdoutPrinter::try_from(&m)?;
|
let mut printer = StdoutPrinter::try_from(&m)?;
|
||||||
let disable_cache = cache::args::parse_disable_cache_flag(&m);
|
let disable_cache = cache::args::parse_disable_cache_flag(&m);
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ fn main() -> Result<()> {
|
||||||
let backend = BackendBuilder::new()
|
let backend = BackendBuilder::new()
|
||||||
.sessions_pool_size(8)
|
.sessions_pool_size(8)
|
||||||
.disable_cache(true)
|
.disable_cache(true)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
account::handlers::sync(
|
account::handlers::sync(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -137,7 +136,7 @@ fn main() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Some(account::args::Cmd::Configure(reset)) => {
|
Some(account::args::Cmd::Configure(reset)) => {
|
||||||
return account::handlers::configure(&account_config, &backend_config, reset);
|
return account::handlers::configure(&account_config, reset);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -151,13 +150,13 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder)?;
|
let folder = account_config.folder_alias(folder)?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return folder::handlers::create(&mut printer, backend.as_mut(), &folder);
|
return folder::handlers::create(&mut printer, backend.as_mut(), &folder);
|
||||||
}
|
}
|
||||||
Some(folder::args::Cmd::List(max_width)) => {
|
Some(folder::args::Cmd::List(max_width)) => {
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return folder::handlers::list(
|
return folder::handlers::list(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -169,7 +168,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder);
|
return folder::handlers::expunge(&mut printer, backend.as_mut(), &folder);
|
||||||
}
|
}
|
||||||
Some(folder::args::Cmd::Delete) => {
|
Some(folder::args::Cmd::Delete) => {
|
||||||
|
@ -179,7 +178,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder)?;
|
let folder = account_config.folder_alias(folder)?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder);
|
return folder::handlers::delete(&mut printer, backend.as_mut(), &folder);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -191,7 +190,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::attachments(
|
return email::handlers::attachments(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -206,7 +205,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::copy(
|
return email::handlers::copy(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -222,7 +221,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::delete(
|
return email::handlers::delete(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -237,7 +236,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::forward(
|
return email::handlers::forward(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -255,7 +254,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::list(
|
return email::handlers::list(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -272,7 +271,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::move_(
|
return email::handlers::move_(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -288,7 +287,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::read(
|
return email::handlers::read(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -307,7 +306,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::reply(
|
return email::handlers::reply(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -326,7 +325,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::save(
|
return email::handlers::save(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -341,7 +340,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::search(
|
return email::handlers::search(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -359,7 +358,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return email::handlers::sort(
|
return email::handlers::sort(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -377,7 +376,7 @@ fn main() -> Result<()> {
|
||||||
Some(email::args::Cmd::Send(raw_email)) => {
|
Some(email::args::Cmd::Send(raw_email)) => {
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return email::handlers::send(
|
return email::handlers::send(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -391,7 +390,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return flag::handlers::set(
|
return flag::handlers::set(
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -406,7 +405,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return flag::handlers::add(
|
return flag::handlers::add(
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -421,7 +420,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return flag::handlers::remove(
|
return flag::handlers::remove(
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -439,7 +438,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return tpl::handlers::forward(
|
return tpl::handlers::forward(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -459,7 +458,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return tpl::handlers::reply(
|
return tpl::handlers::reply(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -477,7 +476,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
let id_mapper = IdMapper::new(backend.as_ref(), &account_config.name, &folder)?;
|
||||||
return tpl::handlers::save(
|
return tpl::handlers::save(
|
||||||
&account_config,
|
&account_config,
|
||||||
|
@ -492,7 +491,7 @@ fn main() -> Result<()> {
|
||||||
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
let folder = account_config.folder_alias(folder.unwrap_or(DEFAULT_INBOX_FOLDER))?;
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return tpl::handlers::send(
|
return tpl::handlers::send(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
@ -507,7 +506,7 @@ fn main() -> Result<()> {
|
||||||
Some(email::args::Cmd::Write(headers, body)) => {
|
Some(email::args::Cmd::Write(headers, body)) => {
|
||||||
let mut backend = BackendBuilder::new()
|
let mut backend = BackendBuilder::new()
|
||||||
.disable_cache(disable_cache)
|
.disable_cache(disable_cache)
|
||||||
.build(account_config.clone(), backend_config.clone())?;
|
.build(&account_config)?;
|
||||||
return email::handlers::write(
|
return email::handlers::write(
|
||||||
&account_config,
|
&account_config,
|
||||||
&mut printer,
|
&mut printer,
|
||||||
|
|
|
@ -75,8 +75,8 @@ pub fn edit_tpl_with_editor<P: Printer>(
|
||||||
printer.print_log("Sending email…")?;
|
printer.print_log("Sending email…")?;
|
||||||
let email = tpl.compile(
|
let email = tpl.compile(
|
||||||
CompilerBuilder::default()
|
CompilerBuilder::default()
|
||||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||||
)?;
|
)?;
|
||||||
sender.send(&email)?;
|
sender.send(&email)?;
|
||||||
let sent_folder = config.sent_folder_alias()?;
|
let sent_folder = config.sent_folder_alias()?;
|
||||||
|
@ -98,8 +98,8 @@ pub fn edit_tpl_with_editor<P: Printer>(
|
||||||
let draft_folder = config.folder_alias("drafts")?;
|
let draft_folder = config.folder_alias("drafts")?;
|
||||||
let email = tpl.compile(
|
let email = tpl.compile(
|
||||||
CompilerBuilder::default()
|
CompilerBuilder::default()
|
||||||
.some_pgp_sign_cmd(config.email_writing_sign_cmd.as_ref())
|
.some_pgp_sign_cmd(config.email_writing_sign_cmd.clone())
|
||||||
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.as_ref()),
|
.some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()),
|
||||||
)?;
|
)?;
|
||||||
backend.add_email(
|
backend.add_email(
|
||||||
&draft_folder,
|
&draft_folder,
|
||||||
|
|
Loading…
Reference in a new issue