summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-11 00:32:55 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-11 18:11:04 +0300
commit9ce62c735a80a5168c9d5982fe8ae9ac0e250fe0 (patch)
treed5ab8f3be917cfe8f4b49482efa94ab725f38ba6
parent39fab67523e6785f23f36e44f36681476b68aae4 (diff)
downloadmeli-9ce62c735a80a5168c9d5982fe8ae9ac0e250fe0.zip
compose: add key selection state for gpg operations
Closes #81
-rw-r--r--config_macros.rs10
-rw-r--r--docs/meli.conf.55
-rw-r--r--melib/src/conf.rs21
-rw-r--r--melib/src/gpgme/bindings.rs54
-rw-r--r--melib/src/gpgme/mod.rs331
-rw-r--r--samples/sample-config.toml1
-rw-r--r--src/components/mail.rs1
-rw-r--r--src/components/mail/compose.rs609
-rw-r--r--src/components/mail/pgp.rs270
-rw-r--r--src/components/mail/view.rs262
-rw-r--r--src/components/utilities/widgets.rs4
-rw-r--r--src/conf/overrides.rs42
-rw-r--r--src/conf/pgp.rs47
13 files changed, 1058 insertions, 599 deletions
diff --git a/config_macros.rs b/config_macros.rs
index a70fd1e3..e4c3ca5c 100644
--- a/config_macros.rs
+++ b/config_macros.rs
@@ -87,6 +87,14 @@ use super::*;
}
let override_ident: syn::Ident = format_ident!("{}Override", s.ident);
let mut field_tokentrees = vec![];
+ let mut attrs_tokens = vec![];
+ for attr in &s.attrs {
+ if let Ok(syn::Meta::List(ml)) = attr.parse_meta() {
+ if ml.path.get_ident().is_some() && ml.path.get_ident().unwrap() == "cfg" {
+ attrs_tokens.push(attr);
+ }
+ }
+ }
let mut field_idents = vec![];
for f in &s.fields {
let ident = &f.ident;
@@ -146,6 +154,7 @@ use super::*;
//let fields = &s.fields;
let literal_struct = quote! {
+ #(#attrs_tokens)*
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct #override_ident {
@@ -153,6 +162,7 @@ use super::*;
}
+ #(#attrs_tokens)*
impl Default for #override_ident {
fn default() -> Self {
#override_ident {
diff --git a/docs/meli.conf.5 b/docs/meli.conf.5
index 174bec26..f42477ff 100644
--- a/docs/meli.conf.5
+++ b/docs/meli.conf.5
@@ -892,11 +892,6 @@ Always sign sent messages
Key to be used when signing/encrypting (not functional yet)
.\" default value
.Pq Em none
-.It Ic gpg_binary Ar String
-.Pq Em optional
-The gpg binary name or file location to use
-.\" default value
-.Pq Em "gpg2"
.El
.Sh TERMINAL
.Bl -tag -width 36n
diff --git a/melib/src/conf.rs b/melib/src/conf.rs
index a78cbc14..0395dd4d 100644
--- a/melib/src/conf.rs
+++ b/melib/src/conf.rs
@@ -128,6 +128,7 @@ pub enum ToggleFlag {
InternalVal(bool),
False,
True,
+ Ask,
}
impl From<bool> for ToggleFlag {
@@ -157,9 +158,15 @@ impl ToggleFlag {
false
}
}
+
+ pub fn is_ask(&self) -> bool {
+ *self == ToggleFlag::Ask
+ }
+
pub fn is_false(&self) -> bool {
ToggleFlag::False == *self || ToggleFlag::InternalVal(false) == *self
}
+
pub fn is_true(&self) -> bool {
ToggleFlag::True == *self || ToggleFlag::InternalVal(true) == *self
}
@@ -174,6 +181,7 @@ impl Serialize for ToggleFlag {
ToggleFlag::Unset | ToggleFlag::InternalVal(_) => serializer.serialize_none(),
ToggleFlag::False => serializer.serialize_bool(false),
ToggleFlag::True => serializer.serialize_bool(true),
+ ToggleFlag::Ask => serializer.serialize_str("ask"),
}
}
}
@@ -183,10 +191,17 @@ impl<'de> Deserialize<'de> for ToggleFlag {
where
D: Deserializer<'de>,
{
- let s = <bool>::deserialize(deserializer);
+ let s = <String>::deserialize(deserializer);
Ok(match s? {
- true => ToggleFlag::True,
- false => ToggleFlag::False,
+ s if s.eq_ignore_ascii_case("true") => ToggleFlag::True,
+ s if s.eq_ignore_ascii_case("false") => ToggleFlag::False,
+ s if s.eq_ignore_ascii_case("ask") => ToggleFlag::Ask,
+ s => {
+ return Err(serde::de::Error::custom(format!(
+ r#"expected one of "true", "false", "ask", found `{}`"#,
+ s
+ )))
+ }
})
}
}
diff --git a/melib/src/gpgme/bindings.rs b/melib/src/gpgme/bindings.rs
index 390e3bc7..49273015 100644
--- a/melib/src/gpgme/bindings.rs
+++ b/melib/src/gpgme/bindings.rs
@@ -7220,12 +7220,8 @@ extern "C" {
extern "C" {
pub fn gpgme_get_protocol_name(proto: gpgme_protocol_t) -> *const ::std::os::raw::c_char;
}
-extern "C" {
- pub fn gpgme_set_armor(ctx: gpgme_ctx_t, yes: ::std::os::raw::c_int);
-}
-extern "C" {
- pub fn gpgme_get_armor(ctx: gpgme_ctx_t) -> ::std::os::raw::c_int;
-}
+pub type gpgme_set_armor = unsafe extern "C" fn(ctx: gpgme_ctx_t, yes: ::std::os::raw::c_int);
+pub type gpgme_get_armor = unsafe extern "C" fn(ctx: gpgme_ctx_t) -> ::std::os::raw::c_int;
extern "C" {
pub fn gpgme_set_textmode(ctx: gpgme_ctx_t, yes: ::std::os::raw::c_int);
}
@@ -7309,12 +7305,10 @@ extern "C" {
home_dir: *const ::std::os::raw::c_char,
) -> gpgme_error_t;
}
-extern "C" {
- pub fn gpgme_signers_clear(ctx: gpgme_ctx_t);
-}
-extern "C" {
- pub fn gpgme_signers_add(ctx: gpgme_ctx_t, key: gpgme_key_t) -> gpgme_error_t;
-}
+pub type gpgme_signers_clear = unsafe extern "C" fn(ctx: gpgme_ctx_t);
+
+pub type gpgme_signers_add =
+ unsafe extern "C" fn(ctx: gpgme_ctx_t, key: gpgme_key_t) -> gpgme_error_t;
extern "C" {
pub fn gpgme_signers_count(ctx: gpgme_ctx_t) -> ::std::os::raw::c_uint;
}
@@ -7763,9 +7757,7 @@ fn bindgen_test_layout__gpgme_op_encrypt_result() {
);
}
pub type gpgme_encrypt_result_t = *mut _gpgme_op_encrypt_result;
-extern "C" {
- pub fn gpgme_op_encrypt_result(ctx: gpgme_ctx_t) -> gpgme_encrypt_result_t;
-}
+pub type gpgme_op_encrypt_result = unsafe extern "C" fn(ctx: gpgme_ctx_t) -> gpgme_encrypt_result_t;
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_ALWAYS_TRUST: gpgme_encrypt_flags_t = 1;
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_ENCRYPT_TO: gpgme_encrypt_flags_t = 2;
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_PREPARE: gpgme_encrypt_flags_t = 4;
@@ -7776,15 +7768,13 @@ pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_THROW_KEYIDS: gpgme_encrypt_flags_
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_WRAP: gpgme_encrypt_flags_t = 128;
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_WANT_ADDRESS: gpgme_encrypt_flags_t = 256;
pub type gpgme_encrypt_flags_t = u32;
-extern "C" {
- pub fn gpgme_op_encrypt_start(
- ctx: gpgme_ctx_t,
- recp: *mut gpgme_key_t,
- flags: gpgme_encrypt_flags_t,
- plain: gpgme_data_t,
- cipher: gpgme_data_t,
- ) -> gpgme_error_t;
-}
+pub type gpgme_op_encrypt_start = unsafe extern "C" fn(
+ ctx: gpgme_ctx_t,
+ recp: *mut gpgme_key_t,
+ flags: gpgme_encrypt_flags_t,
+ plain: gpgme_data_t,
+ cipher: gpgme_data_t,
+) -> gpgme_error_t;
extern "C" {
pub fn gpgme_op_encrypt(
ctx: gpgme_ctx_t,
@@ -7814,15 +7804,13 @@ extern "C" {
cipher: gpgme_data_t,
) -> gpgme_error_t;
}
-extern "C" {
- pub fn gpgme_op_encrypt_sign_start(
- ctx: gpgme_ctx_t,
- recp: *mut gpgme_key_t,
- flags: gpgme_encrypt_flags_t,
- plain: gpgme_data_t,
- cipher: gpgme_data_t,
- ) -> gpgme_error_t;
-}
+pub type gpgme_op_encrypt_sign_start = unsafe extern "C" fn(
+ ctx: gpgme_ctx_t,
+ recp: *mut gpgme_key_t,
+ flags: gpgme_encrypt_flags_t,
+ plain: gpgme_data_t,
+ cipher: gpgme_data_t,
+) -> gpgme_error_t;
extern "C" {
pub fn gpgme_op_encrypt_sign(
ctx: gpgme_ctx_t,
diff --git a/melib/src/gpgme/mod.rs b/melib/src/gpgme/mod.rs
index e7d334d6..b0674619 100644
--- a/melib/src/gpgme/mod.rs
+++ b/melib/src/gpgme/mod.rs
@@ -25,11 +25,16 @@ use crate::email::{
};
use crate::error::{ErrorKind, IntoMeliError, MeliError, Result, ResultIntoMeliError};
use futures::FutureExt;
+use serde::{
+ de::{self, Deserialize},
+ Deserializer, Serialize, Serializer,
+};
use smol::Async;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::{CStr, CString, OsStr};
use std::future::Future;
+use std::io::Seek;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
@@ -66,6 +71,7 @@ pub enum GpgmeFlag {
///"auto-key-retrieve"
AutoKeyRetrieve,
OfflineMode,
+ AsciiArmor,
}
bitflags! {
@@ -91,6 +97,85 @@ bitflags! {
}
}
+impl<'de> Deserialize<'de> for LocateKey {
+ fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ if let Ok(s) = <String>::deserialize(deserializer) {
+ LocateKey::from_string_de::<'de, D, String>(s)
+ } else {
+ Err(de::Error::custom("LocateKey value must be a string."))
+ }
+ }
+}
+
+impl Serialize for LocateKey {
+ fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(&self.to_string())
+ }
+}
+
+impl LocateKey {
+ pub fn from_string_de<'de, D, T: AsRef<str>>(s: T) -> std::result::Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Ok(match s.as_ref().trim() {
+ s if s.eq_ignore_ascii_case("cert") => LocateKey::CERT,
+ s if s.eq_ignore_ascii_case("pka") => LocateKey::PKA,
+ s if s.eq_ignore_ascii_case("dane") => LocateKey::DANE,
+ s if s.eq_ignore_ascii_case("wkd") => LocateKey::WKD,
+ s if s.eq_ignore_ascii_case("ldap") => LocateKey::LDAP,
+ s if s.eq_ignore_ascii_case("keyserver") => LocateKey::KEYSERVER,
+ s if s.eq_ignore_ascii_case("keyserver-url") => LocateKey::KEYSERVER_URL,
+ s if s.eq_ignore_ascii_case("local") => LocateKey::LOCAL,
+ combination if combination.contains(",") => {
+ let mut ret = LocateKey::NODEFAULT;
+ for c in combination.trim().split(",") {
+ ret |= Self::from_string_de::<'de, D, &str>(c.trim())?;
+ }
+ ret
+ }
+ _ => {
+ return Err(de::Error::custom(
+ r#"Takes valid auto-key-locate GPG values: "cert", "pka", "dane", "wkd", "ldap", "keyserver", "keyserver-URL", "local", "nodefault""#,
+ ))
+ }
+ })
+ }
+}
+
+impl std::fmt::Display for LocateKey {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
+ if *self == LocateKey::NODEFAULT {
+ write!(fmt, "clear,nodefault")
+ } else {
+ let mut accum = String::new();
+ macro_rules! is_set {
+ ($flag:expr, $string:literal) => {{
+ if self.intersects($flag) {
+ accum.push_str($string);
+ accum.push_str(",");
+ }
+ }};
+ }
+ is_set!(LocateKey::CERT, "cert");
+ is_set!(LocateKey::PKA, "pka");
+ is_set!(LocateKey::WKD, "wkd");
+ is_set!(LocateKey::LDAP, "ldap");
+ is_set!(LocateKey::KEYSERVER, "keyserver");
+ is_set!(LocateKey::KEYSERVER_URL, "keyserver-url");
+ is_set!(LocateKey::LOCAL, "local");
+ accum.pop();
+ write!(fmt, "{}", accum)
+ }
+ }
+}
+
struct IoState {
max_idx: usize,
ops: HashMap<usize, GpgmeFd>,
@@ -187,6 +272,7 @@ impl Context {
};
ret.set_flag(GpgmeFlag::AutoKeyRetrieve, false)?;
ret.set_flag(GpgmeFlag::OfflineMode, true)?;
+ ret.set_flag(GpgmeFlag::AsciiArmor, true)?;
ret.set_auto_key_locate(LocateKey::LOCAL)?;
Ok(ret)
}
@@ -221,12 +307,21 @@ impl Context {
};
return Ok(());
}
+ GpgmeFlag::AsciiArmor => {
+ unsafe {
+ call!(&self.inner.lib, gpgme_set_armor)(
+ self.inner.inner.as_ptr(),
+ if value { 1 } else { 0 },
+ );
+ };
+ return Ok(());
+ }
};
const VALUE_ON: &[u8; 2] = b"1\0";
const VALUE_OFF: &[u8; 2] = b"0\0";
let raw_flag = match flag {
GpgmeFlag::AutoKeyRetrieve => c_string_literal!("auto-key-retrieve"),
- GpgmeFlag::OfflineMode => unreachable!(),
+ GpgmeFlag::AsciiArmor | GpgmeFlag::OfflineMode => unreachable!(),
};
self.set_flag_inner(
raw_flag,
@@ -249,6 +344,11 @@ impl Context {
call!(&self.inner.lib, gpgme_get_offline)(self.inner.inner.as_ptr()) > 0
});
}
+ GpgmeFlag::AsciiArmor => {
+ return Ok(unsafe {
+ call!(&self.inner.lib, gpgme_get_armor)(self.inner.inner.as_ptr()) > 0
+ });
+ }
};
let val = self.get_flag_inner(raw_flag);
Ok(!val.is_null())
@@ -259,23 +359,7 @@ impl Context {
if val == LocateKey::NODEFAULT {
self.set_flag_inner(auto_key_locate, c_string_literal!("clear,nodefault"))
} else {
- let mut accum = String::new();
- macro_rules! is_set {
- ($flag:expr, $string:literal) => {{
- if val.intersects($flag) {
- accum.push_str($string);
- accum.push_str(",");
- }
- }};
- }
- is_set!(LocateKey::CERT, "cert");
- is_set!(LocateKey::PKA, "pka");
- is_set!(LocateKey::WKD, "wkd");
- is_set!(LocateKey::LDAP, "ldap");
- is_set!(LocateKey::KEYSERVER, "keyserver");
- is_set!(LocateKey::KEYSERVER_URL, "keyserver-url");
- is_set!(LocateKey::LOCAL, "local");
- accum.pop();
+ let mut accum = val.to_string();
accum.push('\0');
self.set_flag_inner(
auto_key_locate,
@@ -592,11 +676,34 @@ impl Context {
})
}
- pub fn sign<'d>(
+ pub fn sign(
&mut self,
- text: &'d mut Data,
- ) -> Result<impl Future<Output = Result<()>> + 'd> {
- let sig = std::ptr::null_mut();
+ sign_keys: Vec<Key>,
+ mut text: Data,
+ ) -> Result<impl Future<Output = Result<Vec<u8>>>> {
+ if sign_keys.is_empty() {
+ return Err(
+ MeliError::new("gpgme: Call to sign() with zero keys.").set_kind(ErrorKind::Bug)
+ );
+ }
+ let mut sig: gpgme_data_t = std::ptr::null_mut();
+ unsafe {
+ gpgme_error_try(
+ &self.inner.lib,
+ call!(&self.inner.lib, gpgme_data_new)(&mut sig),
+ )?;
+ call!(&self.inner.lib, gpgme_signers_clear)(self.inner.inner.as_ptr());
+ for k in sign_keys {
+ gpgme_error_try(
+ &self.inner.lib,
+ call!(&self.inner.lib, gpgme_signers_add)(
+ self.inner.inner.as_ptr(),
+ k.inner.inner.as_ptr(),
+ ),
+ )?;
+ }
+ }
+
unsafe {
gpgme_error_try(
&self.inner.lib,
@@ -608,6 +715,13 @@ impl Context {
),
)?;
}
+ let mut sig = Data {
+ lib: self.inner.lib.clone(),
+ kind: DataKind::Memory,
+ inner: core::ptr::NonNull::new(sig).ok_or_else(|| {
+ MeliError::new("internal libgpgme error").set_kind(ErrorKind::Bug)
+ })?,
+ };
let io_state = self.io_state.clone();
let io_state_lck = self.io_state.lock().unwrap();
@@ -672,13 +786,17 @@ impl Context {
};
let _ = rcv.recv().await;
let io_state_lck = io_state.lock().unwrap();
- let ret = io_state_lck
+ io_state_lck
.done
.lock()
.unwrap()
.take()
- .unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")));
- ret
+ .unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")))?;
+ sig.seek(std::io::SeekFrom::Start(0))
+ .chain_err_summary(|| {
+ "libgpgme error: could not perform seek on signature data object"
+ })?;
+ Ok(sig.into_bytes()?)
})
}
@@ -830,7 +948,6 @@ impl Context {
recipient_iter = (*recipient_iter).next;
}
}
- use std::io::Seek;
/* Rewind cursor */
plain
.seek(std::io::SeekFrom::Start(0))
@@ -846,6 +963,168 @@ impl Context {
))
})
}
+
+ pub fn encrypt(
+ &mut self,
+ sign_keys: Option<Vec<Key>>,
+ encrypt_keys: Vec<Key>,
+ mut plain: Data,
+ ) -> Result<impl Future<Output = Result<Vec<u8>>> + Send> {
+ if encrypt_keys.is_empty() {
+ return Err(
+ MeliError::new("gpgme: Call to encrypt() with zero keys.").set_kind(ErrorKind::Bug)
+ );
+ }
+ unsafe {
+ call!(&self.inner.lib, gpgme_signers_clear)(self.inner.inner.as_ptr());
+ }
+
+ let also_sign: bool = if let Some(keys) = sign_keys {
+ if keys.is_empty() {
+ false
+ } else {
+ for k in keys {
+ unsafe {
+ gpgme_error_try(
+ &self.inner.lib,
+ call!(&self.inner.lib, gpgme_signers_add)(
+ self.inner.inner.as_ptr(),
+ k.inner.inner.as_ptr(),
+ ),
+ )?;
+ }
+ }
+ true
+ }
+ } else {
+ false
+ };
+ let mut cipher: gpgme_data_t = std::ptr::null_mut();
+ let mut raw_keys: Vec<gpgme_key_t> = Vec::with_capacity(encrypt_keys.len() + 1);
+ raw_keys.extend(encrypt_keys.iter().map(|k| k.inner.inner.as_ptr()));
+ raw_keys.push(std::ptr::null_mut());
+ unsafe {
+ gpgme_error_try(
+ &self.inner.lib,
+ call!(&self.inner.lib, gpgme_data_new)(&mut cipher),
+ )?;
+ gpgme_error_try(
+ &self.inner.lib,
+ if also_sign {
+ call!(&self.inner.lib, gpgme_op_encrypt_sign_start)(
+ self.inner.inner.as_ptr(),
+ raw_keys.as_mut_ptr(),
+ gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_ENCRYPT_TO
+ | gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_COMPRESS,
+ plain.inner.as_mut(),
+ cipher,
+ )
+ } else {
+ call!(&self.inner.lib, gpgme_op_encrypt_start)(
+ self.inner.inner.as_ptr(),
+ raw_keys.as_mut_ptr(),
+ gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_ENCRYPT_TO
+ | gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_COMPRESS,
+ plain.inner.as_mut(),
+ cipher,
+ )
+ },
+ )?;
+ }
+ let mut cipher = Data {
+ lib: self.inner.lib.clone(),
+ kind: DataKind::Memory,
+ inner: core::ptr::NonNull::new(cipher).ok_or_else(|| {
+ MeliError::new("internal libgpgme error").set_kind(ErrorKind::Bug)
+ })?,
+ };
+
+ let ctx = self.inner.clone();
+ let io_state = self.io_state.clone();
+ let io_state_lck = self.io_state.lock().unwrap();
+ let done = io_state_lck.done.clone();
+ let fut = io_state_lck
+ .ops
+ .values()
+ .map(|a| Async::new(a.clone()).unwrap())
+ .collect::<Vec<Async<GpgmeFd>>>();
+ drop(io_state_lck);
+ Ok(async move {
+ futures::future::join_all(fut.iter().map(|fut| {
+ let done = done.clone();
+ if fut.get_ref().write {
+ futures::future::select(
+ fut.get_ref().receiver.recv().boxed(),
+ fut.write_with(move |_f| {
+ if done.lock().unwrap().is_some() {
+ return Ok(());
+ }
+ unsafe {
+ (fut.get_ref().fnc.unwrap())(
+ fut.get_ref().fnc_data,
+ fut.get_ref().fd,
+ )
+ };
+ if done.lock().unwrap().is_none() {
+ return Err(std::io::ErrorKind::WouldBlock.into());
+ }
+ Ok(())
+ })
+ .boxed(),
+ )
+ .boxed()
+ } else {
+ futures::future::select(
+ fut.get_ref().receiver.recv().boxed(),
+ fut.read_with(move |_f| {
+ if done.lock().unwrap().is_some() {
+ return Ok(());
+ }
+ unsafe {
+ (fut.get_ref().fnc.unwrap())(
+ fut.get_ref().fnc_data,
+ fut.get_ref().fd,
+ )
+ };
+ if done.lock().unwrap().is_none() {
+ return Err(std::io::ErrorKind::WouldBlock.into());
+ }
+ Ok(())
+ })
+ .boxed(),
+ )
+ .boxed()
+ }
+ }))
+ .await;
+ let rcv = {
+ let io_state_lck = io_state.lock().unwrap();
+ io_state_lck.receiver.clone()
+ };
+ let _ = rcv.recv().await;
+ let io_state_lck = io_state.lock().unwrap();
+ io_state_lck
+ .done
+ .lock()
+ .unwrap()
+ .take()
+ .unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")))?;
+
+ let encrypt_result =
+ unsafe { call!(&ctx.lib, gpgme_op_encrypt_result)(ctx.inner.as_ptr()) };
+ if encrypt_result.is_null() {
+ return Err(MeliError::new(
+ "Unspecified libgpgme error: gpgme_op_encrypt_result returned NULL.",
+ )
+ .set_err_kind(ErrorKind::External));
+ }
+ /* Rewind cursor */
+ cipher
+ .seek(std::io::SeekFrom::Start(0))
+ .chain_err_summary(|| "libgpgme error: could not perform seek on plain text")?;
+ Ok(cipher.into_bytes()?)
+ })
+ }
}
fn gpgme_error_try(lib: &libloading::Library, error_code: GpgmeError) -> Result<()> {
diff --git a/samples/sample-config.toml b/samples/sample-config.toml
index fad4bd15..367310a7 100644
--- a/samples/sample-config.toml
+++ b/samples/sample-config.toml
@@ -112,7 +112,6 @@
#[pgp]
#auto_sign = false # always sign sent messages
#auto_verify_signatures = true # always verify signatures when reading signed e-mails
-#gpg_binary = "/usr/bin/gpg2" #optional
#
#[terminal]
#theme = "dark" # or "light"
diff --git a/src/components/mail.rs b/src/components/mail.rs
index 0aa19489..cb9be3d2 100644
--- a/src/components/mail.rs
+++ b/src/components/mail.rs
@@ -33,6 +33,7 @@ pub use crate::view::*;
mod compose;
pub use self::compose::*;
+#[cfg(feature = "gpgme")]
pub mod pgp;
mod status;
diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs
index c5f3e53f..31cbf5b5 100644
--- a/src/components/mail/compose.rs
+++ b/src/components/mail/compose.rs
@@ -84,8 +84,8 @@ pub struct Composer {
embed_area: Area,
embed: Option<EmbedStatus>,
- sign_mail: ToggleFlag,
- encrypt_mail: ToggleFlag,
+ #[cfg(feature = "gpgme")]
+ gpg_state: gpg::GpgComposeState,
dirty: bool,
has_changes: bool,
initialized: bool,
@@ -107,8 +107,8 @@ impl Default for Composer {
form: FormWidget::default(),
mode: ViewMode::Edit,
- sign_mail: ToggleFlag::Unset,
- encrypt_mail: ToggleFlag::Unset,
+ #[cfg(feature = "gpgme")]
+ gpg_state: gpg::GpgComposeState::new(),
dirty: true,
has_changes: false,
embed_area: ((0, 0), (0, 0)),
@@ -125,6 +125,8 @@ enum ViewMode {
Edit,
Embed,
SelectRecipients(UIDialog<Address>),
+ #[cfg(feature = "gpgme")]
+ SelectEncryptKey(bool, gpg::KeySelection),
Send(UIConfirmationDialog),
WaitingForSendResult(UIDialog<char>, JoinHandle<Result<()>>),
}
@@ -430,14 +432,23 @@ impl Composer {
let attachments_no = self.draft.attachments().len();
let theme_default = crate::conf::value(context, "theme_default");
clear_area(grid, area, theme_default);
- if self.sign_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.sign_mail.is_true() {
+ let key_list = self
+ .gpg_state
+ .sign_keys
+ .iter()
+ .map(|k| k.fingerprint())
+ .collect::<Vec<_>>()
+ .join(", ");
write_string_to_grid(
&format!(
"☑ sign with {}",
- account_settings!(context[self.account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or("default key")
+ if self.gpg_state.sign_keys.is_empty() {
+ "default key"
+ } else {
+ key_list.as_str()
+ }
),
grid,
theme_default.fg,
@@ -465,14 +476,29 @@ impl Composer {
None,
);
}
- if self.encrypt_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.encrypt_mail.is_true() {
+ let key_list = self
+ .gpg_state
+ .encrypt_keys
+ .iter()
+ .map(|k| k.fingerprint())
+ .collect::<Vec<_>>()
+ .join(", ");
+
write_string_to_grid(
&format!(
- "☑ encrypt with {}",
- account_settings!(context[self.account_hash].pgp.encrypt_key)
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or("default key")
+ "{}{}",
+ if self.gpg_state.encrypt_keys.is_empty() {
+ "☐ no keys to encrypt with!"
+ } else {
+ "☑ encrypt with "
+ },
+ if self.gpg_state.encrypt_keys.is_empty() {
+ ""
+ } else {
+ key_list.as_str()
+ }
),
grid,
theme_default.fg,
@@ -575,8 +601,9 @@ impl Component for Composer {
let width = width!(area);
if !self.initialized {
- if self.sign_mail.is_unset() {
- self.sign_mail = ToggleFlag::InternalVal(*account_settings!(
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.sign_mail.is_unset() {
+ self.gpg_state.sign_mail = ToggleFlag::InternalVal(*account_settings!(
context[self.account_hash].pgp.auto_sign
));
}
@@ -752,6 +779,18 @@ impl Component for Composer {
ViewMode::Send(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(
+ _,
+ gpg::KeySelection::Loaded {
+ ref mut widget,
+ keys: _,
+ },
+ ) => {
+ widget.draw(grid, center_area(area, widget.content.size()), context);
+ }
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(_, _) => {}
ViewMode::SelectRecipients(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
@@ -783,8 +822,8 @@ impl Component for Composer {
if let Some(true) = result.downcast_ref::<bool>() {
self.update_draft();
match send_draft_async(
- self.sign_mail,
- self.encrypt_mail,
+ #[cfg(feature = "gpgme")]
+ self.gpg_state.clone(),
context,
self.account_hash,
self.draft.clone(),
@@ -846,6 +885,14 @@ impl Component for Composer {
self.mode = ViewMode::Edit;
self.set_dirty(true);
}
+ #[cfg(feature = "gpgme")]
+ (ViewMode::SelectEncryptKey(_, ref mut selector), UIEvent::ComponentKill(ref id))
+ if *id == selector.id() =>
+ {
+ self.mode = ViewMode::Edit;
+ self.set_dirty(true);
+ return true;
+ }
(ViewMode::Send(ref mut selector), _) => {
if selector.process_event(event, context) {
return true;
@@ -954,6 +1001,34 @@ impl Component for Composer {
return true;
}
}
+ #[cfg(feature = "gpgme")]
+ (
+ ViewMode::SelectEncryptKey(is_encrypt, ref mut selector),
+ UIEvent::FinishedUIDialog(id, result),
+ ) if *id == selector.id() => {
+ debug!(&result);
+ if let Some(key) = result.downcast_mut::<Option<melib::gpgme::Key>>() {
+ debug!("got key {:?}", key);
+ if let Some(key) = key {
+ if *is_encrypt {
+ self.gpg_state.encrypt_keys.clear();
+ self.gpg_state.encrypt_keys.push(key.clone());
+ } else {
+ self.gpg_state.sign_keys.clear();
+ self.gpg_state.sign_keys.push(key.clone());
+ }
+ }
+ }
+ self.mode = ViewMode::Edit;
+ self.set_dirty(true);
+ return true;
+ }
+ #[cfg(feature = "gpgme")]
+ (ViewMode::SelectEncryptKey(_, ref mut selector), _) => {
+ if selector.process_event(event, context) {
+ return true;
+ }
+ }
_ => {}
}
if self.cursor == Cursor::Headers
@@ -1025,14 +1100,15 @@ impl Component for Composer {
if self.mode.is_edit()
&& (self.cursor == Cursor::Sign || self.cursor == Cursor::Encrypt) =>
{
+ #[cfg(feature = "gpgme")]
match self.cursor {
Cursor::Sign => {
- let is_true = self.sign_mail.is_true();
- self.sign_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.sign_mail.is_true();
+ self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
}
Cursor::Encrypt => {
- let is_true = self.encrypt_mail.is_true();
- self.encrypt_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.encrypt_mail.is_true();
+ self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
}
_ => {}
};
@@ -1195,6 +1271,86 @@ impl Component for Composer {
return true;
}
UIEvent::Input(ref key)
+ if self.mode.is_edit()
+ && self.cursor == Cursor::Sign
+ && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
+ {
+ #[cfg(feature = "gpgme")]
+ match melib::email::parser::address::rfc2822address_list(
+ self.form.values()["From"].as_str().as_bytes(),
+ )
+ .map_err(|_err| -> MeliError { "No valid sender address in `From:`".into() })
+ .and_then(|(_, list)| {
+ list.get(0)
+ .cloned()
+ .ok_or_else(|| "No valid sender address in `From:`".into())
+ })
+ .and_then(|addr| {
+ gpg::KeySelection::new(
+ false,
+ account_settings!(context[self.account_hash].pgp.allow_remote_lookup)
+ .is_true(),
+ addr.get_email(),
+ *account_settings!(context[self.account_hash].pgp.allow_remote_lookup),
+ context,
+ )
+ }) {
+ Ok(widget) => {
+ self.gpg_state.sign_mail = ToggleFlag::from(true);
+ self.mode = ViewMode::SelectEncryptKey(false, widget);
+ }
+ Err(err) => {
+ context.replies.push_back(UIEvent::Notification(
+ Some("Could not list keys.".to_string()),
+ format!("libgpgme error: {}", &err),
+ Some(NotificationType::Error(melib::error::ErrorKind::External)),
+ ));
+ }
+ }
+ self.set_dirty(true);
+ return true;
+ }
+ UIEvent::Input(ref key)
+ if self.mode.is_edit()
+ && self.cursor == Cursor::Encrypt
+ && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
+ {
+ #[cfg(feature = "gpgme")]
+ match melib::email::parser::address::rfc2822address_list(
+ self.form.values()["To"].as_str().as_bytes(),
+ )
+ .map_err(|_err| -> MeliError { "No valid recipient addresses in `To:`".into() })
+ .and_then(|(_, list)| {
+ list.get(0)
+ .cloned()
+ .ok_or_else(|| "No valid recipient addresses in `To:`".into())
+ })
+ .and_then(|addr| {
+ gpg::KeySelection::new(
+ false,
+ account_settings!(context[self.account_hash].pgp.allow_remote_lookup)
+ .is_true(),
+ addr.get_email(),
+ *account_settings!(context[self.account_hash].pgp.allow_remote_lookup),
+ context,
+ )
+ }) {
+ Ok(widget) => {
+ self.gpg_state.encrypt_mail = ToggleFlag::from(true);
+ self.mode = ViewMode::SelectEncryptKey(true, widget);
+ }
+ Err(err) => {
+ context.replies.push_back(UIEvent::Notification(
+ Some("Could not list keys.".to_string()),
+ format!("libgpgme error: {}", &err),
+ Some(NotificationType::Error(melib::error::ErrorKind::External)),
+ ));
+ }
+ }
+ self.set_dirty(true);
+ return true;
+ }
+ UIEvent::Input(ref key)
if self.embed.is_some()
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
{
@@ -1498,15 +1654,17 @@ impl Component for Composer {
);
return true;
}
+ #[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleSign) => {
- let is_true = self.sign_mail.is_true();
- self.sign_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.sign_mail.is_true();
+ self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
self.dirty = true;
return true;
}
+ #[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleEncrypt) => {
- let is_true = self.encrypt_mail.is_true();
- self.encrypt_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.encrypt_mail.is_true();
+ self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
self.dirty = true;
return true;
}
@@ -1527,6 +1685,10 @@ impl Component for Composer {
ViewMode::SelectRecipients(ref widget) => {
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
}
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(_, ref widget) => {
+ widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
+ }
ViewMode::Send(ref widget) => {
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
}
@@ -1626,7 +1788,7 @@ impl Component for Composer {
}
pub fn send_draft(
- sign_mail: ToggleFlag,
+ _sign_mail: ToggleFlag,
context: &mut Context,
account_hash: AccountHash,
mut draft: Draft,
@@ -1635,7 +1797,7 @@ pub fn send_draft(
complete_in_background: bool,
) -> Result<Option<JoinHandle<Result<()>>>> {
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
- if sign_mail.is_true() {
+ /* if sign_mail.is_true() {
let mut content_type = ContentType::default();
if format_flowed {
if let ContentType::Text {
@@ -1667,41 +1829,44 @@ pub fn send_draft(
)
.into();
}
- let output = crate::components::mail::pgp::sign(
- body.into(),
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.as_str()),
- account_settings!(context[account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.as_str()),
- );
- match output {
- Err(err) => {
- debug!("{:?} could not sign draft msg", err);
- log(
- format!(
- "Could not sign draft in account `{}`: {}.",
- context.accounts[&account_hash].name(),
- err.to_string()
- ),
- ERROR,
- );
- context.replies.push_back(UIEvent::Notification(
- Some(format!(
- "Could not sign draft in account `{}`.",
- context.accounts[&account_hash].name()
- )),
- err.to_string(),
- Some(NotificationType::Error(err.kind)),
- ));
- return Err(err);
- }
- Ok(output) => {
- draft.attachments.push(output);
- }
+ let output = todo!();
+ crate::components::mail::pgp::sign(
+ body.into(),
+ account_settings!(context[account_hash].pgp.gpg_binary)
+ .as_ref()
+ .map(|s| s.as_str()),
+ account_settings!(context[account_hash].pgp.sign_key)
+ .as_ref()
+ .map(|s| s.as_str()),
+ );
+ match output {
+ Err(err) => {
+ debug!("{:?} could not sign draft msg", err);
+ log(
+ format!(
+ "Could not sign draft in account `{}`: {}.",
+ context.accounts[&account_hash].name(),
+ err.to_string()
+ ),
+ ERROR,
+ );
+ context.replies.push_back(UIEvent::Notification(
+ Some(format!(
+ "Could not sign draft in account `{}`.",
+ context.accounts[&account_hash].name()
+ )),
+ err.to_string(),
+ Some(NotificationType::Error(err.kind)),
+ ));
+ return Err(err);
+ }
+ Ok(output) => {
+ draft.attachments.push(output);
}
+ }
} else {
+ */
+ {
let mut content_type = ContentType::default();
if format_flowed {
if let ContentType::Text {
@@ -1762,8 +1927,7 @@ pub fn save_draft(
}
pub fn send_draft_async(
- sign_mail: ToggleFlag,
- encrypt_mail: ToggleFlag,
+ #[cfg(feature = "gpgme")] gpg_state: gpg::GpgComposeState,
context: &mut Context,
account_hash: AccountHash,
mut draft: Draft,
@@ -1772,6 +1936,7 @@ pub fn send_draft_async(
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
let event_sender = context.sender.clone();
+ #[cfg(feature = "gpgme")]
let mut filters_stack: Vec<
Box<
dyn FnOnce(
@@ -1781,40 +1946,19 @@ pub fn send_draft_async(
+ Send,
>,
> = vec![];
- if sign_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.to_string()),
- account_settings!(context[account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.to_string()),
+ gpg_state.sign_keys.clone(),
)?));
- }
- if encrypt_mail.is_true() {
- let mut recipients = vec![];
- if let Ok((_, v)) =
- melib::email::parser::address::rfc2822address_list(draft.headers()["To"].as_bytes())
- {
- for addr in v {
- recipients.push(addr.get_email());
- }
- }
- if let Ok((_, v)) =
- melib::email::parser::address::rfc2822address_list(draft.headers()["Cc"].as_bytes())
- {
- for addr in v {
- recipients.push(addr.get_email());
- }
- }
+ } else if gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.to_string()),
- account_settings!(context[account_hash].pgp.encrypt_key)
- .as_ref()
- .map(|s| s.to_string()),
- recipients,
+ if gpg_state.sign_mail.is_true() {
+ Some(gpg_state.sign_keys.clone())
+ } else {
+ None
+ },
+ gpg_state.encrypt_keys.clone(),
)?));
}
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
@@ -1850,6 +1994,7 @@ pub fn send_draft_async(
.into();
}
Ok(Box::pin(async move {
+ #[cfg(feature = "gpgme")]
for f in filters_stack {
body = f(body).await?;
}
@@ -1882,3 +2027,277 @@ pub fn send_draft_async(
ret
}))
}
+
+#[cfg(feature = "gpgme")]
+mod gpg {
+ use super::*;
+
+ #[derive(Debug)]
+ pub enum KeySelection {
+ LoadingKeys {
+ handle: JoinHandle<Result<Vec<melib::gpgme::Key>>>,
+ progress_spinner: ProgressSpinner,
+ secret: bool,
+ local: bool,
+ pattern: String,
+ allow_remote_lookup: ToggleFlag,
+ },
+ Error {
+ id: ComponentId,
+ err: MeliError,
+ },
+ Loaded {
+ widget: UIDialog<melib::gpgme::Key>,
+ keys: Vec<melib::gpgme::Key>,
+ },
+ }
+
+ impl std::fmt::Display for KeySelection {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "select pgp keys")
+ }
+ }
+
+ impl KeySelection {
+ pub fn new(
+ secret: bool,
+ local: bool,
+ pattern: String,
+ allow_remote_lookup: ToggleFlag,
+ context: &mut Context,
+ ) -> Result<Self> {
+ use melib::gpgme::*;
+ debug!("KeySelection::new");
+ debug!(&secret);
+ debug!(&local);
+ debug!(&pattern);
+ debug!(&allow_remote_lookup);
+ let mut ctx = Context::new()?;
+ if local {
+ ctx.set_auto_key_locate(LocateKey::LOCAL)?;
+ } else {
+ ctx.set_auto_key_locate(LocateKey::WKD | LocateKey::LOCAL)?;
+ }
+ let job = ctx.keylist(secret, Some(pattern.clone()))?;
+ let handle = context.job_executor.spawn_specialized(job);
+ let mut progress_spinner = ProgressSpinner::new(8);
+ progress_spinner.start();
+ Ok(KeySelection::LoadingKeys {
+ handle,
+ secret,
+ local,
+ pattern,
+ allow_remote_lookup,
+ progress_spinner,
+ })
+ }
+ }
+
+ impl Component for KeySelection {
+ fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.draw(grid, center_area(area, (2, 2)), context),
+ KeySelection::Error { ref err, .. } => {
+ let theme_default = crate::conf::value(context, "theme_default");
+ write_string_to_grid(
+ &err.to_string(),
+ grid,
+ theme_default.fg,
+ theme_default.bg,
+ theme_default.attrs,
+ center_area(area, (15, 2)),
+ Some(0),
+ );
+ }
+ KeySelection::Loaded { ref mut widget, .. } => {
+ widget.draw(grid, center_area(area, widget.content.size()), context)
+ }
+ }
+ }
+
+ fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
+ debug!(&self);
+ debug!(&event);
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ref mut handle,
+ secret,
+ local,
+ ref mut pattern,
+ allow_remote_lookup,
+ ..
+ } => match event {
+ UIEvent::StatusEvent(StatusEvent::JobFinished(ref id))
+ if *id == handle.job_id =>
+ {
+ match handle.chan.try_recv().unwrap().unwrap() {
+ Ok(keys) => {
+ if keys.is_empty() {
+ let id = progress_spinner.id();
+ if allow_remote_lookup.is_true() {
+ match Self::new(
+ *secret,
+ *local,
+ std::mem::replace(pattern, String::new()),
+ *allow_remote_lookup,
+ context,
+ ) {
+ Ok(w) => {
+ *self = w;
+ }
+ Err(err) => *self = KeySelection::Error { err, id },
+ }
+ } else if !*local && allow_remote_lookup.is_ask() {
+ *self = KeySelection::Error {
+ err: MeliError::new(format!(
+ "No keys found for {}, perform remote lookup?",
+ pattern
+ )),
+ id,
+ }
+ } else {
+ *self = KeySelection::Error {
+ err: MeliError::new(format!(
+ "No keys found for {}.",
+ pattern
+ )),
+ id,
+ }
+ }
+ if let KeySelection::Error { ref err, .. } = self {
+ context.replies.push_back(UIEvent::StatusEvent(
+ StatusEvent::DisplayMessage(err.to_string()),
+ ));
+ let res: Option<melib::gpgme::Key> = None;
+ context.replies.push_back(UIEvent::FinishedUIDialog(
+ id,
+ Box::new(res),
+ ));
+ }
+ return true;
+ }
+ let mut widget = UIDialog::new(
+ "select key",
+ keys.iter()
+ .map(|k| {
+ (
+ k.clone(),
+ if let Some(primary_uid) = k.primary_uid() {
+ format!("{} {}", k.fingerprint(), primary_uid)
+ } else {
+ k.fingerprint().to_string()
+ },
+ )
+ })
+ .collect::<Vec<(melib::gpgme::Key, String)>>(),
+ true,
+ Some(Box::new(
+ move |id: ComponentId, results: &[melib::gpgme::Key]| {
+ Some(UIEvent::FinishedUIDialog(
+ id,
+ Box::new(results.get(0).map(|k| k.clone())),
+ ))
+ },
+ )),
+ context,
+ );
+ widget.set_dirty(true);
+ *self = KeySelection::Loaded { widget, keys };
+ }
+ Err(err) => {
+ *self = KeySelection::Error {
+ err,
+ id: ComponentId::new_v4(),
+ };
+ }
+ }
+ true
+ }
+ _ => progress_spinner.process_event(event, context),
+ },
+ KeySelection::Error { .. } => false,
+ KeySelection::Loaded { ref mut widget, .. } => widget.process_event(event, context),
+ }
+ }
+
+ fn is_dirty(&self) -> bool {
+ match self {
+ KeySelection::LoadingKeys {
+ ref progress_spinner,
+ ..
+ } => progress_spinner.is_dirty(),
+ KeySelection::Error { .. } => true,
+ KeySelection::Loaded { ref widget, .. } => widget.is_dirty(),
+ }
+ }
+
+ fn set_dirty(&mut self, value: bool) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.set_dirty(value),
+ KeySelection::Error { .. } => {}
+ KeySelection::Loaded { ref mut widget, .. } => widget.set_dirty(value),
+ }
+ }
+
+ fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
+
+ fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
+ match self {
+ KeySelection::LoadingKeys { .. } | KeySelection::Error { .. } => {
+ ShortcutMaps::default()
+ }
+ KeySelection::Loaded { ref widget, .. } => widget.get_shortcuts(context),
+ }
+ }
+
+ fn id(&self) -> ComponentId {
+ match self {
+ KeySelection::LoadingKeys {
+ ref progress_spinner,
+ ..
+ } => progress_spinner.id(),
+ KeySelection::Error { ref id, .. } => *id,
+ KeySelection::Loaded { ref widget, .. } => widget.id(),
+ }
+ }
+
+ fn set_id(&mut self, new_id: ComponentId) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.set_id(new_id),
+ KeySelection::Error { ref mut id, .. } => *id = new_id,
+ KeySelection::Loaded { ref mut widget, .. } => widget.set_id(new_id),
+ }
+ }
+ }
+
+ #[derive(Debug, Clone)]
+ pub struct GpgComposeState {
+ pub sign_mail: ToggleFlag,
+ pub encrypt_mail: ToggleFlag,
+ pub encrypt_keys: Vec<melib::gpgme::Key>,
+ pub encrypt_for_self: bool,
+ pub sign_keys: Vec<melib::gpgme::Key>,
+ }
+
+ impl GpgComposeState {
+ pub fn new() -> Self {
+ GpgComposeState {
+ sign_mail: ToggleFlag::Unset,
+ encrypt_mail: ToggleFlag::Unset,
+ encrypt_keys: vec![],
+ encrypt_for_self: true,
+ sign_keys: vec![],
+ }
+ }
+ }
+}
diff --git a/src/components/mail/pgp.rs b/src/components/mail/pgp.rs
index 64c13c16..c378ca46 100644
--- a/src/components/mail/pgp.rs
+++ b/src/components/mail/pgp.rs
@@ -19,215 +19,49 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
-use super::*;
-use melib::email::pgp as melib_pgp;
+use melib::email::{
+ attachment_types::{ContentDisposition, ContentType, MultipartType},
+ pgp as melib_pgp, Attachment, AttachmentBuilder,
+};
+use melib::error::*;
+use melib::gpgme::*;
use std::future::Future;
-use std::io::Write;
use std::pin::Pin;
-use std::process::{Command, Stdio};
-pub fn verify_signature(a: &Attachment, context: &mut Context) -> Result<Vec<u8>> {
- let (bytes, sig) =
- melib_pgp::verify_signature(a).chain_err_summary(|| "Could not verify signature.")?;
- let bytes_file = create_temp_file(&bytes, None, None, true);
- let signature_file = create_temp_file(sig, None, None, true);
- let binary = context
- .settings
- .pgp
- .gpg_binary
- .as_ref()
- .map(String::as_str)
- .unwrap_or("gpg2");
- Ok(Command::new(binary)
- .args(&[
- "--output",
- "-",
- "--verify",
- signature_file.path.to_str().unwrap(),
- bytes_file.path.to_str().unwrap(),
- ])
- .stdin(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .and_then(|gpg| gpg.wait_with_output())
- .map(|gpg| gpg.stderr)
- .chain_err_summary(|| {
- format!(
- "Failed to launch {} to verify PGP signature",
- context
- .settings
- .pgp
- .gpg_binary
- .as_ref()
- .map(String::as_str)
- .unwrap_or("gpg2"),
- )
- })?)
+pub async fn decrypt(raw: Vec<u8>) -> Result<(melib_pgp::DecryptionMetadata, Vec<u8>)> {
+ let mut ctx = Context::new()?;
+ let cipher = ctx.new_data_mem(&raw)?;
+ ctx.decrypt(cipher)?.await
}
-/// Returns multipart/signed
-pub fn sign(
- a: AttachmentBuilder,
- gpg_binary: Option<&str>,
- pgp_key: Option<&str>,
-) -> Result<AttachmentBuilder> {
- let binary = gpg_binary.unwrap_or("gpg2");
- let mut command = Command::new(binary);
- command.args(&[
- "--digest-algo",
- "sha512",
- "--output",
- "-",
- "--detach-sig",
- "--armor",
- ]);
- if let Some(key) = pgp_key {
- command.args(&["--local-user", key]);
- }
- let a: Attachment = a.into();
-
- let sig_attachment = command
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::null())
- .spawn()
- .and_then(|mut gpg| {
- gpg.stdin
- .as_mut()
- .expect("Could not get gpg stdin")
- .write_all(&melib_pgp::convert_attachment_to_rfc_spec(
- a.into_raw().as_bytes(),
- ))?;
- let gpg = gpg.wait_with_output()?;
- Ok(Attachment::new(
- ContentType::PGPSignature,
- Default::default(),
- gpg.stdout,
- ))
- })
- .chain_err_summary(|| format!("Failed to launch {} to verify PGP signature", binary))?;
-
- 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::<Vec<_>>(),
- },
- Default::default(),
- Vec::new(),
- )
- .into())
-}
-
-pub async fn decrypt(
- raw: Vec<u8>,
- gpg_binary: Option<String>,
- decrypt_key: Option<String>,
-) -> Result<(melib_pgp::DecryptionMetadata, Vec<u8>)> {
- let bin = gpg_binary.as_ref().map(|s| s.as_str()).unwrap_or("gpg2");
- let mut command = Command::new(bin);
- command.args(&["--digest-algo", "sha512", "--output", "-"]);
- if let Some(ref key) = decrypt_key {
- command.args(&["--local-user", key]);
- }
-
- let stdout = command
- .args(&["--decrypt"])
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .and_then(|mut gpg| {
- gpg.stdin
- .as_mut()
- .expect("Could not get gpg stdin")
- .write_all(&raw)?;
- let gpg = gpg.wait_with_output()?;
- Ok(gpg.stdout)
- })
- .chain_err_summary(|| format!("Failed to launch {} to verify PGP signature", bin))?;
- Ok((melib_pgp::DecryptionMetadata::default(), stdout))
-}
-
-pub async fn verify(a: Attachment, gpg_binary: Option<String>) -> Result<Vec<u8>> {
- let (bytes, sig) =
+pub async fn verify(a: Attachment) -> Result<()> {
+ let (data, sig) =
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
- let bytes_file = create_temp_file(&bytes, None, None, true);
- let signature_file = create_temp_file(sig, None, None, true);
- Ok(
- Command::new(gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"))
- .args(&[
- "--output",
- "-",
- "--verify",
- signature_file.path.to_str().unwrap(),
- bytes_file.path.to_str().unwrap(),
- ])
- .stdin(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .and_then(|gpg| gpg.wait_with_output())
- .map(|gpg| gpg.stderr)
- .chain_err_summary(|| {
- format!(
- "Failed to launch {} to verify PGP signature",
- gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"),
- )
- })?,
- )
+ let mut ctx = Context::new()?;
+ let sig = ctx.new_data_mem(&sig)?;
+ let data = ctx.new_data_mem(&data)?;
+ ctx.verify(sig, data)?.await
}
pub fn sign_filter(
- gpg_binary: Option<String>,
- pgp_key: Option<String>,
+ sign_keys: Vec<Key>,
) -> Result<
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
+ Send,
> {
- let binary = gpg_binary.unwrap_or("gpg2".to_string());
- let mut command = Command::new(&binary);
- command.args(&[
- "--digest-algo",
- "sha512",
- "--output",
- "-",
- "--detach-sig",
- "--armor",
- ]);
- if let Some(key) = pgp_key.as_ref() {
- command.args(&["--local-user", key]);
- }
Ok(
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
Box::pin(async move {
let a: Attachment = a.into();
-
- let sig_attachment = command
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::null())
- .spawn()
- .and_then(|mut gpg| {
- gpg.stdin
- .as_mut()
- .expect("Could not get gpg stdin")
- .write_all(&melib_pgp::convert_attachment_to_rfc_spec(
+ let mut ctx = Context::new()?;
+ let data = ctx.new_data_mem(&melib_pgp::convert_attachment_to_rfc_spec(
a.into_raw().as_bytes(),
- ))?;
- let gpg = gpg.wait_with_output()?;
- Ok(Attachment::new(
- ContentType::PGPSignature,
- Default::default(),
- gpg.stdout,
- ))
- })
- .chain_err_summary(|| {
- format!("Failed to launch {} to verify PGP signature", binary)
- })?;
-
+ ))?;
+ 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);
@@ -247,61 +81,31 @@ pub fn sign_filter(
}
pub fn encrypt_filter(
- gpg_binary: Option<String>,
- my_public_key: Option<String>,
- recipients: Vec<String>,
+ sign_keys: Option<Vec<Key>>,
+ encrypt_keys: Vec<Key>,
) -> Result<
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
+ Send,
> {
- let binary = gpg_binary.unwrap_or("gpg2".to_string());
- let mut command = Command::new(&binary);
- command.args(&[
- "--batch",
- "--no-tty",
- "--encrypt",
- "--armor",
- "--output",
- "-",
- ]);
- if let Some(key) = my_public_key.as_ref() {
- command.args(&["--recipient", key]);
- } else {
- command.arg("--default-recipient-self");
- }
- for r in &recipients {
- command.args(&["--recipient", r.as_str()]);
- }
Ok(
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
Box::pin(async move {
let a: Attachment = a.into();
+ debug!("main attachment is {:?}", &a);
+ let mut ctx = Context::new()?;
+ let data = ctx.new_data_mem(
+ a.into_raw().as_bytes()
+ )?;
- let sig_attachment = command
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::null())
- .spawn()
- .and_then(|mut gpg| {
- gpg.stdin
- .as_mut()
- .expect("Could not get gpg stdin")
- .write_all(&melib_pgp::convert_attachment_to_rfc_spec(
- a.into_raw().as_bytes(),
- ))?;
- let gpg = gpg.wait_with_output()?;
- let mut a = Attachment::new(
+ let sig_attachment = {
+ let mut a = Attachment::new(
ContentType::OctetStream { name: None },
Default::default(),
- gpg.stdout,
+ ctx.encrypt(sign_keys, encrypt_keys, data)?.await?,
);
a.content_disposition = ContentDisposition::from(r#"attachment; filename="msg.asc""#.as_bytes());
- Ok(a)
- })
- .chain_err_summary(|| {
- format!("Failed to launch {} to verify PGP signature", binary)
- })?;
-
+ a
+ };
let mut a: AttachmentBuilder = AttachmentBuilder::new("Version: 1".as_bytes());
a.set_content_type_from_bytes("application/pgp-encrypted".as_bytes());
a.set_content_disposition(ContentDisposition::from("attachment".as_bytes()));
diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs
index 62a03302..3757a02c 100644
--- a/src/components/mail/view.rs
+++ b/src/components/mail/view.rs
@@ -105,7 +105,7 @@ pub enum AttachmentDisplay {
SignedPending {
inner: Attachment,
display: Vec<AttachmentDisplay>,
- handle: std::result::Result<JoinHandle<Result<()>>, JoinHandle<Result<Vec<u8>>>>,
+ handle: JoinHandle<Result<()>>,
job_id: JobId,
},
SignedFailed {
@@ -645,165 +645,78 @@ impl MailView {
}
}
MultipartType::Signed => {
- if *mailbox_settings!(
- context[coordinates.0][&coordinates.1]
- .pgp
- .auto_verify_signatures
- ) {
- if let Some(bin) = mailbox_settings!(
- context[coordinates.0][&coordinates.1].pgp.gpg_binary
+ #[cfg(not(feature = "gpgme"))]
+ {
+ acc.push(AttachmentDisplay::SignedUnverified {
+ inner: a.clone(),
+ display: {
+ let mut v = vec![];
+ rec(&parts[0], context, coordinates, &mut v, active_jobs);
+ v
+ },
+ });
+ }
+ #[cfg(feature = "gpgme")]
+ {
+ if *mailbox_settings!(
+ context[coordinates.0][&coordinates.1]
+ .pgp
+ .auto_verify_signatures
) {
- let verify_fut = crate::components::mail::pgp::verify(
- a.clone(),
- Some(bin.to_string()),
- );
- let handle = context.job_executor.spawn_blocking(verify_fut);
+ let verify_fut = crate::components::mail::pgp::verify(a.clone());
+ let handle = context.job_executor.spawn_specialized(verify_fut);
active_jobs.insert(handle.job_id);
acc.push(AttachmentDisplay::SignedPending {
inner: a.clone(),
+ job_id: handle.job_id,
display: {
let mut v = vec![];
rec(&parts[0], context, coordinates, &mut v, active_jobs);
v
},
- job_id: handle.job_id,
- handle: Err(handle),
+ handle,
});
} else {
- #[cfg(not(feature = "gpgme"))]
- {
- acc.push(AttachmentDisplay::SignedUnverified {
- inner: a.clone(),
- display: {
- let mut v = vec![];
- rec(
- &parts[0],
- context,
- coordinates,
- &mut v,
- active_jobs,
- );
- v
- },
- });
- }
- #[cfg(feature = "gpgme")]
- match melib::gpgme::Context::new().and_then(|mut ctx| {
- let sig = ctx.new_data_mem(&parts[1].raw())?;
- let mut f = std::fs::File::create("/tmp/sig").unwrap();
- f.write_all(&parts[1].raw())?;
- let mut f = std::fs::File::create("/tmp/data").unwrap();
- f.write_all(&parts[0].raw())?;
- let data = ctx.new_data_mem(&parts[0].raw())?;
- ctx.verify(sig, data)
- }) {
- Ok(verify_fut) => {
- let handle =
- context.job_executor.spawn_specialized(verify_fut);
- active_jobs.insert(handle.job_id);
- acc.push(AttachmentDisplay::SignedPending {
- inner: a.clone(),
- job_id: handle.job_id,
- display: {
- let mut v = vec![];
- rec(
- &parts[0],
- context,
- coordinates,
- &mut v,
- active_jobs,
- );
- v
- },
- handle: Ok(handle),
- });
- }
- Err(error) => {
- acc.push(AttachmentDisplay::SignedFailed {
- inner: a.clone(),
- display: {
- let mut v = vec![];
- rec(
- &parts[0],
- context,
- coordinates,
- &mut v,
- active_jobs,
- );
- v
- },
- error,
- });
- }
- }
+ acc.push(AttachmentDisplay::SignedUnverified {
+ inner: a.clone(),
+ display: {
+ let mut v = vec![];
+ rec(&parts[0], context, coordinates, &mut v, active_jobs);
+ v
+ },
+ });
}
- } else {
- acc.push(AttachmentDisplay::SignedUnverified {
- inner: a.clone(),
- display: {
- let mut v = vec![];
- rec(&parts[0], context, coordinates, &mut v, active_jobs);
- v
- },
- });
}
}
MultipartType::Encrypted => {
for a in parts {
if a.content_type == "application/octet-stream" {
- if *mailbox_settings!(
- context[coordinates.0][&coordinates.1].pgp.auto_decrypt
- ) {
- let _verify = *mailbox_settings!(
- context[coordinates.0][&coordinates.1]
- .pgp
- .auto_verify_signatures
- );
- if let Some(bin) = mailbox_settings!(
- context[coordinates.0][&coordinates.1].pgp.gpg_binary
+ #[cfg(not(feature = "gpgme"))]
+ {
+ acc.push(AttachmentDisplay::EncryptedFailed {
+ inner: a.clone(),
+ error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."),
+ });
+ }
+ #[cfg(feature = "gpgme")]
+ {
+ if *mailbox_settings!(
+ context[coordinates.0][&coordinates.1].pgp.auto_decrypt
) {
- let decrypt_fut = crate::components::mail::pgp::decrypt(
- a.raw().to_vec(),
- Some(bin.to_string()),
- None,
- );
+ let decrypt_fut =
+ crate::components::mail::pgp::decrypt(a.raw().to_vec());
let handle =
- context.job_executor.spawn_blocking(decrypt_fut);
+ context.job_executor.spawn_specialized(decrypt_fut);
active_jobs.insert(handle.job_id);
acc.push(AttachmentDisplay::EncryptedPending {
inner: a.clone(),
handle,
});
} else {
- #[cfg(not(feature = "gpgme"))]
- {
- acc.push(AttachmentDisplay::EncryptedFailed {
- inner: a.clone(),
- error: MeliError::new("Cannot decrypt: define `gpg_binary` in configuration."),
- });
- }
- #[cfg(feature = "gpgme")]
- match melib::gpgme::Context::new().and_then(|mut ctx| {
- let cipher = ctx.new_data_mem(&a.raw())?;
- ctx.decrypt(cipher)
- }) {
- Ok(decrypt_fut) => {
- let handle = context
- .job_executor
- .spawn_specialized(decrypt_fut);
- active_jobs.insert(handle.job_id);
- acc.push(AttachmentDisplay::EncryptedPending {
- inner: a.clone(),
- handle,
- });
- }
- Err(error) => {
- acc.push(AttachmentDisplay::EncryptedFailed {
- inner: a.clone(),
- error,
- });
- }
- }
+ acc.push(AttachmentDisplay::EncryptedFailed {
+ inner: a.clone(),
+ error: MeliError::new("Undecrypted."),
+ });
}
}
}
@@ -1529,64 +1442,27 @@ impl Component for MailView {
} if *our_job_id == *job_id => {
caught = true;
self.initialised = false;
- match handle.as_mut() {
- Ok(handle) => match handle
- .chan
- .try_recv()
- .unwrap()
- .unwrap()
- {
- Ok(()) => {
- *d = AttachmentDisplay::SignedVerified {
- inner: std::mem::replace(
- inner,
- AttachmentBuilder::new(&[]).build(),
- ),
- display: std::mem::replace(display, vec![]),
- description: String::new(),
- };
- }
- Err(error) => {
- *d = AttachmentDisplay::SignedFailed {
- inner: std::mem::replace(
- inner,
- AttachmentBuilder::new(&[]).build(),
- ),
- display: std::mem::replace(display, vec![]),
- error,
- };
- }
- },
- Err(handle) => match handle
- .chan
- .try_recv()
- .unwrap()
- .unwrap()
- {
- Ok(verify_bytes) => {
- *d = AttachmentDisplay::SignedVerified {
- inner: std::mem::replace(
- inner,
- AttachmentBuilder::new(&[]).build(),
- ),
- display: std::mem::replace(display, vec![]),
- description: String::from_utf8_lossy(
- &verify_bytes,
- )
- .to_string(),
- };
- }
- Err(error) => {
- *d = AttachmentDisplay::SignedFailed {
- inner: std::mem::replace(
- inner,
- AttachmentBuilder::new(&[]).build(),
- ),
- display: std::mem::replace(display, vec![]),
- error,
- };
- }
- },
+ match handle.chan.try_recv().unwrap().unwrap() {
+ Ok(()) => {
+ *d = AttachmentDisplay::SignedVerified {
+ inner: std::mem::replace(
+ inner,
+ AttachmentBuilder::new(&[]).build(),
+ ),
+ display: std::mem::replace(display, vec![]),
+ description: String::new(),
+ };
+ }
+ Err(error) => {
+ *d = AttachmentDisplay::SignedFailed {
+ inner: std::mem::replace(
+ inner,
+ AttachmentBuilder::new(&[]).build(),
+ ),
+ display: std::mem::replace(display, vec![]),
+ error,
+ };
+ }
}
}
AttachmentDisplay::EncryptedPending { inner, handle }
diff --git a/src/components/utilities/widgets.rs b/src/components/utilities/widgets.rs
index 045e6d80..363bf37b 100644
--- a/src/components/utilities/widgets.rs
+++ b/src/components/utilities/widgets.rs
@@ -375,6 +375,10 @@ impl FormWidget {
self.fields.insert(value.0, value.1);
}
+ pub fn values(&self) -> &HashMap<String, Field> {
+ &self.fields
+ }
+
pub fn values_mut(&mut self) -> &mut HashMap<String, Field> {
&mut self.fields
}
diff --git a/src/conf/overrides.rs b/src/conf/overrides.rs
index de24f536..577e084e 100644
--- a/src/conf/overrides.rs
+++ b/src/conf/overrides.rs
@@ -285,45 +285,77 @@ impl Default for TagsSettingsOverride {
}
}
+#[cfg(feature = "gpgme")]
#[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<bool>,
#[doc = " auto decrypt encrypted e-mail"]
+ #[doc = " Default: true"]
#[serde(alias = "auto-decrypt")]
#[serde(default)]
pub auto_decrypt: Option<bool>,
- #[doc = " always sign sent messages"]
+ #[doc = " always sign sent e-mail"]
+ #[doc = " Default: false"]
#[serde(alias = "auto-sign")]
#[serde(default)]
pub auto_sign: Option<bool>,
+ #[doc = " Auto encrypt sent e-mail"]
+ #[doc = " Default: false"]
+ #[serde(alias = "auto-encrypt")]
+ #[serde(default)]
+ pub auto_encrypt: Option<bool>,
+ #[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 = " gpg binary name or file location to use"]
- #[serde(alias = "gpg-binary")]
+ #[doc = " Allow remote lookups"]
+ #[doc = " Default: None"]
+ #[serde(alias = "allow-remote-lookups")]
#[serde(default)]
- pub gpg_binary: Option<Option<String>>,
+ pub allow_remote_lookup: Option<ToggleFlag>,
+ #[doc = " Remote lookup mechanisms."]
+ #[doc = " Default: \"local,wkd\""]
+ #[serde(alias = "remote-lookup-mechanisms")]
+ #[serde(default)]
+ pub remote_lookup_mechanisms: Option<melib::gpgme::LocateKey>,
}
+#[cfg(feature = "gpgme")]
impl Default for PGPSettingsOverride {
fn default() -> Self {
PGPSettingsOverride {
auto_verify_signatures: None,
auto_decrypt: None,
auto_sign: None,
+ auto_encrypt: None,
sign_key: None,
decrypt_key: None,
encrypt_key: None,
- gpg_binary: None,
+ allow_remote_lookup: None,
+ remote_lookup_mechanisms: None,
}
}
}
+
+#[cfg(not(feature = "gpgme"))]
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[serde(deny_unknown_fields)]
+pub struct PGPSettingsOverride {}
+#[cfg(not(feature = "gpgme"))]
+impl Default for PGPSettingsOverride {
+ fn default() -> Self {
+ PGPSettingsOverride {}
+ }
+}
diff --git a/src/conf/pgp.rs b/src/conf/pgp.rs
index bdb8584c..7d9ce7e2 100644
--- a/src/conf/pgp.rs
+++ b/src/conf/pgp.rs
@@ -19,49 +19,86 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
+#[cfg(feature = "gpgme")]
use super::default_vals::*;
+#[cfg(feature = "gpgme")]
+use melib::conf::ToggleFlag;
+#[cfg(feature = "gpgme")]
/// Settings for digital signing and encryption
#[derive(Debug, Deserialize, Clone, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PGPSettings {
/// auto verify signed e-mail according to RFC3156
+ /// Default: true
#[serde(default = "true_val", alias = "auto-verify-signatures")]
pub auto_verify_signatures: bool,
/// auto decrypt encrypted e-mail
+ /// Default: true
#[serde(default = "true_val", alias = "auto-decrypt")]
pub auto_decrypt: bool,
- /// always sign sent messages
+ /// always sign sent e-mail
+ /// Default: false
#[serde(default = "false_val", alias = "auto-sign")]
pub auto_sign: bool,
+ /// Auto encrypt sent e-mail
+ /// Default: false
+ #[serde(default = "false_val", alias = "auto-encrypt")]
+ pub auto_encrypt: bool,
+
// https://tools.ietf.org/html/rfc4880#section-12.2
+ /// Default: None
#[serde(default = "none", alias = "sign-key")]
pub sign_key: Option<String>,
+ /// Default: None
#[serde(default = "none", alias = "decrypt-key")]
pub decrypt_key: Option<String>,
+ /// Default: None
#[serde(default = "none", alias = "encrypt-key")]
pub encrypt_key: Option<String>,
- /// gpg binary name or file location to use
- #[serde(default, alias = "gpg-binary")]
- pub gpg_binary: Option<String>,
+ /// Allow remote lookups
+ /// Default: None
+ #[serde(default = "internal_value_false", alias = "allow-remote-lookups")]
+ pub allow_remote_lookup: ToggleFlag,
+
+ /// Remote lookup mechanisms.
+ /// Default: "local,wkd"
+ #[serde(
+ default = "default_lookup_mechanism",
+ alias = "remote-lookup-mechanisms"
+ )]
+ pub remote_lookup_mechanisms: melib::gpgme::LocateKey,
+}
+
+#[cfg(feature = "gpgme")]
+fn default_lookup_mechanism() -> melib::gpgme::LocateKey {
+ melib::gpgme::LocateKey::LOCAL | melib::gpgme::LocateKey::WKD
}
+#[cfg(feature = "gpgme")]
impl Default for PGPSettings {
fn default() -> Self {
PGPSettings {
auto_verify_signatures: true,
auto_decrypt: true,
auto_sign: false,
+ auto_encrypt: false,
sign_key: None,
decrypt_key: None,
encrypt_key: None,
- gpg_binary: None,
+ allow_remote_lookup: internal_value_false::<ToggleFlag>(),
+ remote_lookup_mechanisms: default_lookup_mechanism(),
}
}
}
+
+#[cfg(not(feature = "gpgme"))]
+#[derive(Debug, Default, Deserialize, Clone, Serialize)]
+#[serde(deny_unknown_fields)]
+pub struct PGPSettings;