From 99ec7c6d97aa359a433e9830f00402bd94a9ed6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Wed, 2 Aug 2023 18:03:47 +0200 Subject: [PATCH] add pgp support --- Cargo.lock | 103 ++++++++++++++++++++++----------- Cargo.toml | 10 ++-- src/config/prelude.rs | 38 +++++++++++- src/domain/account/config.rs | 47 ++------------- src/domain/account/handlers.rs | 7 +++ src/domain/email/handlers.rs | 8 +-- src/domain/tpl/handlers.rs | 16 ++--- src/main.rs | 2 - src/ui/editor.rs | 8 +-- 9 files changed, 141 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74b801a..5d4c369 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1096,9 +1096,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1348,7 +1348,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "himalaya" -version = "0.8.4-beta" +version = "0.9.0-beta" dependencies = [ "anyhow", "async-trait", @@ -1517,6 +1517,22 @@ dependencies = [ "tokio-rustls 0.23.4", ] +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls 0.21.1", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.0", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1569,9 +1585,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2291,9 +2307,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" @@ -2397,7 +2413,7 @@ dependencies = [ [[package]] name = "pimalaya-email" version = "0.14.1-beta" -source = "git+https://git.sr.ht/~soywod/pimalaya#a912fa5d3babfdaa8d240581f9a831d7118cb729" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "advisory-lock", "ammonia", @@ -2418,13 +2434,12 @@ dependencies = [ "notmuch", "once_cell", "ouroboros", - "pgp", "pimalaya-email-tpl", "pimalaya-keyring", "pimalaya-oauth2", + "pimalaya-pgp", "pimalaya-process", "pimalaya-secret", - "rand", "rayon", "regex", "rfc2047-decoder", @@ -2432,7 +2447,6 @@ dependencies = [ "rustls 0.21.1", "rustls-native-certs", "shellexpand", - "smallvec", "thiserror", "tokio", "tokio-rustls 0.24.0", @@ -2445,9 +2459,8 @@ dependencies = [ [[package]] name = "pimalaya-email-tpl" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a8b37bec4947fb310d807e92e2970d088da3416bc2c7d897814187c639f50a" +version = "0.3.2-beta" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "async-recursion", "chumsky 0.9.0", @@ -2455,7 +2468,9 @@ dependencies = [ "mail-builder", "mail-parser", "nanohtml2text", - "pimalaya-process", + "pimalaya-keyring", + "pimalaya-pgp", + "pimalaya-secret", "shellexpand", "thiserror", "tree_magic", @@ -2463,9 +2478,8 @@ dependencies = [ [[package]] name = "pimalaya-keyring" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7b7da6f9ce8a647ab83dda8cfd657dc0acb62e6ac1edd95f3b434341fadb03" +version = "0.0.6-beta" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "keyring", "log", @@ -2474,9 +2488,8 @@ dependencies = [ [[package]] name = "pimalaya-oauth2" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c47b1326c05b2f2ed610faf2eb03aecd5caaf4b3a78db34cfa97fc102c9b72ca" +version = "0.0.5-beta" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "log", "oauth2", @@ -2486,11 +2499,30 @@ dependencies = [ "url", ] +[[package]] +name = "pimalaya-pgp" +version = "0.0.1" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" +dependencies = [ + "async-recursion", + "futures", + "hyper", + "hyper-rustls 0.24.1", + "log", + "pgp", + "rand", + "sha1", + "smallvec", + "thiserror", + "tokio", + "url", + "z-base-32", +] + [[package]] name = "pimalaya-process" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9d1e1ab6334d4e4d06613cd65f3fed34e5d2d7b1488f3d1a0d2fdffdba5d1c" +version = "0.0.6-beta" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "log", "thiserror", @@ -2499,9 +2531,8 @@ dependencies = [ [[package]] name = "pimalaya-secret" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966d3dd6e8d9dd39ac80c20597d68b47eb5170c968fbe3ebbb425fca530037f6" +version = "0.0.6-beta" +source = "git+https://git.sr.ht/~soywod/pimalaya#da0eb6f289cf16971b62347ac00a799c2fa05a99" dependencies = [ "log", "pimalaya-keyring", @@ -2787,7 +2818,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls", + "hyper-rustls 0.23.2", "ipnet", "js-sys", "log", @@ -3681,9 +3712,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -3714,12 +3745,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", "serde", ] @@ -4087,6 +4118,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "z-base-32" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e897c250a7dfbb7002a324817f2540ab789a8f32d505032ca623a4109245e87" + [[package]] name = "zeroize" version = "1.5.7" diff --git a/Cargo.toml b/Cargo.toml index 27bfa98..ca87eb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "himalaya" description = "CLI to manage your emails." -version = "0.8.4-beta" +version = "0.9.0-beta" authors = ["soywod "] edition = "2021" license = "MIT" @@ -46,10 +46,10 @@ log = "0.4" md5 = "0.7.0" once_cell = "1.16.0" pimalaya-email = { git = "https://git.sr.ht/~soywod/pimalaya", default-features = false } -pimalaya-keyring = "=0.0.5" -pimalaya-oauth2 = "=0.0.4" -pimalaya-process = "=0.0.5" -pimalaya-secret = "=0.0.5" +pimalaya-keyring = { git = "https://git.sr.ht/~soywod/pimalaya" } +pimalaya-oauth2 = { git = "https://git.sr.ht/~soywod/pimalaya" } +pimalaya-process = { git = "https://git.sr.ht/~soywod/pimalaya" } +pimalaya-secret = { git = "https://git.sr.ht/~soywod/pimalaya" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" shellexpand = "2.1" diff --git a/src/config/prelude.rs b/src/config/prelude.rs index 91b3388..34db8ba 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -5,7 +5,9 @@ use pimalaya_email::backend::{ImapAuthConfig, ImapConfig}; #[cfg(feature = "smtp-sender")] use pimalaya_email::sender::{SmtpAuthConfig, SmtpConfig}; use pimalaya_email::{ - account::{OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig}, + account::{ + OAuth2Config, OAuth2Method, OAuth2Scopes, PasswdConfig, PgpConfig, PgpKey, PgpNativeConfig, + }, backend::{BackendConfig, MaildirConfig}, email::{EmailHooks, EmailTextPlainFormat}, folder::sync::FolderSyncStrategy, @@ -387,3 +389,37 @@ pub enum FolderSyncStrategyDef { #[serde(alias = "ignore")] Exclude(HashSet), } + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(remote = "PgpConfig", tag = "backend", rename_all = "kebab-case")] +pub enum PgpConfigDef { + #[default] + None, + #[serde(with = "PgpNativeConfigDef")] + Native(PgpNativeConfig), +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(remote = "PgpNativeConfig", rename_all = "kebab-case")] +pub struct PgpNativeConfigDef { + #[serde(default, with = "PgpKeyDef")] + secret_key: PgpKey, + #[serde(default, with = "SecretDef")] + secret_key_passwd: Secret, + #[serde(default, with = "PgpKeyDef")] + public_key: PgpKey, + #[serde(default = "PgpNativeConfig::default_wkd")] + wkd: bool, + #[serde(default = "PgpNativeConfig::default_key_servers")] + key_servers: Vec, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(remote = "PgpKey", rename_all = "kebab-case")] +pub enum PgpKeyDef { + #[default] + None, + Path(PathBuf), + #[serde(with = "EntryDef")] + Keyring(Entry), +} diff --git a/src/domain/account/config.rs b/src/domain/account/config.rs index b942214..dd3e2f8 100644 --- a/src/domain/account/config.rs +++ b/src/domain/account/config.rs @@ -8,7 +8,7 @@ use pimalaya_email::backend::ImapAuthConfig; #[cfg(feature = "smtp-sender")] use pimalaya_email::sender::SmtpAuthConfig; use pimalaya_email::{ - account::AccountConfig, + account::{AccountConfig, PgpConfig}, backend::BackendConfig, email::{EmailHooks, EmailTextPlainFormat}, folder::sync::FolderSyncStrategy, @@ -90,6 +90,9 @@ pub struct DeserializedAccountConfig { pub backend: BackendConfig, #[serde(flatten, with = "SenderConfigDef")] pub sender: SenderConfig, + + #[serde(default, with = "PgpConfigDef")] + pub pgp: PgpConfig, } impl DeserializedAccountConfig { @@ -155,46 +158,6 @@ impl DeserializedAccountConfig { .map(ToOwned::to_owned) .or_else(|| config.email_reading_headers.as_ref().map(ToOwned::to_owned)), email_reading_format: self.email_reading_format.clone(), - email_reading_verify_cmd: self - .email_reading_verify_cmd - .as_ref() - .map(ToOwned::to_owned) - .or_else(|| { - config - .email_reading_verify_cmd - .as_ref() - .map(ToOwned::to_owned) - }), - email_reading_decrypt_cmd: self - .email_reading_decrypt_cmd - .as_ref() - .map(ToOwned::to_owned) - .or_else(|| { - config - .email_reading_decrypt_cmd - .as_ref() - .map(ToOwned::to_owned) - }), - email_writing_sign_cmd: self - .email_writing_sign_cmd - .as_ref() - .map(ToOwned::to_owned) - .or_else(|| { - config - .email_writing_sign_cmd - .as_ref() - .map(ToOwned::to_owned) - }), - email_writing_encrypt_cmd: self - .email_writing_encrypt_cmd - .as_ref() - .map(ToOwned::to_owned) - .or_else(|| { - config - .email_writing_encrypt_cmd - .as_ref() - .map(ToOwned::to_owned) - }), email_writing_headers: self .email_writing_headers .as_ref() @@ -258,7 +221,7 @@ impl DeserializedAccountConfig { sender }, - pgp: Default::default(), + pgp: self.pgp.clone(), } } } diff --git a/src/domain/account/handlers.rs b/src/domain/account/handlers.rs index be8d5d6..4319735 100644 --- a/src/domain/account/handlers.rs +++ b/src/domain/account/handlers.rs @@ -72,6 +72,8 @@ pub async fn configure(config: &AccountConfig, reset: bool) -> Result<()> { warn!("{err}"); } } + + config.pgp.reset().await?; } #[cfg(feature = "imap-backend")] @@ -102,6 +104,11 @@ pub async fn configure(config: &AccountConfig, reset: bool) -> Result<()> { }?; } + config + .pgp + .configure(&config.email, || prompt_passwd("PGP secret key password")) + .await?; + println!( "Account successfully {}configured!", if reset { "re" } else { "" } diff --git a/src/domain/email/handlers.rs b/src/domain/email/handlers.rs index ffe4421..ba43d6e 100644 --- a/src/domain/email/handlers.rs +++ b/src/domain/email/handlers.rs @@ -185,7 +185,7 @@ pub async fn mailto( let tpl = config .generate_tpl_interpreter() - .show_only_headers(config.email_writing_headers()) + .with_show_only_headers(config.email_writing_headers()) .interpret_msg_builder(builder) .await?; @@ -235,9 +235,9 @@ pub async fn read( let tpl: String = email .to_read_tpl(&config, |tpl| match text_mime { "html" => tpl - .hide_all_headers() - .filter_parts(FilterParts::Only("text/html".into())), - _ => tpl.show_additional_headers(&headers), + .with_hide_all_headers() + .with_filter_parts(FilterParts::Only("text/html".into())), + _ => tpl.with_show_additional_headers(&headers), }) .await? .into(); diff --git a/src/domain/tpl/handlers.rs b/src/domain/tpl/handlers.rs index 17ec628..3e2b210 100644 --- a/src/domain/tpl/handlers.rs +++ b/src/domain/tpl/handlers.rs @@ -3,7 +3,7 @@ use atty::Stream; use pimalaya_email::{ account::AccountConfig, backend::Backend, - email::{Flags, Message, Tpl}, + email::{Flag, Flags, Message, Tpl}, sender::Sender, }; use std::io::{stdin, BufRead}; @@ -86,8 +86,8 @@ pub async fn save( .collect::>() .join("\n") }) - .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) - .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) + .with_pgp_encrypt(config.pgp.clone()) + .with_pgp_sign(config.pgp.clone()) .compile() .await? .write_to_vec()?; @@ -103,9 +103,9 @@ pub async fn send( printer: &mut P, backend: &mut dyn Backend, sender: &mut dyn Sender, - folder: &str, tpl: String, ) -> Result<()> { + let folder = config.sent_folder_alias()?; let email = Tpl::from(if atty::is(Stream::Stdin) || printer.is_json() { tpl.replace("\r", "") } else { @@ -116,8 +116,8 @@ pub async fn send( .collect::>() .join("\n") }) - .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) - .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) + .with_pgp_encrypt(config.pgp.clone()) + .with_pgp_sign(config.pgp.clone()) .compile() .await? .write_to_vec()?; @@ -125,7 +125,9 @@ pub async fn send( sender.send(&email).await?; if config.email_sending_save_copy { - backend.add_email(folder, &email, &Flags::default()).await?; + backend + .add_email(&folder, &email, &Flags::from_iter([Flag::Seen])) + .await?; } printer.print("Template successfully sent!")?; diff --git a/src/main.rs b/src/main.rs index 904822c..f91dc1a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -513,7 +513,6 @@ async fn main() -> Result<()> { return Ok(()); } Some(tpl::args::Cmd::Send(tpl)) => { - let folder = folder.unwrap_or(DEFAULT_INBOX_FOLDER); let mut backend = backend_builder.clone().into_build().await?; let mut sender = sender_builder.build().await?; tpl::handlers::send( @@ -521,7 +520,6 @@ async fn main() -> Result<()> { &mut printer, backend.as_mut(), sender.as_mut(), - &folder, tpl, ) .await?; diff --git a/src/ui/editor.rs b/src/ui/editor.rs index f752fcd..ecbf6b3 100644 --- a/src/ui/editor.rs +++ b/src/ui/editor.rs @@ -76,8 +76,8 @@ pub async fn edit_tpl_with_editor( Ok(PostEditChoice::Send) => { printer.print_log("Sending email…")?; let email = tpl - .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) - .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) + .with_pgp_encrypt(config.pgp.clone()) + .with_pgp_sign(config.pgp.clone()) .compile() .await? .write_to_vec()?; @@ -103,8 +103,8 @@ pub async fn edit_tpl_with_editor( } Ok(PostEditChoice::RemoteDraft) => { let email = tpl - .some_pgp_sign_cmd(config.email_writing_sign_cmd.clone()) - .some_pgp_encrypt_cmd(config.email_writing_encrypt_cmd.clone()) + .with_pgp_encrypt(config.pgp.clone()) + .with_pgp_sign(config.pgp.clone()) .compile() .await? .write_to_vec()?;