diff --git a/meli/src/conf/overrides.rs b/meli/src/conf/overrides.rs index 4c30445c..8d205059 100644 --- a/meli/src/conf/overrides.rs +++ b/meli/src/conf/overrides.rs @@ -42,5 +42,5 @@ use crate::conf::{*, data_types::*}; # [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct TagsSettingsOverride { # [serde (deserialize_with = "tag_color_de")] # [serde (default)] pub colors : Option < IndexMap < TagHash , Color > > , # [serde (deserialize_with = "tag_set_de" , alias = "ignore-tags")] # [serde (default)] pub ignore_tags : Option < IndexSet < TagHash > > } impl Default for TagsSettingsOverride { fn default () -> Self { Self { colors : None , ignore_tags : None } } } -# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PGPSettingsOverride { # [doc = " auto verify signed e-mail according to RFC3156"] # [doc = " Default: true"] # [serde (alias = "auto-verify-signatures")] # [serde (default)] pub auto_verify_signatures : Option < ActionFlag > , # [doc = " auto decrypt encrypted e-mail"] # [doc = " Default: true"] # [serde (alias = "auto-decrypt")] # [serde (default)] pub auto_decrypt : Option < ActionFlag > , # [doc = " always sign sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-sign")] # [serde (default)] pub auto_sign : Option < ActionFlag > , # [doc = " Auto encrypt sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-encrypt")] # [serde (default)] pub auto_encrypt : Option < ActionFlag > , # [doc = " Default: None"] # [serde (alias = "sign-key")] # [serde (default)] pub sign_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "decrypt-key")] # [serde (default)] pub decrypt_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "encrypt-key")] # [serde (default)] pub encrypt_key : Option < Option < String > > , # [doc = " Allow remote lookups"] # [doc = " Default: False"] # [serde (alias = "allow-remote-lookups")] # [serde (default)] pub allow_remote_lookup : Option < ActionFlag > , # [doc = " Remote lookup mechanisms."] # [doc = " Default: \"local,wkd\""] # [cfg_attr (feature = "gpgme" , serde (alias = "remote-lookup-mechanisms"))] # [cfg (feature = "gpgme")] # [serde (default)] pub remote_lookup_mechanisms : Option < melib :: gpgme :: LocateKey > , # [cfg (not (feature = "gpgme"))] # [cfg_attr (not (feature = "gpgme") , serde (alias = "remote-lookup-mechanisms"))] # [serde (default)] pub remote_lookup_mechanisms : Option < String > } impl Default for PGPSettingsOverride { fn default () -> Self { Self { auto_verify_signatures : None , auto_decrypt : None , auto_sign : None , auto_encrypt : None , sign_key : None , decrypt_key : None , encrypt_key : None , allow_remote_lookup : None , remote_lookup_mechanisms : None } } } +# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PGPSettingsOverride { # [doc = " auto verify signed e-mail according to RFC3156"] # [doc = " Default: true"] # [serde (alias = "auto-verify-signatures")] # [serde (default)] pub auto_verify_signatures : Option < ActionFlag > , # [doc = " auto decrypt encrypted e-mail"] # [doc = " Default: true"] # [serde (alias = "auto-decrypt")] # [serde (default)] pub auto_decrypt : Option < ActionFlag > , # [doc = " always sign sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-sign")] # [serde (default)] pub auto_sign : Option < ActionFlag > , # [doc = " Auto encrypt sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-encrypt")] # [serde (default)] pub auto_encrypt : Option < ActionFlag > , # [doc = " Default: None"] # [serde (alias = "sign-key")] # [serde (default)] pub sign_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "decrypt-key")] # [serde (default)] pub decrypt_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "encrypt-key")] # [serde (default)] pub encrypt_key : Option < Option < String > > , # [doc = " Default: true"] # [serde (alias = "encrypt-for-self")] # [serde (default)] pub encrypt_for_self : Option < bool > , # [doc = " Allow remote lookups"] # [doc = " Default: False"] # [serde (alias = "allow-remote-lookups")] # [serde (default)] pub allow_remote_lookup : Option < ActionFlag > , # [doc = " Remote lookup mechanisms."] # [doc = " Default: \"local,wkd\""] # [cfg_attr (feature = "gpgme" , serde (alias = "remote-lookup-mechanisms"))] # [cfg (feature = "gpgme")] # [serde (default)] pub remote_lookup_mechanisms : Option < melib :: gpgme :: LocateKey > , # [cfg (not (feature = "gpgme"))] # [cfg_attr (not (feature = "gpgme") , serde (alias = "remote-lookup-mechanisms"))] # [serde (default)] pub remote_lookup_mechanisms : Option < String > } impl Default for PGPSettingsOverride { fn default () -> Self { Self { auto_verify_signatures : None , auto_decrypt : None , auto_sign : None , auto_encrypt : None , sign_key : None , decrypt_key : None , encrypt_key : None , encrypt_for_self : None , allow_remote_lookup : None , remote_lookup_mechanisms : None } } } diff --git a/meli/src/conf/pgp.rs b/meli/src/conf/pgp.rs index 52973277..170e81ef 100644 --- a/meli/src/conf/pgp.rs +++ b/meli/src/conf/pgp.rs @@ -60,6 +60,10 @@ pub struct PGPSettings { #[serde(default = "none", alias = "encrypt-key")] pub encrypt_key: Option, + /// Default: true + #[serde(default = "true_val", alias = "encrypt-for-self")] + pub encrypt_for_self: bool, + /// Allow remote lookups /// Default: False #[serde( @@ -99,6 +103,7 @@ impl Default for PGPSettings { auto_decrypt: true.into(), auto_sign: false.into(), auto_encrypt: false.into(), + encrypt_for_self: true, sign_key: None, decrypt_key: None, encrypt_key: None, diff --git a/meli/src/mail.rs b/meli/src/mail.rs index 34eba20f..44bd9649 100644 --- a/meli/src/mail.rs +++ b/meli/src/mail.rs @@ -21,18 +21,21 @@ //! Entities that handle Mail specific functions. +use std::{future::Future, pin::Pin}; + use indexmap::IndexMap; use melib::{ backends::{AccountHash, Mailbox, MailboxHash}, email::{attachment_types::*, attachments::*}, + text::{TextProcessing, Truncate}, thread::ThreadNodeHash, }; +use uuid::Uuid; use super::*; -use crate::{ - melib::text::{TextProcessing, Truncate}, - uuid::Uuid, -}; + +pub type AttachmentBoxFuture = Pin> + Send>>; +pub type AttachmentFilterBox = Box AttachmentBoxFuture + Send>; pub mod listing; pub use crate::listing::*; diff --git a/meli/src/mail/compose.rs b/meli/src/mail/compose.rs index ebe0e1c7..1500902d 100644 --- a/meli/src/mail/compose.rs +++ b/meli/src/mail/compose.rs @@ -888,6 +888,8 @@ impl Component for Composer { self.gpg_state.sign_mail = Some(*account_settings!(context[self.account_hash].pgp.auto_sign)); } + self.gpg_state.encrypt_for_self = + *account_settings!(context[self.account_hash].pgp.encrypt_for_self); if !self.draft.headers().contains_key(HeaderName::FROM) || self.draft.headers()[HeaderName::FROM].is_empty() { @@ -2564,16 +2566,7 @@ pub fn send_draft_async( let format_flowed = *account_settings!(context[account_hash].composing.format_flowed); let event_sender = context.main_loop_handler.sender.clone(); #[cfg(feature = "gpgme")] - #[allow(clippy::type_complexity)] - let mut filters_stack: Vec< - Box< - dyn FnOnce( - AttachmentBuilder, - ) - -> Pin> + Send>> - + Send, - >, - > = vec![]; + let mut filters_stack: Vec = vec![]; #[cfg(feature = "gpgme")] if gpg_state.sign_mail.unwrap_or(ActionFlag::False).is_true() && !gpg_state @@ -2623,13 +2616,14 @@ pub fn send_draft_async( let send_mail = account_settings!(context[account_hash].send_mail).clone(); let send_cb = context.accounts[&account_hash].send_async(send_mail); let mut content_type = ContentType::default(); - if format_flowed { - if let ContentType::Text { + if let ( + true, + ContentType::Text { ref mut parameters, .. - } = content_type - { - parameters.push((b"format".to_vec(), b"flowed".to_vec())); - } + }, + ) = (format_flowed, &mut content_type) + { + parameters.push((b"format".to_vec(), b"flowed".to_vec())); } let mut body: AttachmentBuilder = Attachment::new( content_type, diff --git a/meli/src/mail/pgp.rs b/meli/src/mail/pgp.rs index 99567dca..27a2f721 100644 --- a/meli/src/mail/pgp.rs +++ b/meli/src/mail/pgp.rs @@ -23,7 +23,6 @@ use std::{ collections::{hash_map::DefaultHasher, BTreeMap}, future::Future, hash::{Hash, Hasher}, - pin::Pin, sync::{Arc, Mutex}, }; @@ -38,6 +37,8 @@ use melib::{ parser::BytesExt, }; +use super::AttachmentBoxFuture; + pub async fn decrypt(raw: Vec) -> Result<(melib_pgp::DecryptionMetadata, Vec)> { let mut ctx = Context::new()?; let cipher = ctx.new_data_mem(&raw)?; @@ -88,31 +89,47 @@ pub fn sign_filter( Box::pin(async move { if let Some(default_key) = default_key { let mut ctx = Context::new()?; - let data = ctx.new_data_mem(&melib_pgp::convert_attachment_to_rfc_spec( - a.into_raw().as_bytes(), - ))?; - let sig_attachment = Attachment::new( - ContentType::PGPSignature, - Default::default(), - ctx.sign(sign_keys, data)?.await?, - ); - let a: AttachmentBuilder = a.into(); - let parts = vec![a, sig_attachment.into()]; - let boundary = ContentType::make_boundary(&parts); - Ok(Attachment::new( - ContentType::Multipart { - boundary: boundary.into_bytes(), - kind: MultipartType::Signed, - parts: parts.into_iter().map(|a| a.into()).collect::>(), - parameters: vec![], - }, - Default::default(), - vec![], - ) - .into()) - }) - }, - ) + ctx.set_auto_key_locate(LocateKey::LOCAL)?; + let keys = ctx.keylist(false, Some(default_key.clone()))?.await?; + if keys.is_empty() { + return Err(Error::new(format!( + "Could not locate sign key with ID `{}`", + default_key + ))); + } + sign_keys.extend(keys); + } + if sign_keys.is_empty() { + return Err(Error::new( + "No key was selected for signing; please select one.", + )); + } + let a: Attachment = a.into(); + let mut ctx = Context::new()?; + let data = ctx.new_data_mem(&melib_pgp::convert_attachment_to_rfc_spec( + a.into_raw().as_bytes(), + ))?; + let sig_attachment = Attachment::new( + ContentType::PGPSignature, + Default::default(), + ctx.sign(sign_keys, data)?.await?, + ); + let a: AttachmentBuilder = a.into(); + let parts = vec![a, sig_attachment.into()]; + let boundary = ContentType::make_boundary(&parts); + Ok(Attachment::new( + ContentType::Multipart { + boundary: boundary.into_bytes(), + kind: MultipartType::Signed, + parts: parts.into_iter().map(|a| a.into()).collect::>(), + parameters: vec![], + }, + Default::default(), + vec![], + ) + .into()) + }) + }) } pub fn encrypt_filter( @@ -166,37 +183,62 @@ pub fn encrypt_filter( } if let Some(encrypt_for_self) = encrypt_for_self { let mut ctx = Context::new()?; - let data = ctx.new_data_mem( - a.into_raw().as_bytes() - )?; - - let sig_attachment = { - let mut a = Attachment::new( - ContentType::OctetStream { name: None, parameters: vec![] }, - Default::default(), - ctx.encrypt(sign_keys, encrypt_keys, data)?.await?, - ); - a.content_disposition = ContentDisposition::from(br#"attachment; filename="msg.asc""#); - a - }; - let mut a: AttachmentBuilder = AttachmentBuilder::new(b"Version: 1\n"); + ctx.set_auto_key_locate(LocateKey::LOCAL)?; + let keys = ctx + .keylist(false, Some(encrypt_for_self.to_string()))? + .await?; + if keys.is_empty() { + return Err(Error::new(format!( + "Could not locate personal encryption key for address `{}`", + encrypt_for_self + ))); + } + for key in keys { + if !encrypt_keys.contains(&key) { + encrypt_keys.push(key); + } + } + } + let a: Attachment = a.into(); + log::trace!( + "main attachment is {:?} sign_keys = {:?} encrypt_keys = {:?}", + &a, + &sign_keys, + &encrypt_keys + ); + let mut ctx = Context::new()?; + let data = ctx.new_data_mem(a.into_raw().as_bytes())?; - a.set_content_type_from_bytes(b"application/pgp-encrypted"); - a.set_content_disposition(ContentDisposition::from(b"attachment")); - let parts = vec![a, sig_attachment.into()]; - let boundary = ContentType::make_boundary(&parts); - Ok(Attachment::new( - ContentType::Multipart { - boundary: boundary.into_bytes(), - kind: MultipartType::Encrypted, - parts: parts.into_iter().map(|a| a.into()).collect::>(), + let enc_attachment = { + let mut a = Attachment::new( + ContentType::OctetStream { + name: None, parameters: vec![], }, Default::default(), - vec![], - ) - .into()) - }) - }, - ) + ctx.encrypt(sign_keys, encrypt_keys, data)?.await?, + ); + a.content_disposition = + ContentDisposition::from(br#"attachment; filename="msg.asc""#); + a + }; + let mut a: AttachmentBuilder = AttachmentBuilder::new(b"Version: 1\n"); + + a.set_content_type_from_bytes(b"application/pgp-encrypted"); + a.set_content_disposition(ContentDisposition::from(b"attachment")); + let parts = vec![a, enc_attachment.into()]; + let boundary = ContentType::make_boundary(&parts); + Ok(Attachment::new( + ContentType::Multipart { + boundary: boundary.into_bytes(), + kind: MultipartType::Encrypted, + parts: parts.into_iter().map(|a| a.into()).collect::>(), + parameters: vec![], + }, + Default::default(), + vec![], + ) + .into()) + }) + }) }