refactor configs to match new nested api from lib

This commit is contained in:
Clément DOUIN 2023-12-11 18:38:00 +01:00
parent 8e05be7f77
commit 2e0ec913cf
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
23 changed files with 214 additions and 292 deletions

64
Cargo.lock generated
View file

@ -272,7 +272,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -307,7 +307,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -679,7 +679,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -898,7 +898,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -1192,9 +1192,9 @@ dependencies = [
[[package]]
name = "email-lib"
version = "0.16.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3f51881639d657795ea95cd2fb065b8d9249912100b297730133ffbf38e0a5"
checksum = "743371f76482a94403ce0ab49da129065fc84cbcf9ed126524882c6ed5389efc"
dependencies = [
"advisory-lock",
"anyhow",
@ -1225,6 +1225,7 @@ dependencies = [
"rustls 0.22.1",
"rustls-native-certs",
"secret-lib",
"serde",
"shellexpand-utils",
"thiserror",
"tokio",
@ -1269,7 +1270,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -1290,7 +1291,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -1557,7 +1558,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -2734,15 +2735,17 @@ dependencies = [
[[package]]
name = "keyring-lib"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad2271300047bf0f2f4c4b6c51cbf7f848621aa373a3e80742bbdc0148110578"
checksum = "2d5fa6d6a6a7d27d09c3e5e6c0371d97dd4cc3b0208eaba9f26dae728f1ae072"
dependencies = [
"keyring",
"log",
"once_cell",
"secret-service",
"serde",
"thiserror",
"tokio",
]
[[package]]
@ -3011,9 +3014,9 @@ dependencies = [
[[package]]
name = "mml-lib"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bda7bfafd584f65bc7bb85a60be2a6ccf6f384f1e1d27b697d8b6524869213"
checksum = "a5498df8f63c5c204deae46529a6cbddbe7f5cfe70723165e6fde42e8e2490df"
dependencies = [
"async-recursion",
"chumsky",
@ -3026,6 +3029,7 @@ dependencies = [
"pgp-lib",
"process-lib",
"secret-lib",
"serde",
"shellexpand-utils",
"thiserror",
"tree_magic_mini",
@ -3129,7 +3133,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -3641,12 +3645,13 @@ dependencies = [
[[package]]
name = "process-lib"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "651bb0e9f091cf2ff67fbc6151d92c97bb2af864bfdbb85def0442250e10562b"
checksum = "76cb01de71b99c9d36dacf51218b950e03f80a86ee5f910ff723a1693796bad3"
dependencies = [
"log",
"once_cell",
"serde",
"thiserror",
"tokio",
]
@ -4152,12 +4157,13 @@ dependencies = [
[[package]]
name = "secret-lib"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5064a00687ed7f19a36e30a8b2695739d60f3d423f229097c8d110f9f76a8a96"
checksum = "b06c6eda723fc17a853234defb5784ec2b3377d9bce8a6577c89788f1b6dc117"
dependencies = [
"keyring-lib",
"process-lib",
"serde",
"thiserror",
]
@ -4226,7 +4232,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -4258,7 +4264,7 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -4490,9 +4496,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -4588,7 +4594,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -4664,7 +4670,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -4786,7 +4792,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -5014,7 +5020,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
"wasm-bindgen-shared",
]
@ -5048,7 +5054,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -5487,7 +5493,7 @@ checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -5507,7 +5513,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]

View file

@ -50,20 +50,20 @@ clap_mangen = "0.2"
console = "0.15.2"
dialoguer = "0.10.2"
dirs = "4.0"
email-lib = { version = "=0.16.0", default-features = false }
email-lib = { version = "=0.17.1", default-features = false }
email_address = "0.2.4"
env_logger = "0.8"
erased-serde = "0.3"
indicatif = "0.17"
keyring-lib = "=0.2.0"
keyring-lib = "=0.3.0"
log = "0.4"
mail-builder = "0.3"
md5 = "0.7.0"
mml-lib = { version = "=1.0.2", default-features = false }
mml-lib = { version = "=1.0.3", default-features = false }
oauth-lib = "=0.1.0"
once_cell = "1.16"
process-lib = "=0.2.0"
secret-lib = "=0.2.0"
process-lib = "=0.3.0"
secret-lib = "=0.3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
shellexpand-utils = "=0.2.0"

View file

@ -85,35 +85,22 @@ Please read the [documentation](https://pimalaya.org/himalaya/cli/configuration/
## Contributing
If you find a **bug** that [does not exist yet](https://todo.sr.ht/~soywod/pimalaya), please send an email at [~soywod/pimalaya@todo.sr.ht](mailto:~soywod/pimalaya@todo.sr.ht).
If you want to **report a bug** that [does not exist yet](https://todo.sr.ht/~soywod/pimalaya), please send an email at [~soywod/pimalaya@todo.sr.ht](mailto:~soywod/pimalaya@todo.sr.ht).
If you have a **question**, please send an email at [~soywod/pimalaya@lists.sr.ht](mailto:~soywod/pimalaya@lists.sr.ht).
If you want to **propose a feature** or **fix a bug**, please send a patch at [~soywod/pimalaya@lists.sr.ht](mailto:~soywod/pimalaya@lists.sr.ht) using [git send-email](https://git-scm.com/docs/git-send-email). Follow [this guide](https://git-send-email.io/) to configure git properly.
If you want to **propose a feature** or **fix a bug**, please send a patch at [~soywod/pimalaya@lists.sr.ht](mailto:~soywod/pimalaya@lists.sr.ht) using [git send-email](https://git-scm.com/docs/git-send-email) (see [this guide](https://git-send-email.io/) on how to configure it).
If you just want to **discuss** about the project, feel free to join the [Matrix](https://matrix.org/) workspace [#pimalaya.general](https://matrix.to/#/#pimalaya.general:matrix.org) or contact me directly [@soywod](https://matrix.to/#/@soywod:matrix.org). You can also use the mailing list [[send an email](mailto:~soywod/pimalaya@lists.sr.ht)|[subscribe](mailto:~soywod/pimalaya+subscribe@lists.sr.ht)|[unsubscribe](mailto:~soywod/pimalaya+unsubscribe@lists.sr.ht)].
If you want to **subscribe** to the mailing list, please send an email at [~soywod/pimalaya+subscribe@lists.sr.ht](mailto:~soywod/pimalaya+subscribe@lists.sr.ht).
If you want to **unsubscribe** to the mailing list, please send an email at [~soywod/pimalaya+unsubscribe@lists.sr.ht](mailto:~soywod/pimalaya+unsubscribe@lists.sr.ht).
If you want to **discuss** about the project, feel free to join the [Matrix](https://matrix.org/) workspace [#pimalaya.himalaya](https://matrix.to/#/#pimalaya.himalaya:matrix.org) or contact me directly [@soywod](https://matrix.to/#/@soywod:matrix.org).
## Credits
## Sponsoring
[![nlnet](https://nlnet.nl/logo/banner-160x60.png)](https://nlnet.nl/project/Himalaya/index.html)
Special thanks to the [nlnet](https://nlnet.nl/project/Himalaya/index.html) foundation that helped Himalaya to receive financial support from the [NGI Assure](https://www.ngi.eu/ngi-projects/ngi-assure/) program of the European Commission in September, 2022.
Special thanks to the [NLnet foundation](https://nlnet.nl/project/Himalaya/index.html) and the [European Commission](https://www.ngi.eu/) that helped the project to receive financial support from:
- [IMAP RFC3501](https://tools.ietf.org/html/rfc3501)
- [Iris](https://github.com/soywod/iris.vim), the himalaya predecessor
- [isync](https://isync.sourceforge.io/), an email synchronizer for offline usage
- [NeoMutt](https://neomutt.org/), an email terminal user interface
- [Alpine](http://alpine.x10host.com/alpine/alpine-info/), an other email terminal user interface
- [mutt-wizard](https://github.com/LukeSmithxyz/mutt-wizard), a tool over NeoMutt and isync
- [rust-imap](https://github.com/jonhoo/rust-imap), a Rust IMAP library
- [lettre](https://github.com/lettre/lettre), a Rust mailer library
- [mailparse](https://github.com/staktrace/mailparse), a Rust MIME email parser.
- [NGI Assure](https://nlnet.nl/assure/) in 2022
- [NGI Zero Untrust](https://nlnet.nl/entrust/) in 2023
## Sponsoring
If you appreciate the project, feel free to donate using one of the following providers:
[![GitHub](https://img.shields.io/badge/-GitHub%20Sponsors-fafbfc?logo=GitHub%20Sponsors)](https://github.com/sponsors/soywod)
[![PayPal](https://img.shields.io/badge/-PayPal-0079c1?logo=PayPal&logoColor=ffffff)](https://www.paypal.com/paypalme/soywod)

View file

@ -1,30 +1,58 @@
[example]
# Make this account the default one to use when no account is given to
# commands.
default = true
# The display-name and email are used to build the full email address:
# "My example account" <example@localhost>
# The display-name and the email are used to build the full email
# address: "My example account" <example@localhost>
display-name = "My example account"
email = "example@localhost"
sync = true
sync-dir = "/tmp/himalaya-sync-example"
# The signature can be a string or a path to a file.
signature = "Regards,"
signature-delim = "-- \n"
# The default backend used for all the features like adding folders,
# Enable the synchronization for this account. Running the command
# `account sync example` will synchronize all folders and all emails
# to a local Maildir at `$XDG_DATA_HOME/himalaya/example`.
sync.enable = true
# Override the default Maildir path for synchronization.
sync.dir = "/tmp/himalaya-sync-example"
# Default backend used for all the features like adding folders,
# listing envelopes or copying messages.
backend = "imap"
envelope.list.page-size = 10
envelope.list.datetime-fmt = "%F %R%:z"
# Date are converted to the user's local timezone.
envelope.list.datetime-local-tz = true
# Override the backend used for listing envelopes.
envelope.list.backend = "imap"
# Override the backend used for sending messages.
message.send.backend = "smtp"
# IMAP config
imap.host = "localhost"
imap.port = 3143
imap.login = "example@localhost"
imap.ssl = false
imap.starttls = false
imap.insecure = true
imap.auth = "passwd"
imap.passwd.keyring = "example-passwd"
imap.auth = "passwd" # or oauth2
# Override the backend used for sending messages.
message.send.backend = "smtp"
# Get password from the raw string (not safe)
# imap.passwd.raw = "password"
# Get password from a shell command
imap.passwd.cmd = ["echo example-imap-password", "cat"]
# Get password from your system keyring using secret service
# Keyring secrets can be (re)set with the command `account configure example`
# imap.passwd.keyring = "example-imap-password"
# SMTP config
smtp.host = "localhost"
@ -32,25 +60,9 @@ smtp.port = 3025
smtp.login = "example@localhost"
smtp.ssl = false
smtp.starttls = false
smtp.insecure = true
smtp.auth = "passwd"
smtp.passwd.raw = "example"
smtp.passwd.raw = "password"
[example-2]
display-name = "My example account 2"
email = "example2@localhost"
backend = "imap"
imap.host = "localhost"
imap.port = 3143
imap.login = "example2@localhost"
imap.ssl = false
imap.starttls = false
imap.insecure = true
imap.auth = "passwd"
imap.passwd.raw = "example"
message.send.backend = "sendmail"
sendmail.cmd = "sendmail"
# PGP needs to be enabled with one of those cargo feature:
# pgp-commands, pgp-gpg or pgp-native
# pgp.backend = "gpg"

View file

@ -44,8 +44,8 @@ impl AccountConfigureCommand {
#[cfg(feature = "imap")]
if let Some(ref config) = account_config.imap {
let reset = match &config.auth {
ImapAuthConfig::Passwd(config) => config.reset(),
ImapAuthConfig::OAuth2(config) => config.reset(),
ImapAuthConfig::Passwd(config) => config.reset().await,
ImapAuthConfig::OAuth2(config) => config.reset().await,
};
if let Err(err) = reset {
warn!("error while resetting imap secrets: {err}");
@ -56,8 +56,8 @@ impl AccountConfigureCommand {
#[cfg(feature = "smtp")]
if let Some(ref config) = account_config.smtp {
let reset = match &config.auth {
SmtpAuthConfig::Passwd(config) => config.reset(),
SmtpAuthConfig::OAuth2(config) => config.reset(),
SmtpAuthConfig::Passwd(config) => config.reset().await,
SmtpAuthConfig::OAuth2(config) => config.reset().await,
};
if let Err(err) = reset {
warn!("error while resetting smtp secrets: {err}");
@ -67,7 +67,7 @@ impl AccountConfigureCommand {
#[cfg(feature = "pgp")]
if let Some(ref config) = account_config.pgp {
account_config.pgp.reset().await?;
config.reset().await?;
}
}
@ -100,10 +100,11 @@ impl AccountConfigureCommand {
}
#[cfg(feature = "pgp")]
if let Some(ref config) = config.pgp {
if let Some(ref config) = account_config.pgp {
config
.pgp
.configure(&config.email, || prompt_passwd("PGP secret key password"))
.configure(&account_config.email, || {
prompt_passwd("PGP secret key password")
})
.await?;
}

View file

@ -28,10 +28,7 @@ impl AccountListCommand {
printer.print_table(
Box::new(accounts),
PrintTableOpts {
format: config
.email_reading_format
.as_ref()
.unwrap_or(&Default::default()),
format: &Default::default(),
max_width: self.table.max_width,
},
)

View file

@ -4,26 +4,21 @@
//! account in the accounts section of the user configuration file.
#[cfg(feature = "pgp")]
use email::account::PgpConfig;
use email::account::config::pgp::PgpConfig;
#[cfg(feature = "imap")]
use email::imap::config::ImapConfig;
#[cfg(feature = "smtp")]
use email::smtp::config::SmtpConfig;
use email::{
email::config::{EmailHooks, EmailTextPlainFormat},
folder::sync::FolderSyncStrategy,
maildir::config::MaildirConfig,
account::sync::config::SyncConfig, maildir::config::MaildirConfig,
sendmail::config::SendmailConfig,
};
use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};
use std::{collections::HashSet, path::PathBuf};
use crate::{
backend::BackendKind, config::prelude::*, envelope::config::EnvelopeConfig,
flag::config::FlagConfig, folder::config::FolderConfig, message::config::MessageConfig,
backend::BackendKind, envelope::config::EnvelopeConfig, flag::config::FlagConfig,
folder::config::FolderConfig, message::config::MessageConfig,
};
/// Represents all existing kind of account config.
@ -34,93 +29,29 @@ pub struct TomlAccountConfig {
pub email: String,
pub display_name: Option<String>,
pub signature_delim: Option<String>,
pub signature: Option<String>,
pub signature_delim: Option<String>,
pub downloads_dir: Option<PathBuf>,
pub folder_listing_page_size: Option<usize>,
pub folder_aliases: Option<HashMap<String, String>>,
pub email_listing_page_size: Option<usize>,
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(
default,
with = "OptionEmailTextPlainFormatDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_writing_headers: Option<Vec<String>>,
pub email_sending_save_copy: Option<bool>,
#[serde(
default,
with = "OptionEmailHooksDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>,
pub sync: Option<bool>,
pub sync_dir: Option<PathBuf>,
#[serde(
default,
with = "OptionFolderSyncStrategyDef",
skip_serializing_if = "Option::is_none"
)]
pub sync_folders_strategy: Option<FolderSyncStrategy>,
pub backend: Option<BackendKind>,
pub sync: Option<SyncConfig>,
pub folder: Option<FolderConfig>,
pub envelope: Option<EnvelopeConfig>,
pub flag: Option<FlagConfig>,
pub message: Option<MessageConfig>,
#[cfg(feature = "imap")]
#[serde(
default,
with = "OptionImapConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub imap: Option<ImapConfig>,
#[serde(
default,
with = "OptionMaildirConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "notmuch")]
#[serde(
default,
with = "OptionNotmuchConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub notmuch: Option<NotmuchConfig>,
#[cfg(feature = "smtp")]
#[serde(
default,
with = "OptionSmtpConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub smtp: Option<SmtpConfig>,
#[serde(
default,
with = "OptionSendmailConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub sendmail: Option<SendmailConfig>,
#[cfg(feature = "pgp")]
#[serde(
default,
with = "OptionPgpConfigDef",
skip_serializing_if = "Option::is_none"
)]
pub pgp: Option<PgpConfig>,
pub backend: Option<BackendKind>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "imap")]
pub imap: Option<ImapConfig>,
#[cfg(feature = "notmuch")]
pub notmuch: Option<NotmuchConfig>,
#[cfg(feature = "smtp")]
pub smtp: Option<SmtpConfig>,
#[cfg(feature = "sendmail")]
pub sendmail: Option<SendmailConfig>,
}
impl TomlAccountConfig {
@ -207,7 +138,7 @@ impl TomlAccountConfig {
pub fn add_raw_message_kind(&self) -> Option<&BackendKind> {
self.message
.as_ref()
.and_then(|msg| msg.add.as_ref())
.and_then(|msg| msg.write.as_ref())
.and_then(|add| add.backend.as_ref())
.or_else(|| self.backend.as_ref())
}
@ -223,7 +154,7 @@ impl TomlAccountConfig {
pub fn get_messages_kind(&self) -> Option<&BackendKind> {
self.message
.as_ref()
.and_then(|message| message.get.as_ref())
.and_then(|message| message.read.as_ref())
.and_then(|get| get.backend.as_ref())
.or_else(|| self.backend.as_ref())
}

View file

@ -1,5 +1,6 @@
use anyhow::{bail, Result};
use dialoguer::{Confirm, Input};
use email::account::sync::config::SyncConfig;
use email_address::EmailAddress;
use crate::{
@ -87,15 +88,20 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
_ => (),
};
config.sync = Some(
Confirm::new()
let should_configure_sync = Confirm::new()
.with_prompt(wizard_prompt!(
"Do you need an offline access to your account?"
))
.default(false)
.interact_opt()?
.unwrap_or_default(),
);
.unwrap_or_default();
if should_configure_sync {
config.sync = Some(SyncConfig {
enable: Some(true),
..Default::default()
});
}
Ok(Some((account_name, config)))
}

View file

@ -173,7 +173,7 @@ impl BackendBuilder {
MaildirSessionBuilder::new(account_config.clone(), mdir_config.clone())
}),
maildir_for_sync: Some(MaildirConfig {
root_dir: account_config.sync_dir()?,
root_dir: account_config.get_sync_dir()?,
})
.filter(|_| is_maildir_for_sync_used)
.map(|mdir_config| MaildirSessionBuilder::new(account_config.clone(), mdir_config)),
@ -691,7 +691,7 @@ impl Backend {
id_mapper = IdMapper::new(
&self.backend.account_config,
folder,
self.backend.account_config.sync_dir()?,
self.backend.account_config.get_sync_dir()?,
)?;
}
#[cfg(feature = "notmuch")]

View file

@ -1,14 +1,12 @@
pub mod args;
pub mod prelude;
pub mod wizard;
use anyhow::{anyhow, Context, Result};
use dialoguer::Confirm;
use dirs::{config_dir, home_dir};
use email::{
account::config::AccountConfig,
config::Config,
email::config::{EmailHooks, EmailTextPlainFormat},
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig,
folder::config::FolderConfig, message::config::MessageConfig,
};
use serde::{Deserialize, Serialize};
use shellexpand_utils::{canonicalize, expand};
@ -20,10 +18,7 @@ use std::{
};
use toml;
use crate::{
account::config::TomlAccountConfig, backend::BackendKind, config::prelude::*, wizard_prompt,
wizard_warn,
};
use crate::{account::config::TomlAccountConfig, backend::BackendKind, wizard_prompt, wizard_warn};
/// Represents the user config file.
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
@ -31,32 +26,10 @@ use crate::{
pub struct TomlConfig {
#[serde(alias = "name")]
pub display_name: Option<String>,
pub signature_delim: Option<String>,
pub signature: Option<String>,
pub signature_delim: Option<String>,
pub downloads_dir: Option<PathBuf>,
pub folder_listing_page_size: Option<usize>,
pub folder_aliases: Option<HashMap<String, String>>,
pub email_listing_page_size: Option<usize>,
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(
default,
with = "OptionEmailTextPlainFormatDef",
skip_serializing_if = "Option::is_none"
)]
pub email_reading_format: Option<EmailTextPlainFormat>,
pub email_writing_headers: Option<Vec<String>>,
pub email_sending_save_copy: Option<bool>,
#[serde(
default,
with = "OptionEmailHooksDef",
skip_serializing_if = "Option::is_none"
)]
pub email_hooks: Option<EmailHooks>,
#[serde(flatten)]
pub accounts: HashMap<String, TomlAccountConfig>,
}
@ -203,7 +176,7 @@ impl TomlConfig {
let (account_name, mut toml_account_config) =
self.into_toml_account_config(account_name)?;
if let Some(true) = toml_account_config.sync {
if let Some(true) = toml_account_config.sync.as_ref().and_then(|c| c.enable) {
if !disable_cache {
toml_account_config.backend = Some(BackendKind::MaildirForSync);
}
@ -211,22 +184,10 @@ impl TomlConfig {
let config = Config {
display_name: self.display_name,
signature_delim: self.signature_delim,
signature: self.signature,
signature_delim: self.signature_delim,
downloads_dir: self.downloads_dir,
folder_listing_page_size: self.folder_listing_page_size,
folder_aliases: self.folder_aliases,
email_listing_page_size: self.email_listing_page_size,
email_listing_datetime_fmt: self.email_listing_datetime_fmt,
email_listing_datetime_local_tz: self.email_listing_datetime_local_tz,
email_reading_headers: self.email_reading_headers,
email_reading_format: self.email_reading_format,
email_writing_headers: self.email_writing_headers,
email_sending_save_copy: self.email_sending_save_copy,
email_hooks: self.email_hooks,
accounts: HashMap::from_iter(self.accounts.clone().into_iter().map(
|(name, config)| {
(
@ -235,27 +196,23 @@ impl TomlConfig {
name,
email: config.email,
display_name: config.display_name,
signature_delim: config.signature_delim,
signature: config.signature,
signature_delim: config.signature_delim,
downloads_dir: config.downloads_dir,
folder_listing_page_size: config.folder_listing_page_size,
folder_aliases: config.folder_aliases.unwrap_or_default(),
email_listing_page_size: config.email_listing_page_size,
email_listing_datetime_fmt: config.email_listing_datetime_fmt,
email_listing_datetime_local_tz: config.email_listing_datetime_local_tz,
email_reading_headers: config.email_reading_headers,
email_reading_format: config.email_reading_format.unwrap_or_default(),
email_writing_headers: config.email_writing_headers,
email_sending_save_copy: config.email_sending_save_copy,
email_hooks: config.email_hooks.unwrap_or_default(),
sync: config.sync.unwrap_or_default(),
sync_dir: config.sync_dir,
sync_folders_strategy: config.sync_folders_strategy.unwrap_or_default(),
folder: config.folder.map(|c| FolderConfig {
aliases: c.remote.aliases,
list: c.list.map(|c| c.remote),
}),
envelope: config.envelope.map(|c| EnvelopeConfig {
list: c.list.map(|c| c.remote),
}),
message: config.message.map(|c| MessageConfig {
read: c.read.map(|c| c.remote),
write: c.write.map(|c| c.remote),
send: c.send.map(|c| c.remote),
}),
sync: config.sync,
#[cfg(feature = "pgp")]
pgp: config.pgp,
},

View file

@ -58,7 +58,7 @@ impl EnvelopeListCommand {
let page_size = self
.page_size
.unwrap_or(account_config.email_listing_page_size());
.unwrap_or(account_config.get_envelope_list_page_size());
let page = 1.max(self.page) - 1;
let envelopes = backend.list_envelopes(folder, page_size, page).await?;
@ -66,7 +66,7 @@ impl EnvelopeListCommand {
printer.print_table(
Box::new(envelopes),
PrintTableOpts {
format: &account_config.email_reading_format,
format: &account_config.get_message_read_format(),
max_width: self.table.max_width,
},
)?;

View file

@ -28,6 +28,9 @@ impl EnvelopeConfig {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct EnvelopeListConfig {
pub backend: Option<BackendKind>,
#[serde(flatten)]
pub remote: email::envelope::list::config::EnvelopeListConfig,
}
impl EnvelopeListConfig {

View file

@ -65,12 +65,11 @@ impl AttachmentDownloadCommand {
))?;
for attachment in attachments {
let filename = attachment
let filename: PathBuf = attachment
.filename
.map(PathBuf::from)
.and_then(|f| f.file_name().map(|f| f.to_string_lossy().to_string()))
.unwrap_or_else(|| Uuid::new_v4().to_string());
let filepath = account_config.download_fpath(&filename)?;
.unwrap_or_else(|| Uuid::new_v4().to_string())
.into();
let filepath = account_config.get_download_file_path(&filename)?;
printer.print_log(format!("Downloading {:?}", filepath))?;
fs::write(&filepath, &attachment.body)
.with_context(|| format!("cannot save attachment at {filepath:?}"))?;

View file

@ -60,7 +60,7 @@ impl MessageMailtoCommand {
}
}
match account_config.signature() {
match account_config.find_full_signature() {
Ok(Some(ref signature)) => builder = builder.text_body(body + "\n\n" + signature),
Ok(None) => builder = builder.text_body(body),
Err(err) => {
@ -71,7 +71,7 @@ impl MessageMailtoCommand {
let tpl = account_config
.generate_tpl_interpreter()
.with_show_only_headers(account_config.email_writing_headers())
.with_show_only_headers(account_config.get_message_write_headers())
.build()
.from_msg_builder(builder)
.await?;

View file

@ -35,7 +35,7 @@ impl MessageSendCommand {
let (toml_account_config, account_config) =
config.clone().into_account_configs(account, cache)?;
let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
let folder = account_config.sent_folder_alias()?;
let folder = account_config.get_sent_folder_alias()?;
let is_tty = io::stdin().is_terminal();
let is_json = printer.is_json();
@ -52,7 +52,7 @@ impl MessageSendCommand {
backend.send_raw_message(msg.as_bytes()).await?;
if account_config.email_sending_save_copy.unwrap_or_default() {
if account_config.should_save_copy_sent_message() {
backend
.add_raw_message_with_flag(&folder, msg.as_bytes(), Flag::Seen)
.await?;

View file

@ -5,12 +5,12 @@ use crate::backend::BackendKind;
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageConfig {
pub add: Option<MessageAddConfig>,
pub write: Option<MessageAddConfig>,
pub send: Option<MessageSendConfig>,
pub peek: Option<MessagePeekConfig>,
pub get: Option<MessageGetConfig>,
pub read: Option<MessageGetConfig>,
pub copy: Option<MessageCopyConfig>,
#[serde(default, rename = "move", skip_serializing_if = "Option::is_none")]
#[serde(rename = "move")]
pub move_: Option<MessageMoveConfig>,
}
@ -18,7 +18,7 @@ impl MessageConfig {
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
let mut kinds = HashSet::default();
if let Some(add) = &self.add {
if let Some(add) = &self.write {
kinds.extend(add.get_used_backends());
}
@ -30,7 +30,7 @@ impl MessageConfig {
kinds.extend(peek.get_used_backends());
}
if let Some(get) = &self.get {
if let Some(get) = &self.read {
kinds.extend(get.get_used_backends());
}
@ -49,6 +49,9 @@ impl MessageConfig {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageAddConfig {
pub backend: Option<BackendKind>,
#[serde(flatten)]
pub remote: email::message::add_raw::config::MessageWriteConfig,
}
impl MessageAddConfig {
@ -66,6 +69,9 @@ impl MessageAddConfig {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageSendConfig {
pub backend: Option<BackendKind>,
#[serde(flatten)]
pub remote: email::message::send_raw::config::MessageSendConfig,
}
impl MessageSendConfig {
@ -100,6 +106,9 @@ impl MessagePeekConfig {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageGetConfig {
pub backend: Option<BackendKind>,
#[serde(flatten)]
pub remote: email::message::get::config::MessageReadConfig,
}
impl MessageGetConfig {

View file

@ -60,7 +60,7 @@ impl TemplateSaveCommand {
let mut compiler = MmlCompilerBuilder::new();
#[cfg(feature = "pgp")]
compiler.set_some_pgp(config.pgp.clone());
compiler.set_some_pgp(account_config.pgp.clone());
let msg = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
backend.add_raw_message(folder, &msg).await?;

View file

@ -38,7 +38,7 @@ impl TemplateSendCommand {
let (toml_account_config, account_config) =
config.clone().into_account_configs(account, cache)?;
let backend = Backend::new(toml_account_config, account_config.clone(), true).await?;
let folder = account_config.sent_folder_alias()?;
let folder = account_config.get_sent_folder_alias()?;
let is_tty = io::stdin().is_terminal();
let is_json = printer.is_json();
@ -57,13 +57,13 @@ impl TemplateSendCommand {
let mut compiler = MmlCompilerBuilder::new();
#[cfg(feature = "pgp")]
compiler.set_some_pgp(config.pgp.clone());
compiler.set_some_pgp(account_config.pgp.clone());
let msg = compiler.build(tpl.as_str())?.compile().await?.into_vec()?;
backend.send_raw_message(&msg).await?;
if account_config.email_sending_save_copy.unwrap_or_default() {
if account_config.should_save_copy_sent_message() {
backend
.add_raw_message_with_flag(&folder, &msg, Flag::Seen)
.await?;

View file

@ -42,7 +42,7 @@ impl FolderListCommand {
printer.print_table(
Box::new(folders),
PrintTableOpts {
format: &account_config.email_reading_format,
format: &account_config.get_message_read_format(),
max_width: self.table.max_width,
},
)?;

View file

@ -10,6 +10,9 @@ pub struct FolderConfig {
pub expunge: Option<FolderExpungeConfig>,
pub purge: Option<FolderPurgeConfig>,
pub delete: Option<FolderDeleteConfig>,
#[serde(flatten)]
pub remote: email::folder::config::FolderConfig,
}
impl FolderConfig {
@ -60,6 +63,9 @@ impl FolderAddConfig {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct FolderListConfig {
pub backend: Option<BackendKind>,
#[serde(flatten)]
pub remote: email::folder::list::config::FolderListConfig,
}
impl FolderListConfig {

View file

@ -89,7 +89,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
let config = match secret {
Some(idx) if SECRETS[idx] == KEYRING => {
Secret::new_keyring_entry(format!("{account_name}-imap-passwd"))
.set_keyring_entry_secret(prompt_passwd("IMAP password")?)?;
.set_keyring_entry_secret(prompt_passwd("IMAP password")?)
.await?;
PasswdConfig::default()
}
Some(idx) if SECRETS[idx] == RAW => PasswdConfig {
@ -130,7 +131,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
.with_prompt("IMAP OAuth 2.0 client secret")
.interact()?;
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))
.set_keyring_entry_secret(&client_secret)?;
.set_keyring_entry_secret(&client_secret)
.await?;
config.auth_url = Input::with_theme(&*THEME)
.with_prompt("IMAP OAuth 2.0 authorization URL")
@ -210,11 +212,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
.await?;
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"))
.set_keyring_entry_secret(access_token)?;
.set_keyring_entry_secret(access_token)
.await?;
if let Some(refresh_token) = &refresh_token {
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))
.set_keyring_entry_secret(refresh_token)?;
.set_keyring_entry_secret(refresh_token)
.await?;
}
ImapAuthConfig::OAuth2(config)

View file

@ -89,7 +89,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
let config = match secret {
Some(idx) if SECRETS[idx] == KEYRING => {
Secret::new_keyring_entry(format!("{account_name}-smtp-passwd"))
.set_keyring_entry_secret(prompt_passwd("SMTP password")?)?;
.set_keyring_entry_secret(prompt_passwd("SMTP password")?)
.await?;
PasswdConfig::default()
}
Some(idx) if SECRETS[idx] == RAW => PasswdConfig {
@ -130,7 +131,8 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
.with_prompt("SMTP OAuth 2.0 client secret")
.interact()?;
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"))
.set_keyring_entry_secret(&client_secret)?;
.set_keyring_entry_secret(&client_secret)
.await?;
config.auth_url = Input::with_theme(&*THEME)
.with_prompt("SMTP OAuth 2.0 authorization URL")
@ -210,11 +212,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
.await?;
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"))
.set_keyring_entry_secret(access_token)?;
.set_keyring_entry_secret(access_token)
.await?;
if let Some(refresh_token) = &refresh_token {
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))
.set_keyring_entry_secret(refresh_token)?;
.set_keyring_entry_secret(refresh_token)
.await?;
}
SmtpAuthConfig::OAuth2(config)

View file

@ -89,8 +89,8 @@ pub async fn edit_tpl_with_editor<P: Printer>(
backend.send_raw_message(&email).await?;
if config.email_sending_save_copy.unwrap_or_default() {
let sent_folder = config.sent_folder_alias()?;
if config.should_save_copy_sent_message() {
let sent_folder = config.get_sent_folder_alias()?;
printer.print_log(format!("Adding email to the {} folder…", sent_folder))?;
backend
.add_raw_message_with_flag(&sent_folder, &email, Flag::Seen)